Coverage Report

Created: 2025-12-05 07:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/utils/downloader_cache.c
Line
Count
Source
1
/*
2
 *          GPAC Multimedia Framework
3
 *
4
 *      Authors: Jean Le Feuvre, Pierre Souchay
5
 *      Copyright (c) Telecom ParisTech 2010-2025
6
 *          All rights reserved
7
 *
8
 *   This file is part of GPAC / downloader 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 "downloader.h"
27
28
#ifndef GPAC_DISABLE_NETWORK
29
30
#include <gpac/network.h>
31
#include <gpac/download.h>
32
#include <gpac/token.h>
33
#include <gpac/thread.h>
34
#include <gpac/list.h>
35
#include <gpac/base_coding.h>
36
#include <gpac/tools.h>
37
#include <gpac/config_file.h>
38
#include <stdio.h>
39
#include <string.h>
40
41
#if defined(_BSD_SOURCE) || _XOPEN_SOURCE >= 500
42
#include <unistd.h>
43
#endif
44
45
static const char * CACHE_SECTION_NAME = "cache";
46
static const char * CACHE_SECTION_KEY_URL = "url";
47
static const char * CACHE_SECTION_KEY_RANGE = "range";
48
static const char * CACHE_SECTION_KEY_ETAG = "ETag";
49
static const char * CACHE_SECTION_KEY_MIME_TYPE = "Content-Type";
50
static const char * CACHE_SECTION_KEY_CONTENT_SIZE = "Content-Length";
51
static const char * CACHE_SECTION_KEY_LAST_MODIFIED = "Last-Modified";
52
static const char * CACHE_SECTION_KEY_MAXAGE = "MaxAge";
53
static const char * CACHE_SECTION_KEY_MUST_REVALIDATE = "MustRevalidate";
54
static const char * CACHE_SECTION_KEY_NOCACHE = "NoCache";
55
static const char * CACHE_SECTION_KEY_CREATED = "Created";
56
static const char * CACHE_SECTION_KEY_LAST_HIT = "LastHit";
57
static const char * CACHE_SECTION_KEY_NUM_HIT = "NumHit";
58
static const char * CACHE_SECTION_KEY_INWRITE = "InWrite";
59
static const char * CACHE_SECTION_KEY_TODELETE = "ToDelete";
60
static const char * CACHE_SECTION_USERS = "users";
61
62
enum CacheValid
63
{
64
  CORRUPTED = 1,
65
  DELETED = 1<<1,
66
  IN_PROGRESS = 1<<2,
67
};
68
69
/**
70
* This opaque structure handles the data from the cache
71
*/
72
struct __DownloadedCacheEntryStruct
73
{
74
  // URL of the cache (never NULL)
75
  char *url;
76
  // Hash of the cache (never NULL)
77
  char *hash;
78
  // Name of the cache filename, (NULL for mem)
79
  char *cache_filename;
80
  // Name of the config filename, (NULL for mem)
81
  char *cfg_filename;
82
  // Theorical size of cache if any
83
  u32 contentLength;
84
  // Real size of cache
85
  u32 cacheSize;
86
  // The last modification time on the server
87
  char *serverLastModified;
88
  //The last modification time of the cache if any
89
  char *diskLastModified;
90
  // server ETag if any
91
  char *serverETag;
92
  //disk ETag if any
93
  char * diskETag;
94
  // Mime-type
95
  char * mimeType;
96
  // Write pointer for the cache
97
  FILE * writeFilePtr;
98
  // Bytes written during this cache session
99
  u32 written_in_cache;
100
  // Flag indicating whether we have to revalidate
101
  enum CacheValid   flags;
102
  //pointer to write session
103
  const GF_DownloadSession * write_session;
104
  //list of read sessions
105
  GF_List * sessions;
106
  //set to true if disk files for this entry shall be removed upon destroy (due to error)
107
  Bool discard_on_delete;
108
109
  u64 max_age;
110
  Bool must_revalidate;
111
  Bool other_in_use;
112
113
  //start and end range of the cache
114
  u64 range_start, range_end;
115
116
  //append to cache
117
  Bool continue_file;
118
  //contentLength before append
119
  u32 previousRangeContentLength;
120
  //set once headers have been processed
121
  Bool headers_done;
122
  // Set to true if file is not stored on disk
123
  Bool memory_stored;
124
  //allocation
125
  u32 mem_allocated;
126
  u8 *mem_storage;
127
  GF_Blob cache_blob;
128
129
  //for external blob only
130
  GF_Blob *external_blob;
131
  char *forced_headers;
132
  u32 downtime;
133
134
  //prevent deleting files when deleting entries, typically for init segments
135
  Bool persistent;
136
};
137
138
Bool gf_sys_check_process_id(u32 pid);
139
140
Bool gf_cache_entry_persistent(const DownloadedCacheEntry entry)
141
0
{
142
0
  return entry ? entry->persistent : GF_FALSE;
143
0
}
144
void gf_cache_entry_set_persistent(const DownloadedCacheEntry entry)
145
0
{
146
0
  if (entry) entry->persistent = GF_TRUE;
147
0
}
148
149
static const char * cache_file_prefix = "gpac_cache_";
150
151
typedef struct
152
{
153
  char *name;
154
  u64 created;
155
  u64 last_hit;
156
  u32 nb_hit;
157
  u32 size;
158
} CacheInfo;
159
160
typedef struct
161
{
162
  GF_List *files;
163
  u64 tot_size;
164
} CacheGather;
165
166
static Bool cache_cleanup_processes(GF_Config *cfg)
167
0
{
168
0
  u32 i, count=gf_cfg_get_key_count(cfg, CACHE_SECTION_USERS);
169
0
  for (i=0;i<count;i++) {
170
0
    const char *opt = gf_cfg_get_key_name(cfg, CACHE_SECTION_USERS, i);
171
0
    if (!opt) break;
172
0
    u32 procid;
173
0
    sscanf(opt, "%u", &procid);
174
0
    if (gf_sys_check_process_id(procid)) continue;
175
0
    gf_cfg_set_key(cfg, CACHE_SECTION_USERS, opt, NULL);
176
0
    count--;
177
0
    i--;
178
0
  }
179
0
  return count ? GF_TRUE : GF_FALSE;
180
0
}
181
182
static Bool gather_cache_files(void *cbck, char *item_name, char *item_path, GF_FileEnumInfo *file_info)
183
0
{
184
0
  CacheGather *gci = (CacheGather *)cbck;
185
0
  GF_Config *file = gf_cfg_new(NULL, item_path);
186
0
  u32 i, count, size=0;
187
0
  const char *opt = gf_cfg_get_key(file, CACHE_SECTION_NAME, CACHE_SECTION_KEY_CONTENT_SIZE);
188
0
  if (opt) sscanf(opt, "%u", &size);
189
0
  gci->tot_size += size;
190
191
0
  if (cache_cleanup_processes(file)) {
192
0
    gf_cfg_del(file);
193
0
    return GF_FALSE;
194
0
  }
195
196
0
  CacheInfo *ci;
197
0
  GF_SAFEALLOC(ci, CacheInfo);
198
0
  if (ci) ci->name = gf_strdup(item_path);
199
0
  if (!ci || !ci->name) {
200
0
    if (ci) gf_free(ci);
201
0
    gf_cfg_del(file);
202
0
    return GF_FALSE;
203
0
  }
204
0
  ci->size = size;
205
0
  opt = gf_cfg_get_key(file, CACHE_SECTION_NAME, CACHE_SECTION_KEY_CREATED);
206
0
  if (opt) sscanf(opt, LLU, &ci->created);
207
0
  opt = gf_cfg_get_key(file, CACHE_SECTION_NAME, CACHE_SECTION_KEY_LAST_HIT);
208
0
  if (opt) sscanf(opt, LLU, &ci->last_hit);
209
0
  opt = gf_cfg_get_key(file, CACHE_SECTION_NAME, CACHE_SECTION_KEY_NUM_HIT);
210
0
  if (opt) { sscanf(opt, "%u", &ci->nb_hit); if (ci->nb_hit) ci->nb_hit--; }
211
0
  gf_cfg_del(file);
212
213
0
  count = gf_list_count(gci->files);
214
0
  for (i=0; i<count; i++) {
215
0
    CacheInfo *a_ci = gf_list_get(gci->files, i);
216
    //sort by nb hits first
217
0
    if (ci->nb_hit<a_ci->nb_hit) {
218
0
      gf_list_insert(gci->files, ci, i);
219
0
      return GF_FALSE;
220
0
    } else if (ci->nb_hit>a_ci->nb_hit) continue;
221
222
    //then by last hit
223
0
    if (ci->last_hit<a_ci->last_hit) {
224
0
      gf_list_insert(gci->files, ci, i);
225
0
      return GF_FALSE;
226
0
    }
227
0
    else if (ci->last_hit>a_ci->last_hit) continue;
228
229
    //then by created
230
0
    if (ci->created<a_ci->created) {
231
0
      gf_list_insert(gci->files, ci, i);
232
0
      return GF_FALSE;
233
0
    }
234
0
    else if (ci->created>a_ci->created) continue;
235
236
    //then by size
237
0
    if (ci->size<a_ci->size) {
238
0
      gf_list_insert(gci->files, ci, i);
239
0
      return GF_FALSE;
240
0
    }
241
0
  }
242
0
  gf_list_add(gci->files, ci);
243
0
  return 0;
244
0
}
245
246
u64 gf_cache_cleanup(const char * directory, u64 max_size)
247
2.90k
{
248
2.90k
  char szLOCK[GF_MAX_PATH];
249
2.90k
  sprintf(szLOCK, "%s/.lock", directory);
250
2.90k
  if (!gf_sys_create_lockfile(szLOCK))
251
0
    return max_size;
252
253
2.90k
  CacheGather gci = {0};
254
2.90k
  gci.files = gf_list_new();
255
2.90k
  gf_enum_directory(directory, GF_FALSE, gather_cache_files, &gci, "*.txt");
256
2.90k
  u64 cache_size = gci.tot_size;
257
258
2.90k
  while (gf_list_count(gci.files)) {
259
0
    CacheInfo *ci = gf_list_pop_back(gci.files);
260
0
    if (cache_size>max_size) {
261
0
      if (cache_size>ci->size) cache_size -= ci->size;
262
0
      else cache_size = 0;
263
0
      gf_file_delete(ci->name);
264
0
      char *sep = strstr(ci->name, ".txt");
265
0
      sep[0] = 0;
266
0
      gf_file_delete(ci->name);
267
0
      sep[0] = '.';
268
0
    }
269
0
    gf_free(ci->name);
270
0
    gf_free(ci);
271
0
  }
272
2.90k
  gf_list_del(gci.files);
273
2.90k
  gf_file_delete(szLOCK);
274
2.90k
  return cache_size;
275
2.90k
}
276
277
void gf_cache_entry_set_delete_files_when_deleted(const DownloadedCacheEntry entry)
278
0
{
279
0
  if (entry && !entry->persistent) {
280
0
    entry->discard_on_delete = GF_TRUE;
281
0
  }
282
0
}
283
284
0
#define CHECK_ENTRY if (!entry) { GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE, ("[CACHE] entry is null at " __FILE__ ":%d\n", __LINE__)); return GF_BAD_PARAM; }
285
286
/*
287
* Getters functions
288
*/
289
290
const char * gf_cache_get_etag_on_server ( const DownloadedCacheEntry entry )
291
0
{
292
0
  return entry ? entry->serverETag : NULL;
293
0
}
294
295
const char * gf_cache_get_mime_type ( const DownloadedCacheEntry entry )
296
0
{
297
0
  return entry ? entry->mimeType : NULL;
298
0
}
299
300
301
GF_Err gf_cache_set_headers_processed(const DownloadedCacheEntry entry)
302
0
{
303
0
  if (!entry) return GF_BAD_PARAM;
304
0
  entry->headers_done = GF_TRUE;
305
0
  return GF_OK;
306
0
}
307
308
Bool gf_cache_are_headers_processed(const DownloadedCacheEntry entry)
309
0
{
310
0
  if (!entry) return GF_FALSE;
311
0
  return entry->headers_done;
312
0
}
313
314
0
GF_Err gf_cache_set_etag_on_server(const DownloadedCacheEntry entry, const char * eTag ) {
315
0
  if (!entry)
316
0
    return GF_BAD_PARAM;
317
0
  if (entry->serverETag)
318
0
    gf_free(entry->serverETag);
319
0
  entry->serverETag = eTag ? gf_strdup(eTag) : NULL;
320
0
  return GF_OK;
321
0
}
322
323
0
GF_Err gf_cache_set_etag_on_disk(const DownloadedCacheEntry entry, const char * eTag ) {
324
0
  if (!entry)
325
0
    return GF_BAD_PARAM;
326
0
  if (entry->diskETag)
327
0
    gf_free(entry->diskETag);
328
0
  entry->diskETag = eTag ? gf_strdup(eTag) : NULL;
329
0
  return GF_OK;
330
0
}
331
332
0
GF_Err gf_cache_set_mime_type(const DownloadedCacheEntry entry, const char * mime_type ) {
333
0
  if (!entry)
334
0
    return GF_BAD_PARAM;
335
0
  if (entry->mimeType)
336
0
    gf_free(entry->mimeType);
337
0
  entry->mimeType = mime_type? gf_strdup( mime_type) : NULL;
338
0
  return GF_OK;
339
0
}
340
341
u64 gf_cache_get_start_range( const DownloadedCacheEntry entry )
342
0
{
343
0
  return entry ? entry->range_start : 0;
344
0
}
345
346
u64 gf_cache_get_end_range( const DownloadedCacheEntry entry )
347
0
{
348
0
  return entry ? entry->range_end : 0;
349
0
}
350
351
const char * gf_cache_get_url ( const DownloadedCacheEntry entry )
352
0
{
353
0
  return entry ? entry->url : NULL;
354
0
}
355
356
const char * gf_cache_get_last_modified_on_server ( const DownloadedCacheEntry entry )
357
0
{
358
0
  return entry ? entry->serverLastModified : NULL;
359
0
}
360
361
GF_Err gf_cache_set_last_modified_on_server ( const DownloadedCacheEntry entry, const char * newLastModified )
362
0
{
363
0
  if (!entry)
364
0
    return GF_BAD_PARAM;
365
0
  if (entry->serverLastModified)
366
0
    gf_free(entry->serverLastModified);
367
0
  entry->serverLastModified = newLastModified ? gf_strdup(newLastModified) : NULL;
368
0
  return GF_OK;
369
0
}
370
371
GF_Err gf_cache_set_last_modified_on_disk ( const DownloadedCacheEntry entry, const char * newLastModified )
372
0
{
373
0
  if (!entry)
374
0
    return GF_BAD_PARAM;
375
0
  if (entry->diskLastModified)
376
0
    gf_free(entry->diskLastModified);
377
0
  entry->diskLastModified = newLastModified ? gf_strdup(newLastModified) : NULL;
378
0
  return GF_OK;
379
0
}
380
381
static GF_LockStatus cache_entry_lock(const char *lockfile)
382
0
{
383
0
  GF_LockStatus lock_type;
384
0
  u32 start=gf_sys_clock();
385
0
  while (1) {
386
0
    lock_type = gf_sys_create_lockfile(lockfile);
387
0
    if (lock_type) break;
388
0
    if (gf_sys_clock()-start>50) break;
389
0
  }
390
0
  return lock_type;
391
0
}
392
393
394
GF_Err gf_cache_flush_disk_cache ( const DownloadedCacheEntry entry, Bool success )
395
0
{
396
0
  char buff[100];
397
0
  CHECK_ENTRY;
398
0
  if (entry->mem_storage)
399
0
    return GF_OK;
400
401
0
  char szLOCK[GF_MAX_PATH];
402
0
  sprintf(szLOCK, "%s.lock", entry->cfg_filename);
403
0
  GF_LockStatus lock_type = cache_entry_lock(szLOCK);
404
0
  GF_Config *cfg = gf_cfg_new(NULL, entry->cfg_filename);
405
406
0
  if (success) {
407
0
    char szSize[50];
408
0
    sprintf(szSize, "%u", entry->written_in_cache);
409
0
    gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_CONTENT_SIZE, szSize);
410
0
  }
411
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_INWRITE, NULL);
412
413
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_URL, entry->url);
414
415
0
  if (entry->range_start || entry->range_end) {
416
0
    sprintf(buff, LLD"-"LLD, entry->range_start, entry->range_end);
417
0
    gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_RANGE, buff);
418
0
  }
419
420
0
  if (entry->mimeType)
421
0
    gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_MIME_TYPE, entry->mimeType);
422
0
  if (entry->diskETag)
423
0
    gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_ETAG, entry->diskETag);
424
0
  if (entry->diskLastModified)
425
0
    gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_LAST_MODIFIED, entry->diskLastModified);
426
427
0
  snprintf(buff, 16, "%d", entry->contentLength);
428
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_CONTENT_SIZE, buff);
429
  //save and destroy
430
0
  gf_cfg_del(cfg);
431
0
  if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
432
0
  return GF_OK;
433
0
}
434
435
u32 gf_cache_get_cache_filesize(const DownloadedCacheEntry entry )
436
0
{
437
0
  return entry ? entry->cacheSize : -1;
438
0
}
439
440
const char * gf_cache_get_cache_filename( const DownloadedCacheEntry entry )
441
0
{
442
0
  return entry ? entry->cache_filename : NULL;
443
0
}
444
445
GF_Err gf_cache_get_http_headers(const DownloadedCacheEntry entry, const char **etag, const char **last_modif)
446
0
{
447
0
  if (!entry || !etag || !last_modif)
448
0
    return GF_BAD_PARAM;
449
450
0
  *etag = *last_modif = NULL;
451
0
  if (entry->flags & (CORRUPTED|DELETED))
452
0
    return GF_OK;
453
454
0
  *etag = entry->diskETag;
455
0
  *last_modif = entry->diskLastModified;
456
0
  if (entry->persistent && entry->memory_stored) {
457
0
    *etag = entry->serverETag;
458
0
    *last_modif = entry->serverLastModified;
459
0
  }
460
0
  return GF_OK;
461
0
}
462
463
static void gf_cache_check_if_cache_file_is_corrupted(const DownloadedCacheEntry entry, const char *url, GF_Config *cfg)
464
0
{
465
0
  const char * keyValue = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_URL );
466
0
  if ( keyValue == NULL || stricmp ( url, keyValue ) ) {
467
0
    entry->flags |= CORRUPTED;
468
0
    return;
469
0
  }
470
471
0
  keyValue = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_RANGE);
472
0
  if (keyValue) {
473
0
    u64 s, e;
474
0
    sscanf(keyValue, LLD"-"LLD, &s, &e);
475
    /*mark as corrupted if not same range (we don't support this for the time being ...*/
476
0
    if ((s!=entry->range_start) || (e!=entry->range_end)) {
477
0
      entry->flags |= CORRUPTED;
478
0
      return;
479
0
    }
480
0
  }
481
482
0
  if (gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_NOCACHE) ) {
483
0
    entry->flags |= CORRUPTED;
484
0
    return;
485
0
  }
486
487
0
  FILE *the_cache = NULL;
488
0
  if (entry->cache_filename && strncmp(entry->cache_filename, "gmem://", 7))
489
0
    the_cache = gf_fopen ( entry->cache_filename, "rb" );
490
491
0
  if ( the_cache ) {
492
0
    char * endPtr;
493
0
    const char * keyValue = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_INWRITE);
494
0
    if (keyValue) {
495
0
      entry->cacheSize = 0;
496
0
      entry->flags |= IN_PROGRESS;
497
0
      gf_fclose ( the_cache );
498
0
      return;
499
0
    }
500
0
    keyValue = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_CONTENT_SIZE );
501
502
0
    entry->cacheSize = ( u32 ) gf_fsize(the_cache);
503
0
    gf_fclose ( the_cache );
504
0
    if (keyValue) {
505
0
      entry->contentLength = (u32) strtoul( keyValue, &endPtr, 10);
506
0
      if (*endPtr!='\0' || entry->contentLength != entry->cacheSize) {
507
0
        entry->flags |= CORRUPTED;
508
0
        GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[CACHE] Corrupted: file and cache info size mismatch\n"));
509
0
      }
510
0
    } else {
511
0
      entry->flags |= CORRUPTED;
512
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[CACHE] Corrupted: missing cache content size\n"));
513
0
    }
514
0
  } else if (!entry->mem_storage) {
515
0
    entry->flags |= CORRUPTED;
516
0
  }
517
0
}
518
519
#define _CACHE_HASH_SIZE 20
520
0
#define _CACHE_MAX_EXTENSION_SIZE 6
521
static const char * default_cache_file_suffix = ".dat";
522
static const char * cache_file_info_suffix = ".txt";
523
524
DownloadedCacheEntry gf_cache_create_entry(const char * cache_directory, const char * url , u64 start_range, u64 end_range, Bool mem_storage, GF_Mutex *mx)
525
0
{
526
0
  char tmp[GF_MAX_PATH];
527
0
  u8 hash[_CACHE_HASH_SIZE];
528
0
  int sz;
529
0
  char ext[_CACHE_MAX_EXTENSION_SIZE];
530
0
  DownloadedCacheEntry entry = NULL;
531
0
  if (!url || !cache_directory) return NULL;
532
533
0
  sz = (u32) strlen ( url );
534
0
  if ( sz > GF_MAX_PATH ) {
535
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE,
536
0
           ("[CACHE] ERROR, URL is too long (%d chars), more than %d chars.\n", sz, GF_MAX_PATH ));
537
0
    return NULL;
538
0
  }
539
0
  tmp[0] = '\0';
540
  /*generate hash of the full url*/
541
0
  if (start_range && end_range) {
542
0
    sprintf(tmp, "%s_"LLD"-"LLD, url, start_range, end_range );
543
0
  } else {
544
0
    strcpy ( tmp, url );
545
0
  }
546
0
  gf_sha1_csum ((u8*) tmp, (u32) strlen ( tmp ), hash );
547
0
  tmp[0] = 0;
548
0
  {
549
0
    int i;
550
0
    for ( i=0; i<20; i++ )
551
0
    {
552
0
      char t[3];
553
0
      t[2] = 0;
554
0
      sprintf ( t, "%02X", hash[i] );
555
0
      strcat ( tmp, t );
556
0
    }
557
0
  }
558
0
  assert ( strlen ( tmp ) == (_CACHE_HASH_SIZE * 2) );
559
560
0
  GF_SAFEALLOC(entry, struct __DownloadedCacheEntryStruct);
561
0
  if ( !entry ) return NULL;
562
563
0
  entry->url = gf_strdup ( url );
564
0
  entry->hash = gf_strdup ( tmp );
565
0
  entry->memory_stored = mem_storage;
566
0
  entry->cacheSize = 0;
567
0
  entry->contentLength = 0;
568
0
  entry->serverETag = NULL;
569
0
  entry->diskETag = NULL;
570
0
  entry->flags = 0;
571
0
  entry->diskLastModified = NULL;
572
0
  entry->serverLastModified = NULL;
573
0
  entry->range_start = start_range;
574
0
  entry->range_end = end_range;
575
576
0
  entry->discard_on_delete = GF_FALSE;
577
0
  entry->write_session = NULL;
578
0
  entry->sessions = gf_list_new();
579
580
0
  if (entry->memory_stored) {
581
0
    entry->cache_filename = (char*)gf_malloc ( strlen ("gmem://") + 8 + strlen("@") + 16 + 1);
582
0
  } else {
583
    /* Sizeof cache directory + hash + possible extension */
584
0
    entry->cache_filename = (char*)gf_malloc ( strlen ( cache_directory ) + strlen(cache_file_prefix) + strlen(tmp) + _CACHE_MAX_EXTENSION_SIZE + 1);
585
0
  }
586
587
0
  if ( !entry->hash || !entry->url || !entry->cache_filename || !entry->sessions) {
588
    /* Probably out of memory */
589
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE, ("[CACHE] Failed to create cache entry, out of memory\n"));
590
0
    gf_cache_delete_entry ( entry );
591
0
    return NULL;
592
0
  }
593
594
0
  if (entry->memory_stored) {
595
0
    entry->cache_blob.mx = mx;
596
0
    entry->cache_blob.data = entry->mem_storage;
597
0
    entry->cache_blob.size = entry->contentLength;
598
0
    char *burl = gf_blob_register(&entry->cache_blob);
599
0
    if (burl) {
600
0
      strcpy(entry->cache_filename, burl);
601
0
      gf_free(burl);
602
0
    }
603
0
    return entry;
604
0
  }
605
606
607
0
  tmp[0] = '\0';
608
0
  strcpy ( entry->cache_filename, cache_directory );
609
0
  strcat( entry->cache_filename, cache_file_prefix );
610
0
  strcat ( entry->cache_filename, entry->hash );
611
0
  strcpy ( tmp, url );
612
613
0
  {
614
0
    char * parser;
615
0
    parser = strrchr ( tmp, '?' );
616
0
    if ( parser )
617
0
      parser[0] = '\0';
618
0
    parser = strrchr ( tmp, '#' );
619
0
    if ( parser )
620
0
      parser[0] = '\0';
621
0
    parser = strrchr ( tmp, '.' );
622
0
    if ( parser && ( strlen ( parser ) < _CACHE_MAX_EXTENSION_SIZE ) )
623
0
      strncpy(ext, parser, _CACHE_MAX_EXTENSION_SIZE);
624
0
    else
625
0
      strncpy(ext, default_cache_file_suffix, _CACHE_MAX_EXTENSION_SIZE);
626
0
    assert (strlen(ext));
627
0
    strcat( entry->cache_filename, ext);
628
0
  }
629
630
0
  gf_dynstrcat(&entry->cfg_filename, entry->cache_filename, NULL);
631
0
  gf_dynstrcat(&entry->cfg_filename, cache_file_info_suffix, NULL);
632
633
0
  char szLOCK[GF_MAX_PATH];
634
0
  sprintf(szLOCK, "%s.lock", entry->cfg_filename);
635
0
  GF_LockStatus lock_type = cache_entry_lock(szLOCK);
636
0
  if (!lock_type) {
637
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] Failed to grab cache lock for entry %s, request will not be cached\n", url));
638
0
    gf_cache_delete_entry ( entry );
639
0
    return NULL;
640
0
  }
641
642
0
  Bool in_cache = gf_file_exists(entry->cfg_filename);
643
644
0
  GF_Config *cfg = gf_cfg_force_new ( NULL, entry->cfg_filename);
645
0
  if ( !cfg ) {
646
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE, ("[CACHE] Failed to create cache entry for %s, request will not be cached\n", url));
647
0
    gf_cache_delete_entry ( entry );
648
0
    if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
649
0
    return NULL;
650
0
  }
651
652
0
  if (in_cache) {
653
0
    gf_cache_check_if_cache_file_is_corrupted(entry, url, cfg);
654
0
    if ((entry->flags & CORRUPTED) && (gf_cfg_get_key_count(cfg, CACHE_SECTION_USERS)==0)) {
655
0
      GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[CACHE] Cache entry for %s corrupted but no other users, recreating\n", url));
656
0
      entry->flags &= ~CORRUPTED;
657
0
      gf_file_delete(entry->cache_filename);
658
0
      gf_file_delete(entry->cfg_filename);
659
0
      gf_cfg_del_section(cfg, CACHE_SECTION_NAME);
660
0
      gf_cfg_del_section(cfg, CACHE_SECTION_USERS);
661
0
    }
662
0
  }
663
664
0
  if (entry->flags & CORRUPTED) {
665
0
    gf_cfg_del(cfg);
666
0
    GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[CACHE] Incompatible cache entry for %s, request will not be cached\n", url));
667
0
    gf_cache_delete_entry ( entry );
668
0
    if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
669
0
    return NULL;
670
0
  }
671
672
0
  gf_cache_set_etag_on_disk(entry, gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_ETAG));
673
0
  gf_cache_set_etag_on_server(entry, gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_ETAG));
674
0
  gf_cache_set_mime_type(entry, gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_MIME_TYPE));
675
0
  gf_cache_set_last_modified_on_disk(entry, gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_LAST_MODIFIED));
676
0
  gf_cache_set_last_modified_on_server(entry, gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_LAST_MODIFIED));
677
678
0
  const char *opt = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_MAXAGE);
679
0
  if (opt) sscanf(opt, LLU, &entry->max_age);
680
0
  opt = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_MUST_REVALIDATE);
681
0
  if (opt && !strcmp(opt, "true")) entry->must_revalidate = GF_TRUE;
682
683
0
  char szBUF[20];
684
0
  sprintf(szBUF, LLU, gf_net_get_utc()/1000);
685
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_LAST_HIT, szBUF);
686
687
0
  u32 nb_hits=0;
688
0
  opt = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_NUM_HIT );
689
0
  if (opt) nb_hits = atoi(opt);
690
0
  nb_hits++;
691
0
  sprintf(szBUF, "%u", nb_hits);
692
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_NUM_HIT, szBUF);
693
694
0
  if (!mem_storage) {
695
0
    char szID[20];
696
0
    if (cache_cleanup_processes(cfg))
697
0
      entry->other_in_use = GF_TRUE;
698
0
    sprintf(szID, "%u", gf_sys_get_process_id());
699
0
    gf_cfg_set_key(cfg, CACHE_SECTION_USERS, szID, "true");
700
0
  }
701
702
  //rewrite to disk
703
0
  gf_cfg_del(cfg);
704
0
  if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
705
706
0
  return entry;
707
0
}
708
709
GF_Err gf_cache_set_content_length( const DownloadedCacheEntry entry, u32 length )
710
0
{
711
0
  CHECK_ENTRY;
712
0
  if (entry->continue_file) {
713
0
    entry->contentLength = entry->previousRangeContentLength + length;
714
0
  } else {
715
0
    entry->contentLength = length;
716
0
  }
717
0
  return GF_OK;
718
0
}
719
720
u32 gf_cache_get_content_length( const DownloadedCacheEntry entry)
721
0
{
722
0
  if (!entry) return 0;
723
0
  if (entry->external_blob) {
724
0
    return entry->external_blob->size;
725
0
  }
726
0
  return entry->contentLength;
727
0
}
728
729
GF_Err gf_cache_close_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, Bool success )
730
0
{
731
0
  GF_Err e = GF_OK;
732
0
  CHECK_ENTRY;
733
0
  if (!sess || !entry->write_session || entry->write_session != sess)
734
0
    return GF_OK;
735
0
  gf_assert( sess == entry->write_session );
736
0
  if (entry->writeFilePtr) {
737
0
    GF_LOG(GF_LOG_INFO, GF_LOG_CACHE,
738
0
           ("[CACHE] Closing file %s, %d bytes written.\n", entry->cache_filename, entry->written_in_cache));
739
740
0
    if (gf_fflush( entry->writeFilePtr ) || gf_fclose( entry->writeFilePtr )) {
741
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] Failed to flush/close file on disk\n"));
742
0
      e = GF_IO_ERR;
743
0
    }
744
0
    if (!e && success) {
745
0
      e = gf_cache_set_last_modified_on_disk( entry, gf_cache_get_last_modified_on_server(entry));
746
0
      if (e) {
747
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] Failed to set last-modified\n"));
748
0
      }
749
0
      if (!e) {
750
0
        e = gf_cache_set_etag_on_disk( entry, gf_cache_get_etag_on_server(entry));
751
0
        if (e) {
752
0
          GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] Failed to set etag\n"));
753
0
        }
754
0
      }
755
0
    }
756
757
0
    e = gf_cache_flush_disk_cache(entry, success);
758
0
    if (e) {
759
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] Failed to flush cache entry on disk after etag/last-modified\n"));
760
0
    }
761
762
#if defined(_BSD_SOURCE) || _XOPEN_SOURCE >= 500
763
    /* On  UNIX, be sure to flush all the data */
764
    sync();
765
#endif
766
0
    entry->writeFilePtr = NULL;
767
0
    if (GF_OK != e) {
768
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] Failed to fully write file on cache, e=%d\n", e));
769
0
    }
770
771
0
    if (!success) {
772
0
      entry->discard_on_delete = GF_TRUE;
773
0
    }
774
0
  }
775
0
  entry->write_session = NULL;
776
0
  return e;
777
0
}
778
779
GF_Err gf_cache_open_write_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess)
780
0
{
781
0
  CHECK_ENTRY;
782
0
  if (!sess)
783
0
    return GF_BAD_PARAM;
784
0
  entry->write_session = sess;
785
0
  if (!entry->continue_file) {
786
0
    gf_assert( ! entry->writeFilePtr);
787
788
0
    entry->written_in_cache = 0;
789
0
  }
790
0
  entry->flags &= ~CORRUPTED;
791
792
0
  if (entry->memory_stored) {
793
0
    gf_mx_p(entry->cache_blob.mx);
794
795
0
    GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[CACHE] Opening cache file %s for write (%s)...\n", entry->cache_filename, entry->url));
796
0
    if (!entry->mem_allocated || (entry->mem_allocated < entry->contentLength)) {
797
0
      if (entry->contentLength) entry->mem_allocated = entry->contentLength;
798
0
      else if (!entry->mem_allocated) entry->mem_allocated = 81920;
799
0
      entry->mem_storage = (u8*)gf_realloc(entry->mem_storage, sizeof(char)* (entry->mem_allocated + 2) );
800
0
    }
801
0
    entry->cache_blob.data = entry->mem_storage;
802
0
    entry->cache_blob.size = entry->contentLength;
803
0
    char *burl = gf_blob_register(&entry->cache_blob);
804
0
    if (burl) {
805
0
      strcpy(entry->cache_filename, burl);
806
0
      gf_free(burl);
807
0
    }
808
0
    gf_mx_v(entry->cache_blob.mx);
809
810
0
    if (!entry->mem_allocated) {
811
0
      GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] Failed to create memory storage for file %s\n", entry->url));
812
0
      return GF_OUT_OF_MEM;
813
0
    }
814
0
    return GF_OK;
815
0
  }
816
817
0
  GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[CACHE] Opening cache file %s for write (%s)...\n", entry->cache_filename, entry->url));
818
0
  entry->writeFilePtr = gf_fopen(entry->cache_filename, entry->continue_file ? "a+b" : "wb");
819
0
  if (!entry->writeFilePtr) {
820
0
    GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE,
821
0
           ("[CACHE] Error while opening cache file %s for writting.\n", entry->cache_filename));
822
0
    entry->write_session = NULL;
823
0
    return GF_IO_ERR;
824
0
  }
825
826
0
  if (entry->continue_file )
827
0
    gf_fseek(entry->writeFilePtr, 0, SEEK_END);
828
829
0
  char szLOCK[GF_MAX_PATH];
830
0
  sprintf(szLOCK, "%s.lock", entry->cfg_filename);
831
0
  GF_LockStatus lock_type = cache_entry_lock(szLOCK);
832
0
  GF_Config *cfg = gf_cfg_new(NULL, entry->cfg_filename);
833
0
  char szUTC[20];
834
0
  sprintf(szUTC, LLU, gf_net_get_utc()/1000);
835
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_CREATED, szUTC);
836
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_INWRITE, "true");
837
0
  gf_cfg_del(cfg);
838
0
  if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
839
840
0
  return GF_OK;
841
0
}
842
843
0
GF_Err gf_cache_write_to_cache( const DownloadedCacheEntry entry, const GF_DownloadSession * sess, const char * data, const u32 size, GF_Mutex *mx) {
844
0
  u32 read;
845
0
  CHECK_ENTRY;
846
847
0
  if (!data || (!entry->writeFilePtr && !entry->mem_storage) || sess != entry->write_session) {
848
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE, ("Incorrect parameter : data=%p, writeFilePtr=%p mem_storage=%p at "__FILE__"\n", data, entry->writeFilePtr, entry->mem_storage));
849
0
    return GF_BAD_PARAM;
850
0
  }
851
852
0
  if (entry->memory_stored) {
853
0
    if (!entry->cache_blob.mx)
854
0
      entry->cache_blob.mx = mx;
855
0
    gf_assert(mx);
856
0
    gf_mx_p(entry->cache_blob.mx);
857
0
    if (entry->written_in_cache + size > entry->mem_allocated) {
858
0
      u32 new_size = MAX(entry->mem_allocated*2, entry->written_in_cache + size);
859
0
      entry->mem_storage = (u8*)gf_realloc(entry->mem_storage, (new_size+2));
860
0
      entry->mem_allocated = new_size;
861
0
      entry->cache_blob.data = entry->mem_storage;
862
0
      entry->cache_blob.size = entry->contentLength;
863
0
      char *burl = gf_blob_register(&entry->cache_blob);
864
0
      if (burl) {
865
0
        strcpy(entry->cache_filename, burl);
866
0
        gf_free(burl);
867
0
      }
868
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Reallocating memory cache to %d bytes\n", new_size));
869
0
    }
870
0
    memcpy(entry->mem_storage + entry->written_in_cache, data, size);
871
0
    entry->written_in_cache += size;
872
0
    memset(entry->mem_storage + entry->written_in_cache, 0, 2);
873
0
    entry->cache_blob.size = entry->written_in_cache;
874
0
    gf_mx_v(entry->cache_blob.mx);
875
876
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Storing %d bytes to memory\n", size));
877
0
    return GF_OK;
878
0
  }
879
880
0
  read = (u32) gf_fwrite(data, size, entry->writeFilePtr);
881
0
  if (read > 0)
882
0
    entry->written_in_cache+= read;
883
0
  if (read != size) {
884
    /* Something bad happened */
885
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE,
886
0
           ("[CACHE] Error while writting %d bytes of data to cache : has written only %d bytes.", size, read));
887
0
    gf_cache_close_write_cache(entry, sess, GF_FALSE);
888
0
    return GF_IO_ERR;
889
0
  }
890
0
  if (gf_fflush(entry->writeFilePtr)) {
891
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE,
892
0
           ("[CACHE] Error while flushing data bytes to cache file : %s.", entry->cache_filename));
893
0
    gf_cache_close_write_cache(entry, sess, GF_FALSE);
894
0
    return GF_IO_ERR;
895
0
  }
896
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Writing %d bytes to cache\n", size));
897
0
  return GF_OK;
898
0
}
899
900
void gf_cache_delete_entry( const DownloadedCacheEntry entry )
901
0
{
902
0
  if ( !entry ) return;
903
904
0
  if (entry->writeFilePtr) {
905
    /** Cache should have been close before, abornormal situation */
906
0
    GF_LOG(GF_LOG_WARNING, GF_LOG_CACHE, ("[CACHE] Cache for %s has not been closed properly\n", entry->url));
907
0
    gf_fclose(entry->writeFilePtr);
908
0
  }
909
0
  gf_blob_unregister(&entry->cache_blob);
910
0
  if (entry->external_blob) {
911
0
    gf_blob_unregister(entry->external_blob);
912
0
  }
913
914
0
  if (!entry->mem_storage ) {
915
0
    char szLOCK[GF_MAX_PATH];
916
0
    sprintf(szLOCK, "%s.lock", entry->cfg_filename);
917
0
    GF_LockStatus lock_type = cache_entry_lock(szLOCK);
918
919
0
    GF_Config *cfg = gf_cfg_new(NULL, entry->cfg_filename);
920
0
    if (cfg) {
921
0
      char szID[20];
922
0
      sprintf(szID, "%u", gf_sys_get_process_id());
923
0
      gf_cfg_set_key(cfg, CACHE_SECTION_USERS, szID, NULL);
924
925
0
      Bool still_in_use = cache_cleanup_processes(cfg);
926
0
      if (still_in_use && entry->discard_on_delete) {
927
0
        gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_TODELETE, "true");
928
0
        entry->discard_on_delete = GF_FALSE;
929
0
      } else if (!still_in_use) {
930
0
        if (gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_TODELETE)) {
931
0
          gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_TODELETE, NULL);
932
0
          entry->discard_on_delete = GF_TRUE;
933
0
        }
934
0
      }
935
0
      gf_cfg_del(cfg);
936
0
    }
937
0
    if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
938
939
0
    if (entry->discard_on_delete) {
940
0
      gf_file_delete(entry->cache_filename);
941
0
      gf_file_delete(entry->cfg_filename);
942
0
    }
943
0
  }
944
0
  if (entry->cfg_filename) gf_free(entry->cfg_filename);
945
0
  if (entry->cache_filename) gf_free(entry->cache_filename);
946
0
  if (entry->serverETag) gf_free(entry->serverETag);
947
0
  if (entry->diskETag) gf_free(entry->diskETag);
948
0
  if (entry->serverLastModified) gf_free(entry->serverLastModified);
949
0
  if (entry->diskLastModified) gf_free(entry->diskLastModified);
950
0
  if (entry->hash) gf_free(entry->hash);
951
0
  if (entry->url) gf_free(entry->url);
952
0
  if (entry->mimeType) gf_free(entry->mimeType);
953
0
  if (entry->mem_storage && entry->mem_allocated) gf_free(entry->mem_storage);
954
0
  if (entry->forced_headers) gf_free(entry->forced_headers);
955
956
957
0
  if (entry->sessions) {
958
0
    gf_assert( gf_list_count(entry->sessions) == 0);
959
0
    gf_list_del(entry->sessions);
960
0
  }
961
0
  gf_free (entry);
962
0
}
963
964
965
0
s32 gf_cache_remove_session_from_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess) {
966
0
  u32 i;
967
0
  s32 count;
968
0
  if (!entry || !sess || !entry->sessions)
969
0
    return -1;
970
0
  count = gf_list_count(entry->sessions);
971
0
  for (i = 0 ; i < (u32)count; i++) {
972
0
    GF_DownloadSession *s = (GF_DownloadSession*)gf_list_get(entry->sessions, i);
973
0
    if (s == sess) {
974
0
      gf_list_rem(entry->sessions, i);
975
0
      count --;
976
0
      break;
977
0
    }
978
0
  }
979
0
  if (entry->write_session == sess) {
980
    /* OK, this is not optimal to close it since we are in a mutex,
981
    * but we don't want to risk to have another session opening
982
    * a not fully closed cache entry */
983
0
    if (entry->writeFilePtr) {
984
0
      if (gf_fclose(entry->writeFilePtr)) {
985
0
        GF_LOG(GF_LOG_ERROR, GF_LOG_CACHE, ("[CACHE] gf_cache_remove_session_from_cache_entry:%d, Failed to properly close cache file '%s' of url '%s', cache may be corrupted !\n", __LINE__, entry->cache_filename, entry->url));
986
0
      }
987
0
    }
988
0
    entry->writeFilePtr = NULL;
989
0
    entry->write_session = NULL;
990
0
  }
991
0
  return count;
992
0
}
993
994
void gf_cache_set_max_age(const DownloadedCacheEntry entry, u32 max_age, Bool must_revalidate)
995
0
{
996
0
  char szVal[100];
997
0
  if (!entry || entry->mem_storage) return;
998
999
0
  char szLOCK[GF_MAX_PATH];
1000
0
  sprintf(szLOCK, "%s.lock", entry->cfg_filename);
1001
0
  GF_LockStatus lock_type = cache_entry_lock(szLOCK);
1002
0
  GF_Config *cfg = gf_cfg_new(NULL, entry->cfg_filename);
1003
1004
0
  entry->must_revalidate = must_revalidate;
1005
0
  if (max_age)
1006
0
    entry->max_age = gf_net_get_utc()/1000 + max_age;
1007
0
  sprintf(szVal, LLU, entry->max_age);
1008
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_MAXAGE, szVal);
1009
0
  gf_cfg_set_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_MUST_REVALIDATE, must_revalidate ? "true" : "false");
1010
0
  gf_cfg_del(cfg);
1011
1012
0
  if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
1013
0
}
1014
1015
static u32 gf_cache_get_sessions_count_for_cache_entry(const DownloadedCacheEntry entry)
1016
0
{
1017
0
  if (!entry)
1018
0
    return 0;
1019
0
  return gf_list_count(entry->sessions);
1020
0
}
1021
1022
1023
0
s32 gf_cache_add_session_to_cache_entry(DownloadedCacheEntry entry, GF_DownloadSession * sess) {
1024
0
  u32 i;
1025
0
  s32 count;
1026
0
  if (!entry || !sess || !entry->sessions)
1027
0
    return -1;
1028
0
  count = gf_list_count(entry->sessions);
1029
0
  for (i = 0 ; i < (u32)count; i++) {
1030
0
    GF_DownloadSession *s = (GF_DownloadSession*)gf_list_get(entry->sessions, i);
1031
0
    if (s == sess) {
1032
0
      return count;
1033
0
    }
1034
0
  }
1035
0
  gf_list_add(entry->sessions, sess);
1036
0
  return count + 1;
1037
0
}
1038
1039
void gf_cache_set_end_range(DownloadedCacheEntry entry, u64 range_end)
1040
0
{
1041
0
  if (entry) {
1042
0
    entry->previousRangeContentLength = entry->contentLength;
1043
0
    entry->range_end = range_end;
1044
0
    entry->continue_file = GF_TRUE;
1045
0
  }
1046
0
}
1047
1048
Bool gf_cache_is_in_progress(const DownloadedCacheEntry entry)
1049
0
{
1050
0
  if (!entry) return GF_FALSE;
1051
0
  if (entry->writeFilePtr) return GF_TRUE;
1052
0
  if (entry->mem_storage && entry->written_in_cache && entry->contentLength && (entry->written_in_cache<entry->contentLength))
1053
0
    return GF_TRUE;
1054
0
  return GF_FALSE;
1055
0
}
1056
1057
Bool gf_cache_set_mime(const DownloadedCacheEntry entry, const char *mime)
1058
0
{
1059
0
  if (!entry || !entry->memory_stored) return GF_FALSE;
1060
0
  if (entry->mimeType) gf_free(entry->mimeType);
1061
0
  entry->mimeType = gf_strdup(mime);
1062
0
  return GF_TRUE;
1063
0
}
1064
1065
Bool gf_cache_set_range(const DownloadedCacheEntry entry, u64 size, u64 start_range, u64 end_range)
1066
0
{
1067
0
  if (!entry || !entry->memory_stored) return GF_FALSE;
1068
0
  entry->range_start = start_range;
1069
0
  entry->range_end = end_range;
1070
0
  entry->contentLength = (u32) size;
1071
0
  entry->continue_file = GF_FALSE;
1072
0
  return GF_TRUE;
1073
0
}
1074
1075
Bool gf_cache_set_headers(const DownloadedCacheEntry entry, const char *headers)
1076
0
{
1077
0
  if (!entry || !entry->memory_stored) return GF_FALSE;
1078
0
  if (entry->forced_headers) gf_free(entry->forced_headers);
1079
0
  entry->forced_headers = headers ? gf_strdup(headers) : NULL;
1080
0
  return GF_TRUE;
1081
0
}
1082
1083
char *gf_cache_get_forced_headers(const DownloadedCacheEntry entry)
1084
0
{
1085
0
  if (!entry) return NULL;
1086
0
  return entry->forced_headers;
1087
0
}
1088
void gf_cache_set_downtime(const DownloadedCacheEntry entry, u32 download_time_ms)
1089
0
{
1090
0
  if (entry) entry->downtime = download_time_ms;
1091
0
}
1092
u32 gf_cache_get_downtime(const DownloadedCacheEntry entry)
1093
0
{
1094
0
  if (!entry) return 0;
1095
0
  return entry->downtime;
1096
0
}
1097
u32 gf_cache_is_done(const DownloadedCacheEntry entry)
1098
0
{
1099
0
  if (!entry) return 1;
1100
0
  u32 res = 1;
1101
0
  if (entry->external_blob) {
1102
0
    gf_mx_p(entry->external_blob->mx);
1103
0
    res = (entry->external_blob->flags & GF_BLOB_IN_TRANSFER) ? 0 : 1;
1104
0
    if (res && (entry->external_blob->flags & GF_BLOB_CORRUPTED))
1105
0
      res = 2;
1106
0
    gf_mx_v(entry->external_blob->mx);
1107
0
  } else if (entry->mem_storage) {
1108
0
    res = (entry->cache_blob.flags & GF_BLOB_IN_TRANSFER) ? 0 : 1;
1109
0
  } else {
1110
0
    if (!(entry->flags & IN_PROGRESS)) return 1;
1111
0
    char szLOCK[GF_MAX_PATH];
1112
0
    sprintf(szLOCK, "%s.lock", entry->cfg_filename);
1113
0
    GF_LockStatus lock_type = cache_entry_lock(szLOCK);
1114
0
    GF_Config *cfg = gf_cfg_new(NULL, entry->cfg_filename);
1115
0
    const char *opt = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_INWRITE);
1116
0
    res = opt ? 0 : 1;
1117
0
    if (res) {
1118
0
      entry->flags &= ~IN_PROGRESS;
1119
0
      opt = gf_cfg_get_key(cfg, CACHE_SECTION_NAME, CACHE_SECTION_KEY_CONTENT_SIZE);
1120
0
      entry->contentLength = atoi(opt);
1121
0
    }
1122
0
    gf_cfg_del(cfg);
1123
0
    if (lock_type==GF_LOCKFILE_NEW) gf_file_delete(szLOCK);
1124
0
  }
1125
0
  return res;
1126
0
}
1127
const u8 *gf_cache_get_content(const DownloadedCacheEntry entry, u32 *size, u32 *max_valid_size, Bool *was_modified)
1128
0
{
1129
0
  if (!entry) return NULL;
1130
0
  *was_modified = GF_FALSE;
1131
0
  if (entry->external_blob) {
1132
0
    u8 *data;
1133
0
    GF_Err e = gf_blob_get_ex(entry->external_blob, &data, size, NULL);
1134
0
    if (e) return NULL;
1135
0
    *max_valid_size = *size;
1136
0
    if (entry->external_blob->range_valid) {
1137
0
      gf_mx_p(entry->external_blob->mx);
1138
0
      entry->external_blob->range_valid(entry->external_blob, 0, max_valid_size);
1139
0
      gf_mx_v(entry->external_blob->mx);
1140
0
    }
1141
0
    if (entry->external_blob->last_modification_time != entry->cache_blob.last_modification_time) {
1142
0
      *was_modified = GF_TRUE;
1143
0
      entry->cache_blob.last_modification_time = entry->external_blob->last_modification_time;
1144
0
    }
1145
0
    return data;
1146
0
  }
1147
0
  *max_valid_size = *size = entry->cache_blob.size;
1148
0
  return entry->cache_blob.data;
1149
0
}
1150
void gf_cache_release_content(const DownloadedCacheEntry entry)
1151
0
{
1152
0
  if (!entry) return;
1153
0
  if (!entry->external_blob) return;
1154
0
  gf_blob_release_ex(entry->external_blob);
1155
0
}
1156
Bool gf_cache_is_deleted(const DownloadedCacheEntry entry)
1157
0
{
1158
0
  if (!entry) return GF_TRUE;
1159
0
  if (entry->flags & DELETED) return GF_TRUE;
1160
0
  return GF_FALSE;
1161
0
}
1162
1163
Bool gf_cache_set_content(const DownloadedCacheEntry entry, GF_Blob *blob, Bool copy, GF_Mutex *mx)
1164
0
{
1165
0
  if (!entry || !entry->memory_stored) return GF_FALSE;
1166
1167
0
  if (!blob) {
1168
0
    entry->flags |= DELETED;
1169
0
    if (entry->external_blob) {
1170
0
      gf_blob_unregister(entry->external_blob);
1171
0
      entry->external_blob = NULL;
1172
0
    }
1173
0
    return GF_TRUE;
1174
0
  }
1175
0
  if (blob->mx)
1176
0
    gf_mx_p(blob->mx);
1177
1178
0
  if (!copy) {
1179
0
    if (entry->mem_allocated) gf_free(entry->mem_storage);
1180
0
    entry->mem_storage = (u8 *) blob->data;
1181
0
    if (!entry->written_in_cache) {
1182
0
      char *burl = gf_blob_register(blob);
1183
0
      if (burl) {
1184
0
        strcpy(entry->cache_filename, burl);
1185
0
        gf_free(burl);
1186
0
      }
1187
0
    }
1188
1189
0
    entry->written_in_cache = blob->size;
1190
0
    entry->mem_allocated = 0;
1191
0
    entry->cache_blob.data = NULL;
1192
0
    entry->cache_blob.size = 0;
1193
0
    entry->external_blob = blob;
1194
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Storing %d bytes to memory from external module\n", blob->size));
1195
0
  } else {
1196
0
    if (!entry->cache_blob.mx)
1197
0
      entry->cache_blob.mx = mx;
1198
0
    gf_mx_p(entry->cache_blob.mx);
1199
1200
0
    if (blob->size >= entry->mem_allocated) {
1201
0
      u32 new_size;
1202
0
      new_size = MAX(entry->mem_allocated*2, blob->size+1);
1203
0
      entry->mem_storage = (u8*)gf_realloc(entry->mem_allocated ? entry->mem_storage : NULL, (new_size+2));
1204
0
      entry->mem_allocated = new_size;
1205
0
      entry->cache_blob.data = entry->mem_storage;
1206
0
      entry->cache_blob.size = entry->contentLength;
1207
0
      if (!entry->written_in_cache) {
1208
0
        char *burl = gf_blob_register(&entry->cache_blob);
1209
0
        if (burl) {
1210
0
          strcpy(entry->cache_filename, burl);
1211
0
          gf_free(burl);
1212
0
        }
1213
0
      }
1214
0
      GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Reallocating memory cache to %d bytes\n", new_size));
1215
0
    }
1216
0
    memcpy(entry->mem_storage, blob->data, blob->size);
1217
0
    entry->mem_storage[blob->size] = 0;
1218
0
    entry->cache_blob.size = entry->written_in_cache = blob->size;
1219
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_CACHE, ("[CACHE] Storing %d bytes to cache memory\n", blob->size));
1220
1221
0
    gf_mx_v(entry->cache_blob.mx);
1222
1223
0
    entry->cache_blob.flags = blob->flags;
1224
0
  }
1225
0
  if (blob->flags & GF_BLOB_IN_TRANSFER)
1226
0
    entry->contentLength = 0;
1227
0
  else
1228
0
    entry->contentLength = blob->size;
1229
1230
0
  if (blob->mx)
1231
0
    gf_mx_v(blob->mx);
1232
1233
0
  return GF_TRUE;
1234
0
}
1235
1236
static Bool gf_cache_entry_can_reuse(const DownloadedCacheEntry entry, Bool skip_revalidate)
1237
0
{
1238
0
  if (!entry) return GF_FALSE;
1239
  //if corrupted or deleted, we cannot reuse
1240
0
  if (entry->flags & (CORRUPTED|DELETED) ) return GF_FALSE;
1241
0
  if (!entry->max_age) return GF_FALSE;
1242
0
  s64 expires = entry->max_age;
1243
0
  expires -= gf_net_get_utc()/1000;
1244
  //max age not reached, allow reuse
1245
0
  if (expires>0)
1246
0
    return GF_TRUE;
1247
0
  if (entry->must_revalidate)
1248
0
    return GF_FALSE;
1249
  //we consider that if not expired and no must-revalidate, we can go on
1250
0
  return GF_TRUE;
1251
0
}
1252
1253
Bool gf_cache_entry_is_shared(const DownloadedCacheEntry entry)
1254
0
{
1255
0
  return entry ? entry->other_in_use : GF_FALSE;
1256
0
}
1257
1258
Bool gf_cache_is_mem(const DownloadedCacheEntry entry)
1259
0
{
1260
0
  return entry->mem_storage ? GF_TRUE : GF_FALSE;
1261
0
}
1262
FILE *gf_cache_open_read(const DownloadedCacheEntry entry)
1263
0
{
1264
0
  if (!entry) return NULL;
1265
0
  if (entry->mem_storage) return NULL;
1266
0
  return gf_fopen(entry->cache_filename, "r");
1267
0
}
1268
1269
1270
/*!
1271
 * Finds an existing entry in the cache for a given URL
1272
\param sess The session configured with the URL
1273
\return NULL if none found, the DownloadedCacheEntry otherwise
1274
 */
1275
static DownloadedCacheEntry gf_cache_find_entry_by_url(GF_DownloadSession * sess)
1276
0
{
1277
0
  u32 i, count;
1278
0
  gf_assert( sess && sess->dm && sess->dm->cache_entries );
1279
0
  gf_mx_p( sess->dm->cache_mx );
1280
0
  count = gf_list_count(sess->dm->cache_entries);
1281
0
  for (i = 0 ; i < count; i++) {
1282
0
    const char * url;
1283
0
    DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
1284
0
    gf_assert(e);
1285
0
    url = gf_cache_get_url(e);
1286
0
    gf_assert( url );
1287
1288
0
    if (!strncmp(url, "http://gmcast/", 14)) {
1289
0
      char *sep_1 = strchr(url+14, '/');
1290
0
      char *sep_2 = strchr(sess->orig_url+14, '/');
1291
0
      if (!sep_1 || !sep_2 || strcmp(sep_1, sep_2))
1292
0
        continue;
1293
0
    } else if (strcmp(url, sess->orig_url)) continue;
1294
1295
0
    if (! sess->is_range_continuation) {
1296
0
      if (sess->range_start != gf_cache_get_start_range(e)) continue;
1297
0
      if (sess->range_end != gf_cache_get_end_range(e)) continue;
1298
0
    }
1299
    /*OK that's ours*/
1300
0
    gf_mx_v( sess->dm->cache_mx );
1301
0
    return e;
1302
0
  }
1303
0
  gf_mx_v( sess->dm->cache_mx );
1304
0
  return NULL;
1305
0
}
1306
1307
1308
/**
1309
 * Removes a cache entry from cache and performs a cleanup if possible.
1310
 * If the cache entry is marked for deletion and has no sessions associated with it, it will be
1311
 * removed (so some modules using a streaming like cache will still work).
1312
 */
1313
void gf_cache_remove_entry_from_session(GF_DownloadSession * sess)
1314
0
{
1315
0
  if (!sess || !sess->cache_entry) return;
1316
1317
0
  gf_cache_remove_session_from_cache_entry(sess->cache_entry, sess);
1318
0
  if (sess->dm
1319
      /*JLF - not sure what the rationale of this test is, and it prevents cleanup of cache entry
1320
      which then results to crash when restarting the session (entry->writeFilePtr is not set back to NULL)*/
1321
0
      && sess->cache_entry->discard_on_delete
1322
1323
0
      && (0 == gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)))
1324
0
  {
1325
0
    u32 i, count;
1326
0
    gf_mx_p( sess->dm->cache_mx );
1327
0
    count = gf_list_count( sess->dm->cache_entries );
1328
0
    for (i = 0; i < count; i++) {
1329
0
      DownloadedCacheEntry ex = (DownloadedCacheEntry)gf_list_get(sess->dm->cache_entries, i);
1330
0
      if (ex == sess->cache_entry) {
1331
0
        gf_list_rem(sess->dm->cache_entries, i);
1332
0
        gf_cache_delete_entry( sess->cache_entry );
1333
0
        sess->cache_entry = NULL;
1334
0
        break;
1335
0
      }
1336
0
    }
1337
0
    gf_mx_v( sess->dm->cache_mx );
1338
0
  }
1339
1340
0
}
1341
1342
1343
void gf_dm_configure_cache(GF_DownloadSession *sess)
1344
0
{
1345
0
  DownloadedCacheEntry entry;
1346
0
  gf_cache_remove_entry_from_session(sess);
1347
1348
  //session is not cached and we don't cache the first URL
1349
0
  if ((sess->flags & GF_NETIO_SESSION_NOT_CACHED) && !(sess->flags & GF_NETIO_SESSION_KEEP_FIRST_CACHE))  {
1350
0
    sess->reused_cache_entry = GF_FALSE;
1351
0
    if (sess->cache_entry)
1352
0
      gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
1353
1354
0
    sess->cache_entry = NULL;
1355
0
    return;
1356
0
  }
1357
1358
0
  Bool found = GF_FALSE;
1359
0
  u32 i, count;
1360
0
  entry = gf_cache_find_entry_by_url(sess);
1361
0
  if (!entry) {
1362
0
    if (sess->local_cache_only) {
1363
0
      sess->cache_entry = NULL;
1364
0
      SET_LAST_ERR(GF_URL_ERROR)
1365
0
      return;
1366
0
    }
1367
    /* We found the existing session */
1368
0
    if (sess->cache_entry) {
1369
0
      Bool delete_cache = GF_TRUE;
1370
1371
0
      if (sess->flags & GF_NETIO_SESSION_KEEP_CACHE) {
1372
0
        delete_cache = GF_FALSE;
1373
0
      }
1374
0
      if (gf_cache_entry_persistent(sess->cache_entry))
1375
0
        delete_cache = GF_FALSE;
1376
1377
      /*! indicate we can destroy file upon destruction, except if disabled at session level*/
1378
0
      if (delete_cache)
1379
0
        gf_cache_entry_set_delete_files_when_deleted(sess->cache_entry);
1380
1381
0
      if (!gf_cache_entry_persistent(sess->cache_entry)
1382
0
        && !gf_cache_get_sessions_count_for_cache_entry(sess->cache_entry)
1383
0
      ) {
1384
0
        gf_mx_p( sess->dm->cache_mx );
1385
        /* No session attached anymore... we can delete it */
1386
0
        gf_list_del_item(sess->dm->cache_entries, sess->cache_entry);
1387
0
        gf_mx_v( sess->dm->cache_mx );
1388
0
        gf_cache_delete_entry(sess->cache_entry);
1389
0
      }
1390
0
      sess->cache_entry = NULL;
1391
0
    }
1392
0
    Bool use_mem = (sess->flags & (GF_NETIO_SESSION_MEMORY_CACHE | GF_NETIO_SESSION_NO_STORE)) ? GF_TRUE : GF_FALSE;
1393
0
    entry = gf_cache_create_entry(sess->dm->cache_directory, sess->orig_url, sess->range_start, sess->range_end, use_mem, sess->dm->cache_mx);
1394
0
    if (!entry) {
1395
0
      SET_LAST_ERR(GF_OUT_OF_MEM)
1396
0
      return;
1397
0
    }
1398
0
    gf_mx_p( sess->dm->cache_mx );
1399
0
    gf_list_add(sess->dm->cache_entries, entry);
1400
0
    gf_mx_v( sess->dm->cache_mx );
1401
0
    sess->is_range_continuation = GF_FALSE;
1402
0
  }
1403
0
  sess->cache_entry = entry;
1404
0
  sess->reused_cache_entry =  gf_cache_is_in_progress(entry);
1405
0
  gf_mx_p(sess->dm->cache_mx);
1406
0
  count = gf_list_count(sess->dm->all_sessions);
1407
0
  for (i=0; i<count; i++) {
1408
0
    GF_DownloadSession *a_sess = (GF_DownloadSession*)gf_list_get(sess->dm->all_sessions, i);
1409
0
    gf_assert(a_sess);
1410
0
    if (a_sess==sess) continue;
1411
0
    if (a_sess->cache_entry==entry) {
1412
0
      found = GF_TRUE;
1413
0
      break;
1414
0
    }
1415
0
  }
1416
0
  gf_mx_v(sess->dm->cache_mx);
1417
0
  if (!found) {
1418
0
    sess->reused_cache_entry = GF_FALSE;
1419
0
    if (sess->cache_entry)
1420
0
      gf_cache_close_write_cache(sess->cache_entry, sess, GF_FALSE);
1421
0
  }
1422
0
  gf_cache_add_session_to_cache_entry(sess->cache_entry, sess);
1423
0
  if (sess->needs_range)
1424
0
    gf_cache_set_range(sess->cache_entry, 0, sess->range_start, sess->range_end);
1425
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[CACHE] Cache for %s setup %s\n", sess->orig_url, gf_cache_get_cache_filename(sess->cache_entry)));
1426
1427
0
  Bool no_revalidate = GF_FALSE;
1428
0
  if (sess->allow_direct_reuse)
1429
0
    no_revalidate = GF_TRUE;
1430
1431
0
  if (sess->cache_entry) {
1432
0
    if (sess->flags & GF_NETIO_SESSION_KEEP_FIRST_CACHE) {
1433
0
      sess->flags &= ~GF_NETIO_SESSION_KEEP_FIRST_CACHE;
1434
0
      gf_cache_entry_set_persistent(sess->cache_entry);
1435
0
    }
1436
    //this one is only used for init segments in dash/hls
1437
0
    if ((sess->flags & GF_NETIO_SESSION_MEMORY_CACHE) && (sess->flags & GF_NETIO_SESSION_KEEP_CACHE) ) {
1438
0
      gf_cache_entry_set_persistent(sess->cache_entry);
1439
0
    }
1440
1441
0
    if (gf_cache_entry_can_reuse(sess->cache_entry, sess->dm->allow_offline_cache)) {
1442
0
      no_revalidate = GF_TRUE;
1443
0
    }
1444
    //we cannot reuse and others are using the cache file - for now we don't have any other possibility
1445
    //than disabling the cache to avoid overwriting the resource
1446
0
    else if (gf_cache_entry_is_shared(sess->cache_entry)) {
1447
0
      gf_cache_remove_entry_from_session(sess);
1448
0
      sess->cache_entry = NULL;
1449
0
      sess->reused_cache_entry = GF_FALSE;
1450
0
      return;
1451
0
    }
1452
0
  }
1453
1454
0
  if (no_revalidate)
1455
0
    sess->cached_file = gf_cache_open_read(sess->cache_entry);
1456
1457
0
  if (sess->cached_file) {
1458
0
    sess->connect_time = 0;
1459
0
    sess->status = GF_NETIO_CONNECTED;
1460
0
    const char *mime = gf_cache_get_mime_type(sess->cache_entry);
1461
0
    if (mime)
1462
0
      gf_dm_sess_set_header_ex(sess, "Content-Type", mime, GF_TRUE);
1463
0
    u32 size = gf_cache_get_content_length(sess->cache_entry);
1464
0
    if (size) {
1465
0
      char szHdr[20];
1466
0
      sprintf(szHdr, "%u", size);
1467
0
      gf_dm_sess_set_header_ex(sess, "Content-Length", szHdr, GF_TRUE);
1468
0
    }
1469
1470
0
    GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[%s] using existing cache entry\n", sess->log_name));
1471
0
    gf_dm_sess_notify_state(sess, GF_NETIO_CONNECTED, GF_OK);
1472
0
  }
1473
0
}
1474
1475
void gf_dm_delete_cached_file_entry(const GF_DownloadManager * dm,  const char * url)
1476
0
{
1477
0
  GF_Err e;
1478
0
  u32 count, i;
1479
0
  char * realURL;
1480
0
  GF_URL_Info info;
1481
0
  if (!url || !dm)
1482
0
    return;
1483
0
  gf_mx_p( dm->cache_mx );
1484
0
  gf_dm_url_info_init(&info);
1485
0
  e = gf_dm_get_url_info(url, &info, NULL);
1486
0
  if (e != GF_OK) {
1487
0
    gf_mx_v( dm->cache_mx );
1488
0
    gf_dm_url_info_del(&info);
1489
0
    return;
1490
0
  }
1491
0
  realURL = gf_strdup(info.canonicalRepresentation);
1492
0
  gf_dm_url_info_del(&info);
1493
0
  gf_assert( realURL );
1494
0
  count = gf_list_count(dm->cache_entries);
1495
0
  for (i = 0 ; i < count; i++) {
1496
0
    const char * e_url;
1497
0
    DownloadedCacheEntry cache_ent = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
1498
0
    gf_assert(cache_ent);
1499
0
    e_url = gf_cache_get_url(cache_ent);
1500
0
    gf_assert( e_url );
1501
0
    if (!strcmp(e_url, realURL)) {
1502
      /* We found the existing session */
1503
0
      gf_cache_entry_set_delete_files_when_deleted(cache_ent);
1504
0
      if (0 == gf_cache_get_sessions_count_for_cache_entry( cache_ent )) {
1505
        /* No session attached anymore... we can delete it */
1506
0
        gf_list_rem(dm->cache_entries, i);
1507
0
        gf_cache_delete_entry(cache_ent);
1508
0
      }
1509
      /* If deleted or not, we don't search further */
1510
0
      gf_mx_v( dm->cache_mx );
1511
0
      gf_free(realURL);
1512
0
      return;
1513
0
    }
1514
0
  }
1515
  /* If we are here it means we did not found this URL in cache */
1516
0
  gf_mx_v( dm->cache_mx );
1517
0
  gf_free(realURL);
1518
0
  GF_LOG(GF_LOG_DEBUG, GF_LOG_HTTP, ("[CACHE] Cannot find URL %s, cache file won't be deleted.\n", url));
1519
0
}
1520
1521
GF_EXPORT
1522
void gf_dm_delete_cached_file_entry_session(const GF_DownloadSession * sess,  const char * url, Bool force)
1523
0
{
1524
0
  if (sess && sess->dm && url) {
1525
0
    GF_LOG(GF_LOG_INFO, GF_LOG_HTTP, ("[CACHE] Removing cache entry for %s\n", url));
1526
0
    gf_dm_delete_cached_file_entry(sess->dm, url);
1527
0
    if (sess->local_cache_only && sess->dm->local_cache_url_provider_cbk)
1528
0
      sess->dm->local_cache_url_provider_cbk(sess->dm->lc_udta, (char *) url, GF_TRUE);
1529
0
  }
1530
0
}
1531
1532
GF_EXPORT
1533
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)
1534
2.90k
{
1535
2.90k
  if (!dm)
1536
0
    return GF_BAD_PARAM;
1537
2.90k
  dm->local_cache_url_provider_cbk = local_cache_url_provider_cbk;
1538
2.90k
  dm->lc_udta = lc_udta;
1539
2.90k
  return GF_OK;
1540
1541
2.90k
}
1542
1543
GF_EXPORT
1544
DownloadedCacheEntry gf_dm_add_cache_entry(GF_DownloadManager *dm, const char *szURL, GF_Blob *blob, u64 start_range, u64 end_range, const char *mime, Bool clone_memory, u32 download_time_ms)
1545
0
{
1546
0
  u32 i, count;
1547
0
  DownloadedCacheEntry the_entry = NULL;
1548
1549
0
  gf_mx_p(dm->cache_mx );
1550
0
  if (blob)
1551
0
    GF_LOG(GF_LOG_INFO, GF_LOG_CACHE, ("[CACHE] Pushing %s to cache "LLU" bytes (done %s)\n", szURL, blob->size, (blob->flags & GF_BLOB_IN_TRANSFER) ? "no" : "yes"));
1552
0
  count = gf_list_count(dm->cache_entries);
1553
0
  for (i = 0 ; i < count; i++) {
1554
0
    const char * url;
1555
0
    DownloadedCacheEntry e = (DownloadedCacheEntry)gf_list_get(dm->cache_entries, i);
1556
0
    gf_assert(e);
1557
0
    url = gf_cache_get_url(e);
1558
0
    gf_assert( url );
1559
0
    if (strcmp(url, szURL)) continue;
1560
1561
0
    if (end_range) {
1562
0
      if (start_range != gf_cache_get_start_range(e)) continue;
1563
0
      if (end_range != gf_cache_get_end_range(e)) continue;
1564
0
    }
1565
    /*OK that's ours*/
1566
0
    the_entry = e;
1567
0
    break;
1568
0
  }
1569
0
  if (!the_entry) {
1570
0
    the_entry = gf_cache_create_entry("", szURL, 0, 0, GF_TRUE, dm->cache_mx);
1571
0
    if (!the_entry) {
1572
0
      gf_mx_v(dm->cache_mx );
1573
0
      return NULL;
1574
0
    }
1575
0
    gf_list_add(dm->cache_entries, the_entry);
1576
0
  }
1577
1578
0
  gf_cache_set_mime(the_entry, mime);
1579
0
  if (blob && ! (blob->flags & GF_BLOB_IN_TRANSFER))
1580
0
    gf_cache_set_range(the_entry, blob->size, start_range, end_range);
1581
1582
0
  gf_cache_set_content(the_entry, blob, clone_memory ? GF_TRUE : GF_FALSE, dm->cache_mx);
1583
0
  gf_cache_set_downtime(the_entry, download_time_ms);
1584
0
  gf_mx_v(dm->cache_mx );
1585
0
  return the_entry;
1586
0
}
1587
1588
void gf_dm_sess_reload_cached_headers(GF_DownloadSession *sess)
1589
0
{
1590
0
  char *hdrs;
1591
1592
0
  if (!sess || !sess->local_cache_only) return;
1593
1594
0
  hdrs = gf_cache_get_forced_headers(sess->cache_entry);
1595
1596
0
  gf_dm_sess_clear_headers(sess);
1597
0
  while (hdrs) {
1598
0
    char *sep2, *sepL = strstr(hdrs, "\r\n");
1599
0
    if (sepL) sepL[0] = 0;
1600
0
    sep2 = strchr(hdrs, ':');
1601
0
    if (sep2) {
1602
0
      GF_HTTPHeader *hdr;
1603
0
      GF_SAFEALLOC(hdr, GF_HTTPHeader);
1604
0
      if (!hdr) break;
1605
0
      sep2[0]=0;
1606
0
      hdr->name = gf_strdup(hdrs);
1607
0
      sep2[0]=':';
1608
0
      sep2++;
1609
0
      while (sep2[0]==' ') sep2++;
1610
0
      hdr->value = gf_strdup(sep2);
1611
0
      gf_list_add(sess->headers, hdr);
1612
0
    }
1613
0
    if (!sepL) break;
1614
0
    sepL[0] = '\r';
1615
0
    hdrs = sepL + 2;
1616
0
  }
1617
0
}
1618
GF_EXPORT
1619
GF_Err gf_dm_force_headers(GF_DownloadManager *dm, const DownloadedCacheEntry entry, const char *headers)
1620
0
{
1621
0
  u32 i, count;
1622
0
  Bool res;
1623
0
  if (!entry)
1624
0
    return GF_BAD_PARAM;
1625
0
  gf_mx_p(dm->cache_mx);
1626
0
  res = gf_cache_set_headers(entry, headers);
1627
0
  count = gf_list_count(dm->all_sessions);
1628
0
  for (i=0; i<count; i++) {
1629
0
    GF_DownloadSession *sess = gf_list_get(dm->all_sessions, i);
1630
0
    if (sess->cache_entry != entry) continue;
1631
0
    gf_dm_sess_reload_cached_headers(sess);
1632
0
  }
1633
1634
0
  gf_mx_v(dm->cache_mx);
1635
0
  if (res) return GF_OK;
1636
0
  return GF_BAD_PARAM;
1637
0
}
1638
1639
#endif //GPAC_DISABLE_NETWORK