/src/gdal/netcdf-c-4.7.4/libdispatch/ncuri.c
Line | Count | Source (jump to first uncovered line) |
1 | | /********************************************************************* |
2 | | * Copyright 2018, UCAR/Unidata |
3 | | * See netcdf/COPYRIGHT file for copying and redistribution conditions. |
4 | | * $Header$ |
5 | | *********************************************************************/ |
6 | | #include "config.h" |
7 | | #include <stdlib.h> |
8 | | #include <string.h> |
9 | | #include <stdio.h> |
10 | | #include <assert.h> |
11 | | |
12 | | #include "ncuri.h" |
13 | | #include "ncbytes.h" |
14 | | #include "nclist.h" |
15 | | |
16 | | /* Include netcdf.h to allow access to |
17 | | NC_ error return codes. */ |
18 | | #include "netcdf.h" |
19 | | |
20 | | #define NCURIDEBUG |
21 | | |
22 | | /* Extra debug info */ |
23 | | #undef NCXDEBUG |
24 | | |
25 | | #ifdef NCURIDEBUG |
26 | 798 | #define THROW(n) {ret=(n); goto done;} |
27 | | #else |
28 | | #define THROW(n) {goto done;} |
29 | | #endif |
30 | | |
31 | | #define PADDING 8 |
32 | | |
33 | 798 | #define LBRACKET '[' |
34 | | #define RBRACKET ']' |
35 | 0 | #define EOFCHAR '\0' |
36 | 0 | #define RBRACKETSTR "]" |
37 | | |
38 | 0 | #define DRIVELETTERS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" |
39 | | |
40 | | #ifndef FIX |
41 | | #define FIX(s) ((s)==NULL?"NULL":(s)) |
42 | | #endif |
43 | | |
44 | | #ifndef NILLEN |
45 | | #define NILLEN(s) ((s)==NULL?0:strlen(s)) |
46 | | #endif |
47 | | |
48 | | #ifndef nulldup |
49 | | #define nulldup(s) ((s)==NULL?NULL:strdup(s)) |
50 | | #endif |
51 | | |
52 | 0 | #define terminate(p) {*(p) = EOFCHAR;} |
53 | | |
54 | | #define endof(p) ((p)+strlen(p)) |
55 | | |
56 | | #define lshift(buf,buflen) {memmove(buf,buf+1,buflen+1);} |
57 | | #define rshift(buf,buflen) {memmove(buf+1,buf,buflen+1);} |
58 | | |
59 | | /* Allowable character sets for encode */ |
60 | | static const char* pathallow = |
61 | | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~"; |
62 | | |
63 | | static const char* queryallow = |
64 | | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~"; |
65 | | |
66 | | /* user+pwd allow = path allow - "@:" */ |
67 | | static const char* userpwdallow = |
68 | | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$&'()*+,-.;=_~?#/"; |
69 | | |
70 | | #ifndef HAVE_STRNDUP |
71 | | #define strndup ncstrndup |
72 | | /* Not all systems have strndup, so provide one*/ |
73 | | char* |
74 | | ncstrndup(const char* s, size_t len) |
75 | | { |
76 | | char* dup; |
77 | | if(s == NULL) return NULL; |
78 | | dup = (char*)malloc(len+1); |
79 | | if(dup == NULL) return NULL; |
80 | | memcpy((void*)dup,s,len); |
81 | | dup[len] = '\0'; |
82 | | return dup; |
83 | | } |
84 | | #endif |
85 | | /* Forward */ |
86 | | static int collectprefixparams(char* text, char** nextp); |
87 | | static void freestringlist(NClist* list); |
88 | | static void freestringvec(char** list); |
89 | | static int ncfind(char** params, const char* key); |
90 | | static char* nclocate(char* p, const char* charlist); |
91 | | static int parselist(const char* ptext, NClist* list); |
92 | | |
93 | | /**************************************************/ |
94 | | /* |
95 | | A note about parameter support: |
96 | | In the original url format for opendap (dap2), client parameters were |
97 | | assumed to be one or more instances of bracketed pairs: e.g |
98 | | "[...][...]...". |
99 | | These were assumed to be placed at the front of the url. In this newer |
100 | | version, the parameters may be encoded after a trailing # character each |
101 | | separated by ampersand (&). For back compatibility, the bracketed |
102 | | parameter form is supported. However, if ncuribuild is used, all |
103 | | parameters will be converted to the |
104 | | #...&...& format. |
105 | | In any case, each parameter in turn is assumed to be a of the form |
106 | | <name>=<value> or <name>; e.g. #x=y&z&a=b&w. |
107 | | If the same parameter is specified more than once, then the first |
108 | | occurrence is used; this is so that is possible to forcibly override |
109 | | user specified parameters by prefixing. |
110 | | IMPORTANT: the client parameter string is assumed to have blanks compressed out. |
111 | | */ |
112 | | |
113 | | /**************************************************/ |
114 | | |
115 | | /* Do a simple uri parse: return NC_NOERR if success, NC_EXXX if failed */ |
116 | | int |
117 | | ncuriparse(const char* uri0, NCURI** durip) |
118 | 798 | { |
119 | 798 | int ret = NC_NOERR; |
120 | 798 | NCURI tmp; |
121 | 798 | char* p; |
122 | 798 | char* q; |
123 | 798 | int isfile; |
124 | 798 | int hashost; |
125 | 798 | char* uri = NULL; |
126 | 798 | NCURI* duri = NULL; |
127 | 798 | char* prefix = NULL; |
128 | 798 | char* next = NULL; |
129 | 798 | NClist* params = nclistnew(); |
130 | 798 | NClist* querylist = nclistnew(); |
131 | 798 | size_t len0; |
132 | 798 | int pathchar; |
133 | | |
134 | 798 | tmp.fraglist = NULL; |
135 | 798 | tmp.querylist = NULL; |
136 | | |
137 | 798 | if(uri0 == NULL) |
138 | 0 | {THROW(NC_EURL);} |
139 | | |
140 | 798 | len0 = strlen(uri0); |
141 | 798 | if(len0 == 0) |
142 | 0 | {THROW(NC_EURL);} |
143 | | |
144 | | /* Create a local NCURI instance to hold |
145 | | pointers into the parsed string |
146 | | */ |
147 | 798 | memset(&tmp,0,sizeof(tmp)); |
148 | | |
149 | | /* make mutable copy. Add some extra space |
150 | | because we will need to null terminate the host section |
151 | | without losing the first character of the path section. |
152 | | */ |
153 | 798 | uri = (char*)malloc(len0+1+1); /* +2 for nul term and for host section terminator */ |
154 | 798 | if(uri == NULL) |
155 | 0 | {THROW(NC_ENOMEM);} |
156 | 798 | strncpy(uri,uri0,len0+1); |
157 | | |
158 | | /* Walk the uri and do the following: |
159 | | 1. remove leading and trailing whitespace |
160 | | 2. convert all '\\' -> '\' (Temp hack to remove escape characters |
161 | | inserted by Windows or MinGW) |
162 | | */ |
163 | 21.6k | for(q=uri,p=uri;*p;p++) {if((*p == '\\' && p[1] == '\\') || *p < ' ') {continue;} else {*q++ = *p;}} |
164 | 798 | *q = '\0'; |
165 | | |
166 | 798 | p = uri; |
167 | | |
168 | | /* break up the url into coarse pieces */ |
169 | 798 | if(*p == LBRACKET) { |
170 | 0 | prefix = p; |
171 | 0 | ret = collectprefixparams(p,&next); /* collect the prefix */ |
172 | 0 | if(ret != NC_NOERR) |
173 | 0 | {THROW(NC_EURL);} |
174 | 0 | p = next; |
175 | 798 | } else { |
176 | 798 | prefix = NULL; |
177 | 798 | } |
178 | 798 | tmp.uri = p; /* will be the core */ |
179 | | /* Skip past the core of the url */ |
180 | 798 | next = nclocate(p,"?#"); |
181 | 798 | if(next != NULL) { |
182 | 0 | int c = *next; |
183 | 0 | terminate(next); |
184 | 0 | next++; |
185 | 0 | if(c == '?') { |
186 | 0 | tmp.query = next; |
187 | 0 | next = nclocate(next,"#"); |
188 | 0 | if(next == NULL) |
189 | 0 | tmp.fragment = NULL; |
190 | 0 | else { |
191 | 0 | terminate(next); |
192 | 0 | next++; |
193 | 0 | tmp.fragment = next; |
194 | 0 | } |
195 | 0 | } else { /*c == '#'*/ |
196 | 0 | tmp.fragment = next; |
197 | 0 | } |
198 | 0 | } |
199 | | |
200 | | /* Parse the prefix parameters */ |
201 | 798 | if(prefix != NULL) { |
202 | 0 | if(parselist(prefix,params) != NC_NOERR) |
203 | 0 | {THROW(NC_EURL);} |
204 | 0 | } |
205 | | /* Parse the fragment parameters */ |
206 | 798 | if(tmp.fragment != NULL) { |
207 | 0 | if(parselist(tmp.fragment,params) != NC_NOERR) |
208 | 0 | {THROW(NC_EURL);} |
209 | 0 | } |
210 | 798 | if(nclistlength(params) > 0) { |
211 | 0 | nclistpush(params,NULL); |
212 | 0 | tmp.fraglist = nclistextract(params); |
213 | 0 | } else |
214 | 798 | tmp.fraglist = NULL; |
215 | | /* Parse the query */ |
216 | 798 | if(tmp.query != NULL) { |
217 | 0 | if(parselist(tmp.query,querylist) != NC_NOERR) |
218 | 0 | {THROW(NC_EURL);} |
219 | 0 | if(nclistlength(querylist) > 0) { |
220 | 0 | nclistpush(querylist,NULL); |
221 | 0 | tmp.querylist = nclistextract(querylist); |
222 | 0 | } |
223 | 0 | } |
224 | | |
225 | | /* Now parse the core of the url */ |
226 | 798 | p = tmp.uri; |
227 | | |
228 | | /* Mark the protocol */ |
229 | 798 | tmp.protocol = p; |
230 | 798 | p = strchr(p,':'); |
231 | 798 | if(!p) |
232 | 798 | {THROW(NC_EURL);} |
233 | 0 | terminate(p); /*overwrite colon*/ |
234 | 0 | p++; /* skip the colon */ |
235 | 0 | if(strlen(tmp.protocol)==0) |
236 | 0 | {THROW(NC_EURL);} |
237 | | /* |
238 | | The legal formats for file: urls are a problem since |
239 | | many variants are often accepted. |
240 | | By RFC, the proper general format is: file://host/path, |
241 | | where the 'host' can be omitted and defaults to 'localhost'. |
242 | | and the path includes the leading '/'. |
243 | | So, assuming no host, the format is: "file:///path". |
244 | | Some implementations, however, ignore the host, and allow |
245 | | the format: file:/path. |
246 | | We also simplify things by assuming the host part is always empty. |
247 | | which means we can have file:///path, but not file://..../path. |
248 | | Note also in all cases, the leading '/' is considered part of the path, |
249 | | which is then assumed to be an absolute path. But also note that |
250 | | the windows drive letter has to be taken into account. Our rule is that |
251 | | if the path looks like D:..., |
252 | | where D is a single alphabetic letter (a-z or A-Z), |
253 | | then it is a windows path and can be use in place of a /path. |
254 | | Note also that it is desirable to support relative paths even |
255 | | though the RFC technically does not allow this. This will occur |
256 | | if the form is file://path where path does not start with '/'. |
257 | | The rules implemented here (for file:) are then as follows |
258 | | 1. file:D:... : assume D: is a windows drive letter and treat D:... as the path |
259 | | 2. file:/X, where X does not start with a slash: treat /X as the path. |
260 | | 3. file://D:... : assume D: is a windows drive letter and treat as the path |
261 | | 4. file:///X, where X does not start with a slash: treat /X as the path. |
262 | | 5. file://X, where X does not start with a slash: treat X as the |
263 | | relative path. |
264 | | All other cases are disallowed. |
265 | | */ |
266 | | |
267 | 0 | isfile = (strcmp(tmp.protocol,"file")==0); |
268 | 0 | if(isfile) { |
269 | 0 | size_t l = strlen(p); /* to test if we have enough characters */ |
270 | 0 | hashost = 0; /* always */ |
271 | 0 | if(l >= 2 && p[1] == ':' && strchr(DRIVELETTERS,p[0]) != NULL) { /* case 1 */ |
272 | 0 | ; /* p points to the start of the path */ |
273 | 0 | } else if(l >= 2 && p[0] == '/' && p[1] != '/') { /* case 2 */ |
274 | 0 | ; /* p points to the start of the path */ |
275 | 0 | } else if(l >= 4 && p[0] == '/' && p[1] == '/' |
276 | 0 | && p[3] == ':' && strchr(DRIVELETTERS,p[2]) != NULL) { /* case 3 */ |
277 | 0 | p = p+2; /* points to the start of the windows path */ |
278 | 0 | } else if(l >= 4 && p[0] == '/' && p[1] == '/' && p[2] == '/' && p[3] != '/') { /* case 4 */ |
279 | 0 | p += 2; /* points to the start of the path */ |
280 | 0 | } else if(l >= 4 && p[0] == '/' && p[1] == '/' && p[2] != '/') { /* case 5 */ |
281 | 0 | p += 2; /* points to the start of the path */ |
282 | 0 | } else /* everything else is illegal */ |
283 | 0 | {THROW(NC_EACCESS);} |
284 | 0 | } else { |
285 | 0 | if(p[0] != '/' || p[1] != '/') /* must be proto:// */ |
286 | 0 | {THROW(NC_EACCESS);} |
287 | 0 | p += 2; |
288 | 0 | hashost = 1; /* Assume we have a hostname */ |
289 | 0 | } |
290 | 0 | if(!hashost) { |
291 | 0 | tmp.path = p; |
292 | 0 | pathchar = EOFCHAR; |
293 | 0 | } else { /* assume there should be a host section */ |
294 | | /* We already extracted the query and/or fragment sections above, |
295 | | splocate the end of the host section and therefore the start |
296 | | of the path. |
297 | | */ |
298 | 0 | tmp.host = p; |
299 | 0 | p = nclocate(p,"/"); |
300 | 0 | if(p == NULL) { /* no path */ |
301 | 0 | tmp.path = NULL; /* default */ |
302 | 0 | pathchar = EOFCHAR; |
303 | 0 | } else { |
304 | 0 | tmp.path = p; /* save ptr to rest of the path */ |
305 | 0 | pathchar = *p; /* save leading char of the path */ |
306 | 0 | terminate(p); /* overwrite the leading char of the path; restored below */ |
307 | 0 | } |
308 | 0 | } |
309 | | /* Nullify tmp.host for consistency */ |
310 | 0 | if(tmp.host != NULL && strlen(tmp.host)==0) {tmp.host = NULL;} |
311 | |
|
312 | 0 | if(tmp.host != NULL) {/* Parse the host section */ |
313 | 0 | char* pp; |
314 | | /* Check for leading user:pwd@ */ |
315 | 0 | char* newhost = strchr(tmp.host,'@'); |
316 | 0 | if(newhost != NULL) { |
317 | 0 | if(newhost == tmp.host) |
318 | 0 | {THROW(NC_EURL);} /* we have proto://@ */ |
319 | 0 | terminate(newhost); /* overwrite '@' */ |
320 | 0 | newhost++; /* should point past usr+pwd */ |
321 | 0 | tmp.user = tmp.host; |
322 | | /* Break user+pwd into two pieces */ |
323 | 0 | pp = strchr(tmp.user,':'); |
324 | 0 | if(pp == NULL) |
325 | 0 | {THROW(NC_EURL);} /* we have user only */ |
326 | 0 | terminate(pp); /* overwrite ':' */ |
327 | 0 | pp++; |
328 | 0 | if(strlen(tmp.user)==0) |
329 | 0 | {THROW(NC_EURL);} /* we have empty user */ |
330 | 0 | if(strlen(pp)==0) |
331 | 0 | {THROW(NC_EURL);} /* we have empty password */ |
332 | 0 | tmp.password = pp; |
333 | 0 | tmp.host = newhost; |
334 | 0 | } |
335 | | /* Breakup host into host + port */ |
336 | 0 | pp = tmp.host; |
337 | 0 | pp = strchr(pp,':'); |
338 | 0 | if(pp != NULL) { /* there is a port */ |
339 | 0 | terminate(pp); /* overwrite ':' */ |
340 | 0 | pp++; /* skip colon */ |
341 | 0 | if(strlen(tmp.host) == 0) |
342 | 0 | {THROW(NC_EURL);} /* empty host */ |
343 | 0 | if(strlen(pp)==0) |
344 | 0 | {THROW(NC_EURL);} /* empty port */ |
345 | 0 | tmp.port = pp; |
346 | | /* The port must look something like a number */ |
347 | 0 | for(pp=tmp.port;*pp;pp++) { |
348 | 0 | if(strchr("0123456789-",*pp) == NULL) |
349 | 0 | {THROW(NC_EURL);} /* probably not a real port, fail */ |
350 | 0 | } |
351 | 0 | } /* else no port */ |
352 | 0 | } |
353 | | |
354 | | /* Fill in duri from tmp */ |
355 | 0 | duri = (NCURI*)calloc(1,sizeof(NCURI)); |
356 | 0 | if(duri == NULL) |
357 | 0 | {THROW(NC_ENOMEM);} |
358 | | /* save original uri */ |
359 | 0 | duri->uri = strdup(uri0); |
360 | 0 | duri->protocol = nulldup(tmp.protocol); |
361 | | /* before saving, we need to decode the user+pwd */ |
362 | 0 | duri->user = NULL; |
363 | 0 | duri->password = NULL; |
364 | 0 | if(tmp.user != NULL) |
365 | 0 | duri->user = ncuridecode(tmp.user); |
366 | 0 | if(tmp.password != NULL) |
367 | 0 | duri->password = ncuridecode(tmp.password); |
368 | 0 | duri->host = nulldup(tmp.host); |
369 | 0 | duri->port = nulldup(tmp.port); |
370 | 0 | if(tmp.path != NULL) { |
371 | | /* We need to add back the previously overwritten path lead char (if necessary); |
372 | | this must be done after all host section related pieces have been captured */ |
373 | 0 | if(pathchar != EOFCHAR) |
374 | 0 | *tmp.path = pathchar; |
375 | 0 | duri->path = nulldup(tmp.path); |
376 | 0 | } |
377 | 0 | duri->query = nulldup(tmp.query); |
378 | 0 | duri->fragment = nulldup(tmp.fragment); |
379 | 0 | duri->fraglist = tmp.fraglist; tmp.fraglist = NULL; |
380 | 0 | duri->querylist = tmp.querylist; tmp.querylist = NULL; |
381 | 0 | if(durip) |
382 | 0 | *durip = duri; |
383 | 0 | else |
384 | 0 | free(duri); |
385 | |
|
386 | | #ifdef NCXDEBUG |
387 | | { |
388 | | fprintf(stderr,"duri:"); |
389 | | fprintf(stderr," protocol=|%s|",FIX(duri->protocol)); |
390 | | fprintf(stderr," user=|%s|",FIX(duri->user)); |
391 | | fprintf(stderr," password=|%s|",FIX(duri->password)); |
392 | | fprintf(stderr," host=|%s|",FIX(duri->host)); |
393 | | fprintf(stderr," port=|%s|",FIX(duri->port)); |
394 | | fprintf(stderr," path=|%s|",FIX(duri->path)); |
395 | | fprintf(stderr," query=|%s|",FIX(duri->query)); |
396 | | fprintf(stderr," fragment=|%s|",FIX(duri->fragment)); |
397 | | fprintf(stderr,"\n"); |
398 | | } |
399 | | #endif |
400 | |
|
401 | 798 | done: |
402 | 798 | if(uri != NULL) |
403 | 798 | free(uri); |
404 | | |
405 | 798 | freestringlist(params); |
406 | 798 | freestringlist(querylist); |
407 | 798 | if(tmp.fraglist) |
408 | 0 | freestringvec(tmp.fraglist); |
409 | 798 | if(tmp.querylist) |
410 | 0 | freestringvec(tmp.querylist); |
411 | | |
412 | 798 | return ret; |
413 | 0 | } |
414 | | |
415 | | static void |
416 | | freestringlist(NClist* list) |
417 | 1.59k | { |
418 | 1.59k | if(list != NULL) { |
419 | 1.59k | int i; |
420 | 1.59k | for(i=0;i<nclistlength(list);i++) { |
421 | 0 | void* p = nclistget(list,i); |
422 | 0 | nullfree(p); |
423 | 0 | } |
424 | 1.59k | nclistfree(list); |
425 | 1.59k | } |
426 | 1.59k | } |
427 | | |
428 | | static void |
429 | | freestringvec(char** list) |
430 | 0 | { |
431 | 0 | if(list != NULL) { |
432 | 0 | char** p; |
433 | 0 | for(p=list;*p;p++) {nullfree(*p);} |
434 | 0 | nullfree(list); |
435 | 0 | } |
436 | 0 | } |
437 | | |
438 | | void |
439 | | ncurifree(NCURI* duri) |
440 | 0 | { |
441 | 0 | if(duri == NULL) return; |
442 | 0 | nullfree(duri->uri); |
443 | 0 | nullfree(duri->protocol); |
444 | 0 | nullfree(duri->user); |
445 | 0 | nullfree(duri->password); |
446 | 0 | nullfree(duri->host); |
447 | 0 | nullfree(duri->port); |
448 | 0 | nullfree(duri->path); |
449 | 0 | nullfree(duri->query); |
450 | 0 | nullfree(duri->fragment); |
451 | 0 | freestringvec(duri->querylist); |
452 | 0 | freestringvec(duri->fraglist); |
453 | 0 | free(duri); |
454 | 0 | } |
455 | | |
456 | | /* Replace the protocol */ |
457 | | int |
458 | | ncurisetprotocol(NCURI* duri,const char* protocol) |
459 | 0 | { |
460 | 0 | nullfree(duri->protocol); |
461 | 0 | duri->protocol = strdup(protocol); |
462 | 0 | return (NC_NOERR); |
463 | 0 | } |
464 | | |
465 | | /* Replace the query */ |
466 | | int |
467 | | ncurisetquery(NCURI* duri,const char* query) |
468 | 0 | { |
469 | 0 | int ret = NC_NOERR; |
470 | 0 | freestringvec(duri->querylist); |
471 | 0 | nullfree(duri->query); |
472 | 0 | duri->query = NULL; |
473 | 0 | duri->querylist = NULL; |
474 | 0 | if(query != NULL && strlen(query) > 0) { |
475 | 0 | NClist* params = nclistnew(); |
476 | 0 | duri->query = strdup(query); |
477 | 0 | ret = parselist(duri->query,params); |
478 | 0 | if(ret != NC_NOERR) |
479 | 0 | {THROW(NC_EURL);} |
480 | 0 | nclistpush(params,NULL); |
481 | 0 | duri->querylist = nclistextract(params); |
482 | 0 | nclistfree(params); |
483 | 0 | } |
484 | 0 | done: |
485 | 0 | return ret; |
486 | 0 | } |
487 | | |
488 | | /* Replace the fragments*/ |
489 | | int |
490 | | ncurisetfragments(NCURI* duri,const char* fragments) |
491 | 0 | { |
492 | 0 | int ret = NC_NOERR; |
493 | 0 | freestringvec(duri->fraglist); |
494 | 0 | nullfree(duri->fragment); |
495 | 0 | duri->fragment = NULL; |
496 | 0 | duri->fraglist = NULL; |
497 | 0 | if(fragments != NULL && strlen(fragments) > 0) { |
498 | 0 | NClist* params = nclistnew(); |
499 | 0 | duri->fragment = strdup(fragments); |
500 | 0 | ret = parselist(duri->fragment,params); |
501 | 0 | if(ret != NC_NOERR) |
502 | 0 | {THROW(NC_EURL);} |
503 | 0 | nclistpush(params,NULL); |
504 | 0 | duri->fraglist = nclistextract(params); |
505 | 0 | nclistfree(params); |
506 | 0 | } |
507 | 0 | done: |
508 | 0 | return ret; |
509 | 0 | } |
510 | | |
511 | | #if 0 |
512 | | /* Replace the constraints */ |
513 | | int |
514 | | ncurisetconstraints(NCURI* duri,const char* constraints) |
515 | | { |
516 | | char* proj = NULL; |
517 | | char* select = NULL; |
518 | | const char* p; |
519 | | |
520 | | if(duri->constraint != NULL) free(duri->constraint); |
521 | | if(duri->projection != NULL) free(duri->projection); |
522 | | if(duri->selection != NULL) free(duri->selection); |
523 | | duri->constraint = NULL; |
524 | | duri->projection = NULL; |
525 | | duri->selection = NULL; |
526 | | |
527 | | if(constraints == NULL || strlen(constraints)==0) return (NC_ECONSTRAINTS); |
528 | | |
529 | | duri->constraint = nulldup(constraints); |
530 | | if(*duri->constraint == '?') |
531 | | nclshift1(duri->constraint); |
532 | | |
533 | | p = duri->constraint; |
534 | | proj = (char*) p; |
535 | | select = strchr(proj,'&'); |
536 | | if(select != NULL) { |
537 | | size_t plen = (size_t)(select - proj); |
538 | | if(plen == 0) { |
539 | | proj = NULL; |
540 | | } else { |
541 | | proj = (char*)malloc(plen+1); |
542 | | memcpy((void*)proj,p,plen); |
543 | | proj[plen] = EOFCHAR; |
544 | | } |
545 | | select = nulldup(select); |
546 | | } else { |
547 | | proj = nulldup(proj); |
548 | | select = NULL; |
549 | | } |
550 | | duri->projection = proj; |
551 | | duri->selection = select; |
552 | | return NC_NOERR; |
553 | | } |
554 | | #endif |
555 | | |
556 | | /* Construct a complete NC URI. |
557 | | Optionally with the constraints. |
558 | | Optionally with the user parameters. |
559 | | Caller frees returned string. |
560 | | Optionally encode the pieces. |
561 | | */ |
562 | | |
563 | | char* |
564 | | ncuribuild(NCURI* duri, const char* prefix, const char* suffix, int flags) |
565 | 0 | { |
566 | 0 | char* newuri = NULL; |
567 | 0 | NCbytes* buf = ncbytesnew(); |
568 | 0 | const int encode = (flags&NCURIENCODE ? 1 : 0); |
569 | |
|
570 | 0 | if(prefix != NULL) |
571 | 0 | ncbytescat(buf,prefix); |
572 | |
|
573 | 0 | ncbytescat(buf,duri->protocol); |
574 | 0 | ncbytescat(buf,"://"); /* this will produce file:///... */ |
575 | |
|
576 | 0 | if((flags & NCURIPWD) && duri->user != NULL && duri->password != NULL) { |
577 | | /* The user and password must be encoded */ |
578 | 0 | char* encoded = ncuriencodeonly(duri->user,userpwdallow); |
579 | 0 | ncbytescat(buf,encoded); |
580 | 0 | nullfree(encoded); |
581 | 0 | ncbytescat(buf,":"); |
582 | 0 | encoded = ncuriencodeonly(duri->password,userpwdallow); |
583 | 0 | ncbytescat(buf,encoded); |
584 | 0 | nullfree(encoded); |
585 | 0 | ncbytescat(buf,"@"); |
586 | 0 | } |
587 | 0 | if(duri->host != NULL) ncbytescat(buf,duri->host); |
588 | 0 | if(duri->port != NULL) { |
589 | 0 | ncbytescat(buf,":"); |
590 | 0 | ncbytescat(buf,duri->port); |
591 | 0 | } |
592 | 0 | if((flags & NCURIPATH)) { |
593 | 0 | if(duri->path == NULL) |
594 | 0 | ncbytescat(buf,"/"); |
595 | 0 | else if(encode) { |
596 | 0 | char* encoded = ncuriencodeonly(duri->path,pathallow); |
597 | 0 | ncbytescat(buf,encoded); |
598 | 0 | nullfree(encoded); |
599 | 0 | } else |
600 | 0 | ncbytescat(buf,duri->path); |
601 | 0 | } |
602 | | |
603 | | /* The suffix is intended to some kind of path extension (e.g. .dds) |
604 | | so insert here |
605 | | */ |
606 | 0 | if(suffix != NULL) |
607 | 0 | ncbytescat(buf,suffix); |
608 | | |
609 | | /* The query and the querylist are assumed to be unencoded */ |
610 | 0 | if(flags & NCURIQUERY && duri->querylist != NULL) { |
611 | 0 | char** p; |
612 | 0 | int first = 1; |
613 | 0 | for(p=duri->querylist;*p;p+=2,first=0) { |
614 | 0 | ncbytescat(buf,(first?"?":"&")); |
615 | 0 | if(encode) { |
616 | 0 | char* encoded = ncuriencodeonly(p[0],queryallow); |
617 | 0 | ncbytescat(buf,encoded); |
618 | 0 | nullfree(encoded); |
619 | 0 | } else |
620 | 0 | ncbytescat(buf,p[0]); |
621 | 0 | if(p[1] != NULL && strlen(p[1]) > 0) { |
622 | 0 | ncbytescat(buf,"="); |
623 | 0 | if(encode) { |
624 | 0 | char* encoded = ncuriencodeonly(p[1],queryallow); |
625 | 0 | ncbytescat(buf,encoded); |
626 | 0 | nullfree(encoded); |
627 | 0 | } else |
628 | 0 | ncbytescat(buf,p[1]); |
629 | 0 | } |
630 | 0 | } |
631 | 0 | } |
632 | 0 | if((flags & NCURIFRAG) && duri->fraglist != NULL) { |
633 | 0 | char** p; |
634 | 0 | int first = 1; |
635 | 0 | for(p=duri->fraglist;*p;p+=2,first=0) { |
636 | 0 | ncbytescat(buf,(first?"#":"&")); |
637 | 0 | ncbytescat(buf,p[0]); |
638 | 0 | if(p[1] != NULL && strlen(p[1]) > 0) { |
639 | 0 | ncbytescat(buf,"="); |
640 | 0 | if(encode) { |
641 | 0 | char* encoded = ncuriencodeonly(p[1],queryallow); |
642 | 0 | ncbytescat(buf,encoded); |
643 | 0 | nullfree(encoded); |
644 | 0 | } else |
645 | 0 | ncbytescat(buf,p[1]); |
646 | 0 | } |
647 | 0 | } |
648 | 0 | } |
649 | 0 | ncbytesnull(buf); |
650 | 0 | newuri = ncbytesextract(buf); |
651 | 0 | ncbytesfree(buf); |
652 | 0 | return newuri; |
653 | 0 | } |
654 | | |
655 | | |
656 | | const char* |
657 | | ncurilookup(NCURI* uri, const char* key) |
658 | 0 | { |
659 | 0 | int i; |
660 | 0 | char* value = NULL; |
661 | 0 | if(uri == NULL || key == NULL || uri->fraglist == NULL) return NULL; |
662 | 0 | i = ncfind(uri->fraglist,key); |
663 | 0 | if(i < 0) |
664 | 0 | return NULL; |
665 | 0 | value = uri->fraglist[(2*i)+1]; |
666 | 0 | return value; |
667 | 0 | } |
668 | | |
669 | | const char* |
670 | | ncuriquerylookup(NCURI* uri, const char* key) |
671 | 0 | { |
672 | 0 | int i; |
673 | 0 | char* value = NULL; |
674 | 0 | if(uri == NULL || key == NULL || uri->querylist == NULL) return NULL; |
675 | 0 | i = ncfind(uri->querylist,key); |
676 | 0 | if(i < 0) |
677 | 0 | return NULL; |
678 | 0 | value = uri->querylist[(2*i)+1]; |
679 | 0 | return value; |
680 | 0 | } |
681 | | |
682 | | /* Obtain the complete list of fragment pairs in envv format */ |
683 | | const char** |
684 | | ncurifragmentparams(NCURI* uri) |
685 | 0 | { |
686 | 0 | return (const char**)uri->fraglist; |
687 | 0 | } |
688 | | |
689 | | /* Obtain the complete list of query pairs in envv format */ |
690 | | const char** |
691 | | ncuriqueryparams(NCURI* uri) |
692 | 0 | { |
693 | 0 | return (const char**)uri->querylist; |
694 | 0 | } |
695 | | |
696 | | #if 0 |
697 | | int |
698 | | ncuriremoveparam(NCURI* uri, const char* key) |
699 | | { |
700 | | char** p; |
701 | | char** q = NULL; |
702 | | |
703 | | if(uri->fraglist == NULL) return NC_NOERR; |
704 | | for(q=uri->fraglist,p=uri->fraglist;*p;) { |
705 | | if(strcmp(key,*p)==0) { |
706 | | p += 2; /* skip this entry */ |
707 | | } else { |
708 | | *q++ = *p++; /* move key */ |
709 | | *q++ = *p++; /* move value */ |
710 | | } |
711 | | } |
712 | | return NC_NOERR; |
713 | | } |
714 | | #endif |
715 | | |
716 | | |
717 | | /* Internal version of lookup; returns the paired index of the key; |
718 | | case insensitive |
719 | | */ |
720 | | static int |
721 | | ncfind(char** params, const char* key) |
722 | 0 | { |
723 | 0 | int i; |
724 | 0 | char** p; |
725 | 0 | for(i=0,p=params;*p;p+=2,i++) { |
726 | 0 | if(strcasecmp(key,*p)==0) return i; |
727 | 0 | } |
728 | 0 | return -1; |
729 | 0 | } |
730 | | |
731 | | |
732 | | #if 0 |
733 | | static void |
734 | | ncparamfree(char** params) |
735 | | { |
736 | | char** p; |
737 | | if(params == NULL) return; |
738 | | for(p=params;*p;p+=2) { |
739 | | free(*p); |
740 | | if(p[1] != NULL) free(p[1]); |
741 | | } |
742 | | free(params); |
743 | | } |
744 | | #endif |
745 | | |
746 | | /* Return the ptr to the first occurrence of |
747 | | any char in the list. Return NULL if no |
748 | | occurrences |
749 | | */ |
750 | | static char* |
751 | | nclocate(char* p, const char* charlist) |
752 | 798 | { |
753 | 21.6k | for(;*p;p++) { |
754 | 20.8k | if(*p == '\\') p++; |
755 | 20.8k | else if(strchr(charlist,*p) != NULL) |
756 | 0 | return p; |
757 | 20.8k | } |
758 | 798 | return NULL; |
759 | 798 | } |
760 | | |
761 | | #if 0 |
762 | | /* Shift every char starting at p 1 place to the left */ |
763 | | static void |
764 | | nclshift1(char* p) |
765 | | { |
766 | | if(p != NULL && *p != EOFCHAR) { |
767 | | char* q = p++; |
768 | | while((*q++=*p++)); |
769 | | } |
770 | | } |
771 | | |
772 | | /* Shift every char starting at p 1 place to the right */ |
773 | | static void |
774 | | ncrshift1(char* p) |
775 | | { |
776 | | char cur; |
777 | | cur = 0; |
778 | | do { |
779 | | char next = *p; |
780 | | *p++ = cur; |
781 | | cur = next; |
782 | | } while(cur != 0); |
783 | | *p = 0; /* make sure we are still null terminated */ |
784 | | } |
785 | | #endif |
786 | | |
787 | | /* Provide % encoders and decoders */ |
788 | | |
789 | | static const char* hexchars = "0123456789abcdefABCDEF"; |
790 | | |
791 | | static void |
792 | | toHex(unsigned int b, char hex[2]) |
793 | 0 | { |
794 | 0 | hex[0] = hexchars[(b >> 4) & 0xf]; |
795 | 0 | hex[1] = hexchars[(b) & 0xf]; |
796 | 0 | } |
797 | | |
798 | | |
799 | | static int |
800 | | fromHex(int c) |
801 | 0 | { |
802 | 0 | if(c >= '0' && c <= '9') return (int) (c - '0'); |
803 | 0 | if(c >= 'a' && c <= 'f') return (int) (10 + (c - 'a')); |
804 | 0 | if(c >= 'A' && c <= 'F') return (int) (10 + (c - 'A')); |
805 | 0 | return 0; |
806 | 0 | } |
807 | | |
808 | | /* |
809 | | Support encode of user and password fields |
810 | | */ |
811 | | char* |
812 | | ncuriencodeuserpwd(char* s) |
813 | 0 | { |
814 | 0 | return ncuriencodeonly(s,userpwdallow); |
815 | 0 | } |
816 | | |
817 | | /* Return a string representing encoding of input; caller must free; |
818 | | watch out: will encode whole string, so watch what you give it. |
819 | | Allowable argument specifies characters that do not need escaping. |
820 | | */ |
821 | | |
822 | | char* |
823 | | ncuriencodeonly(char* s, const char* allowable) |
824 | 0 | { |
825 | 0 | size_t slen; |
826 | 0 | char* encoded; |
827 | 0 | char* inptr; |
828 | 0 | char* outptr; |
829 | |
|
830 | 0 | if(s == NULL) return NULL; |
831 | | |
832 | 0 | slen = strlen(s); |
833 | 0 | encoded = (char*)malloc((3*slen) + 1); /* max possible size */ |
834 | |
|
835 | 0 | for(inptr=s,outptr=encoded;*inptr;) { |
836 | 0 | int c = *inptr++; |
837 | 0 | if(c == ' ') { |
838 | 0 | *outptr++ = '+'; |
839 | 0 | } else { |
840 | | /* search allowable */ |
841 | 0 | char* p = strchr(allowable,c); |
842 | 0 | if(p != NULL) { |
843 | 0 | *outptr++ = (char)c; |
844 | 0 | } else { |
845 | 0 | char hex[2]; |
846 | 0 | toHex(c,hex); |
847 | 0 | *outptr++ = '%'; |
848 | 0 | *outptr++ = hex[0]; |
849 | 0 | *outptr++ = hex[1]; |
850 | 0 | } |
851 | 0 | } |
852 | 0 | } |
853 | 0 | *outptr = EOFCHAR; |
854 | 0 | return encoded; |
855 | 0 | } |
856 | | |
857 | | /* Return a string representing decoding of input; caller must free;*/ |
858 | | char* |
859 | | ncuridecode(char* s) |
860 | 0 | { |
861 | 0 | size_t slen; |
862 | 0 | char* decoded; |
863 | 0 | char* outptr; |
864 | 0 | char* inptr; |
865 | 0 | unsigned int c; |
866 | |
|
867 | 0 | if (s == NULL) return NULL; |
868 | | |
869 | 0 | slen = strlen(s); |
870 | 0 | decoded = (char*)malloc(slen+1); /* Should be max we need */ |
871 | |
|
872 | 0 | outptr = decoded; |
873 | 0 | inptr = s; |
874 | 0 | while((c = (unsigned int)*inptr++)) { |
875 | 0 | if(c == '%') { |
876 | | /* try to pull two hex more characters */ |
877 | 0 | if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR |
878 | 0 | && strchr(hexchars,inptr[0]) != NULL |
879 | 0 | && strchr(hexchars,inptr[1]) != NULL) { |
880 | | /* test conversion */ |
881 | 0 | int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1])); |
882 | 0 | inptr += 2; /* decode it */ |
883 | 0 | c = (unsigned int)xc; |
884 | 0 | } |
885 | 0 | } |
886 | 0 | *outptr++ = (char)c; |
887 | 0 | } |
888 | 0 | *outptr = EOFCHAR; |
889 | 0 | return decoded; |
890 | 0 | } |
891 | | |
892 | | /* |
893 | | Partially decode a string. Only characters in 'decodeset' |
894 | | are decoded. Return decoded string; caller must free. |
895 | | */ |
896 | | char* |
897 | | ncuridecodepartial(char* s, const char* decodeset) |
898 | 0 | { |
899 | 0 | size_t slen; |
900 | 0 | char* decoded; |
901 | 0 | char* outptr; |
902 | 0 | char* inptr; |
903 | 0 | unsigned int c; |
904 | |
|
905 | 0 | if (s == NULL || decodeset == NULL) return NULL; |
906 | | |
907 | 0 | slen = strlen(s); |
908 | 0 | decoded = (char*)malloc(slen+1); /* Should be max we need */ |
909 | |
|
910 | 0 | outptr = decoded; |
911 | 0 | inptr = s; |
912 | 0 | while((c = (unsigned int)*inptr++)) { |
913 | 0 | if(c == '+' && strchr(decodeset,'+') != NULL) |
914 | 0 | *outptr++ = ' '; |
915 | 0 | else if(c == '%') { |
916 | | /* try to pull two hex more characters */ |
917 | 0 | if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR |
918 | 0 | && strchr(hexchars,inptr[0]) != NULL |
919 | 0 | && strchr(hexchars,inptr[1]) != NULL) { |
920 | | /* test conversion */ |
921 | 0 | int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1])); |
922 | 0 | if(strchr(decodeset,xc) != NULL) { |
923 | 0 | inptr += 2; /* decode it */ |
924 | 0 | c = (unsigned int)xc; |
925 | 0 | } |
926 | 0 | } |
927 | 0 | *outptr++ = (char)c; /* pass either the % or decoded char */ |
928 | 0 | } else /* Not a % char */ |
929 | 0 | *outptr++ = (char)c; |
930 | 0 | } |
931 | 0 | *outptr = EOFCHAR; |
932 | 0 | return decoded; |
933 | 0 | } |
934 | | |
935 | | static int |
936 | | collectprefixparams(char* text, char** nextp) |
937 | 0 | { |
938 | 0 | int ret = NC_NOERR; |
939 | 0 | char* sp; |
940 | 0 | char* ep; |
941 | 0 | char* last; |
942 | |
|
943 | 0 | if(text == NULL) return NC_EURL; |
944 | 0 | if(strlen(text) == 0) { |
945 | 0 | if(nextp) *nextp = text; |
946 | 0 | return NC_NOERR; |
947 | 0 | } |
948 | | /* pass 1: locate last rbracket and nul term the prefix */ |
949 | 0 | sp = text; |
950 | 0 | last = NULL; |
951 | 0 | for(;;) { |
952 | 0 | if(*sp != LBRACKET) { |
953 | 0 | if(nextp) *nextp = sp; |
954 | 0 | break; |
955 | 0 | } |
956 | | /* use nclocate because \\ escapes might be present */ |
957 | 0 | ep = nclocate(sp,RBRACKETSTR); |
958 | 0 | if(ep == NULL) {ret = NC_EINVAL; goto done;} /* malformed */ |
959 | 0 | last = ep; /* save this position */ |
960 | 0 | ep++; /* move past rbracket */ |
961 | 0 | sp = ep; |
962 | 0 | } |
963 | | /* nul terminate */ |
964 | 0 | if(last != NULL) |
965 | 0 | terminate(last); |
966 | | |
967 | | /* pass 2: convert [] to & */ |
968 | 0 | sp = text; |
969 | 0 | for(;;) { |
970 | 0 | char* p; char* q; |
971 | | /* by construction, here we are at an LBRACKET: compress it out */ |
972 | 0 | for(p=sp,q=sp+1;(*p++=*q++);) |
973 | 0 | ; |
974 | | /* locate the next RRACKET */ |
975 | 0 | ep = nclocate(sp,RBRACKETSTR); |
976 | 0 | if(ep == NULL) break;/* we are done */ |
977 | | /* convert the BRACKET to '&' */ |
978 | 0 | *ep = '&'; |
979 | 0 | ep++; /* move past rbracket */ |
980 | 0 | sp = ep; |
981 | 0 | } |
982 | 0 | done: |
983 | 0 | return ret; |
984 | 0 | } |
985 | | |
986 | | static int |
987 | | parselist(const char* text, NClist* list) |
988 | 0 | { |
989 | 0 | int ret = NC_NOERR; |
990 | 0 | char* ptext = NULL; |
991 | 0 | char* p; |
992 | 0 | ptext = strdup(text); /* We need to modify */ |
993 | 0 | p = ptext; /* start of next parameter */ |
994 | 0 | for(;;) { |
995 | 0 | char* sp = p; |
996 | 0 | char* eq; |
997 | 0 | char* ep; |
998 | 0 | char* key; |
999 | 0 | char* value; |
1000 | 0 | if(*p == EOFCHAR) break; /* we are done */ |
1001 | | /* use nclocate because \\ escapes might be present */ |
1002 | 0 | ep = nclocate(sp,"&"); |
1003 | 0 | if(ep != NULL) { |
1004 | 0 | terminate(ep); /* overwrite the trailing ampersand */ |
1005 | 0 | p = ep+1; /* next param */ |
1006 | 0 | } |
1007 | | /* split into key + value */ |
1008 | 0 | eq = strchr(sp,'='); |
1009 | 0 | if(eq != NULL) { /* value is present */ |
1010 | 0 | terminate(eq); eq++; |
1011 | 0 | key = strdup(sp); |
1012 | 0 | value = strdup(eq); |
1013 | 0 | } else {/* there is no value */ |
1014 | 0 | key = strdup(sp); |
1015 | 0 | value = strdup(""); |
1016 | 0 | } |
1017 | 0 | nclistpush(list,key); |
1018 | 0 | nclistpush(list,value); |
1019 | 0 | if(ep == NULL) |
1020 | 0 | break; |
1021 | 0 | } |
1022 | 0 | nullfree(ptext); |
1023 | 0 | return ret; |
1024 | 0 | } |