/src/gpac/src/utils/url.c
Line | Count | Source |
1 | | /* |
2 | | * GPAC - Multimedia Framework C SDK |
3 | | * |
4 | | * Authors: Jean Le Feuvre |
5 | | * Copyright (c) Telecom ParisTech 2000-2025 |
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/network.h> |
27 | | |
28 | | /* the length of the URL separator ("://" || "|//") */ |
29 | | #define URL_SEP_LENGTH 3 |
30 | | |
31 | | /* our supported protocol types */ |
32 | | enum |
33 | | { |
34 | | /*absolute path to file*/ |
35 | | GF_URL_TYPE_FILE_PATH = 0, |
36 | | |
37 | | /*absolute file:// URI */ |
38 | | GF_URL_TYPE_FILE_URI, |
39 | | |
40 | | /*relative path or URL*/ |
41 | | GF_URL_TYPE_RELATIVE , |
42 | | |
43 | | /*any other absolute URI*/ |
44 | | GF_URL_TYPE_ANY_URI, |
45 | | |
46 | | /*invalid input */ |
47 | | GF_URL_TYPE_INVALID, |
48 | | }; |
49 | | |
50 | | /*resolve the protocol type, for a std URL: http:// or ftp:// ...*/ |
51 | | static u32 URL_GetProtocolType(const char *pathName) |
52 | 2.42k | { |
53 | 2.42k | char *begin; |
54 | 2.42k | if (!pathName) return GF_URL_TYPE_INVALID; |
55 | | |
56 | | /* URL with the data scheme are not relative to avoid concatenation */ |
57 | 2.42k | if (!strnicmp(pathName, "data:", 5)) return GF_URL_TYPE_ANY_URI; |
58 | | |
59 | | |
60 | | //conditions for a file path to be absolute: |
61 | | // - on posix: absolute iif starts with '/' |
62 | | // - on windows: absolute if |
63 | | // * starts with \ or / (current drive) |
64 | | // * OR starts with <LETTER>: and then \ or / |
65 | | // * OR starts with \\host\share\<path> [NOT HANDLED HERE] |
66 | 2.42k | #ifndef WIN32 |
67 | 2.42k | if (pathName[0] == '/') |
68 | | #else |
69 | | if ( (pathName[0] == '/') || (pathName[0] == '\\') |
70 | | || ( strlen(pathName)>2 && pathName[1]==':' |
71 | | && ((pathName[2] == '/') || (pathName[2] == '\\')) |
72 | | ) |
73 | | ) |
74 | | #endif |
75 | 0 | return GF_URL_TYPE_FILE_PATH; |
76 | | |
77 | | |
78 | 2.42k | begin = strstr(pathName, "://"); |
79 | 2.42k | if (!begin) |
80 | 0 | return GF_URL_TYPE_RELATIVE; |
81 | | |
82 | 2.42k | else if (!strnicmp(pathName, "file://", 7)) |
83 | 0 | return (strlen(pathName)>7 ? GF_URL_TYPE_FILE_URI : GF_URL_TYPE_INVALID); |
84 | | |
85 | 2.42k | return GF_URL_TYPE_ANY_URI; |
86 | 2.42k | } |
87 | | |
88 | | /*gets protocol type*/ |
89 | | GF_EXPORT |
90 | | Bool gf_url_is_local(const char *pathName) |
91 | 2.42k | { |
92 | 2.42k | u32 mode = URL_GetProtocolType(pathName); |
93 | 2.42k | return (mode!=GF_URL_TYPE_INVALID && mode!=GF_URL_TYPE_ANY_URI) ? GF_TRUE : GF_FALSE; |
94 | 2.42k | } |
95 | | |
96 | | GF_EXPORT |
97 | | Bool gf_url_is_relative(const char *pathName) |
98 | 0 | { |
99 | 0 | if (!pathName) return GF_TRUE; |
100 | 0 | u32 mode = URL_GetProtocolType(pathName); |
101 | 0 | return (mode==GF_URL_TYPE_RELATIVE) ? GF_TRUE : GF_FALSE; |
102 | 0 | } |
103 | | |
104 | | GF_EXPORT |
105 | | char *gf_url_get_absolute_path(const char *pathName, const char *parentPath) |
106 | 0 | { |
107 | 0 | char* sep; |
108 | 0 | u32 parent_type; |
109 | 0 | char* res = NULL; |
110 | |
|
111 | 0 | u32 prot_type = URL_GetProtocolType(pathName); |
112 | |
|
113 | 0 | switch (prot_type) { |
114 | | |
115 | | // if it's already absolute, do nothing |
116 | 0 | case GF_URL_TYPE_FILE_PATH: |
117 | 0 | case GF_URL_TYPE_ANY_URI: |
118 | 0 | res = gf_strdup(pathName); |
119 | 0 | break; |
120 | | |
121 | | // if file URI, remove the scheme part |
122 | 0 | case GF_URL_TYPE_FILE_URI: |
123 | |
|
124 | 0 | pathName += 6; // keep a slash in case it's forgotten |
125 | | |
126 | | /* Windows file URIs SHOULD BE in the form "file:///C:\..." |
127 | | * Unix file URIs SHOULD BE in the form "file:///home..." |
128 | | * anything before the 3rd '/' is a hostname |
129 | | */ |
130 | 0 | sep = strchr(pathName+1, '/'); |
131 | 0 | if (sep) { |
132 | 0 | pathName = sep; |
133 | | |
134 | | // dirty way to say if windows |
135 | | // consume the third / in that case |
136 | 0 | if (strlen(pathName) > 2 && pathName[2]==':') |
137 | 0 | pathName++; |
138 | 0 | } |
139 | 0 | res = gf_strdup(pathName); |
140 | 0 | break; |
141 | | |
142 | | // if it's relative, it depends on the parent |
143 | 0 | case GF_URL_TYPE_RELATIVE: |
144 | 0 | parent_type = URL_GetProtocolType(parentPath); |
145 | | |
146 | | // in this case the parent is of no help to find an absolute path so we do nothing |
147 | 0 | if (parent_type == GF_URL_TYPE_RELATIVE || parent_type == GF_URL_TYPE_INVALID ) |
148 | 0 | res = gf_strdup(pathName); |
149 | 0 | else |
150 | 0 | res = gf_url_concatenate(parentPath, pathName); |
151 | |
|
152 | 0 | break; |
153 | |
|
154 | 0 | } |
155 | | |
156 | 0 | return res; |
157 | |
|
158 | 0 | } |
159 | | |
160 | | //set to 0 to disable max URL len - we don't use MAX_PATH as it can be small on some systems |
161 | 0 | #define MAX_URL_LEN 4096 |
162 | | static char *gf_url_concatenate_ex(const char *parentName, const char *pathName, Bool relative_to_parent) |
163 | 0 | { |
164 | 0 | u32 pathSepCount, i, prot_type; |
165 | 0 | Bool had_sep_count = GF_FALSE; |
166 | 0 | char *outPath, *name, *rad, *tmp2; |
167 | 0 | char *tmp = NULL; |
168 | |
|
169 | 0 | if (!pathName && !parentName) return NULL; |
170 | 0 | if (!pathName) return gf_strdup(parentName); |
171 | 0 | if (!parentName || !strlen(parentName)) return gf_strdup(pathName); |
172 | | |
173 | 0 | if (!strncmp(pathName, "data:", 5)) return gf_strdup(pathName); |
174 | 0 | if (!strcmp(pathName, "stderr")) return gf_strdup(pathName); |
175 | 0 | if (!strcmp(pathName, "stdin")) return gf_strdup(pathName); |
176 | 0 | if (!strcmp(pathName, "stdout")) return gf_strdup(pathName); |
177 | 0 | if (!strncmp(parentName, "gmem://", 7)) return NULL; |
178 | 0 | if (!strncmp(parentName, "gfio://", 7)) { |
179 | 0 | GF_Err e; |
180 | 0 | GF_FileIO *gfio = gf_fileio_from_url(parentName); |
181 | 0 | GF_FileIO *gfio_new = gf_fileio_open_url(gfio, pathName, "url", &e); |
182 | 0 | if (!gfio_new) |
183 | 0 | return NULL; |
184 | 0 | return gf_strdup( gf_fileio_url(gfio_new) ); |
185 | 0 | } |
186 | | |
187 | 0 | #if MAX_URL_LEN |
188 | 0 | if ((strlen(parentName) > MAX_URL_LEN) || (strlen(pathName) > MAX_URL_LEN)) { |
189 | 0 | GF_LOG(GF_LOG_ERROR, GF_LOG_CORE, ("URL too long for concatenation: \n%s\n", pathName)); |
190 | 0 | return NULL; |
191 | 0 | } |
192 | 0 | #endif |
193 | | |
194 | 0 | while (!strncmp(parentName, "./.", 3) || !strncmp(parentName, ".\\.", 3)) { |
195 | 0 | parentName += 2; |
196 | 0 | } |
197 | 0 | while (!strncmp(pathName, "./.", 3) || !strncmp(pathName, ".\\.", 3)) { |
198 | 0 | pathName += 2; |
199 | 0 | } |
200 | | //empty path |
201 | 0 | if (pathName[0]=='#') { |
202 | 0 | outPath = gf_strdup(parentName); |
203 | 0 | gf_dynstrcat(&outPath, pathName, NULL); |
204 | 0 | goto check_spaces; |
205 | 0 | } |
206 | | |
207 | 0 | prot_type = URL_GetProtocolType(pathName); |
208 | 0 | if (prot_type != GF_URL_TYPE_RELATIVE) { |
209 | 0 | char *sep = NULL; |
210 | 0 | if (pathName[0]=='/') sep = strstr(parentName, "://"); |
211 | 0 | if (sep) sep = strchr(sep+3, '/'); |
212 | 0 | if (sep) { |
213 | 0 | u32 len; |
214 | 0 | sep[0] = 0; |
215 | 0 | len = (u32) strlen(parentName); |
216 | 0 | outPath = (char*)gf_malloc(sizeof(char)*(len+1+strlen(pathName))); |
217 | 0 | strcpy(outPath, parentName); |
218 | 0 | strcat(outPath, pathName); |
219 | 0 | sep[0] = '/'; |
220 | 0 | } else { |
221 | 0 | outPath = gf_strdup(pathName); |
222 | 0 | } |
223 | 0 | goto check_spaces; |
224 | 0 | } |
225 | | |
226 | | /*old upnp addressing a la Platinum*/ |
227 | 0 | rad = strstr(parentName, "%3fpath="); |
228 | 0 | if (!rad) rad = strstr(parentName, "%3Fpath="); |
229 | 0 | if (!rad) rad = strstr(parentName, "?path="); |
230 | 0 | if (rad) { |
231 | 0 | char *the_path; |
232 | 0 | rad = strchr(rad, '='); |
233 | 0 | rad[0] = 0; |
234 | 0 | the_path = gf_strdup(rad+1); |
235 | 0 | i=0; |
236 | 0 | while (1) { |
237 | 0 | if (the_path[i]==0) break; |
238 | 0 | if (!strnicmp(the_path+i, "%5c", 3) || !strnicmp(the_path+i, "%2f", 3) ) { |
239 | 0 | the_path[i] = '/'; |
240 | 0 | memmove(the_path+i+1, the_path+i+3, strlen(the_path+i+3)+1); |
241 | 0 | } |
242 | 0 | else if (!strnicmp(the_path+i, "%05c", 4) || !strnicmp(the_path+i, "%02f", 4) ) { |
243 | 0 | the_path[i] = '/'; |
244 | 0 | memmove(the_path+i+1, the_path+i+4, strlen(the_path+i+4)+1); |
245 | 0 | } |
246 | 0 | i++; |
247 | 0 | } |
248 | 0 | name = gf_url_concatenate(the_path, pathName); |
249 | 0 | outPath = (char*)gf_malloc(strlen(parentName) + strlen(name) + 2); |
250 | 0 | sprintf(outPath, "%s=%s", parentName, name); |
251 | 0 | rad[0] = '='; |
252 | 0 | gf_free(name); |
253 | 0 | gf_free(the_path); |
254 | 0 | return outPath; |
255 | 0 | } |
256 | | |
257 | | /*rewrite path to use / not % encoding*/ |
258 | 0 | rad = strchr(parentName, '%'); |
259 | 0 | if (rad && (!strnicmp(rad, "%5c", 3) || !strnicmp(rad, "%05c", 4) || !strnicmp(rad, "%2f", 3) || !strnicmp(rad, "%02f", 4))) { |
260 | 0 | char *the_path = gf_strdup(parentName); |
261 | 0 | i=0; |
262 | 0 | while (1) { |
263 | 0 | if (the_path[i]==0) break; |
264 | 0 | if (!strnicmp(the_path+i, "%5c", 3) || !strnicmp(the_path+i, "%2f", 3) ) { |
265 | 0 | the_path[i] = '/'; |
266 | 0 | memmove(the_path+i+1, the_path+i+3, strlen(the_path+i+3)+1); |
267 | 0 | } |
268 | 0 | else if (!strnicmp(the_path+i, "%05c", 4) || !strnicmp(the_path+i, "%02f", 4) ) { |
269 | 0 | the_path[i] = '/'; |
270 | 0 | memmove(the_path+i+1, the_path+i+4, strlen(the_path+i+4)+1); |
271 | 0 | } |
272 | 0 | i++; |
273 | 0 | } |
274 | 0 | name = gf_url_concatenate(the_path, pathName); |
275 | 0 | gf_free(the_path); |
276 | 0 | return name; |
277 | 0 | } |
278 | | |
279 | | |
280 | 0 | pathSepCount = 0; |
281 | 0 | name = NULL; |
282 | 0 | if (pathName[0] == '.') { |
283 | 0 | if (!strcmp(pathName, "..")) { |
284 | 0 | pathSepCount = 1; |
285 | 0 | name = ""; |
286 | 0 | } |
287 | 0 | if (!strcmp(pathName, "./")) { |
288 | 0 | pathSepCount = 0; |
289 | 0 | name = ""; |
290 | 0 | } |
291 | 0 | for (i = 0; i< strlen(pathName) - 2; i++) { |
292 | | /*current dir*/ |
293 | 0 | if ( (pathName[i] == '.') |
294 | 0 | && ( (pathName[i+1] == GF_PATH_SEPARATOR) || (pathName[i+1] == '/') ) ) { |
295 | 0 | i++; |
296 | 0 | continue; |
297 | 0 | } |
298 | | /*parent dir*/ |
299 | 0 | if ( (pathName[i] == '.') && (pathName[i+1] == '.') |
300 | 0 | && ( (pathName[i+2] == GF_PATH_SEPARATOR) || (pathName[i+2] == '/') ) |
301 | 0 | ) { |
302 | 0 | pathSepCount ++; |
303 | 0 | i+=2; |
304 | 0 | name = (char *) &pathName[i+1]; |
305 | 0 | } else { |
306 | 0 | name = (char *) &pathName[i]; |
307 | 0 | break; |
308 | 0 | } |
309 | 0 | } |
310 | 0 | } |
311 | 0 | if (!name) name = (char *) pathName; |
312 | |
|
313 | 0 | gf_dynstrcat(&tmp, parentName, NULL); |
314 | 0 | if (!tmp) return NULL; |
315 | | |
316 | 0 | while (strchr(" \r\n\t", tmp[strlen(tmp)-1])) { |
317 | 0 | tmp[strlen(tmp)-1] = 0; |
318 | 0 | } |
319 | | //strip query part or fragment part |
320 | 0 | rad = strchr(tmp, '?'); |
321 | 0 | if (rad) rad[0] = 0; |
322 | 0 | tmp2 = strrchr(tmp, '/'); |
323 | 0 | if (!tmp2) tmp2 = strrchr(tmp, '\\'); |
324 | 0 | if (!tmp2) tmp2 = tmp; |
325 | 0 | rad = strchr(tmp2, '#'); |
326 | 0 | if (rad) rad[0] = 0; |
327 | | |
328 | | //rule out case where we have the same path |
329 | 0 | if (relative_to_parent) { |
330 | 0 | u32 l1=0, l2=0; |
331 | 0 | rad = strrchr(tmp, '/'); |
332 | 0 | if (rad) l1 = (u32) (rad - tmp); |
333 | 0 | rad = strrchr(pathName, '/'); |
334 | 0 | if (rad) l2 = (u32) (rad - pathName); |
335 | 0 | if (l1 && l2 && (l1==l2) && !strncmp(pathName, parentName, l1)) { |
336 | 0 | outPath = gf_strdup(pathName + l1 + 1); |
337 | 0 | goto check_spaces; |
338 | 0 | } |
339 | 0 | if (!l1 && !l2) { |
340 | 0 | outPath = gf_strdup(pathName); |
341 | 0 | goto check_spaces; |
342 | 0 | } |
343 | 0 | } |
344 | | |
345 | 0 | if (pathSepCount) |
346 | 0 | had_sep_count = GF_TRUE; |
347 | | /*remove the last */ |
348 | 0 | for (i = (u32) strlen(tmp); i > 0; i--) { |
349 | | //break our path at each separator |
350 | 0 | if ((tmp[i-1] == GF_PATH_SEPARATOR) || (tmp[i-1] == '/')) { |
351 | 0 | tmp[i-1] = 0; |
352 | 0 | if (strcmp(tmp, ".")) { |
353 | 0 | if (!pathSepCount) break; |
354 | 0 | pathSepCount--; |
355 | 0 | } |
356 | 0 | } |
357 | 0 | } |
358 | | //if i==0, the parent path was relative, just return the pathName |
359 | 0 | if (!i) { |
360 | 0 | tmp[i] = 0; |
361 | 0 | while (pathSepCount) { |
362 | 0 | gf_dynstrcat(&tmp, "../", NULL); |
363 | 0 | pathSepCount--; |
364 | 0 | } |
365 | 0 | } |
366 | | //path is relative to current dir |
367 | 0 | else if (!relative_to_parent && (pathName[0]=='.') && ((pathName[1]=='/') || (pathName[1]=='\\') ) ) { |
368 | 0 | gf_dynstrcat(&tmp, "/", NULL); |
369 | 0 | } |
370 | | //parent is relative to current dir |
371 | 0 | else if (!had_sep_count && (pathName[0]=='.') && (tmp[0]=='.') && ((tmp[1]=='/') || (tmp[1]=='\\') ) ) { |
372 | 0 | u32 nb_path_sep=0; |
373 | 0 | u32 len = (u32) strlen(tmp); |
374 | |
|
375 | 0 | for (i=0; i<len; i++) { |
376 | 0 | if ((tmp[i]=='/') || (tmp[i]=='\\') ) |
377 | 0 | nb_path_sep++; |
378 | 0 | } |
379 | |
|
380 | 0 | const char *p_src = pathName+2; |
381 | 0 | const char *p_tmp = tmp+2; |
382 | | //strip if same beginning |
383 | 0 | while (1) { |
384 | 0 | char *sep = strchr(p_src, '/'); |
385 | 0 | if (!sep) sep = strchr(p_src, '\\'); |
386 | 0 | if (!sep) break; |
387 | 0 | u32 sep_len = (u32) (sep - p_src); |
388 | 0 | if (!sep_len) break; |
389 | 0 | if (sep_len > len) break; |
390 | 0 | if (strncmp(p_tmp, p_src, sep_len)) break; |
391 | 0 | if ((p_tmp[sep_len] != '/') && (p_tmp[sep_len] != '\\')) break; |
392 | 0 | p_src += sep_len+1; |
393 | 0 | name += sep_len+1; |
394 | 0 | p_tmp += sep_len+1; |
395 | 0 | len -= sep_len; |
396 | 0 | nb_path_sep--; |
397 | 0 | } |
398 | 0 | if (tmp) gf_free(tmp); |
399 | 0 | tmp=NULL; |
400 | 0 | gf_dynstrcat(&tmp, "", NULL); |
401 | 0 | while (nb_path_sep--) |
402 | 0 | gf_dynstrcat(&tmp, "../", NULL); |
403 | 0 | } else { |
404 | 0 | gf_dynstrcat(&tmp, "/", NULL); |
405 | 0 | } |
406 | |
|
407 | 0 | i = (u32) strlen(tmp); |
408 | 0 | outPath = (char *) gf_malloc(i + strlen(name) + 1); |
409 | 0 | sprintf(outPath, "%s%s", tmp, name); |
410 | | |
411 | | /*cleanup paths sep for win32*/ |
412 | 0 | for (i = 0; i<strlen(outPath); i++) |
413 | 0 | if (outPath[i]=='\\') outPath[i] = '/'; |
414 | |
|
415 | 0 | check_spaces: |
416 | 0 | i=0; |
417 | 0 | while (outPath[i]) { |
418 | 0 | if (outPath[i] == '?') break; |
419 | | |
420 | 0 | if (outPath[i] != '%') { |
421 | 0 | i++; |
422 | 0 | continue; |
423 | 0 | } |
424 | 0 | if (!strnicmp(outPath+i, "%3f", 3)) break; |
425 | 0 | if (!strnicmp(outPath+i, "%20", 3)) { |
426 | 0 | outPath[i]=' '; |
427 | 0 | memmove(outPath + i+1, outPath+i+3, strlen(outPath+i)-2); |
428 | 0 | } |
429 | 0 | i++; |
430 | 0 | } |
431 | 0 | if (tmp) gf_free(tmp); |
432 | 0 | return outPath; |
433 | 0 | } |
434 | | GF_EXPORT |
435 | | char *gf_url_concatenate(const char *parentName, const char *pathName) |
436 | 0 | { |
437 | 0 | return gf_url_concatenate_ex(parentName, pathName, GF_FALSE); |
438 | 0 | } |
439 | | GF_EXPORT |
440 | | char *gf_url_concatenate_parent(const char *parentName, const char *pathName) |
441 | 0 | { |
442 | 0 | return gf_url_concatenate_ex(parentName, pathName, GF_TRUE); |
443 | 0 | } |
444 | | |
445 | | GF_EXPORT |
446 | | void gf_url_to_fs_path(char *sURL) |
447 | 0 | { |
448 | 0 | if (!strnicmp(sURL, "file://", 7)) { |
449 | | /*file:///C:\ scheme*/ |
450 | 0 | if ((strlen(sURL)>=10) && (sURL[7]=='/') && (sURL[9]==':')) { |
451 | 0 | memmove(sURL, sURL+8, strlen(sURL)-7); |
452 | 0 | } else { |
453 | 0 | memmove(sURL, sURL+7, strlen(sURL)-6); |
454 | 0 | } |
455 | 0 | } |
456 | |
|
457 | 0 | while (1) { |
458 | 0 | char *sep = strstr(sURL, "%20"); |
459 | 0 | if (!sep) break; |
460 | 0 | sep[0] = ' '; |
461 | 0 | memmove(sep+1, sep+3, strlen(sep)-2); |
462 | 0 | } |
463 | 0 | } |
464 | | |
465 | | //TODO handle reserved characters |
466 | | const char *pce_special = " %"; |
467 | | const char *pce_encoded = "0123456789ABCDEFabcdef"; |
468 | | |
469 | | char *gf_url_percent_encode(const char *path) |
470 | 0 | { |
471 | 0 | char *outpath; |
472 | 0 | u32 i, count, len; |
473 | 0 | if (!path) return NULL; |
474 | | |
475 | 0 | len = (u32) strlen(path); |
476 | 0 | count = 0; |
477 | 0 | for (i=0; i<len; i++) { |
478 | 0 | u8 c = path[i]; |
479 | 0 | if (strchr(pce_special, c) != NULL) { |
480 | 0 | if (c==' ') count+=2; |
481 | 0 | else if ((i+2<len) && ((strchr(pce_encoded, path[i+1]) == NULL) || (strchr(pce_encoded, path[i+2]) == NULL))) { |
482 | 0 | count+=2; |
483 | 0 | } |
484 | 0 | } else if (c>>7) { |
485 | 0 | count+=2; |
486 | 0 | } |
487 | 0 | } |
488 | 0 | if (!count) return gf_strdup(path); |
489 | 0 | outpath = (char*)gf_malloc(sizeof(char) * (len + count + 1)); |
490 | 0 | strcpy(outpath, path); |
491 | |
|
492 | 0 | count = 0; |
493 | 0 | for (i=0; i<len; i++) { |
494 | 0 | Bool do_enc = GF_FALSE; |
495 | 0 | u8 c = path[i]; |
496 | |
|
497 | 0 | if (strchr(pce_special, c) != NULL) { |
498 | 0 | if (c==' ') |
499 | 0 | do_enc = GF_TRUE; |
500 | 0 | else if ((i+2<len) && ((strchr(pce_encoded, path[i+1]) == NULL) || (strchr(pce_encoded, path[i+2]) == NULL))) { |
501 | 0 | do_enc = GF_TRUE; |
502 | 0 | } |
503 | 0 | } else if (c>>7) { |
504 | 0 | do_enc = GF_TRUE; |
505 | 0 | } |
506 | |
|
507 | 0 | if (do_enc) { |
508 | 0 | char szChar[3]; |
509 | 0 | sprintf(szChar, "%02X", c); |
510 | 0 | outpath[i+count] = '%'; |
511 | 0 | outpath[i+count+1] = szChar[0]; |
512 | 0 | outpath[i+count+2] = szChar[1]; |
513 | 0 | count+=2; |
514 | 0 | } else { |
515 | 0 | outpath[i+count] = c; |
516 | 0 | } |
517 | 0 | } |
518 | 0 | outpath[i+count] = 0; |
519 | 0 | return outpath; |
520 | 0 | } |
521 | | |
522 | | char *gf_url_percent_decode(const char *path) |
523 | 0 | { |
524 | 0 | char *outpath; |
525 | 0 | u32 i, count, len; |
526 | 0 | if (!path) return NULL; |
527 | | |
528 | 0 | len = (u32) strlen(path); |
529 | 0 | count = 0; |
530 | 0 | for (i=0; i<len; i++) { |
531 | 0 | u8 c = path[i]; |
532 | 0 | if (c=='%') { |
533 | 0 | i+= 2; |
534 | 0 | } |
535 | 0 | count++; |
536 | 0 | } |
537 | 0 | if (count==len) return gf_strdup(path); |
538 | 0 | outpath = (char*)gf_malloc(sizeof(char) * (count + 1)); |
539 | |
|
540 | 0 | u32 d_idx=0; |
541 | 0 | for (i=0; i<len; i++) { |
542 | 0 | u8 c = path[i]; |
543 | 0 | if (c=='%') { |
544 | 0 | u32 res; |
545 | 0 | char szChar[3]; |
546 | 0 | szChar[0] = path[i+1]; |
547 | 0 | szChar[1] = path[i+2]; |
548 | 0 | szChar[2] = 0; |
549 | 0 | sscanf(szChar, "%02X", &res); |
550 | 0 | i += 2; |
551 | 0 | outpath[d_idx] = (char) res; |
552 | 0 | } else { |
553 | 0 | outpath[d_idx] = c; |
554 | 0 | } |
555 | 0 | d_idx++; |
556 | 0 | } |
557 | 0 | outpath[d_idx] = 0; |
558 | 0 | return outpath; |
559 | 0 | } |
560 | | |
561 | | GF_EXPORT |
562 | | const char *gf_url_get_resource_name(const char *sURL) |
563 | 0 | { |
564 | 0 | char *sep; |
565 | 0 | if (!sURL) return NULL; |
566 | 0 | sep = strrchr(sURL, '/'); |
567 | 0 | if (!sep) sep = strrchr(sURL, '\\'); |
568 | 0 | if (sep) return sep+1; |
569 | 0 | return sURL; |
570 | 0 | } |
571 | | |
572 | | GF_EXPORT |
573 | | const char *gf_url_get_path(const char *sURL) |
574 | 0 | { |
575 | 0 | char *sep = strstr(sURL, "://"); |
576 | 0 | if (!sep) return sURL; |
577 | 0 | sep = strchr(sep + 3, '/'); |
578 | 0 | if (sep) return sep; |
579 | 0 | return NULL; |
580 | 0 | } |
581 | | |
582 | | //exported for python bindings |
583 | | GF_EXPORT |
584 | | void gf_url_free(char *sURL) |
585 | 0 | { |
586 | 0 | if (sURL) gf_free(sURL); |
587 | 0 | } |
588 | | |
589 | | #if 0 //unused |
590 | | Bool gf_url_remove_last_delimiter(const char *sURL, char *res_path) |
591 | | { |
592 | | strcpy(res_path, sURL); |
593 | | if (sURL[strlen(sURL)-1] == GF_PATH_SEPARATOR) { |
594 | | res_path[strlen(sURL)-1] = 0; |
595 | | return GF_TRUE; |
596 | | } |
597 | | |
598 | | return GF_FALSE; |
599 | | } |
600 | | |
601 | | const char* gf_url_get_resource_extension(const char *sURL) { |
602 | | const char *dot = strrchr(sURL, '.'); |
603 | | if(!dot || dot == sURL) return ""; |
604 | | return dot + 1; |
605 | | } |
606 | | #endif //unused |