1 /*
2  *					GPAC Multimedia Framework
3  *
4  *			Authors: Jean Le Feuvre
5  *			Copyright (c) Telecom ParisTech 2005-2020
6  *					All rights reserved
7  *
8  *  This file is part of GPAC / common tools sub-project
9  *
10  *  GPAC is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU Lesser General Public License as published by
12  *  the Free Software Foundation; either version 2, or (at your option)
13  *  any later version.
14  *
15  *  GPAC is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU Lesser General Public License for more details.
19  *
20  *  You should have received a copy of the GNU Lesser General Public
21  *  License along with this library; see the file COPYING.  If not, write to
22  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
23  *
24  */
25 
26 #include <gpac/download.h>
27 #include <gpac/network.h>
28 #include <gpac/token.h>
29 #include <gpac/thread.h>
30 #include <gpac/list.h>
31 #include <gpac/base_coding.h>
32 #include <gpac/tools.h>
33 #include <gpac/cache.h>
34 #include <gpac/filters.h>
35 
36 #ifndef GPAC_DISABLE_CORE_TOOLS
37 
38 #ifdef GPAC_HAS_SSL
39 #include <openssl/ssl.h>
40 #include <openssl/x509.h>
41 #include <openssl/x509v3.h>
42 #include <openssl/err.h>
43 #include <openssl/rand.h>
44 
45 #endif
46 
47 #ifdef __USE_POSIX
48 #include <unistd.h>
49 #endif
50 
51 #define SIZE_IN_STREAM ( 2 << 29 )
52 
53 
54 #define SESSION_RETRY_COUNT	20
55 
56 #include <gpac/revision.h>
57 #define GF_DOWNLOAD_AGENT_NAME		"GPAC/"GPAC_VERSION "-rev" GPAC_GIT_REVISION
58 
59 //let's be agressive with socket buffer size
60 #define GF_DOWNLOAD_BUFFER_SIZE		131072
61 
62 
63 static void gf_dm_connect(GF_DownloadSession *sess);
64 
65 /*internal flags*/
66 enum
67 {
68 	GF_DOWNLOAD_SESSION_USE_SSL     = 1<<10,
69 	GF_DOWNLOAD_SESSION_THREAD_DEAD = 1<<11
70 };
71 
72 typedef struct __gf_user_credentials
73 {
74 	char site[1024];
75 	char username[50];
76 	char digest[1024];
77 	Bool valid;
78 } gf_user_credentials_struct;
79 
80 enum REQUEST_TYPE
81 {
82 	GET = 0,
83 	HEAD = 1,
84 	OTHER = 2
85 };
86 
87 /*!the structure used to store an HTTP header*/
88 typedef struct
89 {
90 	char *name;
91 	char *value;
92 } GF_HTTPHeader;
93 
94 
95 /**
96  * This structure handles partial downloads
97  */
98 typedef struct __partialDownloadStruct {
99 	char * url;
100 	u64 startOffset;
101 	u64 endOffset;
102 	char * filename;
103 } GF_PartialDownload ;
104 
105 typedef struct
106 {
107 	struct __gf_download_session *sess;
108 } GF_SessTask;
109 
110 struct __gf_download_session
111 {
112 	/*this is always 0 and helps differenciating downloads from other interfaces (interfaceType != 0)*/
113 	u32 reserved;
114 
115 	struct __gf_download_manager *dm;
116 	GF_Thread *th;
117 	GF_Mutex *mx;
118 	GF_SessTask *ftask;
119 
120 	Bool in_callback, destroy;
121 	u32 proxy_enabled;
122 	Bool allow_direct_reuse;
123 
124 	char *server_name;
125 	u16 port;
126 
127 	char *orig_url;
128 	char *orig_url_before_redirect;
129 	char *remote_path;
130 	gf_user_credentials_struct * creds;
131 	char cookie[GF_MAX_PATH];
132 	DownloadedCacheEntry cache_entry;
133 	Bool reused_cache_entry, from_cache_only;
134 
135 	//mime type, only used when the session is not cached.
136 	char *mime_type;
137 	GF_List *headers;
138 
139 	GF_Socket *sock;
140 	u32 num_retry;
141 	GF_NetIOStatus status;
142 
143 	u32 flags;
144 	u32 total_size, bytes_done, icy_metaint, icy_count, icy_bytes;
145 	u64 start_time;
146 	u64 chunk_run_time;
147 	u64 active_time, in_time, idle_time;
148 
149 	u32 bytes_per_sec;
150 	u64 start_time_utc;
151 	Bool last_chunk_found;
152 	Bool connection_close;
153 	Bool is_range_continuation;
154 	/*0: no cache reconfig before next GET request: 1: try to rematch the cache entry: 2: force to create a new cache entry (for byte-range cases)*/
155 	u32 needs_cache_reconfig;
156 	/* Range information if needed for the download (cf flag) */
157 	Bool needs_range;
158 	u64 range_start, range_end;
159 
160 	u32 connect_time, ssl_setup_time, reply_time, total_time_since_req, req_hdr_size, rsp_hdr_size;
161 
162 	/*0: GET
163 	  1: HEAD
164 	  2: all the rest
165 	*/
166 	enum REQUEST_TYPE http_read_type;
167 
168 	GF_Err last_error;
169 	char *init_data;
170 	u32 init_data_size;
171 	Bool server_only_understand_get;
172 	/* True if cache file must be stored on disk */
173 	Bool use_cache_file;
174 	Bool disable_cache;
175 	/*forces notification of data exchange to be sent regardless of threading mode*/
176 	Bool force_data_write_callback;
177 #ifdef GPAC_HAS_SSL
178 	SSL *ssl;
179 #endif
180 
181 	void (*do_requests)(struct __gf_download_session *);
182 
183 	/*callback for data reception - may not be NULL*/
184 	gf_dm_user_io user_proc;
185 	void *usr_cbk;
186 	Bool reassigned;
187 
188 	Bool chunked;
189 	u32 nb_left_in_chunk;
190 	u32 current_chunk_size;
191 	u64 current_chunk_start;
192 
193 	u64 request_start_time;
194 	/*private extension*/
195 	void *ext;
196 
197 	char *remaining_data;
198 	u32 remaining_data_size;
199 
200 	u32 head_timeout, request_timeout;
201 
202 	Bool local_cache_only;
203 	Bool server_mode;
204 	//0: not PUT/POST, 1: waiting for body to be completed, 2: body done
205 	u32 put_state;
206 };
207 
208 struct __gf_download_manager
209 {
210 	GF_Mutex *cache_mx;
211 	char *cache_directory;
212 
213 	Bool (*get_user_password)(void *usr_cbk, const char *site_url, char *usr_name, char *password);
214 	void *usr_cbk;
215 
216 	GF_List *sessions;
217 	Bool disable_cache, simulate_no_connection, allow_offline_cache, clean_cache;
218 	u32 limit_data_rate, read_buf_size;
219 	u64 max_cache_size;
220 	Bool allow_broken_certificate;
221 
222 	GF_List *skip_proxy_servers;
223 	GF_List *credentials;
224 	GF_List *cache_entries;
225 	/* FIXME : should be placed in DownloadedCacheEntry maybe... */
226 	GF_List *partial_downloads;
227 #ifdef GPAC_HAS_SSL
228 	SSL_CTX *ssl_ctx;
229 #endif
230 
231 	GF_FilterSession *filter_session;
232 
233 
234 	Bool (*local_cache_url_provider_cbk)(void *udta, char *url, Bool cache_destroy);
235 	void *lc_udta;
236 };
237 
238 #ifdef GPAC_HAS_SSL
239 
init_prng(void)240 static void init_prng (void)
241 {
242 	char namebuf[256];
243 	const char *random_file;
244 
245 	if (RAND_status ()) return;
246 
247 	namebuf[0] = '\0';
248 	random_file = RAND_file_name (namebuf, sizeof (namebuf));
249 
250 	if (random_file && *random_file)
251 		RAND_load_file(random_file, 16384);
252 
253 	if (RAND_status ()) return;
254 }
255 
256 #endif
257 
258 /*
259  * Private methods of cache
260  */
261 
262 /**
263  * \brief Write data to cache
264  * Writes data to the cache. A call to gf_cache_open_write_cache should have been issued before calling this function.
265 \param entry The entry to use
266 \param sess The download session
267 \param data data to write
268 \param size number of elements to write
269 \param GF_OK is everything went fine, GF_BAD_PARAM if cache has not been opened, GF_IO_ERR if a failure occurs
270  */
271 GF_Err gf_cache_write_to_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, const char * data, const u32 size);
272 
273 /**
274  * \brief Close the write file pointer of cache
275  * This function also flushes all buffers, so cache will always be consistent after
276 \param entry The entry to use
277 \param sess The download session
278 \param success 1 if cache write is success, false otherwise
279 \param GF_OK is everything went fine, GF_BAD_PARAM if entry is NULL, GF_IO_ERR if a failure occurs
280  */
281 GF_Err gf_cache_close_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, Bool success);
282 
283 /**
284  * \brief Open the write file pointer of cache
285  * This function prepares calls for gf_cache_write_to_cache
286 \param entry The entry to use
287 \param sess The download session
288 \param GF_OK is everything went fine, GF_BAD_PARAM if entry is NULL, GF_IO_ERR if a failure occurs
289  */
290 GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess );
291 
292 /*modify end range when chaining byte-range requests*/
293 void gf_cache_set_end_range(DownloadedCacheEntry entry, u64 range_end);
294 
295 /*returns 1 if cache is currently open for write*/
296 Bool gf_cache_is_in_progress(const DownloadedCacheEntry entry);
297 
298 /**
299  * Find a User's credentials for a given site
300  */
gf_find_user_credentials_for_site(GF_DownloadManager * dm,const char * server_name)301 static gf_user_credentials_struct* gf_find_user_credentials_for_site(GF_DownloadManager *dm, const char *server_name) {
302 	u32 count, i;
303 	if (!dm || !dm->credentials || !server_name || !strlen(server_name))
304 		return NULL;
305 	count = gf_list_count( dm->credentials);
306 	for (i = 0 ; i < count; i++) {
307 		gf_user_credentials_struct * cred = (gf_user_credentials_struct*)gf_list_get(dm->credentials, i );
308 		assert( cred );
309 		if (!strcmp(cred->site, server_name))
310 			return cred;
311 	}
312 	return NULL;
313 }
314 
315 /**
316  * \brief Saves the digest for authentication of password and username
317 \param dm The download manager
318 \param creds The credentials to fill
319 \param GF_OK if info has been filled, GF_BAD_PARAM if creds == NULL or dm == NULL, GF_AUTHENTICATION_FAILURE if user did not filled the info.
320  */
gf_user_credentials_save_digest(GF_DownloadManager * dm,gf_user_credentials_struct * creds,const char * password)321 static GF_Err gf_user_credentials_save_digest( GF_DownloadManager * dm, gf_user_credentials_struct * creds, const char * password) {
322 	int size;
323 	char pass_buf[1024], range_buf[1024];
324 	if (!dm || !creds || !password)
325 		return GF_BAD_PARAM;
326 	sprintf(pass_buf, "%s:%s", creds->username, password);
327 	size = gf_base64_encode(pass_buf, (u32) strlen(pass_buf), range_buf, 1024);
328 	range_buf[size] = 0;
329 	strcpy(creds->digest, range_buf);
330 	creds->valid = GF_TRUE;
331 	return GF_OK;
332 }
333 
334 /**
335  * \brief Asks the user for credentials for given site
336 \param dm The download manager
337 \param creds The credentials to fill
338 \param GF_OK if info has been filled, GF_BAD_PARAM if creds == NULL or dm == NULL, GF_AUTHENTICATION_FAILURE if user did not filled the info.
339  */
gf_user_credentials_ask_password(GF_DownloadManager * dm,gf_user_credentials_struct * creds)340 static GF_Err gf_user_credentials_ask_password( GF_DownloadManager * dm, gf_user_credentials_struct * creds)
341 {
342 	char szPASS[50];
343 	if (!dm || !creds)
344 		return GF_BAD_PARAM;
345 	memset(szPASS, 0, 50);
346 	if (!dm->get_user_password || !dm->get_user_password(dm->usr_cbk, creds->site, creds->username, szPASS)) {
347 		return GF_AUTHENTICATION_FAILURE;
348 	}
349 	return gf_user_credentials_save_digest(dm, creds, szPASS);
350 }
351 
gf_user_credentials_register(GF_DownloadManager * dm,const char * server_name,const char * username,const char * password,Bool valid)352 static gf_user_credentials_struct * gf_user_credentials_register(GF_DownloadManager * dm, const char * server_name, const char * username, const char * password, Bool valid)
353 {
354 	gf_user_credentials_struct * creds;
355 	if (!dm)
356 		return NULL;
357 	assert( server_name );
358 	creds = gf_find_user_credentials_for_site(dm, server_name);
359 	/* If none found, we create one */
360 	if (!creds) {
361 		creds = (gf_user_credentials_struct*)gf_malloc(sizeof( gf_user_credentials_struct));
362 		if (!creds)
363 			return NULL;
364 		gf_list_insert(dm->credentials, creds, 0);
365 	}
366 	creds->valid = valid;
367 	strncpy(creds->username, username ? username : "", 49);
368 	creds->username[49] = 0;
369 	strcpy(creds->site, server_name);
370 	if (username && password && valid)
371 		gf_user_credentials_save_digest(dm, creds, password);
372 	else {
373 		if (GF_OK != gf_user_credentials_ask_password(dm, creds)) {
374 			GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP,
375 			       ("[HTTP] Failed to get password information.\n"));
376 			gf_list_rem( dm->credentials, 0);
377 			gf_free( creds );
378 			creds = NULL;
379 		}
380 	}
381 	return creds;
382 }
383 
384 #ifdef GPAC_HAS_SSL
385 
386 static Bool _ssl_is_initialized = GF_FALSE;
387 
388 /*!
389  * initialize the SSL library once for all download managers
390 \param GF_FALSE if everyhing is OK, GF_TRUE otherwise
391  */
gf_ssl_init_lib()392 Bool gf_ssl_init_lib() {
393 	if (_ssl_is_initialized)
394 		return GF_FALSE;
395 	GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTPS] Initializing SSL library...\n"));
396 	init_prng();
397 	if (RAND_status() != 1) {
398 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPS] Error while initializing Random Number generator, failed to init SSL !\n"));
399 		return GF_TRUE;
400 	}
401 
402 	/* per https://www.openssl.org/docs/man1.1.0/ssl/OPENSSL_init_ssl.html
403 	** As of version 1.1.0 OpenSSL will automatically allocate all resources that it needs so no explicit initialisation is required.
404 	** Similarly it will also automatically deinitialise as required.
405 	*/
406 #if OPENSSL_VERSION_NUMBER < 0x10100000L
407 	SSL_library_init();
408 	SSL_load_error_strings();
409 	SSLeay_add_all_algorithms();
410 	SSLeay_add_ssl_algorithms();
411 #endif
412 
413 	_ssl_is_initialized = GF_TRUE;
414 	GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTPS] Initalization of SSL library complete.\n"));
415 	return GF_FALSE;
416 }
417 
ssl_init(GF_DownloadManager * dm,u32 mode)418 static int ssl_init(GF_DownloadManager *dm, u32 mode)
419 {
420 #if OPENSSL_VERSION_NUMBER > 0x00909000
421 	const
422 #endif
423 	SSL_METHOD *meth;
424 
425 	if (!dm) return 0;
426 	gf_mx_p(dm->cache_mx);
427 	/* The SSL has already been initialized. */
428 	if (dm->ssl_ctx) {
429 		gf_mx_v(dm->cache_mx);
430 		return 1;
431 	}
432 	/* Init the PRNG.  If that fails, bail out.  */
433 	if (gf_ssl_init_lib()) {
434 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTPS] Failed to properly initialize SSL library\n"));
435 		goto error;
436 	}
437 
438 	switch (mode) {
439 #if OPENSSL_VERSION_NUMBER < 0x10100000L
440 	case 0:
441 		meth = SSLv23_client_method();
442 		break;
443 #if 0 /*SSL v2 is no longer supported in OpenSSL 1.0*/
444 	case 1:
445 		meth = SSLv2_client_method();
446 		break;
447 #endif
448 	case 2:
449 		meth = SSLv3_client_method();
450 		break;
451 	case 3:
452 		meth = TLSv1_client_method();
453 		break;
454 #else /* for openssl 1.1+ this is the preferred method */
455 	case 0:
456 		meth = TLS_client_method();
457 		break;
458 #endif
459 	default:
460 		goto error;
461 	}
462 
463 	dm->ssl_ctx = SSL_CTX_new(meth);
464 	if (!dm->ssl_ctx) goto error;
465 	SSL_CTX_set_default_verify_paths(dm->ssl_ctx);
466 	SSL_CTX_load_verify_locations (dm->ssl_ctx, NULL, NULL);
467 	/* SSL_VERIFY_NONE instructs OpenSSL not to abort SSL_connect if the
468 	 certificate is invalid.  We verify the certificate separately in
469 	 ssl_check_certificate, which provides much better diagnostics
470 	 than examining the error stack after a failed SSL_connect.  */
471 	SSL_CTX_set_verify(dm->ssl_ctx, SSL_VERIFY_NONE, NULL);
472 
473 	/* Since fd_write unconditionally assumes partial writes (and handles them correctly),
474 	allow them in OpenSSL.  */
475 	SSL_CTX_set_mode(dm->ssl_ctx, SSL_MODE_ENABLE_PARTIAL_WRITE);
476 	gf_mx_v(dm->cache_mx);
477 	return 1;
478 error:
479 	if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx);
480 	dm->ssl_ctx = NULL;
481 	gf_mx_v(dm->cache_mx);
482 	return 0;
483 }
484 
485 
gf_ssl_server_context_new(const char * cert,const char * key)486 void *gf_ssl_server_context_new(const char *cert, const char *key)
487 {
488     const SSL_METHOD *method;
489     SSL_CTX *ctx;
490 
491     method = SSLv23_server_method();
492 
493     ctx = SSL_CTX_new(method);
494     if (!ctx) {
495 		GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("Unable to create SSL context\n"));
496 		ERR_print_errors_fp(stderr);
497 		return NULL;
498     }
499     SSL_CTX_set_ecdh_auto(ctx, 1);
500 	if (SSL_CTX_use_certificate_file(ctx, cert, SSL_FILETYPE_PEM) <= 0) {
501 		ERR_print_errors_fp(stderr);
502 		SSL_CTX_free(ctx);
503 		return NULL;
504 	}
505 	if (SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM) <= 0 ) {
506 		ERR_print_errors_fp(stderr);
507 		SSL_CTX_free(ctx);
508 		return NULL;
509 	}
510     return ctx;
511 }
512 
gf_ssl_server_context_del(void * ssl_ctx)513 void gf_ssl_server_context_del(void *ssl_ctx)
514 {
515 	SSL_CTX_free(ssl_ctx);
516 }
gf_ssl_new(void * ssl_ctx,GF_Socket * client_sock,GF_Err * e)517 void *gf_ssl_new(void *ssl_ctx, GF_Socket *client_sock, GF_Err *e)
518 {
519 	SSL *ssl;
520 	ssl = SSL_new(ssl_ctx);
521 	if (!ssl) {
522 		*e = GF_IO_ERR;
523 		return NULL;
524 	}
525 	SSL_set_fd(ssl, gf_sk_get_handle(client_sock) );
526 	if (SSL_accept(ssl) <= 0) {
527 		ERR_print_errors_fp(stderr);
528 		SSL_shutdown(ssl);
529         SSL_free(ssl);
530 		*e = GF_AUTHENTICATION_FAILURE;
531 		return NULL;
532 	}
533 	*e = GF_OK;
534 	return ssl;
535 }
gf_ssl_del(void * ssl)536 void gf_ssl_del(void *ssl)
537 {
538 	SSL_shutdown(ssl);
539 	SSL_free(ssl);
540 }
541 
gf_ssl_write(void * ssl_ctx,const u8 * buffer,u32 size)542 GF_Err gf_ssl_write(void *ssl_ctx, const u8 *buffer, u32 size)
543 {
544 	u32 idx=0;
545 	s32 nb_tls_blocks = size/16000;
546 	while (nb_tls_blocks>=0) {
547 		u32 len, to_write = 16000;
548 		if (nb_tls_blocks==0)
549 			to_write = size - idx*16000;
550 
551 		len = SSL_write(ssl_ctx, buffer + idx*16000, to_write);
552 		nb_tls_blocks--;
553 		idx++;
554 
555 		if (len != to_write) {
556 			return GF_IP_NETWORK_FAILURE;
557 		}
558 	}
559 	return GF_OK;
560 }
561 
562 
563 #endif /* GPAC_HAS_SSL */
564 
565 
gf_dm_is_local(GF_DownloadManager * dm,const char * url)566 static Bool gf_dm_is_local(GF_DownloadManager *dm, const char *url)
567 {
568 	if (!strnicmp(url, "file://", 7)) return GF_TRUE;
569 	if (!strstr(url, "://")) return GF_TRUE;
570 	return GF_FALSE;
571 }
572 
gf_dm_can_handle_url(GF_DownloadManager * dm,const char * url)573 static Bool gf_dm_can_handle_url(GF_DownloadManager *dm, const char *url)
574 {
575 	if (!strnicmp(url, "http://", 7)) return GF_TRUE;
576 #ifdef GPAC_HAS_SSL
577 	if (!strnicmp(url, "https://", 8)) return GF_TRUE;
578 #endif
579 	return GF_FALSE;
580 }
581 
582 /*!
583  * Finds an existing entry in the cache for a given URL
584 \param sess The session configured with the URL
585 \param NULL if none found, the DownloadedCacheEntry otherwise
586  */
gf_dm_find_cached_entry_by_url(GF_DownloadSession * sess)587 DownloadedCacheEntry gf_dm_find_cached_entry_by_url(GF_DownloadSession * sess)
588 {
589 	u32 i, count;
590 	assert( sess && sess->dm && sess->dm->cache_entries );
591 	gf_mx_p( sess->dm->cache_mx );
592 	count = gf_list_count(sess->dm->cache_entries);
593 	for (i = 0 ; i < count; i++) {
594 		const char * url;
595 		DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
596 		assert(e);
597 		url = gf_cache_get_url(e);
598 		assert( url );
599 		if (strcmp(url, sess->orig_url)) continue;
600 		if (sess->needs_cache_reconfig==2)
601 			continue;
602 
603 		if (! sess->is_range_continuation) {
604 			if (sess->range_start != gf_cache_get_start_range(e)) continue;
605 			if (sess->range_end != gf_cache_get_end_range(e)) continue;
606 		}
607 		/*OK that's ours*/
608 		gf_mx_v( sess->dm->cache_mx );
609 		return e;
610 	}
611 	gf_mx_v( sess->dm->cache_mx );
612 	return NULL;
613 }
614 
615 /**
616  * Creates a new cache entry
617 \param dm The download manager to create this entry
618 \param cache_directory The path to the directory containing cache files
619 \param url The full URL
620 \param start_range the start of the byte range request
621 \param end_range the end of the byte range request
622 \param mem_storage Boolean indicating if the cache data should be stored in memory
623 \param The DownloadedCacheEntry
624  */
625 DownloadedCacheEntry gf_cache_create_entry( GF_DownloadManager * dm, const char * cache_directory, const char * url, u64 start_range, u64 end_range, Bool mem_storage);
626 
627 /*!
628  * Removes a session for a DownloadedCacheEntry
629 \param entry The entry
630 \param sess The session to remove
631 \param the number of sessions left in the cached entry, -1 if one of the parameters is wrong
632  */
633 s32 gf_cache_remove_session_from_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess);
634 
635 Bool gf_cache_set_mime(const DownloadedCacheEntry entry, const char *mime);
636 Bool gf_cache_set_range(const DownloadedCacheEntry entry, u64 size, u64 start_range, u64 end_range);
637 Bool gf_cache_set_content(const DownloadedCacheEntry entry, char *data, u32 size, Bool copy);
638 Bool gf_cache_set_headers(const DownloadedCacheEntry entry, const char *headers);
639 Bool gf_cache_set_downtime(const DownloadedCacheEntry entry, u32 download_time_ms);
640 
641 
642 /**
643  * Removes a cache entry from cache and performs a cleanup if possible.
644  * If the cache entry is marked for deletion and has no sessions associated with it, it will be
645  * removed (so some modules using a streaming like cache will still work).
646  */
gf_dm_remove_cache_entry_from_session(GF_DownloadSession * sess)647 static void gf_dm_remove_cache_entry_from_session(GF_DownloadSession * sess) {
648 	if (sess && sess->cache_entry) {
649 		gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess);
650 		if (sess->dm
651 		        /*JLF - not sure what the rationale of this test is, and it prevents cleanup of cache entry
652 		        which then results to crash when restarting the session (entry->writeFilePtr i snot set back to NULL)*/
653 		        && gf_cache_entry_is_delete_files_when_deleted(sess->cache_entry)
654 
655 		        && (0 == gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)))
656 		{
657 			u32 i, count;
658 			gf_mx_p( sess->dm->cache_mx );
659 			count = gf_list_count( sess->dm->cache_entries );
660 			for (i = 0; i < count; i++) {
661 				DownloadedCacheEntry ex = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
662 				if (ex == sess->cache_entry) {
663 					gf_list_rem(sess->dm->cache_entries, i);
664 					gf_cache_delete_entry( sess->cache_entry );
665 					sess->cache_entry = NULL;
666 					break;
667 				}
668 			}
669 			gf_mx_v( sess->dm->cache_mx );
670 		}
671 	}
672 }
673 
674 /*!
675  * Adds a session to a DownloadedCacheEntry.
676  * implemented in cache.c
677 \param entry The entry
678 \param sess The session to add
679 \param the number of sessions in the cached entry, -1 if one of the parameters is wrong
680  */
681 s32 gf_cache_add_session_to_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess);
682 
683 static void gf_dm_sess_notify_state(GF_DownloadSession *sess, GF_NetIOStatus dnload_status, GF_Err error);
684 
gf_dm_configure_cache(GF_DownloadSession * sess)685 static void gf_dm_configure_cache(GF_DownloadSession *sess)
686 {
687 	DownloadedCacheEntry entry;
688 	GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[Downloader] gf_dm_configure_cache(%p), cached=%s\n", sess, (sess->flags & GF_NETIO_SESSION_NOT_CACHED) ? "no" : "yes" ));
689 	gf_dm_remove_cache_entry_from_session(sess);
690 	if (sess->flags & GF_NETIO_SESSION_NOT_CACHED) {
691 		sess->reused_cache_entry = GF_FALSE;
692 		if (sess->cache_entry)
693 			gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
694 	} else {
695 		Bool found = GF_FALSE;
696 		u32 i, count;
697 		entry = gf_dm_find_cached_entry_by_url(sess);
698 		if (!entry) {
699 			if (sess->local_cache_only) {
700 				sess->cache_entry = NULL;
701 				sess->last_error = GF_URL_ERROR;
702 				return;
703 			}
704 			if (sess->cache_entry) {
705 				/* We found the existing session */
706 				gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry);
707 				if (0 == gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)) {
708 					gf_mx_p( sess->dm->cache_mx );
709 					/* No session attached anymore... we can delete it */
710 					gf_list_del_item(sess->dm->cache_entries, sess->cache_entry);
711 					gf_mx_v( sess->dm->cache_mx );
712 					gf_cache_delete_entry(sess->cache_entry);
713 				}
714 				sess->cache_entry = NULL;
715 			}
716 			entry = gf_cache_create_entry(sess->dm, sess->dm->cache_directory, sess->orig_url, sess->range_start, sess->range_end, (sess->flags&GF_NETIO_SESSION_MEMORY_CACHE) ? GF_TRUE : GF_FALSE);
717 			gf_mx_p( sess->dm->cache_mx );
718 			gf_list_add(sess->dm->cache_entries, entry);
719 			gf_mx_v( sess->dm->cache_mx );
720 			sess->is_range_continuation = GF_FALSE;
721 		}
722 		assert( entry );
723 		sess->cache_entry = entry;
724 		sess->reused_cache_entry = 	gf_cache_is_in_progress(entry);
725 		count = gf_list_count(sess->dm->sessions);
726 		for (i=0; i<count; i++) {
727 			GF_DownloadSession *a_sess = (GF_DownloadSession*)gf_list_get(sess->dm->sessions, i);
728 			assert(a_sess);
729 			if (a_sess==sess) continue;
730 			if (a_sess->cache_entry==entry) {
731 				found = GF_TRUE;
732 				break;
733 			}
734 		}
735 		if (!found) {
736 			sess->reused_cache_entry = GF_FALSE;
737 			gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
738 		}
739 		gf_cache_add_session_to_cache_entry(sess->cache_entry, sess);
740 		if (sess->needs_range)
741 			gf_cache_set_range(sess->cache_entry, 0, sess->range_start, sess->range_end);
742 		GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[CACHE] Cache setup to %p %s\n", sess, gf_cache_get_cache_filename(sess->cache_entry)));
743 
744 
745 		if ( (sess->allow_direct_reuse || sess->dm->allow_offline_cache) && !gf_cache_check_if_cache_file_is_corrupted(sess->cache_entry)
746 		) {
747 			sess->from_cache_only = GF_TRUE;
748 			sess->connect_time = 0;
749 			sess->status = GF_NETIO_CONNECTED;
750 			GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] using existing cache entry\n"));
751 			gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
752 		}
753 	}
754 }
755 
gf_dm_delete_cached_file_entry(const GF_DownloadManager * dm,const char * url)756 void gf_dm_delete_cached_file_entry(const GF_DownloadManager * dm,  const char * url)
757 {
758 	GF_Err e;
759 	u32 count, i;
760 	char * realURL;
761 	GF_URL_Info info;
762 	if (!url || !dm)
763 		return;
764 	gf_mx_p( dm->cache_mx );
765 	gf_dm_url_info_init(&info);
766 	e = gf_dm_get_url_info(url, &info, NULL);
767 	if (e != GF_OK) {
768 		gf_mx_p( dm->cache_mx );
769 		gf_dm_url_info_del(&info);
770 		return;
771 	}
772 	realURL = gf_strdup(info.canonicalRepresentation);
773 	gf_dm_url_info_del(&info);
774 	assert( realURL );
775 	count = gf_list_count(dm->cache_entries);
776 	for (i = 0 ; i < count; i++) {
777 		const char * e_url;
778 		DownloadedCacheEntry cache_ent = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
779 		assert(cache_ent);
780 		e_url = gf_cache_get_url(cache_ent);
781 		assert( e_url );
782 		if (!strcmp(e_url, realURL)) {
783 			/* We found the existing session */
784 			gf_cache_entry_set_delete_files_when_deleted(cache_ent);
785 			if (0 == gf_cache_get_sessions_count_for_cache_entry( cache_ent )) {
786 				/* No session attached anymore... we can delete it */
787 				gf_list_rem(dm->cache_entries, i);
788 				gf_cache_delete_entry(cache_ent);
789 			}
790 			/* If deleted or not, we don't search further */
791 			gf_mx_v( dm->cache_mx );
792 			gf_free(realURL);
793 			return;
794 		}
795 	}
796 	/* If we are heren it means we did not found this URL in cache */
797 	gf_mx_v( dm->cache_mx );
798 	gf_free(realURL);
799 	GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[CACHE] Cannot find URL %s, cache file won't be deleted.\n", url));
800 }
801 
802 GF_EXPORT
gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess,const char * url)803 void gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess,  const char * url) {
804 	if (sess && sess->dm && url) {
805 		GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[CACHE] Requesting deletion for %s\n", url));
806 		gf_dm_delete_cached_file_entry(sess->dm, url);
807 		if (sess->local_cache_only && sess->dm->local_cache_url_provider_cbk)
808 			sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *) url, GF_TRUE);
809 	}
810 }
811 
812 
gf_dm_clear_headers(GF_DownloadSession * sess)813 static void gf_dm_clear_headers(GF_DownloadSession *sess)
814 {
815 	while (gf_list_count(sess->headers)) {
816 		GF_HTTPHeader *hdr = (GF_HTTPHeader*)gf_list_last(sess->headers);
817 		gf_list_rem_last(sess->headers);
818 		gf_free(hdr->name);
819 		gf_free(hdr->value);
820 		gf_free(hdr);
821 	}
822 	if (sess->mime_type) {
823 		gf_free(sess->mime_type);
824 		sess->mime_type = NULL;
825 	}
826 }
827 
828 
gf_dm_disconnect(GF_DownloadSession * sess,Bool force_close)829 static void gf_dm_disconnect(GF_DownloadSession *sess, Bool force_close)
830 {
831 	assert( sess );
832 	if (sess->connection_close) force_close = GF_TRUE;
833 	sess->connection_close = GF_FALSE;
834 	sess->remaining_data_size = 0;
835 
836 	if (sess->status >= GF_NETIO_DISCONNECTED) {
837 		if (force_close && sess->use_cache_file && sess->cache_entry) {
838 			gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
839 		}
840 		return;
841 	}
842 	GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] gf_dm_disconnect(%p)\n", sess ));
843 
844 	gf_mx_p(sess->mx);
845 
846 	if (!sess->server_mode) {
847 		if (force_close || !(sess->flags & GF_NETIO_SESSION_PERSISTENT)) {
848 #ifdef GPAC_HAS_SSL
849 			if (sess->ssl) {
850 				SSL_shutdown(sess->ssl);
851 				SSL_free(sess->ssl);
852 				sess->ssl = NULL;
853 			}
854 #endif
855 			if (sess->sock) {
856 				GF_Socket * sx = sess->sock;
857 				sess->sock = NULL;
858 				gf_sk_del(sx);
859 			}
860 		}
861 		if (force_close && sess->use_cache_file) {
862 			gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
863 		}
864 	}
865 	sess->status = GF_NETIO_DISCONNECTED;
866 	if (sess->num_retry) sess->num_retry--;
867 
868 	gf_mx_v(sess->mx);
869 }
870 
871 GF_EXPORT
gf_dm_sess_del(GF_DownloadSession * sess)872 void gf_dm_sess_del(GF_DownloadSession *sess)
873 {
874 	if (!sess)
875 		return;
876 
877 	GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[Downloader] %s session (%p) URL %s\n", sess->server_mode ? "Detach" : "Destroy", sess, sess->orig_url));
878 	/*self-destruction, let the download manager destroy us*/
879 	if ((sess->th || sess->ftask) && sess->in_callback) {
880 		sess->destroy = GF_TRUE;
881 		return;
882 	}
883 	gf_dm_disconnect(sess, GF_TRUE);
884 	gf_dm_clear_headers(sess);
885 
886 	/*if threaded wait for thread exit*/
887 	if (sess->th) {
888 		while (!(sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD))
889 			gf_sleep(1);
890 		gf_th_stop(sess->th);
891 		gf_th_del(sess->th);
892 		sess->th = NULL;
893 	}
894 
895 	if (sess->dm) {
896 		gf_mx_p(sess->dm->cache_mx);
897 		gf_list_del_item(sess->dm->sessions, sess);
898 		gf_mx_v(sess->dm->cache_mx);
899 	}
900 
901 	gf_dm_remove_cache_entry_from_session(sess);
902 	sess->cache_entry = NULL;
903 	if (sess->orig_url) gf_free(sess->orig_url);
904 	if (sess->orig_url_before_redirect) gf_free(sess->orig_url_before_redirect);
905 	if (sess->server_name) gf_free(sess->server_name);
906 	sess->server_name = NULL;
907 	if (sess->remote_path) gf_free(sess->remote_path);
908 	/* Credentials are stored into the sess->dm */
909 	if (sess->creds) sess->creds = NULL;
910 	if (sess->init_data) gf_free(sess->init_data);
911 	if (sess->remaining_data) gf_free(sess->remaining_data);
912 
913 	sess->orig_url = sess->server_name = sess->remote_path;
914 	sess->creds = NULL;
915 	if (sess->sock && !sess->server_mode)
916 		gf_sk_del(sess->sock);
917 	gf_list_del(sess->headers);
918 	gf_mx_del(sess->mx);
919 	if (sess->ftask) {
920 		sess->ftask->sess = NULL;
921 		sess->ftask = NULL;
922 	}
923 	gf_free(sess);
924 }
925 
926 void http_do_requests(GF_DownloadSession *sess);
927 
gf_dm_sess_notify_state(GF_DownloadSession * sess,GF_NetIOStatus dnload_status,GF_Err error)928 static void gf_dm_sess_notify_state(GF_DownloadSession *sess, GF_NetIOStatus dnload_status, GF_Err error)
929 {
930 	if (sess->user_proc) {
931 		GF_NETIO_Parameter par;
932 		sess->in_callback = GF_TRUE;
933 		memset(&par, 0, sizeof(GF_NETIO_Parameter));
934 		par.msg_type = dnload_status;
935 		par.error = error;
936 		par.sess = sess;
937 		par.reply = 200;
938 		sess->user_proc(sess->usr_cbk, &par);
939 		sess->in_callback = GF_FALSE;
940 	}
941 }
942 
gf_dm_sess_user_io(GF_DownloadSession * sess,GF_NETIO_Parameter * par)943 static void gf_dm_sess_user_io(GF_DownloadSession *sess, GF_NETIO_Parameter *par)
944 {
945 	if (sess->user_proc) {
946 		sess->in_callback = GF_TRUE;
947 		par->sess = sess;
948 		sess->user_proc(sess->usr_cbk, par);
949 		sess->in_callback = GF_FALSE;
950 	}
951 }
952 
953 #if 0 //unused
954 /*!
955 \brief is download manager thread dead?
956  *
957  *Indicates whether the thread has ended
958 \param sess the download session
959  */
960 Bool gf_dm_is_thread_dead(GF_DownloadSession *sess)
961 {
962 	if (!sess) return GF_TRUE;
963 	return (sess->flags & GF_DOWNLOAD_SESSION_THREAD_DEAD) ? GF_TRUE : GF_FALSE;
964 }
965 #endif
966 
967 GF_EXPORT
gf_dm_sess_last_error(GF_DownloadSession * sess)968 GF_Err gf_dm_sess_last_error(GF_DownloadSession *sess)
969 {
970 	if (!sess) return GF_BAD_PARAM;
971 	return sess->last_error;
972 }
973 
974 GF_EXPORT
gf_dm_url_info_init(GF_URL_Info * info)975 void gf_dm_url_info_init(GF_URL_Info * info)
976 {
977 	memset(info, 0, sizeof(GF_URL_Info));
978 }
979 
980 GF_EXPORT
gf_dm_url_info_del(GF_URL_Info * info)981 void gf_dm_url_info_del(GF_URL_Info * info) {
982 	if (!info)
983 		return;
984 	if (info->canonicalRepresentation)
985 		gf_free(info->canonicalRepresentation);
986 	if (info->password)
987 		gf_free(info->password);
988 	if (info->userName)
989 		gf_free(info->userName);
990 	if (info->remotePath)
991 		gf_free(info->remotePath);
992 	if (info->server_name)
993 		gf_free(info->server_name);
994 	gf_dm_url_info_init(info);
995 }
996 
997 /**
998 \param url The url to parse for protocol
999 \param info The info to fill
1000 \param Returns the offset in url of the protocol found -1 if not found
1001  */
gf_dm_parse_protocol(const char * url,GF_URL_Info * info)1002 static s32 gf_dm_parse_protocol(const char * url, GF_URL_Info * info) {
1003 	assert(info);
1004 	assert(url);
1005 	if (!strnicmp(url, "http://", 7)) {
1006 		info->port = 80;
1007 		info->protocol = "http://";
1008 		return 7;
1009 	}
1010 	else if (!strnicmp(url, "https://", 8)) {
1011 		info->port = 443;
1012 #ifndef GPAC_HAS_SSL
1013 		return -1;
1014 #endif
1015 		info->protocol = "https://";
1016 		return 8;
1017 	}
1018 	else if (!strnicmp(url, "ftp://", 6)) {
1019 		info->port = 21;
1020 		info->protocol = "ftp://";
1021 		return -1;
1022 	}
1023 	return -1;
1024 }
1025 
1026 GF_EXPORT
gf_dm_get_url_info(const char * url,GF_URL_Info * info,const char * baseURL)1027 GF_Err gf_dm_get_url_info(const char * url, GF_URL_Info * info, const char * baseURL) {
1028 	char *tmp, *tmp_url, *current_pos, *urlConcatenateWithBaseURL, *ipv6;
1029 	char * copyOfUrl;
1030 	s32 proto_offset;
1031 	gf_dm_url_info_del(info);
1032 	urlConcatenateWithBaseURL = NULL;
1033 	proto_offset = gf_dm_parse_protocol(url, info);
1034 	if (proto_offset > 0) {
1035 		url += proto_offset;
1036 	} else {
1037 		/*relative URL*/
1038 		if (!strstr(url, "://")) {
1039 			u32 i;
1040 			info->protocol = "file://";
1041 			if (baseURL) {
1042 				urlConcatenateWithBaseURL = gf_url_concatenate(baseURL, url);
1043 				/*relative file path*/
1044 				if (!strstr(baseURL, "://")) {
1045 					info->canonicalRepresentation = urlConcatenateWithBaseURL;
1046 					return GF_OK;
1047 				}
1048 				proto_offset = gf_dm_parse_protocol(urlConcatenateWithBaseURL, info);
1049 			} else {
1050 				proto_offset = -1;
1051 			}
1052 
1053 			if (proto_offset < 0) {
1054 				tmp = urlConcatenateWithBaseURL;
1055 				assert( ! info->remotePath );
1056 				info->remotePath = gf_url_percent_encode(tmp);
1057 				gf_free( urlConcatenateWithBaseURL );
1058 				urlConcatenateWithBaseURL = NULL;
1059 
1060 				if (!info->remotePath) {
1061 					GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url));
1062 					gf_dm_url_info_del(info);
1063 					return GF_BAD_PARAM;
1064 				}
1065 				for (i=0; i<strlen(info->remotePath); i++)
1066 					if (info->remotePath[i]=='\\') info->remotePath[i]='/';
1067 				info->canonicalRepresentation = (char*)gf_malloc(strlen(info->protocol) + strlen(info->remotePath) + 1);
1068 				strcpy(info->canonicalRepresentation, info->protocol);
1069 				strcat(info->canonicalRepresentation, info->remotePath);
1070 
1071 				return GF_OK;
1072 			} else {
1073 				/* We continue the parsing as usual */
1074 				url = urlConcatenateWithBaseURL + proto_offset;
1075 			}
1076 		} else {
1077 			GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Network] No supported protocol for url %s\n", url));
1078 			gf_dm_url_info_del(info);
1079 			return GF_BAD_PARAM;
1080 		}
1081 	}
1082 	assert( proto_offset >= 0 );
1083 	tmp = strchr(url, '/');
1084 	assert( !info->remotePath );
1085 	info->remotePath = gf_url_percent_encode(tmp ? tmp : "/");
1086 	if (tmp) {
1087 		tmp[0] = 0;
1088 		copyOfUrl = gf_strdup(url);
1089 		tmp[0] = '/';
1090 	} else {
1091 		copyOfUrl = gf_strdup(url);
1092 	}
1093 	tmp_url = copyOfUrl;
1094 	current_pos = tmp_url;
1095 	tmp = strrchr(tmp_url, '@');
1096 	if (tmp) {
1097 		current_pos = tmp + 1;
1098 		assert( ! info->server_name );
1099 		info->server_name = gf_strdup(current_pos);
1100 		tmp[0] = 0;
1101 		tmp = strchr(tmp_url, ':');
1102 
1103 		if (tmp) {
1104 			tmp[0] = 0;
1105 			info->password = gf_strdup(tmp+1);
1106 		}
1107 		info->userName = gf_strdup(tmp_url);
1108 	} else {
1109 		assert( ! info->server_name );
1110 		info->server_name = gf_strdup(tmp_url);
1111 	}
1112 
1113 	//scan for port number after IPv6 address ']' end char
1114 	ipv6 = strrchr(current_pos, ']');
1115 	tmp = strrchr(ipv6 ? ipv6 : current_pos, ':');
1116 
1117 	if (tmp) {
1118 		info->port = atoi(tmp+1);
1119 		tmp[0] = 0;
1120 		if (info->server_name) {
1121 			gf_free(info->server_name);
1122 		}
1123 		info->server_name = gf_strdup(current_pos);
1124 	}
1125 
1126 	/* builds orig_url */
1127 	/* We dont't want orig_url to contain user/passwords for security reasons or mismatch in cache hit */
1128 	{
1129 		char port[8];
1130 		snprintf(port, sizeof(port)-1, ":%d", info->port);
1131 		info->canonicalRepresentation = (char*)gf_malloc(strlen(info->protocol)+strlen(info->server_name)+1+strlen(port)+strlen(info->remotePath));
1132 		strcpy(info->canonicalRepresentation, info->protocol);
1133 		strcat(info->canonicalRepresentation, info->server_name);
1134 		if (info->port!=80)
1135 			strcat(info->canonicalRepresentation, port);
1136 		strcat(info->canonicalRepresentation, info->remotePath);
1137 	}
1138 	gf_free(copyOfUrl);
1139 	if (urlConcatenateWithBaseURL)
1140 		gf_free(urlConcatenateWithBaseURL);
1141 	return GF_OK;
1142 }
1143 
1144 char *gf_cache_get_forced_headers(const DownloadedCacheEntry entry);
1145 u32 gf_cache_get_downtime(const DownloadedCacheEntry entry);
1146 
gf_dm_sess_reload_cached_headers(GF_DownloadSession * sess)1147 static void gf_dm_sess_reload_cached_headers(GF_DownloadSession *sess)
1148 {
1149 	char *hdrs;
1150 
1151 	if (!sess || !sess->local_cache_only) return;
1152 
1153 	hdrs = gf_cache_get_forced_headers(sess->cache_entry);
1154 
1155 	gf_dm_clear_headers(sess);
1156 	while (hdrs) {
1157 		char *sep2, *sepL = strstr(hdrs, "\r\n");
1158 		if (sepL) sepL[0] = 0;
1159 		sep2 = strchr(hdrs, ':');
1160 		if (sep2) {
1161 			GF_HTTPHeader *hdr;
1162 			GF_SAFEALLOC(hdr, GF_HTTPHeader);
1163 			if (!hdr) break;
1164 			sep2[0]=0;
1165 			hdr->name = gf_strdup(hdrs);
1166 			sep2[0]=':';
1167 			sep2++;
1168 			while (sep2[0]==' ') sep2++;
1169 			hdr->value = gf_strdup(sep2);
1170 			gf_list_add(sess->headers, hdr);
1171 		}
1172 		if (!sepL) break;
1173 		sepL[0] = '\r';
1174 		hdrs = sepL + 2;
1175 	}
1176 }
1177 
1178 GF_EXPORT
gf_dm_sess_setup_from_url(GF_DownloadSession * sess,const char * url,Bool allow_direct_reuse)1179 GF_Err gf_dm_sess_setup_from_url(GF_DownloadSession *sess, const char *url, Bool allow_direct_reuse)
1180 {
1181 	Bool socket_changed = GF_FALSE;
1182 	GF_URL_Info info;
1183 	char *sep_frag=NULL;
1184 	if (!url) return GF_BAD_PARAM;
1185 
1186 	gf_dm_clear_headers(sess);
1187 	sess->allow_direct_reuse = allow_direct_reuse;
1188 	gf_dm_url_info_init(&info);
1189 
1190 	if (!sess->sock)
1191 		socket_changed = GF_TRUE;
1192 	else if (sess->status>GF_NETIO_DISCONNECTED)
1193 		socket_changed = GF_TRUE;
1194 
1195 	assert(sess->status != GF_NETIO_WAIT_FOR_REPLY);
1196 	assert(sess->status != GF_NETIO_DATA_EXCHANGE);
1197 
1198 	//strip fragment
1199 	sep_frag = strchr(url, '#');
1200 	if (sep_frag) sep_frag[0]=0;
1201 	sess->last_error = gf_dm_get_url_info(url, &info, sess->orig_url);
1202 	if (sess->last_error) {
1203 		if (sep_frag) sep_frag[0]='#';
1204 		return sess->last_error;
1205 	}
1206 
1207 	if (!strstr(url, "://")) {
1208 		char c, *sep;
1209 		gf_dm_url_info_del(&info);
1210 		info.port = sess->port;
1211 		info.server_name = sess->server_name ? gf_strdup(sess->server_name) : NULL;
1212 		info.remotePath = gf_strdup(url);
1213 		sep = strstr(sess->orig_url_before_redirect, "://");
1214 		assert(sep);
1215 		c = sep[3];
1216 		sep[3] = 0;
1217 		info.protocol = gf_strdup(sess->orig_url_before_redirect);
1218 		sep[3] = c;
1219 	}
1220 
1221 	if (sess->port != info.port) {
1222 		socket_changed = GF_TRUE;
1223 		sess->port = info.port;
1224 	}
1225 
1226 	if (sess->from_cache_only) {
1227 		socket_changed = GF_TRUE;
1228 		sess->from_cache_only = GF_FALSE;
1229 		if (sess->cache_entry) {
1230 			gf_dm_remove_cache_entry_from_session(sess);
1231 			sess->cache_entry = NULL;
1232 		}
1233 	}
1234 
1235 	if (!strcmp("http://", info.protocol) || !strcmp("https://", info.protocol)) {
1236 		if (sess->do_requests != http_do_requests) {
1237 			sess->do_requests = http_do_requests;
1238 			socket_changed = GF_TRUE;
1239 		}
1240 		if (!strcmp("https://", info.protocol)) {
1241 			if (!(sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) {
1242 				sess->flags |= GF_DOWNLOAD_SESSION_USE_SSL;
1243 				socket_changed = GF_TRUE;
1244 			}
1245 		} else if (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) {
1246 			sess->flags &= ~GF_DOWNLOAD_SESSION_USE_SSL;
1247 			socket_changed = GF_TRUE;
1248 		}
1249 	} else {
1250 		sess->do_requests = NULL;
1251 	}
1252 
1253 	if (sess->server_name && info.server_name && !strcmp(sess->server_name, info.server_name)) {
1254 	} else {
1255 		socket_changed = GF_TRUE;
1256 		if (sess->server_name) gf_free(sess->server_name);
1257 		sess->server_name = info.server_name ? gf_strdup(info.server_name) : NULL;
1258 	}
1259 
1260 	if (sess->orig_url) gf_free(sess->orig_url);
1261 	sess->orig_url = gf_strdup(info.canonicalRepresentation);
1262 
1263 	if (!sess->orig_url_before_redirect)
1264 		sess->orig_url_before_redirect = gf_strdup(url);
1265 
1266 	if (sess->remote_path) gf_free(sess->remote_path);
1267 	sess->remote_path = gf_strdup(info.remotePath);
1268 
1269 	if (sess->status==GF_NETIO_STATE_ERROR)
1270 		socket_changed = GF_TRUE;
1271 
1272 	if (!socket_changed && info.userName  && !strcmp(info.userName, sess->creds->username)) {
1273 	} else {
1274 		sess->creds = NULL;
1275 		if (info.userName ) {
1276 			if (! sess->dm) {
1277 				GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Did not found any download manager, credentials not supported\n"));
1278 			} else
1279 				sess->creds = gf_user_credentials_register(sess->dm, sess->server_name, info.userName, info.password, info.userName && info.password);
1280 		}
1281 	}
1282 	gf_dm_url_info_del(&info);
1283 	if (sep_frag) sep_frag[0]='#';
1284 
1285 	if (sess->sock && !socket_changed) {
1286 		sess->status = GF_NETIO_CONNECTED;
1287 		sess->num_retry = SESSION_RETRY_COUNT;
1288 		sess->needs_cache_reconfig = 1;
1289 	} else {
1290 		if (sess->sock) {
1291 			gf_sk_del(sess->sock);
1292 			sess->sock = NULL;
1293 		}
1294 		sess->status = GF_NETIO_SETUP;
1295 #ifdef GPAC_HAS_SSL
1296 		if (sess->ssl) {
1297 			SSL_shutdown(sess->ssl);
1298 			SSL_free(sess->ssl);
1299 			sess->ssl = NULL;
1300 		}
1301 #endif
1302 
1303 	}
1304 	sess->total_size = 0;
1305 	sess->bytes_done = 0;
1306 	assert(sess->remaining_data_size==0);
1307 
1308 	sess->local_cache_only = GF_FALSE;
1309 	if (sess->dm && sess->dm->local_cache_url_provider_cbk) {
1310 		Bool res = sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *)url, GF_FALSE);
1311 		if (res == GF_TRUE) {
1312 			sess->local_cache_only = GF_TRUE;
1313 			gf_free(sess->orig_url);
1314 			sess->orig_url = gf_strdup(url);
1315 			sess->last_error = GF_OK;
1316 			gf_dm_configure_cache(sess);
1317 			sess->status = GF_NETIO_DATA_TRANSFERED;
1318 			sess->total_size = gf_cache_get_content_length(sess->cache_entry);
1319 			sess->bytes_done = sess->total_size;
1320 			sess->total_time_since_req = gf_cache_get_downtime(sess->cache_entry);
1321 			if (sess->total_time_since_req)
1322 				sess->bytes_per_sec = (u32) ((1000 * (u64) sess->bytes_done) / sess->total_time_since_req);
1323 			else
1324 				sess->bytes_per_sec = 0;
1325 			gf_dm_sess_reload_cached_headers(sess);
1326 		}
1327 	}
1328 	return sess->last_error;
1329 }
1330 
1331 
gf_dm_session_do_task(GF_DownloadSession * sess)1332 Bool gf_dm_session_do_task(GF_DownloadSession *sess)
1333 {
1334 	Bool do_run = GF_TRUE;
1335 
1336 	if (sess->destroy) {
1337 		do_run = GF_FALSE;
1338 	} else {
1339 		gf_mx_p(sess->mx);
1340 		if (sess->status >= GF_NETIO_DISCONNECTED) {
1341 			do_run = GF_FALSE;
1342 		} else {
1343 			if (sess->status < GF_NETIO_CONNECTED) {
1344 				gf_dm_connect(sess);
1345 			} else {
1346 				sess->do_requests(sess);
1347 			}
1348 		}
1349 		gf_mx_v(sess->mx);
1350 	}
1351 	if (do_run) return GF_TRUE;
1352 
1353 	/*destroy all session but keep connection active*/
1354 	gf_dm_disconnect(sess, GF_FALSE);
1355 	sess->status = GF_NETIO_STATE_ERROR;
1356 	sess->last_error = GF_OK;
1357 	return GF_FALSE;
1358 }
1359 
gf_dm_session_task(GF_FilterSession * fsess,void * callback,u32 * reschedule_ms)1360 Bool gf_dm_session_task(GF_FilterSession *fsess, void *callback, u32 *reschedule_ms)
1361 {
1362 	GF_SessTask *task = callback;
1363 	GF_DownloadSession *sess = task->sess;
1364 	if (!sess) {
1365 		gf_free(task);
1366 		return GF_FALSE;
1367 	}
1368 	Bool ret = gf_dm_session_do_task(sess);
1369 	if (ret) {
1370 		*reschedule_ms = 1;
1371 		return GF_TRUE;
1372 	}
1373 	assert(sess->ftask);
1374 	gf_free(sess->ftask);
1375 	sess->ftask = NULL;
1376 	if (sess->destroy)
1377 		gf_dm_sess_del(sess);
1378 	return GF_FALSE;
1379 }
1380 
gf_dm_session_thread(void * par)1381 static u32 gf_dm_session_thread(void *par)
1382 {
1383 	GF_DownloadSession *sess = (GF_DownloadSession *)par;
1384 	if (!sess) return 0;
1385 
1386 	GF_LOG(GF_LOG_DEBUG, GF_LOG_CORE, ("[Downloader] Entering thread ID %d\n", gf_th_id() ));
1387 	sess->flags &= ~GF_DOWNLOAD_SESSION_THREAD_DEAD;
1388 	while (!sess->destroy) {
1389 		Bool ret = gf_dm_session_do_task(sess);
1390 		if (!ret) break;
1391 		gf_sleep(0);
1392 	}
1393 	sess->flags |= GF_DOWNLOAD_SESSION_THREAD_DEAD;
1394 	if (sess->destroy)
1395 		gf_dm_sess_del(sess);
1396 	return 1;
1397 }
1398 
gf_dm_sess_new_internal(GF_DownloadManager * dm,const char * url,u32 dl_flags,gf_dm_user_io user_io,void * usr_cbk,GF_Socket * server,GF_Err * e)1399 static GF_DownloadSession *gf_dm_sess_new_internal(GF_DownloadManager * dm, const char *url, u32 dl_flags,
1400         gf_dm_user_io user_io,
1401         void *usr_cbk,
1402         GF_Socket *server,
1403         GF_Err *e)
1404 {
1405 	GF_DownloadSession *sess;
1406 
1407 	GF_SAFEALLOC(sess, GF_DownloadSession);
1408 	if (!sess) {
1409 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("%s:%d Cannot allocate session for URL %s: OUT OF MEMORY!\n", __FILE__, __LINE__, url));
1410 		return NULL;
1411 	}
1412 	sess->headers = gf_list_new();
1413 	sess->flags = dl_flags;
1414 	if (sess->flags & GF_NETIO_SESSION_NOTIFY_DATA)
1415 		sess->force_data_write_callback = GF_TRUE;
1416 	sess->user_proc = user_io;
1417 	sess->usr_cbk = usr_cbk;
1418 	sess->creds = NULL;
1419 
1420 	if (!gf_opts_get_key("core", "head-timeout")) {
1421 		sess->head_timeout = 5000;
1422 	} else {
1423 		sess->head_timeout = gf_opts_get_int("core", "head-timeout");
1424 	}
1425 
1426 	sess->request_timeout = gf_opts_get_int("core", "req-timeout");
1427 	if (!sess->request_timeout) sess->request_timeout = 20000;
1428 
1429 	sess->dm = dm;
1430 	if (server) {
1431 		sess->sock = server;
1432 		sess->flags = GF_NETIO_SESSION_NOT_THREADED;
1433 		sess->status = GF_NETIO_CONNECTED;
1434 		sess->server_mode = GF_TRUE;
1435 		sess->do_requests = http_do_requests;
1436 		if (e) *e = GF_OK;
1437 		return sess;
1438 	}
1439 
1440 	if (!sess->head_timeout) sess->server_only_understand_get = GF_TRUE;
1441 	if (dm)
1442 		sess->disable_cache = dm->disable_cache;
1443 
1444 	if (! (dl_flags&GF_NETIO_SESSION_NOT_THREADED)) {
1445 		sess->mx = gf_mx_new(url);
1446 		if (!sess->mx) {
1447 			gf_free(sess);
1448 			return NULL;
1449 		}
1450 	}
1451 
1452 	*e = gf_dm_sess_setup_from_url(sess, url, GF_FALSE);
1453 	if (*e) {
1454 		GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("%s:%d gf_dm_sess_new_simple: error=%s at setup for '%s'\n", __FILE__, __LINE__, gf_error_to_string(*e), url));
1455 		gf_dm_sess_del(sess);
1456 		return NULL;
1457 	}
1458 	assert( sess );
1459 	sess->num_retry = SESSION_RETRY_COUNT;
1460 	/*threaded session must be started with gf_dm_sess_process*/
1461 	return sess;
1462 }
1463 
1464 GF_EXPORT
gf_dm_sess_new_server(GF_Socket * server,void * ssl_sock_ctx,gf_dm_user_io user_io,void * usr_cbk,GF_Err * e)1465 GF_DownloadSession *gf_dm_sess_new_server(GF_Socket *server,
1466 		void *ssl_sock_ctx,
1467         gf_dm_user_io user_io,
1468         void *usr_cbk,
1469         GF_Err *e)
1470 {
1471 	GF_DownloadSession *sess = gf_dm_sess_new_internal(NULL, NULL, 0, user_io, usr_cbk, server, e);
1472 #ifdef GPAC_HAS_SSL
1473 	if (sess) sess->ssl = ssl_sock_ctx;
1474 #endif
1475 	return sess;
1476 }
1477 
1478 
1479 GF_EXPORT
gf_dm_sess_new_simple(GF_DownloadManager * dm,const char * url,u32 dl_flags,gf_dm_user_io user_io,void * usr_cbk,GF_Err * e)1480 GF_DownloadSession *gf_dm_sess_new_simple(GF_DownloadManager * dm, const char *url, u32 dl_flags,
1481         gf_dm_user_io user_io,
1482         void *usr_cbk,
1483         GF_Err *e)
1484 {
1485 	return gf_dm_sess_new_internal(dm, url, dl_flags, user_io, usr_cbk, NULL, e);
1486 }
1487 GF_EXPORT
gf_dm_sess_new(GF_DownloadManager * dm,const char * url,u32 dl_flags,gf_dm_user_io user_io,void * usr_cbk,GF_Err * e)1488 GF_DownloadSession *gf_dm_sess_new(GF_DownloadManager *dm, const char *url, u32 dl_flags,
1489                                    gf_dm_user_io user_io,
1490                                    void *usr_cbk,
1491                                    GF_Err *e)
1492 {
1493 	GF_DownloadSession *sess;
1494 	GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("%s:%d gf_dm_sess_new(%s)\n", __FILE__, __LINE__, url));
1495 	*e = GF_OK;
1496 	if (gf_dm_is_local(dm, url)) return NULL;
1497 
1498 	if (!gf_dm_can_handle_url(dm, url)) {
1499 		*e = GF_NOT_SUPPORTED;
1500 		return NULL;
1501 	}
1502 	sess = gf_dm_sess_new_simple(dm, url, dl_flags, user_io, usr_cbk, e);
1503 	if (sess && dm) {
1504 		sess->dm = dm;
1505 		gf_mx_p(dm->cache_mx);
1506 		gf_list_add(dm->sessions, sess);
1507 		gf_mx_v(dm->cache_mx);
1508 	}
1509 	return sess;
1510 }
1511 
gf_dm_read_data(GF_DownloadSession * sess,char * data,u32 data_size,u32 * out_read)1512 static GF_Err gf_dm_read_data(GF_DownloadSession *sess, char *data, u32 data_size, u32 *out_read)
1513 {
1514 	GF_Err e;
1515 
1516 	if (sess->dm && sess->dm->simulate_no_connection) {
1517 		if (sess->sock) {
1518 			sess->status = GF_NETIO_DISCONNECTED;
1519 		}
1520 		return GF_IP_NETWORK_FAILURE;
1521 	}
1522 
1523 	if (!sess)
1524 		return GF_BAD_PARAM;
1525 
1526 	gf_mx_p(sess->mx);
1527 	if (!sess->sock) {
1528 		sess->status = GF_NETIO_DISCONNECTED;
1529 		gf_mx_v(sess->mx);
1530 		return GF_IP_CONNECTION_CLOSED;
1531 	}
1532 
1533 #ifdef GPAC_HAS_SSL
1534 	if (sess->ssl) {
1535 		s32 size;
1536 		e = gf_sk_receive(sess->sock, NULL, 0, NULL);
1537 		if (e==GF_IP_NETWORK_EMPTY) {
1538 			gf_mx_v(sess->mx);
1539 			return e;
1540 		}
1541 		size = SSL_read(sess->ssl, data, data_size);
1542 		if (size < 0)
1543 			e = GF_IO_ERR;
1544 		else if (!size)
1545 			e = GF_IP_NETWORK_EMPTY;
1546 		else {
1547 			e = GF_OK;
1548 			data[size] = 0;
1549 			*out_read = size;
1550 		}
1551 	} else
1552 #endif
1553 
1554 		e = gf_sk_receive(sess->sock, data, data_size, out_read);
1555 
1556 	gf_mx_v(sess->mx);
1557 	return e;
1558 }
1559 
1560 
1561 #ifdef GPAC_HAS_SSL
1562 
1563 #define LWR(x) ('A' <= (x) && (x) <= 'Z' ? (x) - 32 : (x))
1564 
rfc2818_match(const char * pattern,const char * string)1565 static Bool rfc2818_match(const char *pattern, const char *string)
1566 {
1567 	char d;
1568 	u32 i=0, k=0;
1569 	while (1) {
1570 		char c = LWR(pattern[i]);
1571 		if (c == '\0') break;
1572 
1573 		if (c=='*') {
1574 			/*remove *** patterns*/
1575 			while (c == '*') {
1576 				i++;
1577 				c = LWR(pattern[i]);
1578 			}
1579 			/*look for same c character*/
1580 			while (1) {
1581 				d = LWR(string[k]);
1582 				if (d == '\0') break;
1583 				/*matched c character, check following substrings*/
1584 				if ((d == c) && rfc2818_match (&pattern[i], &string[k]))
1585 					return GF_TRUE;
1586 				else if (d == '.')
1587 					return GF_FALSE;
1588 
1589 				k++;
1590 			}
1591 			return (c == '\0') ? GF_TRUE : GF_FALSE;
1592 		} else {
1593 			if (c != LWR(string[k]))
1594 				return GF_FALSE;
1595 		}
1596 		i++;
1597 		k++;
1598 	}
1599 	return (string[k]=='\0') ? GF_TRUE : GF_FALSE;
1600 }
1601 #undef LWR
1602 
1603 #endif
1604 
gf_dm_connect(GF_DownloadSession * sess)1605 static void gf_dm_connect(GF_DownloadSession *sess)
1606 {
1607 	GF_Err e;
1608 	u16 proxy_port = 0;
1609 	const char *proxy;
1610 
1611 	if (!sess->sock) {
1612 		sess->num_retry = 40;
1613 		sess->sock = gf_sk_new(GF_SOCK_TYPE_TCP);
1614 	}
1615 
1616 	/*connect*/
1617 	sess->status = GF_NETIO_SETUP;
1618 	gf_dm_sess_notify_state(sess, sess->status, GF_OK);
1619 
1620 	/*PROXY setup*/
1621 	if (sess->proxy_enabled!=2) {
1622 		proxy = NULL;
1623 		if (gf_opts_get_bool("core", "proxy-on")) {
1624 			u32 i;
1625 			Bool use_proxy=GF_TRUE;
1626 			for (i=0; i<gf_list_count(sess->dm->skip_proxy_servers); i++) {
1627 				char *skip = (char*)gf_list_get(sess->dm->skip_proxy_servers, i);
1628 				if (!strcmp(skip, sess->server_name)) {
1629 					use_proxy=GF_FALSE;
1630 					break;
1631 				}
1632 			}
1633 			if (use_proxy) {
1634 				proxy_port = gf_opts_get_int("core", "proxy-port");
1635 				if (!proxy_port) proxy_port = 80;
1636 				proxy = gf_opts_get_key("core", "proxy-name");
1637 				sess->proxy_enabled = 1;
1638 			} else {
1639 				proxy = NULL;
1640 			}
1641 		} else {
1642 			proxy = NULL;
1643 			sess->proxy_enabled = 0;
1644 		}
1645 	} else {
1646 		proxy = NULL;
1647 	}
1648 
1649 
1650 	if (!proxy) {
1651 		proxy = sess->server_name;
1652 		proxy_port = sess->port;
1653 	}
1654 	GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connecting to %s:%d\n", proxy, proxy_port));
1655 
1656 	if (sess->status == GF_NETIO_SETUP) {
1657 		u64 now;
1658 		if (sess->dm && sess->dm->simulate_no_connection) {
1659 			sess->status = GF_NETIO_STATE_ERROR;
1660 			sess->last_error = GF_IP_NETWORK_FAILURE;
1661 			gf_dm_sess_notify_state(sess, sess->status, sess->last_error);
1662 			return;
1663 		}
1664 
1665 		now = gf_sys_clock_high_res();
1666 		e = gf_sk_connect(sess->sock, (char *) proxy, proxy_port, NULL);
1667 
1668 		/*retry*/
1669 		if ((e == GF_IP_SOCK_WOULD_BLOCK) && sess->num_retry) {
1670 			sess->status = GF_NETIO_SETUP;
1671 			sess->num_retry--;
1672 			return;
1673 		}
1674 
1675 		/*failed*/
1676 		if (e) {
1677 			if (!sess->cache_entry && sess->dm && sess->dm->allow_offline_cache) {
1678 				gf_dm_configure_cache(sess);
1679 				if (sess->from_cache_only) return;
1680 			}
1681 			sess->status = GF_NETIO_STATE_ERROR;
1682 			sess->last_error = e;
1683 			gf_dm_sess_notify_state(sess, sess->status, e);
1684 			return;
1685 		}
1686 		if (sess->allow_direct_reuse) {
1687 			gf_dm_configure_cache(sess);
1688 			if (sess->from_cache_only) return;
1689 		}
1690 
1691 		sess->connect_time = (u32) (gf_sys_clock_high_res() - now);
1692 		sess->status = GF_NETIO_CONNECTED;
1693 		GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connected to %s:%d\n", proxy, proxy_port));
1694 		gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
1695 //		gf_sk_set_buffer_size(sess->sock, GF_TRUE, GF_DOWNLOAD_BUFFER_SIZE);
1696 //		gf_sk_set_buffer_size(sess->sock, GF_FALSE, GF_DOWNLOAD_BUFFER_SIZE);
1697 	}
1698 
1699 #ifdef GPAC_HAS_SSL
1700 	if (!sess->ssl && (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL)) {
1701 		u64 now = gf_sys_clock_high_res();
1702 		if (sess->dm && !sess->dm->ssl_ctx)
1703 			ssl_init(sess->dm, 0);
1704 		/*socket is connected, configure SSL layer*/
1705 		if (sess->dm && sess->dm->ssl_ctx) {
1706 			int ret;
1707 			X509 *cert;
1708 			Bool success;
1709 
1710 			sess->ssl = SSL_new(sess->dm->ssl_ctx);
1711 			SSL_set_fd(sess->ssl, gf_sk_get_handle(sess->sock));
1712 			SSL_set_connect_state(sess->ssl);
1713 			ret = SSL_connect(sess->ssl);
1714 			if (ret<=0) {
1715 				GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Cannot connect, error %d\n", ret));
1716 			} else {
1717 				GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[SSL] connected\n"));
1718 			}
1719 
1720 			cert = SSL_get_peer_certificate(sess->ssl);
1721 			/*if we have a cert, check it*/
1722 			if (cert) {
1723 				long vresult;
1724 				SSL_set_verify_result(sess->ssl, 0);
1725 				vresult = SSL_get_verify_result(sess->ssl);
1726 
1727 				if (vresult == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) {
1728 					GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[SSL] Cannot locate issuer's certificate on the local system, will not attempt to validate\n"));
1729 					SSL_set_verify_result(sess->ssl, 0);
1730 					vresult = SSL_get_verify_result(sess->ssl);
1731 				}
1732 
1733 				if (vresult == X509_V_OK) {
1734 					char common_name[256];
1735 					STACK_OF(GENERAL_NAME) *altnames;
1736 					GF_List* valid_names;
1737 					int i;
1738 
1739 					valid_names = gf_list_new();
1740 
1741 					common_name[0] = 0;
1742 					X509_NAME_get_text_by_NID(X509_get_subject_name(cert), NID_commonName, common_name, sizeof (common_name));
1743 					gf_list_add(valid_names, common_name);
1744 
1745 					altnames = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
1746 					if (altnames) {
1747 						for (i = 0; i < sk_GENERAL_NAME_num(altnames); ++i) {
1748 							const GENERAL_NAME *altname = sk_GENERAL_NAME_value(altnames, i);
1749 							if (altname->type == GEN_DNS)
1750 							{
1751 								#if OPENSSL_VERSION_NUMBER < 0x10100000L
1752 									unsigned char *altname_str = ASN1_STRING_data(altname->d.ia5);
1753 								#else
1754 									unsigned char *altname_str = (unsigned char *)ASN1_STRING_get0_data(altname->d.ia5);
1755 								#endif
1756 								gf_list_add(valid_names, altname_str);
1757 							}
1758 						}
1759 					}
1760 
1761 					success = GF_FALSE;
1762 					for (i = 0; i < (int)gf_list_count(valid_names); ++i) {
1763 						const char *valid_name = (const char*) gf_list_get(valid_names, i);
1764 						if (rfc2818_match(valid_name, sess->server_name)) {
1765 							success = GF_TRUE;
1766 							GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[SSL] Hostname %s matches %s\n", sess->server_name, valid_name));
1767 							break;
1768 						}
1769 					}
1770 					if (!success) {
1771 						if (sess->dm && sess->dm->allow_broken_certificate) {
1772 							success = GF_TRUE;
1773 							GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[SSL] Mismatch in certificate names: expected %s\n", sess->server_name));
1774 						} else {
1775 							GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Mismatch in certificate names, try using -broken-cert: expected %s\n", 	sess->server_name));
1776 						}
1777 #ifndef GPAC_DISABLE_LOG
1778 						for (i = 0; i < (int)gf_list_count(valid_names); ++i) {
1779 							const char *valid_name = (const char*) gf_list_get(valid_names, i);
1780 							GF_LOG(success ? GF_LOG_WARNING : GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Tried name: %s\n", valid_name));
1781 						}
1782 #endif
1783 					}
1784 
1785 					gf_list_del(valid_names);
1786 					GENERAL_NAMES_free(altnames);
1787 				} else {
1788 					success = GF_FALSE;
1789 					GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[SSL] Error verifying certificate %x\n", vresult));
1790 				}
1791 
1792 				X509_free(cert);
1793 
1794 				if (!success) {
1795 					gf_dm_disconnect(sess, GF_TRUE);
1796 					sess->status = GF_NETIO_STATE_ERROR;
1797 					sess->last_error = GF_AUTHENTICATION_FAILURE;
1798 					gf_dm_sess_notify_state(sess, sess->status, sess->last_error);
1799 				}
1800 			}
1801 
1802 			sess->ssl_setup_time = (u32) (gf_sys_clock_high_res() - now);
1803 		}
1804 	}
1805 #endif
1806 
1807 	/*this should be done when building HTTP GET request in case we have range directives*/
1808 	gf_dm_configure_cache(sess);
1809 
1810 }
1811 
gf_dm_refresh_cache_entry(GF_DownloadSession * sess)1812 DownloadedCacheEntry gf_dm_refresh_cache_entry(GF_DownloadSession *sess)
1813 {
1814 	Bool go;
1815 	u32 timer = 0;
1816 	u32 flags;
1817 	if (!sess) return NULL;
1818 	flags = sess->flags;
1819 	sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
1820 	go = GF_TRUE;
1821 	while (go) {
1822 		switch (sess->status) {
1823 		/*setup download*/
1824 		case GF_NETIO_SETUP:
1825 			gf_dm_connect(sess);
1826 			break;
1827 		case GF_NETIO_WAIT_FOR_REPLY:
1828 			if (timer == 0)
1829 				timer = gf_sys_clock();
1830 			{
1831 				u32 timer2 = gf_sys_clock();
1832 				if (timer2 - timer > 5000) {
1833 					GF_Err e;
1834 					/* Since HEAD is not understood by this server, we use a GET instead */
1835 					sess->http_read_type = GET;
1836 					sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
1837 					gf_dm_disconnect(sess, GF_FALSE);
1838 					sess->status = GF_NETIO_SETUP;
1839 					sess->server_only_understand_get = GF_TRUE;
1840 					GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Timeout with HEAD, try with GET\n"));
1841 					e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
1842 					if (e) {
1843 						GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("gf_dm_refresh_cache_entry() : Error with GET %d\n", e));
1844 						sess->status = GF_NETIO_STATE_ERROR;
1845 						sess->last_error = e;
1846 						gf_dm_sess_notify_state(sess, sess->status, e);
1847 					} else {
1848 						timer = 0;
1849 						continue;
1850 					}
1851 				}
1852 			}
1853 		case GF_NETIO_CONNECTED:
1854 			sess->do_requests(sess);
1855 			break;
1856 		case GF_NETIO_DATA_EXCHANGE:
1857 		case GF_NETIO_DISCONNECTED:
1858 		case GF_NETIO_STATE_ERROR:
1859 		case GF_NETIO_DATA_TRANSFERED:
1860 			go = GF_FALSE;
1861 			break;
1862 		default:
1863 			break;
1864 		}
1865 	}
1866 	sess->flags = flags;
1867 	if (sess->status==GF_NETIO_STATE_ERROR) return NULL;
1868 	return sess->cache_entry;
1869 }
1870 
1871 GF_EXPORT
gf_dm_sess_mime_type(GF_DownloadSession * sess)1872 const char *gf_dm_sess_mime_type(GF_DownloadSession *sess)
1873 {
1874 	DownloadedCacheEntry entry;
1875 	if (sess->cache_entry) {
1876 		const char * oldMimeIfAny = gf_cache_get_mime_type(sess->cache_entry);
1877 		if (oldMimeIfAny)
1878 			return oldMimeIfAny;
1879 	}
1880 	entry = gf_dm_refresh_cache_entry (sess);
1881 	if (!entry)
1882 		return sess->mime_type;
1883 	assert( entry == sess->cache_entry && entry);
1884 	return gf_cache_get_mime_type( sess->cache_entry );
1885 }
1886 
1887 GF_EXPORT
gf_dm_sess_set_range(GF_DownloadSession * sess,u64 start_range,u64 end_range,Bool discontinue_cache)1888 GF_Err gf_dm_sess_set_range(GF_DownloadSession *sess, u64 start_range, u64 end_range, Bool discontinue_cache)
1889 {
1890 	if (!sess) return GF_BAD_PARAM;
1891 	if (sess->cache_entry) {
1892 		if (!discontinue_cache) {
1893 			if (gf_cache_get_end_range(sess->cache_entry) + 1 != start_range)
1894 				discontinue_cache = GF_TRUE;
1895 		}
1896 		if (sess->sock) {
1897 			if (sess->status != GF_NETIO_CONNECTED) {
1898 				if (sess->status != GF_NETIO_DISCONNECTED) {
1899 					return GF_BAD_PARAM;
1900 				}
1901 			}
1902 		}
1903 		if (!sess->local_cache_only) {
1904 			sess->status = sess->sock ? GF_NETIO_CONNECTED : GF_NETIO_SETUP;
1905 			sess->num_retry = SESSION_RETRY_COUNT;
1906 
1907 			if (!discontinue_cache) {
1908 				gf_cache_set_end_range(sess->cache_entry, end_range);
1909 				/*remember this in case we get disconnected*/
1910 				sess->is_range_continuation = GF_TRUE;
1911 			} else {
1912 				sess->needs_cache_reconfig = 1;
1913 				sess->reused_cache_entry = GF_FALSE;
1914 			}
1915 		}
1916 	} else {
1917 		if ((sess->status != GF_NETIO_SETUP) && (sess->status != GF_NETIO_CONNECTED))
1918 			return GF_BAD_PARAM;
1919 	}
1920 	sess->range_start = start_range;
1921 	sess->range_end = end_range;
1922 	sess->needs_range = (start_range || end_range) ? GF_TRUE : GF_FALSE;
1923 	return GF_OK;
1924 }
1925 
1926 GF_EXPORT
gf_dm_sess_process(GF_DownloadSession * sess)1927 GF_Err gf_dm_sess_process(GF_DownloadSession *sess)
1928 {
1929 	Bool go;
1930 
1931 	/*if session is threaded, start thread*/
1932 	if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED)) {
1933 		if (sess->dm->filter_session && !gf_opts_get_bool("core", "dm-threads")) {
1934 			GF_SAFEALLOC(sess->ftask, GF_SessTask);
1935 			if (!sess->ftask) return GF_OUT_OF_MEM;
1936 			sess->ftask->sess = sess;
1937 			gf_fs_post_user_task(sess->dm->filter_session, gf_dm_session_task, sess->ftask, "download");
1938 			return GF_OK;
1939 		}
1940 		if (sess->th) {
1941 			GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Session already started - ignoring start\n"));
1942 			return GF_OK;
1943 		}
1944 		sess->th = gf_th_new(sess->orig_url);
1945 		if (!sess->th) return GF_OUT_OF_MEM;
1946 		gf_th_run(sess->th, gf_dm_session_thread, sess);
1947 		return GF_OK;
1948 	}
1949 
1950 	if (sess->put_state==2) {
1951 		if (sess->status==GF_NETIO_DATA_TRANSFERED)
1952 			sess->status = GF_NETIO_WAIT_FOR_REPLY;
1953 	}
1954 
1955 	/*otherwise do a synchronous download*/
1956 	go = GF_TRUE;
1957 	while (go) {
1958 		switch (sess->status) {
1959 		/*setup download*/
1960 		case GF_NETIO_SETUP:
1961 			gf_dm_connect(sess);
1962 			break;
1963 		case GF_NETIO_WAIT_FOR_REPLY:
1964 		case GF_NETIO_CONNECTED:
1965 			sess->do_requests(sess);
1966 			break;
1967 		case GF_NETIO_DATA_EXCHANGE:
1968 			if (sess->put_state==2) {
1969 				sess->status = GF_NETIO_DATA_TRANSFERED;
1970 				go = GF_FALSE;
1971 				break;
1972 			}
1973 			sess->do_requests(sess);
1974 			break;
1975 		case GF_NETIO_DATA_TRANSFERED:
1976 		case GF_NETIO_DISCONNECTED:
1977 		case GF_NETIO_STATE_ERROR:
1978 			go = GF_FALSE;
1979 			break;
1980 
1981 		case GF_NETIO_GET_METHOD:
1982 		case GF_NETIO_GET_HEADER:
1983 		case GF_NETIO_GET_CONTENT:
1984 		case GF_NETIO_PARSE_HEADER:
1985 		case GF_NETIO_PARSE_REPLY:
1986 			break;
1987 
1988 		default:
1989 			GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Session in unknown state !! - aborting\n"));
1990 			go = GF_FALSE;
1991 			break;
1992 		}
1993 	}
1994 	return sess->last_error;
1995 }
1996 
1997 GF_EXPORT
gf_dm_sess_process_headers(GF_DownloadSession * sess)1998 GF_Err gf_dm_sess_process_headers(GF_DownloadSession *sess)
1999 {
2000 	Bool go;
2001 	go = GF_TRUE;
2002 	while (go) {
2003 		switch (sess->status) {
2004 		/*setup download*/
2005 		case GF_NETIO_SETUP:
2006 			gf_dm_connect(sess);
2007 			break;
2008 		case GF_NETIO_WAIT_FOR_REPLY:
2009 		case GF_NETIO_CONNECTED:
2010 			sess->do_requests(sess);
2011 
2012 			if (sess->reused_cache_entry && sess->cache_entry && gf_cache_are_headers_processed(sess->cache_entry) ) {
2013 				sess->status = GF_NETIO_DATA_EXCHANGE;
2014 			}
2015 			break;
2016 		case GF_NETIO_DATA_EXCHANGE:
2017 		case GF_NETIO_DATA_TRANSFERED:
2018 		case GF_NETIO_DISCONNECTED:
2019 		case GF_NETIO_STATE_ERROR:
2020 			go = GF_FALSE;
2021 			break;
2022 		default:
2023 			break;
2024 		}
2025 	}
2026 	return sess->last_error;
2027 }
2028 
gf_dm_needs_to_delete_cache(GF_DownloadManager * dm)2029 static Bool gf_dm_needs_to_delete_cache(GF_DownloadManager * dm)
2030 {
2031 	if (!dm) return GF_FALSE;
2032 	return dm->clean_cache;
2033 }
2034 
2035 #ifdef BUGGY_gf_cache_cleanup_cache
2036 /*!
2037  * Cleans up the cache at start and stop.
2038  * Note that this method will perform any cleanup if
2039  * Configuration section [Downloader]/CleanCache is not set, meaning
2040  * that methods that create a "fake" GF_DownloadManager such as
2041  * gf_dm_wget() are not impacted and won't cleanup the cache
2042  *
2043  * FIXME: should be probably threaded to avoid too long start time
2044 \param dm The GF_DownloadManager
2045  */
gf_cache_cleanup_cache(GF_DownloadManager * dm)2046 static void gf_cache_cleanup_cache(GF_DownloadManager * dm) {
2047 	if (gf_dm_needs_to_delete_cache(dm)) {
2048 		gf_cache_delete_all_cached_files(dm->cache_directory);
2049 	}
2050 }
2051 #endif
2052 
2053 typedef struct
2054 {
2055 	Bool check_size;
2056 	u64 out_size;
2057 } cache_probe;
2058 
2059 
gf_dm_clean_cache(GF_DownloadManager * dm)2060 static void gf_dm_clean_cache(GF_DownloadManager *dm)
2061 {
2062 	u64 out_size = gf_cache_get_size(dm->cache_directory);
2063 	if (out_size >= dm->max_cache_size) {
2064 		GF_LOG(dm->max_cache_size ? GF_LOG_WARNING : GF_LOG_INFO, GF_LOG_HTTP, ("[Cache] Cache size %d exceeds max allowed %d, deleting entire cache\n", out_size, dm->max_cache_size));
2065 		gf_cache_delete_all_cached_files(dm->cache_directory);
2066 	}
2067 }
2068 
2069 GF_EXPORT
gf_dm_new(GF_FilterSession * fsess)2070 GF_DownloadManager *gf_dm_new(GF_FilterSession *fsess)
2071 {
2072 	const char *opt;
2073 	const char * default_cache_dir;
2074 	GF_DownloadManager *dm;
2075 	GF_SAFEALLOC(dm, GF_DownloadManager);
2076 	if (!dm) {
2077 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[Downloader] Failed to allocate downloader\n"));
2078 		return NULL;
2079 	}
2080 	dm->sessions = gf_list_new();
2081 	dm->cache_entries = gf_list_new();
2082 	dm->credentials = gf_list_new();
2083 	dm->skip_proxy_servers = gf_list_new();
2084 	dm->partial_downloads = gf_list_new();
2085 	dm->cache_mx = gf_mx_new("download_manager_cache_mx");
2086 	dm->filter_session = fsess;
2087 	default_cache_dir = NULL;
2088 	gf_mx_p( dm->cache_mx );
2089 
2090 	opt = gf_opts_get_key("core", "cache");
2091 
2092 retry_cache:
2093 	if (!opt) {
2094 		default_cache_dir = gf_get_default_cache_directory();
2095 		opt = default_cache_dir;
2096 	}
2097 	if (opt[strlen(opt)-1] != GF_PATH_SEPARATOR) {
2098 		dm->cache_directory = (char *) gf_malloc(sizeof(char)* (strlen(opt)+2));
2099 		sprintf(dm->cache_directory, "%s%c", opt, GF_PATH_SEPARATOR);
2100 	} else {
2101 		dm->cache_directory = gf_strdup(opt);
2102 	}
2103 
2104 	//check cache exists
2105 	if (!default_cache_dir) {
2106 		FILE *test;
2107 		char szTemp[GF_MAX_PATH];
2108 		strcpy(szTemp, dm->cache_directory);
2109 		strcat(szTemp, "gpaccache.test");
2110 		test = gf_fopen(szTemp, "wb");
2111 		if (!test) {
2112 			gf_mkdir(dm->cache_directory);
2113 			test = gf_fopen(szTemp, "wb");
2114 			if (!test) {
2115 				GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[Cache] Cannot write to %s directory, using system temp cache\n", dm->cache_directory ));
2116 				gf_free(dm->cache_directory);
2117 				dm->cache_directory = NULL;
2118 				opt = NULL;
2119 				goto retry_cache;
2120 			}
2121 		}
2122 		if (test) {
2123 			gf_fclose(test);
2124 			gf_file_delete(szTemp);
2125 		}
2126 	}
2127 
2128 	/*use it in in BYTES per second*/
2129 	dm->limit_data_rate = 1000 * gf_opts_get_int("core", "maxrate") / 8;
2130 
2131 	dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE;
2132 	//when rate is limited, use smaller smaller read size
2133 	if (dm->limit_data_rate) {
2134 		dm->read_buf_size = 1024;
2135 	}
2136 
2137 	dm->disable_cache = gf_opts_get_bool("core", "no-cache");
2138 
2139 	dm->allow_offline_cache = gf_opts_get_bool("core", "offline-cache");
2140 
2141 	dm->clean_cache = GF_FALSE;
2142 	dm->allow_broken_certificate = GF_FALSE;
2143 	if ( gf_opts_get_bool("core", "clean-cache")) {
2144 		dm->clean_cache = GF_TRUE;
2145 		dm->max_cache_size=0;
2146 		gf_dm_clean_cache(dm);
2147 	} else {
2148 		dm->max_cache_size = gf_opts_get_int("core", "cache-size");
2149 		if (dm->max_cache_size) {
2150 			gf_dm_clean_cache(dm);
2151 		}
2152 	}
2153 	dm->allow_broken_certificate = gf_opts_get_bool("core", "broken-cert");
2154 
2155 	gf_mx_v( dm->cache_mx );
2156 
2157 #ifdef GPAC_HAS_SSL
2158 	dm->ssl_ctx = NULL;
2159 #endif
2160 	/* TODO: Not ready for now, we should find a locking strategy between several GPAC instances...
2161 	 * gf_cache_cleanup_cache(dm);
2162 	 */
2163 	return dm;
2164 }
2165 
2166 GF_EXPORT
gf_dm_set_auth_callback(GF_DownloadManager * dm,Bool (* get_user_password)(void * usr_cbk,const char * site_url,char * usr_name,char * password),void * usr_cbk)2167 void gf_dm_set_auth_callback(GF_DownloadManager *dm,
2168                              Bool (*get_user_password)(void *usr_cbk, const char *site_url, char *usr_name, char *password),
2169                              void *usr_cbk)
2170 {
2171 	if (dm) {
2172 		dm->get_user_password = get_user_password;
2173 		dm->usr_cbk = usr_cbk;
2174 	}
2175 }
2176 
2177 GF_EXPORT
gf_dm_del(GF_DownloadManager * dm)2178 void gf_dm_del(GF_DownloadManager *dm)
2179 {
2180 	if (!dm)
2181 		return;
2182 	assert( dm->sessions);
2183 	assert( dm->cache_mx );
2184 	gf_mx_p( dm->cache_mx );
2185 
2186 	while (gf_list_count(dm->partial_downloads)) {
2187 		GF_PartialDownload * entry = (GF_PartialDownload*)gf_list_get( dm->partial_downloads, 0);
2188 		gf_list_rem( dm->partial_downloads, 0);
2189 		assert( entry->filename );
2190 		gf_file_delete( entry->filename );
2191 		gf_free(entry->filename );
2192 		entry->filename = NULL;
2193 		entry->url = NULL;
2194 		gf_free( entry );
2195 	}
2196 
2197 	/*destroy all pending sessions*/
2198 	while (gf_list_count(dm->sessions)) {
2199 		GF_DownloadSession *sess = (GF_DownloadSession *) gf_list_get(dm->sessions, 0);
2200 		gf_dm_sess_del(sess);
2201 	}
2202 	gf_list_del(dm->sessions);
2203 	dm->sessions = NULL;
2204 	assert( dm->skip_proxy_servers );
2205 	while (gf_list_count(dm->skip_proxy_servers)) {
2206 		char *serv = (char*)gf_list_get(dm->skip_proxy_servers, 0);
2207 		gf_list_rem(dm->skip_proxy_servers, 0);
2208 		gf_free(serv);
2209 	}
2210 	gf_list_del(dm->skip_proxy_servers);
2211 	dm->skip_proxy_servers = NULL;
2212 	assert( dm->credentials);
2213 	while (gf_list_count(dm->credentials)) {
2214 		gf_user_credentials_struct * cred = (gf_user_credentials_struct*)gf_list_get( dm->credentials, 0);
2215 		gf_list_rem( dm->credentials, 0);
2216 		gf_free( cred );
2217 	}
2218 	gf_list_del( dm->credentials);
2219 	dm->credentials = NULL;
2220 	assert( dm->cache_entries );
2221 	{
2222 		/* Deletes DownloadedCacheEntry and associated files if required */
2223 		Bool delete_my_files = gf_dm_needs_to_delete_cache(dm);
2224 		while (gf_list_count(dm->cache_entries)) {
2225 			const DownloadedCacheEntry entry = (const DownloadedCacheEntry)gf_list_get( dm->cache_entries, 0);
2226 			gf_list_rem( dm->cache_entries, 0);
2227 			if (delete_my_files)
2228 				gf_cache_entry_set_delete_files_when_deleted(entry);
2229 			gf_cache_delete_entry(entry);
2230 		}
2231 		gf_list_del( dm->cache_entries );
2232 		dm->cache_entries = NULL;
2233 	}
2234 
2235 	gf_list_del( dm->partial_downloads );
2236 	dm->partial_downloads = NULL;
2237 	/* TODO: Not ready for now, we should find a locking strategy between several GPAC instances...
2238 	* gf_cache_cleanup_cache(dm);
2239 	*/
2240 	if (dm->cache_directory)
2241 		gf_free(dm->cache_directory);
2242 	dm->cache_directory = NULL;
2243 
2244 #ifdef GPAC_HAS_SSL
2245 	if (dm->ssl_ctx) SSL_CTX_free(dm->ssl_ctx);
2246 #endif
2247 	/* Stored elsewhere, no need to free */
2248 	gf_mx_v( dm->cache_mx );
2249 	gf_mx_del( dm->cache_mx);
2250 	dm->cache_mx = NULL;
2251 	gf_free(dm);
2252 }
2253 
2254 /*!
2255  * Skip ICY metadata from SHOUTCAST or ICECAST streams.
2256  * Data will be skipped and parsed and sent as a GF_NETIO_Parameter to the user_io,
2257  * so modules interrested by those streams may use the data
2258 \param sess The GF_DownloadSession
2259 \param data last data received
2260 \param nbBytes The number of bytes contained into data
2261  */
gf_icy_skip_data(GF_DownloadSession * sess,const char * data,u32 nbBytes)2262 static void gf_icy_skip_data(GF_DownloadSession * sess, const char * data, u32 nbBytes)
2263 {
2264 	u32 icy_metaint;
2265 	if (!sess || !data ) return;
2266 
2267 	icy_metaint = sess->icy_metaint;
2268 	assert( icy_metaint > 0 );
2269 	while (nbBytes) {
2270 		if (sess->icy_bytes == icy_metaint) {
2271 			sess->icy_count = 1 + 16* (u8) data[0];
2272 			/*skip icy metadata*/
2273 			if (sess->icy_count > nbBytes) {
2274 				sess->icy_count -= nbBytes;
2275 				nbBytes = 0;
2276 			} else {
2277 				if (sess->icy_count > 1) {
2278 					GF_NETIO_Parameter par;
2279 					char szData[4096];
2280 					memset(szData, 0, 4096);
2281 					memcpy(szData, data+1, sess->icy_count-1);
2282 					szData[sess->icy_count] = 0;
2283 
2284 					par.error = GF_OK;
2285 					par.msg_type = GF_NETIO_PARSE_HEADER;
2286 					par.name = "icy-meta";
2287 					par.value = szData;
2288 					par.sess = sess;
2289 					GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[ICY] Found metainfo in stream=%s, (every %d bytes)\n", szData, icy_metaint));
2290 					gf_dm_sess_user_io(sess, &par);
2291 				} else {
2292 					GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[ICY] Empty metainfo in stream, (every %d bytes)\n", icy_metaint));
2293 				}
2294 				nbBytes -= sess->icy_count;
2295 				data += sess->icy_count;
2296 				sess->icy_count = 0;
2297 				sess->icy_bytes = 0;
2298 			}
2299 		} else {
2300 			GF_NETIO_Parameter par;
2301 			u32 left = icy_metaint - sess->icy_bytes;
2302 			if (left > nbBytes) {
2303 				left = nbBytes;
2304 				sess->icy_bytes += left;
2305 				nbBytes = 0;
2306 			} else {
2307 				sess->icy_bytes = icy_metaint;
2308 				nbBytes -= left;
2309 			}
2310 
2311 			par.msg_type = GF_NETIO_DATA_EXCHANGE;
2312 			par.data = data;
2313 			par.size = left;
2314 			gf_dm_sess_user_io(sess, &par);
2315 
2316 			data += left;
2317 		}
2318 	}
2319 }
2320 
2321 
gf_dm_get_chunk_data(GF_DownloadSession * sess,Bool first_chunk_in_payload,char * body_start,u32 * payload_size,u32 * header_size)2322 static char *gf_dm_get_chunk_data(GF_DownloadSession *sess, Bool first_chunk_in_payload, char *body_start, u32 *payload_size, u32 *header_size)
2323 {
2324 	u32 size;
2325 	s32 res;
2326 	char *te_header, *sep;
2327 
2328 	if (!sess || !body_start) return NULL;
2329 	if (!sess->chunked) return body_start;
2330 
2331 	if (sess->nb_left_in_chunk) {
2332 		if (sess->nb_left_in_chunk > *payload_size) {
2333 			sess->nb_left_in_chunk -= (*payload_size);
2334 			GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: still %d bytes to get\n", sess->nb_left_in_chunk));
2335 		} else {
2336 			*payload_size = sess->nb_left_in_chunk;
2337 			sess->nb_left_in_chunk = 0;
2338 			GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: last bytes in chunk received\n"));
2339 		}
2340 		*header_size = 0;
2341 		return body_start;
2342 	}
2343 
2344 	if (*payload_size == 2) {
2345 		*header_size = 0;
2346 	}
2347 	*header_size = 0;
2348 	/*skip remaining CRLF from previous chunk if any*/
2349 	if (*payload_size >= 2) {
2350 		if ((body_start[0]=='\r') && (body_start[1]=='\n')) {
2351 			body_start += 2;
2352 			*header_size = 2;
2353 			//chunk exactly ends our packet, reset session start time
2354 			if (*payload_size == 2) {
2355 				sess->chunk_run_time += gf_sys_clock_high_res() - sess->start_time;
2356 				sess->start_time = 0;
2357 			}
2358 		}
2359 		if (*payload_size <= 4) {
2360 			*header_size = 0;
2361 			return NULL;
2362 		}
2363 		te_header = strstr((char *) body_start, "\r\n");
2364 	} else {
2365 		//not enough bytes to read CRLF, don't bother parsing
2366 		te_header = NULL;
2367 	}
2368 
2369 	//start of a new chunk, update start time
2370 	if (!sess->start_time) {
2371 		sess->start_time = gf_sys_clock_high_res();
2372 		//assume RTT is session reply time, and that chunk transfer started RTT/2 ago
2373 		if (first_chunk_in_payload && sess->start_time > sess->reply_time/2)
2374 			sess->start_time -= sess->reply_time/2;
2375 		GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] First byte in chunk received (%d bytes in packet), new start time %u ms\n", *payload_size, (u32) sess->start_time/1000));
2376 	}
2377 
2378 	//cannot parse now, copy over the bytes
2379 	if (!te_header) {
2380 		*header_size = 0;
2381 		GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk encoding: current buffer does not contain enough bytes (%d) to read the size\n", *payload_size));
2382 		return NULL;
2383 	}
2384 
2385 	te_header[0] = 0;
2386 	//assert(strlen(body_start));
2387 	*header_size += (u32) (strlen(body_start)) + 2;
2388 
2389 	sep = strchr(body_start, ';');
2390 	if (sep) sep[0] = 0;
2391 	res = sscanf(body_start, "%x", &size);
2392 	if (res<0) {
2393 		te_header[0] = '\r';
2394 		if (sep) sep[0] = ';';
2395 		*header_size = 0;
2396 		*payload_size = 0;
2397 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Chunk encoding: fail to read chunk size from buffer %s, aborting\n", body_start));
2398 		return NULL;
2399 	}
2400 	if (sep) sep[0] = ';';
2401 	*payload_size = size;
2402 
2403 	GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Chunk Start: Header \"%s\" - header size %d - payload size %d (bytes done %d) at UTC "LLD"\n", body_start, 2+strlen(body_start), size, sess->bytes_done, gf_net_get_utc()));
2404 
2405 	te_header[0] = '\r';
2406 	if (!size)
2407 		sess->last_chunk_found = GF_TRUE;
2408 
2409 	sess->current_chunk_size = size;
2410 	sess->current_chunk_start = gf_sys_clock_high_res();
2411 	return te_header+2;
2412 }
2413 
2414 
dm_sess_update_download_rate(GF_DownloadSession * sess,Bool always_check)2415 static void dm_sess_update_download_rate(GF_DownloadSession * sess, Bool always_check)
2416 {
2417 	u64 runtime;
2418 	if (!always_check && (sess->bytes_done==sess->total_size)) return;
2419 
2420 	/*update state*/
2421 	runtime = sess->chunk_run_time;
2422 	if (sess->start_time) {
2423 		runtime += (gf_sys_clock_high_res() - sess->start_time);
2424 		if (sess->active_time) {
2425 			runtime = sess->active_time;
2426 		}
2427 	}
2428 	if (!runtime) runtime=1;
2429 
2430 	sess->bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / runtime);
2431 
2432 	if (sess->chunked) {
2433 		GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] bandwidth estimation: download time "LLD" us (chunk download time "LLD" us) - bytes %u - rate %u kbps\n", runtime, sess->chunk_run_time, sess->bytes_done, sess->bytes_per_sec*8/1000));
2434 	} else {
2435 		GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] bandwidth estimation: download time "LLD" us - bytes %u - rate %u kbps\n", runtime, sess->bytes_done, sess->bytes_per_sec*8/1000));
2436 	}
2437 }
2438 
2439 
gf_dm_data_received(GF_DownloadSession * sess,u8 * payload,u32 payload_size,Bool store_in_init,u32 * rewrite_size,u8 * original_payload)2440 static GFINLINE void gf_dm_data_received(GF_DownloadSession *sess, u8 *payload, u32 payload_size, Bool store_in_init, u32 *rewrite_size, u8 *original_payload)
2441 {
2442 	u32 nbBytes, remaining, hdr_size;
2443 	u8 *data;
2444 	Bool first_chunk_in_payload = GF_TRUE;
2445 	Bool flush_chunk = GF_FALSE;
2446 	GF_NETIO_Parameter par;
2447 
2448 	nbBytes = payload_size;
2449 	hdr_size = 0;
2450 	remaining = 0;
2451 	if (!payload)
2452 		return; //nothing to do
2453 	if (sess->chunked) {
2454  		data = (u8 *) gf_dm_get_chunk_data(sess, first_chunk_in_payload, (char *) payload, &nbBytes, &hdr_size);
2455 		//first_chunk_in_payload = GF_FALSE;
2456 		if (!hdr_size && !data) {
2457 			/* keep the data and wait for the rest */
2458 			sess->remaining_data_size = nbBytes;
2459 			sess->remaining_data = (char *)gf_realloc(sess->remaining_data, nbBytes * sizeof(char));
2460 			memcpy(sess->remaining_data, payload, nbBytes);
2461 			payload_size = 0;
2462 			payload = NULL;
2463 		} else if (hdr_size + nbBytes > payload_size) {
2464 			/* chunk header is processed but we will need several TCP frames to get the entire chunk*/
2465 			remaining = nbBytes + hdr_size - payload_size;
2466 			assert(payload_size >= hdr_size);
2467 			nbBytes = payload_size - hdr_size;
2468 			payload_size = 0;
2469 			payload = NULL;
2470 		} else {
2471 			payload_size -= hdr_size + nbBytes;
2472 			payload += hdr_size + nbBytes;
2473 			flush_chunk = GF_TRUE;
2474 		}
2475 		/*chunk transfer is done*/
2476 		if (sess->last_chunk_found) {
2477 			sess->total_size = sess->bytes_done;
2478 		}
2479 	} else {
2480 		data = payload;
2481 		remaining = payload_size = 0;
2482 	}
2483 
2484 	if (data && nbBytes && store_in_init) {
2485 		sess->init_data = (char *) gf_realloc(sess->init_data , sizeof(char) * (sess->init_data_size + nbBytes) );
2486 		memcpy(sess->init_data+sess->init_data_size, data, nbBytes);
2487 		sess->init_data_size += nbBytes;
2488 	}
2489 
2490 	//we have some new bytes received
2491 	if (nbBytes && !sess->remaining_data_size) {
2492 		sess->bytes_done += nbBytes;
2493 		dm_sess_update_download_rate(sess, GF_TRUE);
2494 
2495 		GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] url %s received %d new bytes (%d kbps)\n", gf_cache_get_url(sess->cache_entry), nbBytes, 8*sess->bytes_per_sec/1000));
2496 		if (sess->total_size && (sess->bytes_done > sess->total_size)) {
2497 			GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] url %s received more bytes than planned!! Got %d bytes vs %d content length\n", gf_cache_get_url(sess->cache_entry), sess->bytes_done , sess->total_size ));
2498 			sess->bytes_done = sess->total_size;
2499 		}
2500 
2501 		if (sess->icy_metaint > 0)
2502 			gf_icy_skip_data(sess, (char *) data, nbBytes);
2503 		else {
2504 			if (sess->use_cache_file)
2505 				gf_cache_write_to_cache( sess->cache_entry, sess, (char *) data, nbBytes);
2506 
2507 			par.msg_type = GF_NETIO_DATA_EXCHANGE;
2508 			par.error = GF_OK;
2509 			par.data = (char *) data;
2510 			par.size = nbBytes;
2511 			par.reply = flush_chunk;
2512 			gf_dm_sess_user_io(sess, &par);
2513 		}
2514 	}
2515 	//and we're done
2516 	if (sess->total_size && (sess->bytes_done == sess->total_size)) {
2517 		u64 run_time;
2518 		if (sess->use_cache_file) {
2519 			gf_cache_close_write_cache(sess->cache_entry, sess, GF_TRUE);
2520 			GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP,
2521 			       ("[CACHE] url %s saved as %s\n", gf_cache_get_url(sess->cache_entry), gf_cache_get_cache_filename(sess->cache_entry)));
2522 		}
2523 
2524 		gf_dm_disconnect(sess, GF_FALSE);
2525 		par.msg_type = GF_NETIO_DATA_TRANSFERED;
2526 		par.error = GF_OK;
2527 
2528 		gf_dm_sess_user_io(sess, &par);
2529 		sess->total_time_since_req = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
2530 		run_time = gf_sys_clock_high_res() - sess->start_time;
2531 		if (sess->in_time) {
2532 			sess->active_time += gf_sys_clock_high_res() - sess->in_time;
2533 			if (run_time > sess->active_time) {
2534 				sess->total_time_since_req -= (u32) (run_time - sess->active_time);
2535 				run_time = sess->active_time; // + (run_time - sess->active_time)/3;
2536 				sess->bytes_per_sec = (u32) ((1000000 * (u64) sess->bytes_done) / run_time);
2537 			}
2538 		}
2539 
2540 		GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] url %s (%d bytes) downloaded in "LLU" us (%d kbps) (%d us since request - got response in %d us - active time %d us)\n", gf_cache_get_url(sess->cache_entry), sess->bytes_done,
2541 		                                     run_time, 8*sess->bytes_per_sec/1000, sess->total_time_since_req, sess->reply_time, sess->active_time ? sess->active_time : sess->reply_time ));
2542 
2543 		if (sess->chunked && (payload_size==2))
2544 			payload_size=0;
2545 	}
2546 
2547 	if (rewrite_size && sess->chunked && data && original_payload) {
2548 		//use memmove since regions overlap
2549 		memmove(original_payload + *rewrite_size, data, nbBytes);
2550 		*rewrite_size += nbBytes;
2551 	}
2552 
2553 	if (!sess->nb_left_in_chunk && remaining) {
2554 		sess->nb_left_in_chunk = remaining;
2555 	} else if (payload_size) {
2556 		gf_dm_data_received(sess, payload, payload_size, store_in_init, rewrite_size, original_payload);
2557 	}
2558 }
2559 
dm_exceeds_cap_rate(GF_DownloadManager * dm)2560 static Bool dm_exceeds_cap_rate(GF_DownloadManager * dm)
2561 {
2562 	u32 cumul_rate = 0;
2563 	u32 nb_sess = 0;
2564 	u32 i, count = gf_list_count(dm->sessions);
2565 
2566 	//check if this fits with all other sessions
2567 	for (i=0; i<count; i++) {
2568 		GF_DownloadSession * sess = (GF_DownloadSession*)gf_list_get(dm->sessions, i);
2569 
2570 		//session not running done
2571 		if (sess->status != GF_NETIO_DATA_EXCHANGE) continue;
2572 
2573 		dm_sess_update_download_rate(sess, GF_FALSE);
2574 		cumul_rate += sess->bytes_per_sec;
2575 		nb_sess ++;
2576 	}
2577 	if ( cumul_rate >= nb_sess * dm->limit_data_rate)
2578 		return GF_TRUE;
2579 
2580 	return GF_FALSE;
2581 }
2582 
2583 
2584 GF_EXPORT
gf_dm_sess_fetch_data(GF_DownloadSession * sess,char * buffer,u32 buffer_size,u32 * read_size)2585 GF_Err gf_dm_sess_fetch_data(GF_DownloadSession *sess, char *buffer, u32 buffer_size, u32 *read_size)
2586 {
2587 	u32 size;
2588 	GF_Err e;
2589 
2590 	if (!buffer || !buffer_size) {
2591 		if (sess->put_state) {
2592 			sess->put_state = 2;
2593 			sess->status = GF_NETIO_WAIT_FOR_REPLY;
2594 			return GF_OK;
2595 		}
2596 		return GF_BAD_PARAM;
2597 	}
2598 	if (sess->th) return GF_BAD_PARAM;
2599 	if (sess->status == GF_NETIO_DISCONNECTED) {
2600 		if (!sess->init_data_size)
2601 			return GF_EOS;
2602 	}
2603 	else if (sess->status > GF_NETIO_DATA_TRANSFERED) return GF_BAD_PARAM;
2604 
2605 	*read_size = 0;
2606 	if (sess->status == GF_NETIO_DATA_TRANSFERED) {
2607 		if (!sess->server_mode) return GF_EOS;
2608 		if (!sess->init_data_size && sess->total_size && (sess->total_size==sess->bytes_done)) return GF_EOS;
2609 		sess->status = GF_NETIO_DATA_EXCHANGE;
2610 	}
2611 
2612 	sess->in_time = gf_sys_clock_high_res();
2613 	if (sess->status == GF_NETIO_SETUP) {
2614 		gf_dm_connect(sess);
2615 		if (sess->last_error) return sess->last_error;
2616 		e = GF_OK;
2617 	} else if (sess->status < GF_NETIO_DATA_EXCHANGE) {
2618 		sess->do_requests(sess);
2619 		e = sess->last_error;
2620 	}
2621 	/*we're running but we had data previously*/
2622 	else if (sess->init_data) {
2623 		e = GF_OK;
2624 		if (sess->init_data_size<=buffer_size) {
2625 			memcpy(buffer, sess->init_data, sizeof(char)*sess->init_data_size);
2626 			*read_size = sess->init_data_size;
2627 			gf_free(sess->init_data);
2628 			sess->init_data = NULL;
2629 			if (sess->init_data_size==sess->total_size)
2630 				e = GF_EOS;
2631 			sess->init_data_size = 0;
2632 		} else {
2633 			memcpy(buffer, sess->init_data, sizeof(char)*buffer_size);
2634 			*read_size = buffer_size;
2635 			sess->init_data_size -= buffer_size;
2636 			memmove(sess->init_data, sess->init_data+buffer_size, sizeof(char)*sess->init_data_size);
2637 			e = GF_OK;
2638 		}
2639 	} else {
2640 
2641 		if (sess->dm && sess->dm->limit_data_rate && dm_exceeds_cap_rate(sess->dm)) {
2642 			if (sess->idle_time) sess->active_time += sess->in_time - sess->idle_time;
2643 			sess->idle_time = sess->in_time;
2644 			return GF_IP_NETWORK_EMPTY;
2645 		}
2646 
2647 		if (sess->remaining_data && sess->remaining_data_size) {
2648 			if (sess->remaining_data_size >= buffer_size) {
2649 				GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] No HTTP chunk header found for %d bytes, assuming broken chunk transfer and aborting\n", sess->remaining_data_size));
2650 				return GF_NON_COMPLIANT_BITSTREAM;
2651 			}
2652 			memcpy(buffer, sess->remaining_data, sess->remaining_data_size);
2653 		}
2654 
2655 		e = gf_dm_read_data(sess, buffer + sess->remaining_data_size, buffer_size - sess->remaining_data_size, read_size);
2656 		if (!e) {
2657 			size = sess->remaining_data_size + (*read_size);
2658 			sess->remaining_data_size = 0;
2659 			*read_size = 0;
2660 			gf_dm_data_received(sess, (u8 *) buffer, size, GF_FALSE, read_size, buffer);
2661 			if (!sess->chunked)
2662 				*read_size = size;
2663 		}
2664 	}
2665 	sess->active_time += gf_sys_clock_high_res() - sess->in_time;
2666 
2667 	if (sess->server_mode && (sess->status == GF_NETIO_DATA_EXCHANGE)) {
2668 		sess->status = GF_NETIO_DATA_TRANSFERED;
2669 	}
2670 
2671 	return e;
2672 }
2673 
2674 GF_EXPORT
gf_dm_sess_get_stats(GF_DownloadSession * sess,const char ** server,const char ** path,u64 * total_size,u64 * bytes_done,u32 * bytes_per_sec,GF_NetIOStatus * net_status)2675 GF_Err gf_dm_sess_get_stats(GF_DownloadSession * sess, const char **server, const char **path, u64 *total_size, u64 *bytes_done, u32 *bytes_per_sec, GF_NetIOStatus *net_status)
2676 {
2677 	if (!sess) return GF_BAD_PARAM;
2678 	if (server) *server = sess->server_name;
2679 	if (path) *path = sess->remote_path;
2680 	if (total_size) {
2681 		if (sess->total_size==SIZE_IN_STREAM) *total_size  = 0;
2682 		else *total_size = sess->total_size;
2683 	}
2684 	if (bytes_done) *bytes_done = sess->bytes_done;
2685 	if (bytes_per_sec) *bytes_per_sec = sess->bytes_per_sec;
2686 	if (net_status) *net_status = sess->status;
2687 	if (sess->status == GF_NETIO_DISCONNECTED) return GF_EOS;
2688 	else if (sess->status == GF_NETIO_STATE_ERROR) return GF_SERVICE_ERROR;
2689 	return GF_OK;
2690 }
2691 
2692 GF_EXPORT
gf_dm_sess_get_utc_start(GF_DownloadSession * sess)2693 u64 gf_dm_sess_get_utc_start(GF_DownloadSession * sess)
2694 {
2695 	if (!sess) return 0;
2696 	return sess->start_time_utc;
2697 }
2698 
2699 GF_EXPORT
gf_dm_sess_get_cache_name(GF_DownloadSession * sess)2700 const char *gf_dm_sess_get_cache_name(GF_DownloadSession * sess)
2701 {
2702 	if (!sess) return NULL;
2703 	if (! sess->cache_entry || sess->needs_cache_reconfig) return NULL;
2704 	if (!sess->use_cache_file) return NULL;
2705 	return gf_cache_get_cache_filename(sess->cache_entry);
2706 }
2707 
2708 #if 0 //unused
2709 /*!
2710  * Tells whether session can be cached on disk.
2711  * Typically, when request has no content length, it deserves being streamed an cannot be cached
2712  * (ICY or MPEG-streamed content
2713 \param sess The session
2714 \param True if a cache can be created
2715  */
2716 Bool gf_dm_sess_can_be_cached_on_disk(const GF_DownloadSession *sess)
2717 {
2718 	if (!sess) return GF_FALSE;
2719 	return gf_cache_get_content_length(sess->cache_entry) != 0;
2720 }
2721 #endif
2722 
2723 GF_EXPORT
gf_dm_sess_abort(GF_DownloadSession * sess)2724 void gf_dm_sess_abort(GF_DownloadSession * sess)
2725 {
2726 	if (sess) {
2727 		gf_mx_p(sess->mx);
2728 		gf_dm_disconnect(sess, GF_TRUE);
2729 		sess->status = GF_NETIO_STATE_ERROR;
2730 		gf_mx_v(sess->mx);
2731 	}
2732 }
2733 
2734 #if 0 //unused
2735 /*!
2736 \brief gets private data
2737  *
2738  *Gets private data associated with the session.
2739 \param sess the download session
2740 \return the private data
2741 \warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
2742  */
2743 void *gf_dm_sess_get_private(GF_DownloadSession * sess)
2744 {
2745 	return sess ? sess->ext : NULL;
2746 }
2747 
2748 /*!
2749 \brief sets private data
2750  *
2751  *associate private data with the session.
2752 \param sess the download session
2753 \param private_data the private data
2754 \warning the private_data parameter is reserved for bandwidth statistics per service when used in the GPAC terminal.
2755  */
2756 void gf_dm_sess_set_private(GF_DownloadSession * sess, void *private_data)
2757 {
2758 	if (sess) sess->ext = private_data;
2759 }
2760 #endif
2761 
2762 /*!
2763  * Sends the HTTP headers
2764 \param sess The GF_DownloadSession
2765 \param sHTTP buffer containing the request
2766 \param GF_OK if everything went fine, the error otherwise
2767  */
http_send_headers(GF_DownloadSession * sess,char * sHTTP)2768 static GF_Err http_send_headers(GF_DownloadSession *sess, char * sHTTP) {
2769 	GF_Err e;
2770 	GF_NETIO_Parameter par;
2771 	Bool no_cache = GF_FALSE;
2772 	char range_buf[1024];
2773 	char pass_buf[1124];
2774 	const char *user_agent;
2775 	const char *url;
2776 	const char *user_profile;
2777 	const char *param_string;
2778 	Bool has_accept, has_connection, has_range, has_agent, has_language, send_profile, has_mime;
2779 	assert (sess->status == GF_NETIO_CONNECTED);
2780 
2781 	gf_dm_clear_headers(sess);
2782 	sess->active_time = 0;
2783 
2784 	assert(sess->remaining_data_size == 0);
2785 
2786 	if (sess->needs_cache_reconfig) {
2787 		gf_dm_configure_cache(sess);
2788 		sess->needs_cache_reconfig = 0;
2789 	}
2790 	if (sess->from_cache_only) {
2791 		sess->request_start_time = gf_sys_clock_high_res();
2792 		sess->req_hdr_size = 0;
2793 		sess->status = GF_NETIO_WAIT_FOR_REPLY;
2794 		gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK);
2795 		return GF_OK;
2796 	}
2797 
2798 	/*setup authentification*/
2799 	strcpy(pass_buf, "");
2800 	sess->creds = gf_find_user_credentials_for_site( sess->dm, sess->server_name );
2801 	if (sess->creds && sess->creds->valid) {
2802 		sprintf(pass_buf, "Authorization: Basic %s", sess->creds->digest);
2803 	}
2804 
2805 	user_agent = gf_opts_get_key("core", "ua");
2806 	if (!user_agent) user_agent = GF_DOWNLOAD_AGENT_NAME;
2807 
2808 	sess->put_state = 0;
2809 
2810 	par.error = GF_OK;
2811 	par.msg_type = GF_NETIO_GET_METHOD;
2812 	par.name = NULL;
2813 	gf_dm_sess_user_io(sess, &par);
2814 	if (!par.name || sess->server_only_understand_get) {
2815 		par.name = "GET";
2816 	}
2817 
2818 	if (par.name) {
2819 		if (!strcmp(par.name, "GET")) sess->http_read_type = GET;
2820 		else if (!strcmp(par.name, "HEAD")) sess->http_read_type = HEAD;
2821 		else sess->http_read_type = OTHER;
2822 
2823 		if (!strcmp(par.name, "PUT") || !strcmp(par.name, "POST"))
2824 			sess->put_state = 1;
2825 	} else {
2826 		sess->http_read_type = GET;
2827 	}
2828 
2829 	url = (sess->proxy_enabled==1) ? sess->orig_url : sess->remote_path;
2830 
2831 	param_string = gf_opts_get_key("core", "query-string");
2832 	if (param_string) {
2833 		if (strchr(sess->remote_path, '?')) {
2834 			sprintf(sHTTP, "%s %s&%s HTTP/1.0\r\nHost: %s\r\n" ,
2835 			        par.name ? par.name : "GET", url, param_string, sess->server_name);
2836 		} else {
2837 			sprintf(sHTTP, "%s %s?%s HTTP/1.0\r\nHost: %s\r\n" ,
2838 			        par.name ? par.name : "GET", url, param_string, sess->server_name);
2839 		}
2840 	} else {
2841 		sprintf(sHTTP, "%s %s HTTP/1.1\r\nHost: %s\r\n" ,
2842 		        par.name ? par.name : "GET", url, sess->server_name);
2843 	}
2844 
2845 	/*get all headers*/
2846 	has_agent = has_accept = has_connection = has_range = has_language = has_mime = GF_FALSE;
2847 	while (1) {
2848 		par.msg_type = GF_NETIO_GET_HEADER;
2849 		par.value = NULL;
2850 		gf_dm_sess_user_io(sess, &par);
2851 		if (!par.value) break;
2852 		strcat(sHTTP, par.name);
2853 		strcat(sHTTP, ": ");
2854 		strcat(sHTTP, par.value);
2855 		strcat(sHTTP, "\r\n");
2856 		if (!strcmp(par.name, "Accept")) has_accept = GF_TRUE;
2857 		else if (!strcmp(par.name, "Connection")) has_connection = GF_TRUE;
2858 		else if (!strcmp(par.name, "Range")) has_range = GF_TRUE;
2859 		else if (!strcmp(par.name, "User-Agent")) has_agent = GF_TRUE;
2860 		else if (!strcmp(par.name, "Accept-Language")) has_language = GF_TRUE;
2861 		else if (!strcmp(par.name, "Content-Type")) has_mime = GF_TRUE;
2862 		else if (!stricmp(par.name, "Transfer-Encoding")) {
2863 			if (!stricmp(par.value, "chunked"))
2864 				sess->chunked = GF_TRUE;
2865 		}
2866 
2867 		if (!par.msg_type) break;
2868 	}
2869 	if (!has_agent) {
2870 		strcat(sHTTP, "User-Agent: ");
2871 		strcat(sHTTP, user_agent);
2872 		strcat(sHTTP, "\r\n");
2873 	}
2874 	/*no mime and POST/PUT, default to octet stream*/
2875 	if (!has_mime && (sess->http_read_type==OTHER)) strcat(sHTTP, "Content-Type: application/octet-stream\r\n");
2876 	if (!has_accept && (sess->http_read_type!=OTHER) ) strcat(sHTTP, "Accept: */*\r\n");
2877 	if (sess->proxy_enabled==1) strcat(sHTTP, "Proxy-Connection: Keep-alive\r\n");
2878 	else if (!has_connection) strcat(sHTTP, "Connection: Keep-Alive\r\n");
2879 	if (!has_range && sess->needs_range) {
2880 		if (!sess->range_end) sprintf(range_buf, "Range: bytes="LLD"-\r\n", sess->range_start);
2881 		else sprintf(range_buf, "Range: bytes="LLD"-"LLD"\r\n", sess->range_start, sess->range_end);
2882 		strcat(sHTTP, range_buf);
2883 
2884 		no_cache = GF_TRUE;
2885 	}
2886 	if (!has_language) {
2887 		const char *opt = gf_opts_get_key("core", "lang");
2888 		if (opt) {
2889 			strcat(sHTTP, "Accept-Language: ");
2890 			strcat(sHTTP, opt);
2891 			strcat(sHTTP, "\r\n");
2892 		}
2893 	}
2894 
2895 
2896 	if (strlen(pass_buf)) {
2897 		strcat(sHTTP, pass_buf);
2898 		strcat(sHTTP, "\r\n");
2899 	}
2900 
2901 	par.msg_type = GF_NETIO_GET_CONTENT;
2902 	par.data = NULL;
2903 	par.size = 0;
2904 
2905 	/*check if we have personalization info*/
2906 	send_profile = GF_FALSE;
2907 	user_profile = gf_opts_get_key("core", "user-profileid");
2908 
2909 	if (user_profile) {
2910 		strcat(sHTTP, "X-UserProfileID: ");
2911 		strcat(sHTTP, user_profile);
2912 		strcat(sHTTP, "\r\n");
2913 	} else if ((sess->http_read_type == GET) || (sess->http_read_type == HEAD) ) {
2914 		user_profile = gf_opts_get_key("core", "user-profile");
2915 		if (user_profile && gf_file_exists(user_profile)) {
2916 			FILE *profile = gf_fopen(user_profile, "rb");
2917 			if (profile) {
2918 				par.size = (u32) gf_fsize(profile);
2919 				gf_fclose(profile);
2920 				sprintf(range_buf, "Content-Length: %d\r\n", par.size);
2921 				strcat(sHTTP, range_buf);
2922 				strcat(sHTTP, "Content-Type: text/xml\r\n");
2923 				send_profile = GF_TRUE;
2924 			}
2925 		}
2926 	}
2927 
2928 
2929 	if (!send_profile) {
2930 		gf_dm_sess_user_io(sess, &par);
2931 		if (par.data && par.size) {
2932 			sprintf(range_buf, "Content-Length: %d\r\n", par.size);
2933 			strcat(sHTTP, range_buf);
2934 		} else {
2935 			par.data = NULL;
2936 			par.size = 0;
2937 		}
2938 	}
2939 
2940 	if (sess->http_read_type!=OTHER) {
2941 		/*signal we support title streaming*/
2942 //		if (!strcmp(sess->remote_path, "/")) strcat(sHTTP, "icy-metadata:1\r\n");
2943 		/* This will force the server to respond with Icy-Metaint */
2944 		strcat(sHTTP, "Icy-Metadata: 1\r\n");
2945 
2946 		/*cached headers are not appended in POST*/
2947 		if (!no_cache && !sess->disable_cache && (GF_OK < gf_cache_append_http_headers( sess->cache_entry, sHTTP)) ) {
2948 			GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("Cache Entry : %p, FAILED to append cache directives.", sess->cache_entry));
2949 		}
2950 	}
2951 
2952 	strcat(sHTTP, "\r\n");
2953 
2954 	if (send_profile || par.data) {
2955 		u32 len = (u32) strlen(sHTTP);
2956 		char *tmp_buf = (char*)gf_malloc(sizeof(char)*(len+par.size+1));
2957 		strcpy(tmp_buf, sHTTP);
2958 		if (par.data) {
2959 			memcpy(tmp_buf+len, par.data, par.size);
2960 			tmp_buf[len+par.size] = 0;
2961 
2962 			sess->put_state = 2;
2963 		} else {
2964 			FILE *profile;
2965 			user_profile = gf_opts_get_key("core", "user-profile");
2966 			assert (user_profile);
2967 			profile = gf_fopen(user_profile, "rt");
2968 			if (profile) {
2969 				s32 read = (s32) gf_fread(tmp_buf+len, par.size, profile);
2970 				if ((read<0) || (read< (s32) par.size)) {
2971 					GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP,
2972 					       ("[HTTP] Error while loading UserProfile, size=%d, should be %d.", read, par.size));
2973 					for (; read < (s32) par.size; read++) {
2974 						tmp_buf[len + read] = 0;
2975 					}
2976 				}
2977 				gf_fclose(profile);
2978 			} else {
2979 				GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Error while loading Profile file %s.", user_profile));
2980 			}
2981 		}
2982 
2983 		sess->request_start_time = gf_sys_clock_high_res();
2984 		sess->req_hdr_size = len+par.size;
2985 
2986 #ifdef GPAC_HAS_SSL
2987 		if (sess->ssl) {
2988 			e = gf_ssl_write(sess->ssl, tmp_buf, len+par.size);
2989 		} else
2990 #endif
2991 			e = gf_sk_send(sess->sock, tmp_buf, len+par.size);
2992 
2993 		GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending request at UTC "LLD" %s\n\n", gf_net_get_utc(), tmp_buf));
2994 		gf_free(tmp_buf);
2995 	} else {
2996 		u32 len = (u32) strlen(sHTTP);
2997 
2998 		sess->request_start_time = gf_sys_clock_high_res();
2999 		sess->req_hdr_size = len;
3000 
3001 #ifdef GPAC_HAS_SSL
3002 		if (sess->ssl) {
3003 			e = gf_ssl_write(sess->ssl, sHTTP, len);
3004 		} else
3005 #endif
3006 			e = gf_sk_send(sess->sock, sHTTP, len);
3007 
3008 #ifndef GPAC_DISABLE_LOG
3009 		if (e) {
3010 			GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Error sending request %s\n", gf_error_to_string(e) ));
3011 		} else {
3012 			GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending request at UTC "LLU" %s\n\n", gf_net_get_utc(), sHTTP));
3013 		}
3014 #endif
3015 	}
3016 
3017 	if (e) {
3018 		sess->status = GF_NETIO_STATE_ERROR;
3019 		sess->last_error = e;
3020 		gf_dm_sess_notify_state(sess, GF_NETIO_STATE_ERROR, e);
3021 		return e;
3022 	}
3023 
3024 	gf_dm_sess_notify_state(sess, GF_NETIO_WAIT_FOR_REPLY, GF_OK);
3025 	if (sess->put_state==1) {
3026 		sess->status = GF_NETIO_DATA_TRANSFERED;
3027 	} else {
3028 		sess->status = GF_NETIO_WAIT_FOR_REPLY;
3029 	}
3030 	return GF_OK;
3031 }
3032 
3033 
3034 /*!
3035  * Parse the remaining part of body
3036 \param sess The session
3037 \param sHTTP the data buffer
3038 \param The error code if any
3039  */
http_parse_remaining_body(GF_DownloadSession * sess,char * sHTTP)3040 static GF_Err http_parse_remaining_body(GF_DownloadSession * sess, char * sHTTP)
3041 {
3042 	GF_Err e;
3043 	u32 buf_size = sess->dm ? sess->dm->read_buf_size : GF_DOWNLOAD_BUFFER_SIZE;
3044 
3045 	while (1) {
3046 		u32 remaining_data_size, size=0;
3047 		if (sess->status>=GF_NETIO_DISCONNECTED)
3048 			return GF_REMOTE_SERVICE_ERROR;
3049 
3050 		if (sess->dm && sess->dm->limit_data_rate && sess->bytes_per_sec) {
3051 			if (dm_exceeds_cap_rate(sess->dm)) {
3052 				gf_sleep(1);
3053 				return GF_OK;
3054 			}
3055 		}
3056 
3057 		//the data remaining from the last buffer (i.e size for chunk that couldn't be read because the buffer does not contain enough bytes)
3058 		if (sess->remaining_data && sess->remaining_data_size) {
3059 			if (sess->remaining_data_size >= buf_size) {
3060 				GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] No HTTP chunk header found for %d bytes, assuming broken chunk transfer and aborting\n", sess->remaining_data_size));
3061 				return GF_NON_COMPLIANT_BITSTREAM;
3062 			}
3063 			memcpy(sHTTP, sess->remaining_data, sess->remaining_data_size);
3064 		}
3065 		e = gf_dm_read_data(sess, sHTTP + sess->remaining_data_size, buf_size - sess->remaining_data_size, &size);
3066 		if (e!= GF_IP_CONNECTION_CLOSED && (!size || e == GF_IP_NETWORK_EMPTY)) {
3067 			if (e == GF_IP_CONNECTION_CLOSED || (!sess->total_size && !sess->chunked && (gf_sys_clock_high_res() - sess->start_time > 5000000))) {
3068 				sess->total_size = sess->bytes_done;
3069 				gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3070 				assert(sess->server_name);
3071 				GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e)));
3072 				gf_dm_disconnect(sess, (e == GF_IP_CONNECTION_CLOSED) ? GF_TRUE : GF_FALSE);
3073 			}
3074 			return GF_OK;
3075 		}
3076 
3077 		if (e) {
3078 			if (sess->sock && (e == GF_IP_CONNECTION_CLOSED)) {
3079 				u32 len = gf_cache_get_content_length(sess->cache_entry);
3080 				if (size > 0)
3081 					gf_dm_data_received(sess, (u8 *) sHTTP, size, GF_FALSE, NULL, NULL);
3082 				if ( ( (len == 0) && sess->use_cache_file)
3083 				        /*ivica patch*/
3084 				        || (size==0)
3085 				   ) {
3086 					sess->total_size = sess->bytes_done;
3087 					// HTTP 1.1 without content length...
3088 					gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3089 					assert(sess->server_name);
3090 					GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Disconnected from %s: %s\n", sess->server_name, gf_error_to_string(e)));
3091 					if (sess->use_cache_file)
3092 						gf_cache_set_content_length(sess->cache_entry, sess->bytes_done);
3093 					e = GF_OK;
3094 				}
3095 			}
3096 			gf_dm_disconnect(sess, GF_TRUE);
3097 			sess->last_error = e;
3098 			gf_dm_sess_notify_state(sess, sess->status, e);
3099 			return e;
3100 		}
3101 
3102 		remaining_data_size = sess->remaining_data_size;
3103 		sess->remaining_data_size = 0;
3104 
3105 		sHTTP[size + remaining_data_size] = 0;
3106 
3107 		gf_dm_data_received(sess, (u8 *) sHTTP, size + remaining_data_size, GF_FALSE, NULL, NULL);
3108 
3109 		/*socket empty*/
3110 		if (size < buf_size) {
3111 			return GF_OK;
3112 		}
3113 	}
3114 	return GF_OK;
3115 }
3116 
notify_headers(GF_DownloadSession * sess,char * sHTTP,s32 bytesRead,s32 BodyStart)3117 static void notify_headers(GF_DownloadSession *sess, char * sHTTP, s32 bytesRead, s32 BodyStart)
3118 {
3119 	GF_NETIO_Parameter par;
3120 	u32 i, count;
3121 
3122 	count = gf_list_count(sess->headers);
3123 	memset(&par, 0, sizeof(GF_NETIO_Parameter));
3124 
3125 	for (i=0; i<count; i++) {
3126 		GF_HTTPHeader *hdrp = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
3127 		par.name = hdrp->name;
3128 		par.value = hdrp->value;
3129 
3130 		par.error = GF_OK;
3131 		par.msg_type = GF_NETIO_PARSE_HEADER;
3132 		gf_dm_sess_user_io(sess, &par);
3133 	}
3134 
3135 	if (sHTTP) {
3136 		sHTTP[bytesRead]=0;
3137 		par.error = GF_OK;
3138 		par.data = sHTTP + BodyStart;
3139 		par.size = (u32) strlen(par.data);
3140 		par.msg_type = GF_NETIO_DATA_EXCHANGE;
3141 		gf_dm_sess_user_io(sess, &par);
3142 	}
3143 }
3144 
3145 /*!
3146  * Waits for the response HEADERS, parse the information... and so on
3147 \param sess The session
3148 \param sHTTP the data buffer
3149  */
wait_for_header_and_parse(GF_DownloadSession * sess,char * sHTTP)3150 static GF_Err wait_for_header_and_parse(GF_DownloadSession *sess, char * sHTTP)
3151 {
3152 	GF_NETIO_Parameter par;
3153 	s32 bytesRead, BodyStart;
3154 	u32 res, i, buf_size;
3155 	s32 LinePos, Pos;
3156 	u32 method=0;
3157 	u32 rsp_code, ContentLength, first_byte, last_byte, total_size, range, no_range;
3158 	Bool connection_closed = GF_FALSE;
3159 	char buf[1025];
3160 	char comp[400];
3161 	GF_Err e;
3162 	char * new_location;
3163 	const char * mime_type;
3164 
3165 
3166 	if (sess->server_mode) {
3167 		assert( sess->status == GF_NETIO_CONNECTED );
3168 	} else {
3169 		assert( sess->status == GF_NETIO_WAIT_FOR_REPLY );
3170 		if (!(sess->flags & GF_NETIO_SESSION_NOT_CACHED)) {
3171 			sess->use_cache_file = sess->dm->disable_cache ? GF_FALSE : GF_TRUE;
3172 		}
3173 	}
3174 	bytesRead = res = 0;
3175 	new_location = NULL;
3176 
3177 	if (sess->from_cache_only) {
3178 		sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
3179 		sess->rsp_hdr_size = 0;
3180 		sess->total_size = sess->bytes_done = gf_cache_get_content_length(sess->cache_entry);
3181 
3182 		memset(&par, 0, sizeof(GF_NETIO_Parameter));
3183 		par.msg_type = GF_NETIO_DATA_TRANSFERED;
3184 		par.error = GF_OK;
3185 		gf_dm_sess_user_io(sess, &par);
3186 		gf_dm_disconnect(sess, GF_FALSE);
3187 		return GF_OK;
3188 	}
3189 
3190 	buf_size = sess->dm ? sess->dm->read_buf_size : GF_DOWNLOAD_BUFFER_SIZE;
3191 
3192 	//always set start time to the time at last attempt reply parsing
3193 	sess->start_time = gf_sys_clock_high_res();
3194 	sess->start_time_utc = gf_net_get_utc();
3195 	sess->chunked = GF_FALSE;
3196 	sess->chunk_run_time = 0;
3197 	sess->last_chunk_found = GF_FALSE;
3198 //	gf_sk_reset(sess->sock);
3199 	sHTTP[0] = 0;
3200 
3201 	while (1) {
3202 		e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res);
3203 		switch (e) {
3204 		case GF_IP_NETWORK_EMPTY:
3205 			if (!bytesRead) {
3206 				e = gf_sk_probe(sess->sock);
3207 				if ((e==GF_IP_CONNECTION_CLOSED) || (gf_sys_clock_high_res() - sess->request_start_time > 1000 * sess->request_timeout)
3208 				) {
3209 					sess->last_error = (e==GF_IP_CONNECTION_CLOSED) ? e : GF_IP_NETWORK_EMPTY;
3210 					sess->status = GF_NETIO_STATE_ERROR;
3211 					return GF_IP_NETWORK_EMPTY;
3212 				}
3213 				assert(res==0);
3214 				return GF_OK;
3215 			}
3216 			continue;
3217 		/*socket has been closed while configuring, retry (not sure if the server got the GET)*/
3218 		case GF_IP_CONNECTION_CLOSED:
3219 			if (sess->http_read_type == HEAD) {
3220 				/* Some servers such as shoutcast directly close connection if HEAD or an unknown method is issued */
3221 				sess->server_only_understand_get = GF_TRUE;
3222 			}
3223 			if (sess->server_mode) {
3224 				sess->last_error = GF_IP_CONNECTION_CLOSED;
3225 				sess->status = GF_NETIO_DISCONNECTED;
3226 				GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connection closed by client\n", sess->remote_path));
3227 				return GF_IP_CONNECTION_CLOSED;
3228 			}
3229 			GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Connection closed by server when processing %s - retrying\n", sess->remote_path));
3230 			gf_dm_disconnect(sess, GF_TRUE);
3231 
3232 			if (sess->num_retry)
3233 				sess->status = GF_NETIO_SETUP;
3234 			else {
3235 				sess->last_error = e;
3236 				sess->status = GF_NETIO_STATE_ERROR;
3237 			}
3238 			return e;
3239 		case GF_OK:
3240 			if (!res)
3241 				return GF_OK;
3242 			break;
3243 		default:
3244 			goto exit;
3245 		}
3246 		bytesRead += res;
3247 
3248 		/*locate body start*/
3249 		BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\r\n\r\n");
3250 		if (BodyStart > 0) {
3251 			BodyStart += 4;
3252 			break;
3253 		}
3254 		BodyStart = gf_token_find(sHTTP, 0, bytesRead, "\n\n");
3255 		if (BodyStart > 0) {
3256 			BodyStart += 2;
3257 			break;
3258 		}
3259 	}
3260 	if (bytesRead < 0) {
3261 		e = GF_REMOTE_SERVICE_ERROR;
3262 		goto exit;
3263 	}
3264 	if (!BodyStart)
3265 		BodyStart = bytesRead;
3266 
3267 	sHTTP[BodyStart-1] = 0;
3268 	GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] %s\n\n", sHTTP));
3269 
3270 	sess->reply_time = (u32) (gf_sys_clock_high_res() - sess->request_start_time);
3271 	sess->rsp_hdr_size = BodyStart;
3272 
3273 	LinePos = gf_token_get_line(sHTTP, 0, bytesRead, buf, 1024);
3274 	Pos = gf_token_get(buf, 0, " \t\r\n", comp, 400);
3275 
3276 	if (sess->server_mode) {
3277 		if (!strcmp(comp, "GET")) method = GF_HTTP_GET;
3278 		else if (!strcmp(comp, "HEAD")) method = GF_HTTP_HEAD;
3279 		else if (!strcmp(comp, "OPTIONS")) method = GF_HTTP_OPTIONS;
3280 		else if (!strcmp(comp, "PUT")) method = GF_HTTP_PUT;
3281 		else if (!strcmp(comp, "POST")) method = GF_HTTP_POST;
3282 		else if (!strcmp(comp, "DELETE"))
3283 			method = GF_HTTP_DELETE;
3284 		else if (!strcmp(comp, "CONNECT")) method = GF_HTTP_CONNECT;
3285 		else if (!strcmp(comp, "TRACE")) method = GF_HTTP_TRACE;
3286 		else method = 0;
3287 
3288 		Pos = gf_token_get(buf, Pos, " \t\r\n", comp, 400);
3289 		if (sess->orig_url) gf_free(sess->orig_url);
3290 		sess->orig_url = gf_strdup(comp);
3291 		/*Pos = */gf_token_get(buf, Pos, " \t\r\n", comp, 400);
3292 		if ((strncmp("HTTP", comp, 4) != 0)) {
3293 			e = GF_REMOTE_SERVICE_ERROR;
3294 			goto exit;
3295 		}
3296 		//flush potential body except for PUT/POST
3297 		if ((method==GF_HTTP_PUT) || (method==GF_HTTP_POST))
3298 			rsp_code = 200;
3299 		else
3300 			rsp_code = 300;
3301 	} else {
3302 
3303 		if (!strncmp("ICY", comp, 3)) {
3304 			sess->use_cache_file = GF_FALSE;
3305 			/*be prepared not to receive any mime type from ShoutCast servers*/
3306 			if (!gf_cache_get_mime_type(sess->cache_entry))
3307 				gf_cache_set_mime_type(sess->cache_entry, "audio/mpeg");
3308 		} else if ((strncmp("HTTP", comp, 4) != 0)) {
3309 			e = GF_REMOTE_SERVICE_ERROR;
3310 			goto exit;
3311 		}
3312 		Pos = gf_token_get(buf, Pos, " ", comp, 400);
3313 		if (Pos <= 0) {
3314 			e = GF_REMOTE_SERVICE_ERROR;
3315 			goto exit;
3316 		}
3317 		rsp_code = (u32) atoi(comp);
3318 		/*Pos = */gf_token_get(buf, Pos, " \r\n", comp, 400);
3319 
3320 	}
3321 
3322 	no_range = range = ContentLength = first_byte = last_byte = total_size = 0;
3323 	/* parse headers*/
3324 	while (1) {
3325 		GF_HTTPHeader *hdrp;
3326 		char *sep, *hdr_sep, *hdr, *hdr_val;
3327 		if ( (s32) LinePos + 4 > BodyStart) break;
3328 		LinePos = gf_token_get_line(sHTTP, LinePos , bytesRead, buf, 1024);
3329 		if (LinePos < 0) break;
3330 
3331 		hdr_sep = NULL;
3332 		hdr_val = NULL;
3333 		hdr = buf;
3334 		sep = strchr(buf, ':');
3335 		if (sep) {
3336 			sep[0]=0;
3337 			hdr_val = sep+1;
3338 			while (hdr_val[0]==' ') hdr_val++;
3339 			hdr_sep = strrchr(hdr_val, '\r');
3340 			if (hdr_sep) hdr_sep[0] = 0;
3341 		}
3342 
3343 		GF_SAFEALLOC(hdrp, GF_HTTPHeader);
3344 		if (hdrp) {
3345 			hdrp->name = gf_strdup(hdr);
3346 			hdrp->value = gf_strdup(hdr_val);
3347 			gf_list_add(sess->headers, hdrp);
3348 		}
3349 
3350 		if (sep) sep[0]=':';
3351 		if (hdr_sep) hdr_sep[0] = '\r';
3352 
3353 		if (sess->server_mode) {
3354 			if (!stricmp(hdrp->name, "Transfer-Encoding") && !stricmp(hdrp->value, "chunked"))
3355 				sess->chunked = GF_TRUE;
3356 		}
3357 	}
3358 
3359 	if (!sess->server_mode) {
3360 		Bool cache_no_store = GF_FALSE;
3361 		//default pre-processing of headers - needs cleanup, not all of these have to be parsed before checking reply code
3362 		for (i=0; i<gf_list_count(sess->headers); i++) {
3363 			char *val;
3364 			GF_HTTPHeader *hdrp = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
3365 
3366 			if (!stricmp(hdrp->name, "Content-Length") ) {
3367 				ContentLength = (u32) atoi(hdrp->value);
3368 
3369 				if (rsp_code<300)
3370 					gf_cache_set_content_length(sess->cache_entry, ContentLength);
3371 
3372 			}
3373 			else if (!stricmp(hdrp->name, "Content-Type")) {
3374 				char *mime = gf_strdup(hdrp->value);
3375 				while (1) {
3376 					u32 len = (u32) strlen(mime);
3377 					char c = len ? mime[len-1] : 0;
3378 					if ((c=='\r') || (c=='\n')) {
3379 						mime[len-1] = 0;
3380 					} else {
3381 						break;
3382 					}
3383 				}
3384 				val = strchr(mime, ';');
3385 				if (val) val[0] = 0;
3386 
3387 				strlwr(mime);
3388 				if (rsp_code<300) {
3389 					if (sess->cache_entry) {
3390 						gf_cache_set_mime_type(sess->cache_entry, mime);
3391 					} else {
3392 						sess->mime_type = mime;
3393 						mime = NULL;
3394 					}
3395 				}
3396 				if (mime) gf_free(mime);
3397 			}
3398 			else if (!stricmp(hdrp->name, "Content-Range")) {
3399 				if (!strncmp(hdrp->value, "bytes", 5)) {
3400 					val = hdrp->value + 5;
3401 					if (val[0] == ':') val += 1;
3402 					while (val[0] == ' ') val += 1;
3403 
3404 					if (val[0] == '*') {
3405 						sscanf(val, "*/%u", &total_size);
3406 					} else {
3407 						sscanf(val, "%u-%u/%u", &first_byte, &last_byte, &total_size);
3408 					}
3409 				}
3410 			}
3411 			else if (!stricmp(hdrp->name, "Accept-Ranges")) {
3412 				if (strstr(hdrp->value, "none")) no_range = 1;
3413 			}
3414 			else if (!stricmp(hdrp->name, "Location"))
3415 				new_location = gf_strdup(hdrp->value);
3416 			else if (!strnicmp(hdrp->name, "ice", 3) || !strnicmp(hdrp->name, "icy", 3) ) {
3417 				/* For HTTP icy servers, we disable cache */
3418 				if (sess->icy_metaint == 0)
3419 					sess->icy_metaint = -1;
3420 				sess->use_cache_file = GF_FALSE;
3421 				if (!stricmp(hdrp->name, "icy-metaint")) {
3422 					sess->icy_metaint = atoi(hdrp->value);
3423 				}
3424 			}
3425 			else if (!stricmp(hdrp->name, "Cache-Control")) {
3426 				if (strstr(hdrp->value, "no-store")) {
3427 					cache_no_store = GF_TRUE;
3428 				}
3429 			}
3430 			else if (!stricmp(hdrp->name, "ETag")) {
3431 				if (rsp_code<300)
3432 					gf_cache_set_etag_on_server(sess->cache_entry, hdrp->value);
3433 			}
3434 			else if (!stricmp(hdrp->name, "Last-Modified")) {
3435 				if (rsp_code<300)
3436 					gf_cache_set_last_modified_on_server(sess->cache_entry, hdrp->value);
3437 			}
3438 			else if (!stricmp(hdrp->name, "Transfer-Encoding")) {
3439 				if (!stricmp(hdrp->value, "chunked"))
3440 					sess->chunked = GF_TRUE;
3441 			}
3442 			else if (!stricmp(hdrp->name, "X-UserProfileID") ) {
3443 				gf_opts_set_key("core", "user-profileid", hdrp->value);
3444 			}
3445 			else if (!stricmp(hdrp->name, "Connection") ) {
3446 				if (strstr(hdrp->value, "close"))
3447 					connection_closed = GF_TRUE;
3448 			}
3449 
3450 			if (sess->status==GF_NETIO_DISCONNECTED) return GF_OK;
3451 		}
3452 
3453 		if (cache_no_store) {
3454 			if (sess->cache_entry && !ContentLength && (rsp_code<300) ) {
3455 				sess->use_cache_file = GF_FALSE;
3456 				gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess);
3457 				sess->cache_entry = NULL;
3458 			}
3459 		}
3460 
3461 		if (no_range) first_byte = 0;
3462 
3463 
3464 		gf_cache_set_headers_processed(sess->cache_entry);
3465 	}
3466 
3467 	par.msg_type = GF_NETIO_PARSE_REPLY;
3468 	par.error = GF_OK;
3469 	par.reply = rsp_code;
3470 	par.value = comp;
3471 	/*
3472 	 * If response is correct, it means our credentials are correct
3473 	 */
3474 	if (sess->creds && rsp_code != 304)
3475 		sess->creds->valid = GF_TRUE;
3476 
3477 
3478 	/*try to flush body */
3479 	if (rsp_code>=300) {
3480 		u32 start = gf_sys_clock();
3481 		while (BodyStart + ContentLength > (u32) bytesRead) {
3482 			e = gf_dm_read_data(sess, sHTTP + bytesRead, buf_size - bytesRead, &res);
3483 			switch (e) {
3484 			case GF_IP_NETWORK_EMPTY:
3485 				break;
3486 			case GF_OK:
3487 				bytesRead += res;
3488 				break;
3489 			default:
3490 				start=0;
3491 				break;
3492 			}
3493 			if (gf_sys_clock()-start>100)
3494 				break;
3495 
3496 			//does not fit in our buffer, too bad we'll kill the connection
3497 			if (bytesRead == GF_DOWNLOAD_BUFFER_SIZE)
3498 				break;
3499 		}
3500 
3501 		if (BodyStart + ContentLength > (u32) bytesRead) {
3502 			ContentLength = 0;
3503 			//cannot flush, discard socket
3504 			sess->connection_close = GF_TRUE;
3505 		}
3506 	}
3507 	if (sess->server_mode) {
3508 		if (ContentLength) {
3509 			par.data = sHTTP + BodyStart;
3510 			par.size = ContentLength;
3511 		} else if (BodyStart < (s32) bytesRead) {
3512 			if (sess->init_data) gf_free(sess->init_data);
3513 			sess->init_data_size = 0;
3514 			sess->init_data = NULL;
3515 
3516 			gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, NULL, NULL);
3517 		}
3518 		par.reply = method;
3519 		gf_dm_sess_user_io(sess, &par);
3520 		sess->status = GF_NETIO_DATA_TRANSFERED;
3521 		return GF_OK;
3522 	}
3523 	//remember if we can keep the session alive after the transfer is done
3524 	sess->connection_close = connection_closed;
3525 
3526 	switch (rsp_code) {
3527 	case 200:
3528 	case 201:
3529 	case 202:
3530 	case 206:
3531 		gf_dm_sess_user_io(sess, &par);
3532 		e = GF_OK;
3533 		if (sess->proxy_enabled==2) {
3534 			sess->proxy_enabled=0;
3535 			if (sess->dm)
3536 				gf_list_add(sess->dm->skip_proxy_servers, gf_strdup(sess->server_name));
3537 		}
3538 		break;
3539 	/*redirection: extract the new location*/
3540 	case 301:
3541 	case 302:
3542 	case 307:
3543 		if (!new_location || !strlen(new_location) ) {
3544 			gf_dm_sess_user_io(sess, &par);
3545 			e = GF_URL_ERROR;
3546 			goto exit;
3547 		}
3548 		while (
3549 		    (new_location[strlen(new_location)-1] == '\n')
3550 		    || (new_location[strlen(new_location)-1] == '\r')  )
3551 			new_location[strlen(new_location)-1] = 0;
3552 
3553 		/*reset and reconnect*/
3554 		gf_dm_disconnect(sess, GF_TRUE);
3555 		sess->status = GF_NETIO_SETUP;
3556 		e = gf_dm_sess_setup_from_url(sess, new_location, GF_FALSE);
3557 		if (e) {
3558 			sess->status = GF_NETIO_STATE_ERROR;
3559 			sess->last_error = e;
3560 			gf_dm_sess_notify_state(sess, sess->status, e);
3561 		}
3562 		gf_free(new_location);
3563 		return e;
3564 	case 304:
3565 	{
3566 		sess->status = GF_NETIO_PARSE_REPLY;
3567 		assert(sess->cache_entry);
3568 		sess->total_size = gf_cache_get_cache_filesize(sess->cache_entry);
3569 
3570 		gf_dm_sess_notify_state(sess, GF_NETIO_PARSE_REPLY, GF_OK);
3571 
3572 		gf_dm_disconnect(sess, GF_FALSE);
3573 		if (sess->user_proc) {
3574 			/* For modules that do not use cache and have problems with GF_NETIO_DATA_TRANSFERED ... */
3575 			const char * filename;
3576 			FILE * f;
3577 			filename = gf_cache_get_cache_filename(sess->cache_entry);
3578 			GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] Sending data to modules from %s...\n", filename));
3579 			f = gf_fopen(filename, "rb");
3580 			assert(filename);
3581 			if (!f) {
3582 				GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] FAILED to open cache file %s for reading contents !\n", filename));
3583 				/* Ooops, no cache, redowload everything ! */
3584 				gf_dm_disconnect(sess, GF_FALSE);
3585 				sess->status = GF_NETIO_SETUP;
3586 				e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
3587 				sess->total_size = gf_cache_get_cache_filesize(sess->cache_entry);
3588 				if (e) {
3589 					sess->status = GF_NETIO_STATE_ERROR;
3590 					sess->last_error = e;
3591 					gf_dm_sess_notify_state(sess, sess->status, e);
3592 				}
3593 				return e;
3594 			}
3595 
3596 			par.error = GF_OK;
3597 			par.msg_type = GF_NETIO_PARSE_HEADER;
3598 			par.name = "Content-Type";
3599 			par.value = (char *) gf_cache_get_mime_type(sess->cache_entry);
3600 			gf_dm_sess_user_io(sess, &par);
3601 
3602 			sess->status = GF_NETIO_DATA_EXCHANGE;
3603 			if (! (sess->flags & GF_NETIO_SESSION_NOT_THREADED) || sess->force_data_write_callback) {
3604 				char file_cache_buff[16544];
3605 				s32 read = 0;
3606 				total_size = gf_cache_get_cache_filesize(sess->cache_entry);
3607 				do {
3608 					read = (s32) gf_fread(file_cache_buff, 16384, f);
3609 					if (read > 0) {
3610 						sess->bytes_done += read;
3611 						sess->total_size = total_size;
3612 						sess->bytes_per_sec = 0xFFFFFFFF;
3613 						par.size = read;
3614 						par.msg_type = GF_NETIO_DATA_EXCHANGE;
3615 						par.error = GF_EOS;
3616 						par.reply = 2;
3617 						par.data = file_cache_buff;
3618 						gf_dm_sess_user_io(sess, &par);
3619 					}
3620 				} while ( read > 0);
3621 			}
3622 			gf_fclose(f);
3623 			GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] all data has been sent to modules from %s.\n", filename));
3624 		}
3625 		/* Cache file is the most recent */
3626 		sess->status = GF_NETIO_DATA_TRANSFERED;
3627 		par.error = GF_OK;
3628 		gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3629 		gf_dm_disconnect(sess, GF_FALSE);
3630 		return GF_OK;
3631 	}
3632 	case 401:
3633 	{
3634 		/* Do we have a credentials struct ? */
3635 		sess->creds = gf_user_credentials_register(sess->dm, sess->server_name, NULL, NULL, GF_FALSE);
3636 		if (!sess->creds) {
3637 			/* User credentials have not been filled properly, we have to abort */
3638 			gf_dm_disconnect(sess, GF_TRUE);
3639 			sess->status = GF_NETIO_STATE_ERROR;
3640 			par.error = GF_AUTHENTICATION_FAILURE;
3641 			par.msg_type = GF_NETIO_DISCONNECTED;
3642 			gf_dm_sess_user_io(sess, &par);
3643 			e = GF_AUTHENTICATION_FAILURE;
3644 			sess->last_error = e;
3645 			goto exit;
3646 		}
3647 		gf_dm_disconnect(sess, GF_FALSE);
3648 		sess->status = GF_NETIO_SETUP;
3649 		e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
3650 		if (e) {
3651 			sess->status = GF_NETIO_STATE_ERROR;
3652 			sess->last_error = e;
3653 			gf_dm_sess_notify_state(sess, sess->status, e);
3654 		}
3655 		return e;
3656 	}
3657 	case 404:
3658 		/* File not found */
3659 		gf_dm_sess_user_io(sess, &par);
3660 		if ((BodyStart < (s32) bytesRead)) {
3661 			sHTTP[bytesRead] = 0;
3662 			GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Failure - body: %s\n", sHTTP + BodyStart));
3663 		}
3664 		notify_headers(sess, sHTTP, bytesRead, BodyStart);
3665 		e = GF_URL_ERROR;
3666 		goto exit;
3667 		break;
3668 	case 416:
3669 		/* Range not accepted */
3670 		gf_dm_sess_user_io(sess, &par);
3671 
3672 		notify_headers(sess, sHTTP, bytesRead, BodyStart);
3673 		e = GF_SERVICE_ERROR;
3674 		goto exit;
3675 		break;
3676 	case 400:
3677 	case 501:
3678 		/* Method not implemented ! */
3679 		if (sess->http_read_type == HEAD) {
3680 			/* Since HEAD is not understood by this server, we use a GET instead */
3681 			sess->http_read_type = GET;
3682 			sess->flags |= GF_NETIO_SESSION_NOT_CACHED;
3683 			gf_dm_disconnect(sess, GF_FALSE);
3684 			sess->status = GF_NETIO_SETUP;
3685 			sess->server_only_understand_get = GF_TRUE;
3686 			GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("Method not supported, try with GET.\n"));
3687 			e = gf_dm_sess_setup_from_url(sess, sess->orig_url, GF_FALSE);
3688 			if (e) {
3689 				sess->status = GF_NETIO_STATE_ERROR;
3690 				sess->last_error = e;
3691 				gf_dm_sess_notify_state(sess, sess->status, e);
3692 			}
3693 			return e;
3694 		}
3695 
3696 		gf_dm_sess_user_io(sess, &par);
3697 		notify_headers(sess, sHTTP, bytesRead, BodyStart);
3698 		e = GF_REMOTE_SERVICE_ERROR;
3699 		goto exit;
3700 
3701 	case 503:
3702 		/*retry without proxy*/
3703 		if (sess->proxy_enabled==1) {
3704 			sess->proxy_enabled=2;
3705 			gf_dm_disconnect(sess, GF_TRUE);
3706 			sess->status = GF_NETIO_SETUP;
3707 			return GF_OK;
3708 		}
3709 	case 204:
3710 		gf_dm_sess_user_io(sess, &par);
3711 		notify_headers(sess, sHTTP, bytesRead, BodyStart);
3712 		e = GF_EOS;
3713 		goto exit;
3714 
3715 	default:
3716 		gf_dm_sess_user_io(sess, &par);
3717 		notify_headers(sess, sHTTP, bytesRead, BodyStart);
3718 		e = GF_REMOTE_SERVICE_ERROR;
3719 		goto exit;
3720 	}
3721 
3722 	notify_headers(sess, NULL, bytesRead, BodyStart);
3723 
3724 	if (sess->http_read_type != GET)
3725 		sess->use_cache_file = GF_FALSE;
3726 
3727 	if (sess->http_read_type==HEAD) {
3728 		gf_dm_disconnect(sess, GF_FALSE);
3729 		gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3730 		sess->http_read_type = GET;
3731 		return GF_OK;
3732 
3733 
3734 	}
3735 
3736 
3737 	mime_type = gf_cache_get_mime_type(sess->cache_entry);
3738 	if (!ContentLength && mime_type && ((strstr(mime_type, "ogg") || (!strcmp(mime_type, "audio/mpeg"))))) {
3739 		if (0 == sess->icy_metaint)
3740 			sess->icy_metaint = -1;
3741 		sess->use_cache_file = GF_FALSE;
3742 	}
3743 
3744 #ifndef GPAC_DISABLE_LOG
3745 	if (e) {
3746 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[HTTP] Error processing rely from %s: %s\n", sess->server_name, gf_error_to_string(e) ) );
3747 	} else {
3748 		GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[HTTP] Reply processed from %s\n", sess->server_name ) );
3749 	}
3750 #endif
3751 
3752 	/*some servers may reply without content length, but we MUST have it*/
3753 	if (e) goto exit;
3754 	if (sess->icy_metaint != 0) {
3755 		assert( ! sess->use_cache_file );
3756 		GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[HTTP] ICY protocol detected\n"));
3757 		if (mime_type && !stricmp(mime_type, "video/nsv")) {
3758 			gf_cache_set_mime_type(sess->cache_entry, "audio/aac");
3759 		}
3760 		sess->icy_bytes = 0;
3761 		sess->total_size = SIZE_IN_STREAM;
3762 		sess->status = GF_NETIO_DATA_EXCHANGE;
3763 	} else if (!ContentLength && !sess->chunked) {
3764 		if (sess->http_read_type == GET) {
3765 			sess->total_size = SIZE_IN_STREAM;
3766 			sess->use_cache_file = GF_FALSE;
3767 			sess->status = GF_NETIO_DATA_EXCHANGE;
3768 			sess->bytes_done = 0;
3769 		} else {
3770 			gf_dm_sess_notify_state(sess, GF_NETIO_DATA_TRANSFERED, GF_OK);
3771 			gf_dm_disconnect(sess, GF_FALSE);
3772 			return GF_OK;
3773 		}
3774 	} else {
3775 		sess->total_size = ContentLength;
3776 		if (sess->use_cache_file && sess->http_read_type == GET ) {
3777 			e = gf_cache_open_write_cache(sess->cache_entry, sess);
3778 			if (e) {
3779 				GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ( "[CACHE] Failed to open cache, error=%d\n", e));
3780 				goto exit;
3781 			}
3782 		}
3783 		sess->status = GF_NETIO_DATA_EXCHANGE;
3784 		sess->bytes_done = 0;
3785 	}
3786 
3787 
3788 	/* we may have existing data in this buffer ... */
3789 	if (!e && (BodyStart < (s32) bytesRead)) {
3790 		if (sess->init_data) gf_free(sess->init_data);
3791 		sess->init_data_size = 0;
3792 		sess->init_data = NULL;
3793 
3794 		gf_dm_data_received(sess, (u8 *) sHTTP + BodyStart, bytesRead - BodyStart, GF_TRUE, NULL, NULL);
3795 	}
3796 exit:
3797 	if (e) {
3798 		if (e<0) {
3799 			GF_LOG(GF_LOG_WARNING, GF_LOG_HTTP, ("[HTTP] Error parsing reply: %s for URL %s\nReply was:\n%s\n", gf_error_to_string(e), sess->orig_url, sHTTP ));
3800 		} else {
3801 			e = GF_OK;
3802 		}
3803 		gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry);
3804 		gf_dm_remove_cache_entry_from_session(sess);
3805 		sess->cache_entry = NULL;
3806 		gf_dm_disconnect(sess, GF_FALSE);
3807 		if (connection_closed)
3808 			sess->status = GF_NETIO_STATE_ERROR;
3809 		else
3810 			sess->status = GF_NETIO_DATA_TRANSFERED;
3811 		sess->last_error = e;
3812 		gf_dm_sess_notify_state(sess, sess->status, e);
3813 		return e;
3814 	}
3815 	/*DO NOT call parse_body yet, as the final user may not be connected to our session*/
3816 	return GF_OK;
3817 }
3818 
3819 /**
3820  * Default performing behaviour
3821 \param sess The session
3822  */
http_do_requests(GF_DownloadSession * sess)3823 void http_do_requests(GF_DownloadSession *sess)
3824 {
3825 	char sHTTP[GF_DOWNLOAD_BUFFER_SIZE+1];
3826 
3827 	if (sess->reused_cache_entry) {
3828 		//main session is done downloading, notify - to do we should send progress events on this session also ...
3829 		if (!gf_cache_is_in_progress(sess->cache_entry)) {
3830 			GF_NETIO_Parameter par;
3831 			gf_dm_disconnect(sess, GF_FALSE);
3832 			sess->reused_cache_entry = GF_FALSE;
3833 			memset(&par, 0, sizeof(GF_NETIO_Parameter));
3834 			par.msg_type = GF_NETIO_DATA_TRANSFERED;
3835 			par.error = GF_OK;
3836 			gf_dm_sess_user_io(sess, &par);
3837 		}
3838 		return;
3839 	}
3840 
3841 	switch (sess->status) {
3842 	case GF_NETIO_CONNECTED:
3843 		if (sess->server_mode) {
3844 			wait_for_header_and_parse(sess, sHTTP);
3845 		} else {
3846 			http_send_headers(sess, sHTTP);
3847 		}
3848 		break;
3849 	case GF_NETIO_WAIT_FOR_REPLY:
3850 		if (sess->server_mode) {
3851 			http_send_headers(sess, sHTTP);
3852 		} else {
3853 			wait_for_header_and_parse(sess, sHTTP);
3854 		}
3855 		break;
3856 	case GF_NETIO_DATA_EXCHANGE:
3857 		if (sess->server_mode) {
3858 			sess->status = GF_NETIO_CONNECTED;
3859 			break;
3860 		}
3861 		/*session has been reassigned, resend data retrieved in first GET reply to user but don't write to cache*/
3862 		if (sess->reassigned) {
3863 
3864 			if (sess->icy_metaint > 0) {
3865 				//we are reparsing init data, reset icy status
3866 				sess->icy_bytes = 0;
3867 				gf_icy_skip_data(sess, sess->init_data, sess->init_data_size);
3868 			} else {
3869 				GF_NETIO_Parameter par;
3870 				par.msg_type = GF_NETIO_DATA_EXCHANGE;
3871 				par.error = GF_OK;
3872 				par.data = sess->init_data;
3873 				par.size = sess->init_data_size;
3874 				gf_dm_sess_user_io(sess, &par);
3875 			}
3876 			sess->reassigned = GF_FALSE;
3877 		}
3878 		http_parse_remaining_body(sess, sHTTP);
3879 		break;
3880 	default:
3881 		break;
3882 	}
3883 }
3884 
3885 
3886 /**
3887  * NET IO for MPD, we don't need this anymore since mime-type can be given by session
3888  */
wget_NetIO(void * cbk,GF_NETIO_Parameter * param)3889 static void wget_NetIO(void *cbk, GF_NETIO_Parameter *param)
3890 {
3891 	FILE * f = (FILE*) cbk;
3892 
3893 	/*handle service message*/
3894 	if (param->msg_type == GF_NETIO_DATA_EXCHANGE) {
3895 		s32 written = (u32) gf_fwrite( param->data, param->size, f);
3896 		if (written != param->size) {
3897 			GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("Failed to write data on disk\n"));
3898 		}
3899 	}
3900 }
3901 
3902 GF_EXPORT
gf_dm_wget(const char * url,const char * filename,u64 start_range,u64 end_range,char ** redirected_url)3903 GF_Err gf_dm_wget(const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url)
3904 {
3905 	GF_Err e;
3906 	GF_DownloadManager * dm = gf_dm_new(NULL);
3907 	if (!dm)
3908 		return GF_OUT_OF_MEM;
3909 	e = gf_dm_wget_with_cache(dm, url, filename, start_range, end_range, redirected_url);
3910 	gf_dm_del(dm);
3911 	return e;
3912 }
3913 
3914 GF_EXPORT
gf_dm_wget_with_cache(GF_DownloadManager * dm,const char * url,const char * filename,u64 start_range,u64 end_range,char ** redirected_url)3915 GF_Err gf_dm_wget_with_cache(GF_DownloadManager * dm, const char *url, const char *filename, u64 start_range, u64 end_range, char **redirected_url)
3916 {
3917 	GF_Err e;
3918 	FILE * f;
3919 	GF_DownloadSession *dnload;
3920 	if (!filename || !url || !dm)
3921 		return GF_BAD_PARAM;
3922 	f = gf_fopen(filename, "wb");
3923 	if (!f) {
3924 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[WGET] Failed to open file %s for write.\n", filename));
3925 		return GF_IO_ERR;
3926 	}
3927 	dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e);
3928 	if (!dnload) {
3929 		return GF_BAD_PARAM;
3930 	}
3931 	dnload->use_cache_file = GF_FALSE;
3932 	dnload->force_data_write_callback = GF_TRUE;
3933 	if (end_range) {
3934 		dnload->range_start = start_range;
3935 		dnload->range_end = end_range;
3936 		dnload->needs_range = GF_TRUE;
3937 	}
3938 	if (e == GF_OK) {
3939 		e = gf_dm_sess_process(dnload);
3940 	}
3941 	e |= gf_cache_close_write_cache(dnload->cache_entry, dnload, (e == GF_OK) ? GF_TRUE : GF_FALSE);
3942 	gf_fclose(f);
3943 
3944 	if (redirected_url) {
3945 		if (dnload->orig_url_before_redirect) *redirected_url = gf_strdup(dnload->orig_url);
3946 	}
3947 	gf_dm_sess_del(dnload);
3948 	return e;
3949 }
3950 
3951 #if 0 //unused
3952 
3953 /*
3954 \brief fetches remote file in memory
3955  *
3956  *Fetches remote file in memory.
3957 \param url the data to fetch
3958 \param out_data output data (allocated by function)
3959 \param out_size output data size
3960 \param out_mime if not NULL, pointer will contain the mime type (allocated by function)
3961 \return error code if any
3962  */
3963 GF_Err gf_dm_get_file_memory(const char *url, char **out_data, u32 *out_size, char **out_mime)
3964 {
3965 	GF_Err e;
3966 	FILE * f;
3967 	char * f_fn = NULL;
3968 	GF_DownloadSession *dnload;
3969 	GF_DownloadManager *dm;
3970 
3971 	if (!url || !out_data || !out_size)
3972 		return GF_BAD_PARAM;
3973 	f = gf_file_temp(&f_fn);
3974 	if (!f) {
3975 		GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[WGET] Failed to create temp file for write.\n"));
3976 		return GF_IO_ERR;
3977 	}
3978 
3979 	dm = gf_dm_new(NULL);
3980 	if (!dm) {
3981 		gf_fclose(f);
3982 		gf_file_delete(f_fn);
3983 		return GF_OUT_OF_MEM;
3984 	}
3985 
3986 	dnload = gf_dm_sess_new_simple(dm, (char *)url, GF_NETIO_SESSION_NOT_THREADED, &wget_NetIO, f, &e);
3987 	if (!dnload) {
3988 		gf_dm_del(dm);
3989 		gf_fclose(f);
3990 		gf_file_delete(f_fn);
3991 		return GF_BAD_PARAM;
3992 	}
3993 	dnload->use_cache_file = GF_FALSE;
3994 	dnload->disable_cache = GF_TRUE;
3995 	if (!e)
3996 		e = gf_dm_sess_process(dnload);
3997 
3998 	if (!e)
3999 		e = gf_cache_close_write_cache(dnload->cache_entry, dnload, e == GF_OK);
4000 
4001 	if (!e) {
4002 		u32 size = (u32) gf_ftell(f);
4003 		s32 read;
4004 		*out_size = size;
4005 		*out_data = (char*)gf_malloc(sizeof(char)* ( 1 + size));
4006 		gf_fseek(f, 0, SEEK_SET);
4007 		read = (s32) gf_fread(*out_data, size, f);
4008 		if (read != size) {
4009 			gf_free(*out_data);
4010 			e = GF_IO_ERR;
4011 		} else {
4012 			(*out_data)[size] = 0;
4013 			if (out_mime) {
4014 				const char *mime = gf_dm_sess_mime_type(dnload);
4015 				if (mime) *out_mime = gf_strdup(mime);
4016 			}
4017 		}
4018 	}
4019 	gf_fclose(f);
4020 	gf_file_delete(f_fn);
4021 	gf_free(f_fn);
4022 	gf_dm_sess_del(dnload);
4023 	gf_dm_del(dm);
4024 	return e;
4025 }
4026 #endif
4027 
4028 GF_EXPORT
gf_dm_sess_get_resource_name(GF_DownloadSession * dnload)4029 const char *gf_dm_sess_get_resource_name(GF_DownloadSession *dnload)
4030 {
4031 	return dnload ? dnload->orig_url : NULL;
4032 }
4033 
4034 
4035 #if 0 //unused
4036 /*!
4037 \brief Get session original resource url
4038  *
4039  *Returns the original resource URL before any redirection associated with the session
4040 \param sess the download session
4041 \return the associated URL
4042  */
4043 const char *gf_dm_sess_get_original_resource_name(GF_DownloadSession *dnload)
4044 {
4045 	if (dnload) return dnload->orig_url_before_redirect ? dnload->orig_url_before_redirect : dnload->orig_url;
4046 	return NULL;
4047 }
4048 
4049 /*!
4050 \brief fetch session status
4051  *
4052  *Fetch the session current status
4053 \param sess the download session
4054 \return the session status*/
4055 u32 gf_dm_sess_get_status(GF_DownloadSession *dnload)
4056 {
4057 	return dnload ? dnload->status : GF_NETIO_STATE_ERROR;
4058 }
4059 
4060 
4061 /*!
4062 \brief Reset session
4063  *
4064  *Resets the session for new processing of the same url
4065 \param sess the download session
4066 \return error code if any
4067  */
4068 GF_Err gf_dm_sess_reset(GF_DownloadSession *sess)
4069 {
4070 	if (!sess) return GF_BAD_PARAM;
4071 	sess->status = GF_NETIO_SETUP;
4072 	sess->needs_range = GF_FALSE;
4073 	sess->range_start = sess->range_end = 0;
4074 	sess->bytes_done = sess->bytes_per_sec = 0;
4075 	if (sess->init_data) gf_free(sess->init_data);
4076 	sess->init_data = NULL;
4077 	sess->init_data_size = 0;
4078 	sess->last_error = GF_OK;
4079 	sess->total_size = 0;
4080 	sess->start_time = 0;
4081 	sess->start_time_utc = 0;
4082 	sess->max_chunk_size = 0;
4083 	sess->max_chunk_bytes_per_sec = 0;
4084 	return GF_OK;
4085 }
4086 
4087 /*!
4088  * Get a range of a cache entry file
4089 \param sess The session
4090 \param startOffset The first byte of the request to get
4091 \param endOffset The last byte of request to get
4092 \param The temporary name for the file created to have a range of the file
4093  */
4094 const char * gf_cache_get_cache_filename_range( const GF_DownloadSession * sess, u64 startOffset, u64 endOffset ) {
4095 	u32 i, count;
4096 	if (!sess || !sess->dm || endOffset < startOffset)
4097 		return NULL;
4098 	count = gf_list_count(sess->dm->partial_downloads);
4099 	for (i = 0 ; i < count ; i++) {
4100 		GF_PartialDownload * pd = (GF_PartialDownload*)gf_list_get(sess->dm->partial_downloads, i);
4101 		assert( pd->filename && pd->url);
4102 		if (!strcmp(pd->url, sess->orig_url) && pd->startOffset == startOffset && pd->endOffset == endOffset) {
4103 			/* File already created, just return the file */
4104 			return pd->filename;
4105 		}
4106 	}
4107 	{
4108 		/* Not found, we are gonna create the file */
4109 		char * newFilename;
4110 		GF_PartialDownload * partial;
4111 		FILE * fw, *fr;
4112 		u32 maxLen;
4113 		const char * orig = gf_cache_get_cache_filename(sess->cache_entry);
4114 		if (orig == NULL)
4115 			return NULL;
4116 		/* 22 if 1G + 1G + 2 dashes */
4117 		maxLen = (u32) strlen(orig) + 22;
4118 		newFilename = (char*)gf_malloc( maxLen );
4119 		if (newFilename == NULL)
4120 			return NULL;
4121 		snprintf(newFilename, maxLen, "%s " LLU LLU, orig, startOffset, endOffset);
4122 		fw = gf_fopen(newFilename, "wb");
4123 		if (!fw) {
4124 			GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot open partial cache file %s for write\n", newFilename));
4125 			gf_free( newFilename );
4126 			return NULL;
4127 		}
4128 		fr = gf_fopen(orig, "rb");
4129 		if (!fr) {
4130 			GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot open full cache file %s\n", orig));
4131 			gf_free( newFilename );
4132 			gf_fclose( fw );
4133 		}
4134 		/* Now, we copy ! */
4135 		{
4136 			char copyBuff[GF_DOWNLOAD_BUFFER_SIZE+1];
4137 			s64 read, write, total;
4138 			total = endOffset - startOffset;
4139 			read = gf_fseek(fr, startOffset, SEEK_SET);
4140 			if (read != startOffset) {
4141 				GF_LOG(GF_LOG_ERROR, GF_LOG_HTTP, ("[CACHE] Cannot seek at right start offset in %s\n", orig));
4142 				gf_fclose( fr );
4143 				gf_fclose( fw );
4144 				gf_free( newFilename );
4145 				return NULL;
4146 			}
4147 			do {
4148 				read = gf_fread(copyBuff, MIN(sizeof(copyBuff), (size_t)  total), fr);
4149 				if (read > 0) {
4150 					total-= read;
4151 					write = gf_fwrite(copyBuff, (size_t) read, fw);
4152 					if (write != read) {
4153 						/* Something bad happened */
4154 						gf_fclose( fw );
4155 						gf_fclose (fr );
4156 						gf_free( newFilename );
4157 						return NULL;
4158 					}
4159 				} else {
4160 					if (read < 0) {
4161 						gf_fclose( fw );
4162 						gf_fclose( fr );
4163 						gf_free( newFilename );
4164 						return NULL;
4165 					}
4166 				}
4167 			} while (total > 0);
4168 			gf_fclose( fr );
4169 			gf_fclose (fw);
4170 			partial = (GF_PartialDownload*)gf_malloc( sizeof(GF_PartialDownload));
4171 			if (partial == NULL) {
4172 				gf_free(newFilename);
4173 				return NULL;
4174 			}
4175 			partial->filename = newFilename;
4176 			partial->url = sess->orig_url;
4177 			partial->endOffset = endOffset;
4178 			partial->startOffset = startOffset;
4179 			gf_list_add(sess->dm->partial_downloads, partial);
4180 			return newFilename;
4181 		}
4182 	}
4183 }
4184 
4185 /*!
4186  * Reassigns session flags and callbacks. This is only possible if the session is not threaded.
4187 \param sess The session
4188 \param flags The new flags for the session - if flags is 0xFFFFFFFF, existing flags are not modified
4189 \param user_io The new callback function
4190 \param cbk The new user data to ba used in the callback function
4191 \param GF_OK or error
4192  */
4193 GF_Err gf_dm_sess_reassign(GF_DownloadSession *sess, u32 flags, gf_dm_user_io user_io, void *cbk)
4194 {
4195 	/*shall only be called for non-threaded sessions!! */
4196 	if (sess->th) return GF_BAD_PARAM;
4197 
4198 	if (flags == 0xFFFFFFFF) {
4199 		sess->user_proc = user_io;
4200 		sess->usr_cbk = cbk;
4201 		return GF_OK;
4202 	}
4203 
4204 	if (sess->flags & GF_DOWNLOAD_SESSION_USE_SSL) flags |= GF_DOWNLOAD_SESSION_USE_SSL;
4205 	sess->flags = flags;
4206 	if (sess->flags & GF_NETIO_SESSION_NOTIFY_DATA)
4207 		sess->force_data_write_callback = GF_TRUE;
4208 
4209 	sess->user_proc = user_io;
4210 	sess->usr_cbk = cbk;
4211 	sess->reassigned = sess->init_data ? GF_TRUE : GF_FALSE;
4212 	sess->num_retry = SESSION_RETRY_COUNT;
4213 
4214 	if (sess->status==GF_NETIO_DISCONNECTED)
4215 		sess->status = GF_NETIO_SETUP;
4216 
4217 	/*threaded session shall be started with gf_dm_sess_process*/
4218 	return GF_OK;
4219 }
4220 #endif
4221 
4222 
4223 GF_EXPORT
gf_dm_set_data_rate(GF_DownloadManager * dm,u32 rate_in_bits_per_sec)4224 void gf_dm_set_data_rate(GF_DownloadManager *dm, u32 rate_in_bits_per_sec)
4225 {
4226 	if (rate_in_bits_per_sec == 0xFFFFFFFF) {
4227 		dm->simulate_no_connection=GF_TRUE;
4228 	} else {
4229 		char opt[100];
4230 		dm->simulate_no_connection=GF_FALSE;
4231 		dm->limit_data_rate = rate_in_bits_per_sec/8;
4232 
4233 		sprintf(opt, "%d", rate_in_bits_per_sec);
4234 		gf_opts_set_key("core", "maxrate", opt);
4235 
4236 		dm->read_buf_size = GF_DOWNLOAD_BUFFER_SIZE;
4237 		//when rate is limited, use smaller smaller read size
4238 		if (dm->limit_data_rate) dm->read_buf_size = 1024;
4239 
4240 #ifdef GPAC_ENABLE_COVERAGE
4241 		if (gf_sys_is_cov_mode()) {
4242 			dm_exceeds_cap_rate(dm);
4243 		}
4244 #endif
4245 
4246 	}
4247 }
4248 
4249 GF_EXPORT
gf_dm_get_data_rate(GF_DownloadManager * dm)4250 u32 gf_dm_get_data_rate(GF_DownloadManager *dm)
4251 {
4252 	return dm->limit_data_rate*8;
4253 }
4254 
4255 GF_EXPORT
gf_dm_get_global_rate(GF_DownloadManager * dm)4256 u32 gf_dm_get_global_rate(GF_DownloadManager *dm)
4257 {
4258 	u32 ret = 0;
4259 	u32 i, count;
4260 	if (!dm) return 0;
4261 	gf_mx_p(dm->cache_mx);
4262 	count = gf_list_count(dm->sessions);
4263 
4264 	for (i=0; i<count; i++) {
4265 		GF_DownloadSession *sess = (GF_DownloadSession*)gf_list_get(dm->sessions, i);
4266 		if (sess->total_size==sess->bytes_done) {
4267 			if (gf_sys_clock_high_res() - sess->start_time>2000000) {
4268 				continue;
4269 			}
4270 		}
4271 		dm_sess_update_download_rate(sess, GF_FALSE);
4272 		ret += sess->bytes_per_sec;
4273 	}
4274 	gf_mx_v(dm->cache_mx);
4275 	return 8*ret;
4276 }
4277 
4278 GF_EXPORT
gf_dm_sess_get_header(GF_DownloadSession * sess,const char * name)4279 const char *gf_dm_sess_get_header(GF_DownloadSession *sess, const char *name)
4280 {
4281 	u32 i, count;
4282 	if( !sess || !name) return NULL;
4283 	count = gf_list_count(sess->headers);
4284 	for (i=0; i<count; i++) {
4285 		GF_HTTPHeader *header = (GF_HTTPHeader*)gf_list_get(sess->headers, i);
4286 		if (!strcmp(header->name, name)) return header->value;
4287 	}
4288 	return NULL;
4289 }
4290 
4291 GF_EXPORT
gf_dm_sess_enum_headers(GF_DownloadSession * sess,u32 * idx,const char ** hdr_name,const char ** hdr_val)4292 GF_Err gf_dm_sess_enum_headers(GF_DownloadSession *sess, u32 *idx, const char **hdr_name, const char **hdr_val)
4293 {
4294 	GF_HTTPHeader *hdr;
4295 	if( !sess || !idx || !hdr_name || !hdr_val) return GF_BAD_PARAM;
4296 	hdr = gf_list_get(sess->headers, *idx);
4297 	if (!hdr) return GF_EOS;
4298 	(*idx) = (*idx) + 1;
4299 	(*hdr_name) = hdr->name;
4300 	(*hdr_val) = hdr->value;
4301 	return GF_OK;
4302 }
4303 
4304 GF_EXPORT
gf_dm_sess_get_header_sizes_and_times(GF_DownloadSession * sess,u32 * req_hdr_size,u32 * rsp_hdr_size,u32 * connect_time,u32 * reply_time,u32 * download_time)4305 GF_Err gf_dm_sess_get_header_sizes_and_times(GF_DownloadSession *sess, u32 *req_hdr_size, u32 *rsp_hdr_size, u32 *connect_time, u32 *reply_time, u32 *download_time)
4306 {
4307 	if (!sess) return GF_BAD_PARAM;
4308 
4309 	if (req_hdr_size) *req_hdr_size = sess->req_hdr_size;
4310 	if (rsp_hdr_size) *rsp_hdr_size = sess->rsp_hdr_size;
4311 	if (connect_time) *connect_time = sess->connect_time;
4312 	if (reply_time) *reply_time = sess->reply_time;
4313 	if (download_time) *download_time = sess->total_time_since_req;
4314 	return GF_OK;
4315 }
4316 
4317 GF_EXPORT
gf_dm_sess_force_memory_mode(GF_DownloadSession * sess)4318 void gf_dm_sess_force_memory_mode(GF_DownloadSession *sess)
4319 {
4320 	if (sess) sess->flags |= GF_NETIO_SESSION_MEMORY_CACHE;
4321 }
4322 
4323 
4324 GF_EXPORT
gf_dm_set_localcache_provider(GF_DownloadManager * dm,Bool (* local_cache_url_provider_cbk)(void * udta,char * url,Bool is_cache_destroy),void * lc_udta)4325 GF_Err gf_dm_set_localcache_provider(GF_DownloadManager *dm, Bool (*local_cache_url_provider_cbk)(void *udta, char *url, Bool is_cache_destroy), void *lc_udta)
4326 {
4327 	if (!dm) return GF_BAD_PARAM;
4328 	dm->local_cache_url_provider_cbk = local_cache_url_provider_cbk;
4329 	dm->lc_udta = lc_udta;
4330 	return GF_OK;
4331 
4332 }
4333 
4334 GF_EXPORT
gf_dm_add_cache_entry(GF_DownloadManager * dm,const char * szURL,u8 * data,u64 size,u64 start_range,u64 end_range,const char * mime,Bool clone_memory,u32 download_time_ms)4335 const DownloadedCacheEntry gf_dm_add_cache_entry(GF_DownloadManager *dm, const char *szURL, u8 *data, u64 size, u64 start_range, u64 end_range,  const char *mime, Bool clone_memory, u32 download_time_ms)
4336 {
4337 	u32 i, count;
4338 	DownloadedCacheEntry the_entry = NULL;
4339 
4340 	gf_mx_p(dm->cache_mx );
4341 	GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[HTTP] Pushing %s to cache\n", szURL));
4342 	count = gf_list_count(dm->cache_entries);
4343 	for (i = 0 ; i < count; i++) {
4344 		const char * url;
4345 		DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
4346 		assert(e);
4347 		url = gf_cache_get_url(e);
4348 		assert( url );
4349 		if (strcmp(url, szURL)) continue;
4350 
4351 		if (end_range) {
4352 			if (start_range != gf_cache_get_start_range(e)) continue;
4353 			if (end_range != gf_cache_get_end_range(e)) continue;
4354 		}
4355 		/*OK that's ours*/
4356 		the_entry = e;
4357 		break;
4358 	}
4359 	if (!the_entry) {
4360 		the_entry = gf_cache_create_entry(dm, "", szURL, 0, 0, GF_TRUE);
4361 		if (!the_entry) return NULL;
4362 		gf_list_add(dm->cache_entries, the_entry);
4363 	}
4364 
4365 	gf_cache_set_mime(the_entry, mime);
4366 	gf_cache_set_range(the_entry, size, start_range, end_range);
4367 	gf_cache_set_content(the_entry, data, (u32) size, clone_memory ? GF_TRUE : GF_FALSE);
4368 	gf_cache_set_downtime(the_entry, download_time_ms);
4369 
4370 	gf_mx_v(dm->cache_mx );
4371 	return the_entry;
4372 }
4373 
4374 GF_EXPORT
gf_dm_force_headers(GF_DownloadManager * dm,const DownloadedCacheEntry entry,const char * headers)4375 GF_Err gf_dm_force_headers(GF_DownloadManager *dm, const DownloadedCacheEntry entry, const char *headers)
4376 {
4377 	u32 i, count;
4378 	Bool res;
4379 	if (!entry) return GF_BAD_PARAM;
4380 	gf_mx_p(dm->cache_mx);
4381 	res = gf_cache_set_headers(entry, headers);
4382 	count = gf_list_count(dm->sessions);
4383 	for (i=0; i<count; i++) {
4384 		GF_DownloadSession *sess = gf_list_get(dm->sessions, i);
4385 		if (sess->cache_entry != entry) continue;
4386 		gf_dm_sess_reload_cached_headers(sess);
4387 	}
4388 
4389 #ifdef GPAC_ENABLE_COVERAGE
4390 	if (!count && gf_sys_is_cov_mode()) {
4391 		gf_dm_sess_reload_cached_headers(NULL);
4392 		gf_dm_refresh_cache_entry(NULL);
4393 		gf_dm_session_thread(NULL);
4394 		gf_user_credentials_save_digest(NULL, NULL, NULL);
4395 		gf_user_credentials_ask_password(NULL, NULL);
4396 		gf_user_credentials_register(NULL, NULL, NULL, NULL, GF_FALSE);
4397 		gf_cache_are_headers_processed(NULL);
4398 		gf_cache_get_start_range(NULL);
4399 		gf_cache_get_end_range(NULL);
4400 		gf_cache_get_content_length(NULL);
4401 		gf_cache_set_end_range(NULL, 0);
4402 		gf_cache_get_forced_headers(NULL);
4403 		gf_cache_get_downtime(NULL);
4404 	}
4405 #endif
4406 
4407 	gf_mx_v(dm->cache_mx);
4408 	return res ? GF_OK : GF_BAD_PARAM;
4409 }
4410 
4411 GF_EXPORT
gf_dm_sess_send(GF_DownloadSession * sess,u8 * data,u32 size)4412 GF_Err gf_dm_sess_send(GF_DownloadSession *sess, u8 *data, u32 size)
4413 {
4414 	GF_Err e = GF_OK;
4415 
4416 	if (!data || !size) {
4417 		if (sess->put_state) {
4418 			sess->put_state = 2;
4419 			sess->status = GF_NETIO_WAIT_FOR_REPLY;
4420 			return GF_OK;
4421 		}
4422 		return GF_BAD_PARAM;
4423 	}
4424 
4425 #ifdef GPAC_HAS_SSL
4426 	if (sess->ssl) {
4427 		e = gf_ssl_write(sess->ssl, data, size);
4428 	} else
4429 #endif
4430 		e = gf_sk_send(sess->sock, data, size);
4431 
4432 	if (e==GF_IP_CONNECTION_CLOSED) {
4433 		sess->status = GF_NETIO_STATE_ERROR;
4434 		return e;
4435 	}
4436 	else if (e==GF_IP_SOCK_WOULD_BLOCK) {
4437 		return gf_dm_sess_send(sess, data, size);
4438 	}
4439 	return e;
4440 }
4441 
4442 #endif
4443