Coverage Report

Created: 2025-10-12 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gpac/src/jsmods/storage.c
Line
Count
Source
1
/*
2
 *      GPAC - Multimedia Framework C SDK
3
 *
4
 *      Authors: Jean Le Feuvre
5
 *      Copyright (c) Telecom ParisTech 2007-2023
6
 *      All rights reserved
7
 *
8
 *  This file is part of GPAC / JavaScript Storage bindings
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
/*
27
  ANY CHANGE TO THE API MUST BE REFLECTED IN THE DOCUMENTATION IN gpac/share/doc/idl/storage.js
28
  (no way to define inline JS doc with doxygen)
29
*/
30
31
#include <gpac/setup.h>
32
33
#ifdef GPAC_HAS_QJS
34
35
#include <gpac/config_file.h>
36
#include "../scenegraph/qjs_common.h"
37
38
39
static JSClassID storage_class_id = 0;
40
GF_List *all_storages = NULL;
41
42
static void storage_finalize(JSRuntime *rt, JSValue obj)
43
0
{
44
0
  GF_Config *cfg = JS_GetOpaque(obj, storage_class_id);
45
0
  if (!cfg) return;
46
0
  if (all_storages) {
47
0
    gf_list_del_item(all_storages, cfg);
48
0
    if (!gf_list_count(all_storages)) {
49
0
      gf_list_del(all_storages);
50
0
      all_storages = NULL;
51
0
    }
52
0
  }
53
0
  gf_cfg_del(cfg);
54
0
}
55
56
JSClassDef storageClass = {
57
    "Storage",
58
    .finalizer = storage_finalize,
59
};
60
61
62
static JSValue js_storage_get_option(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
63
0
{
64
0
  const char *opt = NULL;
65
0
  const char *sec_name, *key_name;
66
0
  s32 idx = -1;
67
0
  GF_Config *config = JS_GetOpaque(this_val, storage_class_id);
68
0
  if (!config) return GF_JS_EXCEPTION(ctx);
69
0
  if (argc < 2) return GF_JS_EXCEPTION(ctx);
70
71
0
  if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(ctx);
72
0
  if (!JS_IsString(argv[1]) && !JS_IsInteger(argv[1])) return GF_JS_EXCEPTION(ctx);
73
74
0
  sec_name = JS_ToCString(ctx, argv[0]);
75
0
  if (!strcmp(sec_name, "GPAC")) {
76
0
    JS_FreeCString(ctx, sec_name);
77
0
    return js_throw_err_msg(ctx, GF_BAD_PARAM, "Cannot access section 'GPAC' from script\n");
78
0
  }
79
80
0
  key_name = NULL;
81
0
  if (JS_IsInteger(argv[1])) {
82
0
    JS_ToInt32(ctx, &idx, argv[1]);
83
0
  } else if (JS_IsString(argv[1]) ) {
84
0
    key_name = JS_ToCString(ctx, argv[1]);
85
0
  }
86
87
0
  if (key_name) {
88
0
    opt = gf_cfg_get_key(config, sec_name, key_name);
89
0
  } else if (idx>=0) {
90
0
    opt = gf_cfg_get_key_name(config, sec_name, idx);
91
0
  } else {
92
0
    opt = NULL;
93
0
  }
94
95
0
  JS_FreeCString(ctx, key_name);
96
0
  JS_FreeCString(ctx, sec_name);
97
98
0
  if (opt) {
99
0
    return JS_NewString(ctx, opt);
100
0
  }
101
0
  return JS_NULL;
102
0
}
103
104
static JSValue js_storage_set_option(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
105
0
{
106
0
  const char *sec_name, *key_name, *key_val;
107
0
  GF_Config *config = JS_GetOpaque(this_val, storage_class_id);
108
0
  if (!config) return GF_JS_EXCEPTION(ctx);
109
0
  if (argc < 3) return GF_JS_EXCEPTION(ctx);
110
111
0
  if (!JS_IsString(argv[0])) return GF_JS_EXCEPTION(ctx);
112
0
  if (!JS_IsString(argv[1])) return GF_JS_EXCEPTION(ctx);
113
114
0
  sec_name = JS_ToCString(ctx, argv[0]);
115
0
  if (!strcmp(sec_name, "GPAC")) {
116
0
    JS_FreeCString(ctx, sec_name);
117
0
    return js_throw_err_msg(ctx, GF_BAD_PARAM, "Cannot access section 'GPAC' from script\n");
118
0
  }
119
0
  key_name = JS_ToCString(ctx, argv[1]);
120
0
  key_val = NULL;
121
0
  if (JS_IsString(argv[2]))
122
0
    key_val = JS_ToCString(ctx, argv[2]);
123
124
0
  gf_cfg_set_key(config, sec_name, key_name, key_val);
125
126
0
  JS_FreeCString(ctx, sec_name);
127
0
  JS_FreeCString(ctx, key_name);
128
0
  JS_FreeCString(ctx, key_val);
129
0
  return JS_UNDEFINED;
130
0
}
131
132
static JSValue js_storage_save(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
133
0
{
134
0
  GF_Config *config = JS_GetOpaque(this_val, storage_class_id);
135
0
  if (!config) return GF_JS_EXCEPTION(ctx);
136
0
  gf_cfg_save(config);
137
0
  return JS_UNDEFINED;
138
0
}
139
140
static const JSCFunctionListEntry storage_funcs[] = {
141
  JS_CFUNC_DEF("get_option", 0, js_storage_get_option),
142
  JS_CFUNC_DEF("set_option", 0, js_storage_set_option),
143
  JS_CFUNC_DEF("save", 0, js_storage_save),
144
};
145
146
static JSValue storage_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
147
0
{
148
0
  char szFile[GF_MAX_PATH];
149
0
  JSValue anobj;
150
0
  GF_Config *storage = NULL;
151
0
  const char *storage_url = NULL;
152
0
  u32 i, count;
153
0
  u8 hash[20];
154
0
  char temp[3];
155
156
0
  if (!JS_IsString(argv[0]) )
157
0
    return GF_JS_EXCEPTION(ctx);
158
159
0
  storage_url = JS_ToCString(ctx, argv[0]);
160
0
  if (!storage_url) return JS_NULL;
161
162
0
  szFile[0]=0;
163
0
  gf_sha1_csum((u8 *)storage_url, (u32) strlen(storage_url), hash);
164
0
  for (i=0; i<20; i++) {
165
0
    sprintf(temp, "%02X", hash[i]);
166
0
    strcat(szFile, temp);
167
0
  }
168
0
  strcat(szFile, ".cfg");
169
170
0
  count = gf_list_count(all_storages);
171
0
  for (i=0; i<count; i++) {
172
0
    GF_Config *a_cfg = gf_list_get(all_storages, i);
173
0
    const char *cfg_name = gf_cfg_get_filename(a_cfg);
174
175
0
    if (strstr(cfg_name, szFile)) {
176
0
      storage = a_cfg;
177
0
      break;
178
0
    }
179
0
  }
180
181
0
  if (!storage) {
182
0
    const char *storage_dir = gf_opts_get_key("core", "store-dir");
183
184
0
    storage = gf_cfg_force_new(storage_dir, szFile);
185
0
    if (storage) {
186
0
      gf_cfg_set_key(storage, "GPAC", "StorageURL", storage_url);
187
0
      gf_list_add(all_storages, storage);
188
0
    }
189
0
  }
190
191
0
  JS_FreeCString(ctx, storage_url);
192
193
0
  anobj = JS_NewObjectClass(ctx, storage_class_id);
194
0
  if (JS_IsException(anobj)) return anobj;
195
0
  JS_SetOpaque(anobj, storage);
196
0
  return anobj;
197
0
}
198
199
static int js_storage_init(JSContext *c, JSModuleDef *m)
200
0
{
201
0
  if (!storage_class_id) {
202
0
    JS_NewClassID(&storage_class_id);
203
0
    JS_NewClass(JS_GetRuntime(c), storage_class_id, &storageClass);
204
205
0
    gf_assert(!all_storages);
206
0
    all_storages = gf_list_new();
207
0
  }
208
209
0
  JSValue proto = JS_NewObjectClass(c, storage_class_id);
210
0
  JS_SetPropertyFunctionList(c, proto, storage_funcs, countof(storage_funcs));
211
0
  JS_SetClassProto(c, storage_class_id, proto);
212
213
0
  JSValue ctor = JS_NewCFunction2(c, storage_constructor, "Storage", 1, JS_CFUNC_constructor, 0);
214
0
    JS_SetModuleExport(c, m, "Storage", ctor);
215
0
  return 0;
216
0
}
217
218
219
void qjs_module_init_storage(JSContext *ctx)
220
0
{
221
0
  JSModuleDef *m;
222
0
  m = JS_NewCModule(ctx, "storage", js_storage_init);
223
0
  if (!m) return;
224
225
0
  JS_AddModuleExport(ctx, m, "Storage");
226
0
  return;
227
0
}
228
229
230
#endif
231