/src/netcdf-c/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 | 218 | #define THROW(n) {ret=(n); goto done;} |
27 | | #else |
28 | | #define THROW(n) {goto done;} |
29 | | #endif |
30 | | |
31 | | #define PADDING 8 |
32 | | |
33 | 218 | #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 | | |
61 | | static char* ascii = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; |
62 | | |
63 | | /* Classes according to the URL RFC" */ |
64 | | #define RFCRESERVED " !*'();:@&=+$,/?#[]" |
65 | | #define RFCUNRESERVED "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_.~" |
66 | | #define RFCOTHER "\"%<>\\^`{|}" |
67 | | |
68 | | /* I really hate the URL encoding mess */ |
69 | | |
70 | | static const char* pathallow = |
71 | | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~"; |
72 | | |
73 | | static const char* queryallow = |
74 | | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$&'()*+,-./:;=?@_~"; |
75 | | |
76 | | /* user+pwd allow = path allow - "@:" */ |
77 | | static const char* userpwdallow = |
78 | | "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!$&'()*+,-.;=_~?#/"; |
79 | | |
80 | | #ifndef HAVE_STRNDUP |
81 | | #define strndup ncstrndup |
82 | | /* Not all systems have strndup, so provide one*/ |
83 | | char* |
84 | | ncstrndup(const char* s, size_t len) |
85 | | { |
86 | | char* dup; |
87 | | if(s == NULL) return NULL; |
88 | | dup = (char*)malloc(len+1); |
89 | | if(dup == NULL) return NULL; |
90 | | memcpy((void*)dup,s,len); |
91 | | dup[len] = '\0'; |
92 | | return dup; |
93 | | } |
94 | | #endif |
95 | | /* Forward */ |
96 | | static int collectprefixparams(char* text, char** nextp); |
97 | | static void freestringlist(NClist* list); |
98 | | static void freestringvec(char** list); |
99 | | static int ncfind(char** params, const char* key); |
100 | | static char* nclocate(char* p, const char* charlist); |
101 | | static int parselist(const char* ptext, NClist* list); |
102 | | static int unparselist(const char** vec, const char* prefix, int encode, char** svecp); |
103 | | static int ensurefraglist(NCURI* uri); |
104 | | static int ensurequerylist(NCURI* uri); |
105 | | static void buildlist(const char** list, int encode, NCbytes* buf); |
106 | | static void removedups(NClist* list); |
107 | | static int extendenvv(char*** envvp, int amount, int* oldlen); |
108 | | |
109 | | /**************************************************/ |
110 | | /* |
111 | | A note about parameter support: |
112 | | In the original url format for opendap (dap2), client parameters were |
113 | | assumed to be one or more instances of bracketed pairs: e.g |
114 | | "[...][...]...". |
115 | | These were assumed to be placed at the front of the url. In this newer |
116 | | version, the parameters may be encoded after a trailing # character each |
117 | | separated by ampersand (&). For back compatibility, the bracketed |
118 | | parameter form is supported. However, if ncuribuild is used, all |
119 | | parameters will be converted to the |
120 | | #...&...& format. |
121 | | In any case, each parameter in turn is assumed to be a of the form |
122 | | <name>=<value> or <name>; e.g. #x=y&z&a=b&w. |
123 | | If the same parameter is specified more than once, then the first |
124 | | occurrence is used; this is so that is possible to forcibly override |
125 | | user specified parameters by prefixing. |
126 | | IMPORTANT: the client parameter string is assumed to have blanks compressed out. |
127 | | */ |
128 | | |
129 | | /**************************************************/ |
130 | | |
131 | | /* Do a simple uri parse: return NC_NOERR if success, NC_EXXX if failed */ |
132 | | int |
133 | | ncuriparse(const char* uri0, NCURI** durip) |
134 | 218 | { |
135 | 218 | int ret = NC_NOERR; |
136 | 218 | NCURI tmp; |
137 | 218 | char* p; |
138 | 218 | char* q; |
139 | 218 | int isfile; |
140 | 218 | int hashost; |
141 | 218 | char* uri = NULL; |
142 | 218 | NCURI* duri = NULL; |
143 | 218 | char* prefix = NULL; |
144 | 218 | char* next = NULL; |
145 | 218 | NClist* params = nclistnew(); |
146 | 218 | NClist* querylist = nclistnew(); |
147 | 218 | size_t len0; |
148 | 218 | int pathchar; |
149 | | |
150 | 218 | tmp.fraglist = NULL; |
151 | 218 | tmp.querylist = NULL; |
152 | | |
153 | 218 | if(uri0 == NULL) |
154 | 0 | {THROW(NC_EURL);} |
155 | | |
156 | 218 | len0 = strlen(uri0); |
157 | 218 | if(len0 == 0) |
158 | 0 | {THROW(NC_EURL);} |
159 | | |
160 | | /* Create a local NCURI instance to hold |
161 | | pointers into the parsed string |
162 | | */ |
163 | 218 | memset(&tmp,0,sizeof(tmp)); |
164 | | |
165 | | /* make mutable copy. Add some extra space |
166 | | because we will need to null terminate the host section |
167 | | without losing the first character of the path section. |
168 | | */ |
169 | 218 | uri = (char*)malloc(len0+1+1); /* +2 for nul term and for host section terminator */ |
170 | 218 | if(uri == NULL) |
171 | 0 | {THROW(NC_ENOMEM);} |
172 | | /* Safe because we allocated enough space right above (and */ |
173 | | /* `strdup` isn't usable because we need "one more char"). */ |
174 | 218 | strcpy(uri,uri0); |
175 | | |
176 | | /* Walk the uri and do the following: |
177 | | 1. remove leading and trailing whitespace |
178 | | 2. convert all '\\' -> '\' (Temp hack to remove escape characters |
179 | | inserted by Windows or MinGW) |
180 | | */ |
181 | 2.83k | for(q=uri,p=uri;*p;p++) {if((*p == '\\' && p[1] == '\\') || *p < ' ') {continue;} else {*q++ = *p;}} |
182 | 218 | *q = '\0'; |
183 | | |
184 | 218 | p = uri; |
185 | | |
186 | | /* break up the url into coarse pieces */ |
187 | 218 | if(*p == LBRACKET) { |
188 | 0 | prefix = p; |
189 | 0 | ret = collectprefixparams(p,&next); /* collect the prefix; convert to & form */ |
190 | 0 | if(ret != NC_NOERR) |
191 | 0 | {THROW(NC_EURL);} |
192 | 0 | p = next; |
193 | 218 | } else { |
194 | 218 | prefix = NULL; |
195 | 218 | } |
196 | 218 | tmp.uri = p; /* will be the core */ |
197 | | /* Skip past the core of the url */ |
198 | 218 | next = nclocate(p,"?#"); |
199 | 218 | if(next != NULL) { |
200 | 0 | int c = *next; |
201 | 0 | terminate(next); |
202 | 0 | next++; |
203 | 0 | if(c == '?') { |
204 | 0 | tmp.query = next; |
205 | 0 | next = nclocate(next,"#"); |
206 | 0 | if(next == NULL) |
207 | 0 | tmp.fragment = NULL; |
208 | 0 | else { |
209 | 0 | terminate(next); |
210 | 0 | next++; |
211 | 0 | tmp.fragment = next; |
212 | 0 | } |
213 | 0 | } else { /*c == '#'*/ |
214 | 0 | tmp.fragment = next; |
215 | 0 | } |
216 | 0 | } |
217 | | |
218 | | /* Parse the prefix parameters */ |
219 | 218 | if(prefix != NULL) { |
220 | 0 | if(parselist(prefix,params) != NC_NOERR) |
221 | 0 | {THROW(NC_EURL);} |
222 | 0 | } |
223 | | /* Parse the fragment parameters into the params list */ |
224 | 218 | if(tmp.fragment != NULL) { |
225 | 0 | if(parselist(tmp.fragment,params) != NC_NOERR) |
226 | 0 | {THROW(NC_EURL);} |
227 | 0 | } |
228 | | /* Remove duplicates */ |
229 | 218 | removedups(params); |
230 | 218 | if(nclistlength(params) > 0) { |
231 | 0 | nclistpush(params,NULL); |
232 | 0 | tmp.fraglist = nclistextract(params); |
233 | 0 | } else |
234 | 218 | tmp.fraglist = NULL; |
235 | | /* Parse the query */ |
236 | 218 | if(tmp.query != NULL) { |
237 | 0 | if(parselist(tmp.query,querylist) != NC_NOERR) |
238 | 0 | {THROW(NC_EURL);} |
239 | 0 | if(nclistlength(querylist) > 0) { |
240 | 0 | nclistpush(querylist,NULL); |
241 | 0 | tmp.querylist = nclistextract(querylist); |
242 | 0 | } |
243 | 0 | } |
244 | | |
245 | | /* Now parse the core of the url */ |
246 | 218 | p = tmp.uri; |
247 | | |
248 | | /* Mark the protocol */ |
249 | 218 | tmp.protocol = p; |
250 | 218 | p = strchr(p,':'); |
251 | 218 | if(!p) |
252 | 218 | {THROW(NC_EURL);} |
253 | 0 | terminate(p); /*overwrite colon*/ |
254 | 0 | p++; /* skip the colon */ |
255 | 0 | if(strlen(tmp.protocol)==0) |
256 | 0 | {THROW(NC_EURL);} |
257 | | /* |
258 | | The legal formats for file: urls are a problem since |
259 | | many variants are often accepted. |
260 | | By RFC, the proper general format is: file://host/path, |
261 | | where the 'host' can be omitted and defaults to 'localhost'. |
262 | | and the path includes the leading '/'. |
263 | | So, assuming no host, the format is: "file:///path". |
264 | | Some implementations, however, ignore the host, and allow |
265 | | the format: file:/path. |
266 | | We also simplify things by assuming the host part is always empty. |
267 | | which means we can have file:///path, but not file://..../path. |
268 | | Note also in all cases, the leading '/' is considered part of the path, |
269 | | which is then assumed to be an absolute path. But also note that |
270 | | the windows drive letter has to be taken into account. Our rule is that |
271 | | if the path looks like D:..., |
272 | | where D is a single alphabetic letter (a-z or A-Z), |
273 | | then it is a windows path and can be use in place of a /path. |
274 | | Note also that it is desirable to support relative paths even |
275 | | though the RFC technically does not allow this. This will occur |
276 | | if the form is file://path where path does not start with '/'. |
277 | | The rules implemented here (for file:) are then as follows |
278 | | 1. file:D:... : assume D: is a windows drive letter and treat D:... as the path |
279 | | 2. file:/X, where X does not start with a slash: treat /X as the path. |
280 | | 3. file://D:... : assume D: is a windows drive letter and treat as the path |
281 | | 4. file:///X, where X does not start with a slash: treat /X as the path. |
282 | | 5. file://X, where X does not start with a slash: treat X as the |
283 | | relative path. |
284 | | All other cases are disallowed. |
285 | | */ |
286 | | |
287 | 0 | isfile = (strcmp(tmp.protocol,"file")==0); |
288 | 0 | if(isfile) { |
289 | 0 | size_t l = strlen(p); /* to test if we have enough characters */ |
290 | 0 | hashost = 0; /* always */ |
291 | 0 | if(l >= 2 && p[1] == ':' && strchr(DRIVELETTERS,p[0]) != NULL) { /* case 1 */ |
292 | 0 | ; /* p points to the start of the path */ |
293 | 0 | } else if(l >= 2 && p[0] == '/' && p[1] != '/') { /* case 2 */ |
294 | 0 | ; /* p points to the start of the path */ |
295 | 0 | } else if(l >= 4 && p[0] == '/' && p[1] == '/' |
296 | 0 | && p[3] == ':' && strchr(DRIVELETTERS,p[2]) != NULL) { /* case 3 */ |
297 | 0 | p = p+2; /* points to the start of the windows path */ |
298 | 0 | } else if(l >= 4 && p[0] == '/' && p[1] == '/' && p[2] == '/' && p[3] != '/') { /* case 4 */ |
299 | 0 | p += 2; /* points to the start of the path */ |
300 | 0 | } else if(l >= 4 && p[0] == '/' && p[1] == '/' && p[2] != '/') { /* case 5 */ |
301 | 0 | p += 2; /* points to the start of the path */ |
302 | 0 | } else /* everything else is illegal */ |
303 | 0 | {THROW(NC_EURL);} |
304 | 0 | } else { |
305 | 0 | if(p[0] != '/' || p[1] != '/') /* must be proto:// */ |
306 | 0 | {THROW(NC_EURL);} |
307 | 0 | p += 2; |
308 | 0 | hashost = 1; /* Assume we have a hostname */ |
309 | 0 | } |
310 | 0 | if(!hashost) { |
311 | 0 | tmp.path = p; |
312 | 0 | pathchar = EOFCHAR; |
313 | 0 | } else { /* assume there should be a host section */ |
314 | | /* We already extracted the query and/or fragment sections above, |
315 | | splocate the end of the host section and therefore the start |
316 | | of the path. |
317 | | */ |
318 | 0 | tmp.host = p; |
319 | 0 | p = nclocate(p,"/"); |
320 | 0 | if(p == NULL) { /* no path */ |
321 | 0 | tmp.path = NULL; /* default */ |
322 | 0 | pathchar = EOFCHAR; |
323 | 0 | } else { |
324 | 0 | tmp.path = p; /* save ptr to rest of the path */ |
325 | 0 | pathchar = *p; /* save leading char of the path */ |
326 | 0 | terminate(p); /* overwrite the leading char of the path; restored below */ |
327 | 0 | } |
328 | 0 | } |
329 | | /* Nullify tmp.host for consistency */ |
330 | 0 | if(tmp.host != NULL && strlen(tmp.host)==0) {tmp.host = NULL;} |
331 | |
|
332 | 0 | if(tmp.host != NULL) {/* Parse the host section */ |
333 | 0 | char* pp; |
334 | | /* Check for leading user:pwd@ */ |
335 | 0 | char* newhost = strchr(tmp.host,'@'); |
336 | 0 | if(newhost != NULL) { |
337 | 0 | if(newhost == tmp.host) |
338 | 0 | {THROW(NC_EURL);} /* we have proto://@ */ |
339 | 0 | terminate(newhost); /* overwrite '@' */ |
340 | 0 | newhost++; /* should point past usr+pwd */ |
341 | 0 | tmp.user = tmp.host; |
342 | | /* Break user+pwd into two pieces */ |
343 | 0 | pp = strchr(tmp.user,':'); |
344 | 0 | if(pp == NULL) |
345 | 0 | {THROW(NC_EURL);} /* we have user only */ |
346 | 0 | terminate(pp); /* overwrite ':' */ |
347 | 0 | pp++; |
348 | 0 | if(strlen(tmp.user)==0) |
349 | 0 | {THROW(NC_EURL);} /* we have empty user */ |
350 | 0 | if(strlen(pp)==0) |
351 | 0 | {THROW(NC_EURL);} /* we have empty password */ |
352 | 0 | tmp.password = pp; |
353 | 0 | tmp.host = newhost; |
354 | 0 | } |
355 | | /* Breakup host into host + port */ |
356 | 0 | pp = tmp.host; |
357 | 0 | pp = strchr(pp,':'); |
358 | 0 | if(pp != NULL) { /* there is a port */ |
359 | 0 | terminate(pp); /* overwrite ':' */ |
360 | 0 | pp++; /* skip colon */ |
361 | 0 | if(strlen(tmp.host) == 0) |
362 | 0 | {THROW(NC_EURL);} /* empty host */ |
363 | 0 | if(strlen(pp)==0) |
364 | 0 | {THROW(NC_EURL);} /* empty port */ |
365 | 0 | tmp.port = pp; |
366 | | /* The port must look something like a number */ |
367 | 0 | for(pp=tmp.port;*pp;pp++) { |
368 | 0 | if(strchr("0123456789-",*pp) == NULL) |
369 | 0 | {THROW(NC_EURL);} /* probably not a real port, fail */ |
370 | 0 | } |
371 | 0 | } /* else no port */ |
372 | 0 | } |
373 | | |
374 | | /* Fill in duri from tmp */ |
375 | 0 | duri = (NCURI*)calloc(1,sizeof(NCURI)); |
376 | 0 | if(duri == NULL) |
377 | 0 | {THROW(NC_ENOMEM);} |
378 | | /* save original uri */ |
379 | 0 | duri->uri = strdup(uri0); |
380 | 0 | duri->protocol = nulldup(tmp.protocol); |
381 | | /* before saving, we need to decode the user+pwd */ |
382 | 0 | duri->user = NULL; |
383 | 0 | duri->password = NULL; |
384 | 0 | if(tmp.user != NULL) |
385 | 0 | duri->user = ncuridecode(tmp.user); |
386 | 0 | if(tmp.password != NULL) |
387 | 0 | duri->password = ncuridecode(tmp.password); |
388 | 0 | duri->host = nulldup(tmp.host); |
389 | 0 | duri->port = nulldup(tmp.port); |
390 | 0 | if(tmp.path != NULL) { |
391 | | /* We need to add back the previously overwritten path lead char (if necessary); |
392 | | this must be done after all host section related pieces have been captured */ |
393 | 0 | if(pathchar != EOFCHAR) |
394 | 0 | *tmp.path = pathchar; |
395 | 0 | duri->path = nulldup(tmp.path); |
396 | 0 | } |
397 | 0 | duri->query = NULL; /* let ensurequery fix this */ |
398 | 0 | duri->fragment = NULL; /* let ensurefrag fix this */ |
399 | 0 | duri->fraglist = tmp.fraglist; tmp.fraglist = NULL; |
400 | 0 | duri->querylist = tmp.querylist; tmp.querylist = NULL; |
401 | | |
402 | | /* make sure query and fragment strings are defined */ |
403 | 0 | ensurequerylist(duri); |
404 | 0 | ensurefraglist(duri); |
405 | |
|
406 | 0 | if(durip) |
407 | 0 | *durip = duri; |
408 | 0 | else |
409 | 0 | free(duri); |
410 | |
|
411 | | #ifdef NCXDEBUG |
412 | | { |
413 | | fprintf(stderr,"duri:"); |
414 | | fprintf(stderr," protocol=|%s|",FIX(duri->protocol)); |
415 | | fprintf(stderr," user=|%s|",FIX(duri->user)); |
416 | | fprintf(stderr," password=|%s|",FIX(duri->password)); |
417 | | fprintf(stderr," host=|%s|",FIX(duri->host)); |
418 | | fprintf(stderr," port=|%s|",FIX(duri->port)); |
419 | | fprintf(stderr," path=|%s|",FIX(duri->path)); |
420 | | fprintf(stderr," query=|%s|",FIX(duri->query)); |
421 | | fprintf(stderr," fragment=|%s|",FIX(duri->fragment)); |
422 | | fprintf(stderr,"\n"); |
423 | | } |
424 | | #endif |
425 | |
|
426 | 218 | done: |
427 | 218 | if(uri != NULL) |
428 | 218 | free(uri); |
429 | | |
430 | 218 | freestringlist(params); |
431 | 218 | freestringlist(querylist); |
432 | 218 | if(tmp.fraglist) |
433 | 0 | freestringvec(tmp.fraglist); |
434 | 218 | if(tmp.querylist) |
435 | 0 | freestringvec(tmp.querylist); |
436 | | |
437 | 218 | return ret; |
438 | 0 | } |
439 | | |
440 | | static void |
441 | | freestringlist(NClist* list) |
442 | 436 | { |
443 | 436 | if(list != NULL) { |
444 | 436 | int i; |
445 | 436 | for(i=0;i<nclistlength(list);i++) { |
446 | 0 | void* p = nclistget(list,i); |
447 | 0 | nullfree(p); |
448 | 0 | } |
449 | 436 | nclistfree(list); |
450 | 436 | } |
451 | 436 | } |
452 | | |
453 | | static void |
454 | | freestringvec(char** list) |
455 | 0 | { |
456 | 0 | if(list != NULL) { |
457 | 0 | char** p; |
458 | 0 | for(p=list;*p;p++) {nullfree(*p);} |
459 | 0 | nullfree(list); |
460 | 0 | } |
461 | 0 | } |
462 | | |
463 | | void |
464 | | ncurifree(NCURI* duri) |
465 | 114 | { |
466 | 114 | if(duri == NULL) return; |
467 | 0 | nullfree(duri->uri); |
468 | 0 | nullfree(duri->protocol); |
469 | 0 | nullfree(duri->user); |
470 | 0 | nullfree(duri->password); |
471 | 0 | nullfree(duri->host); |
472 | 0 | nullfree(duri->port); |
473 | 0 | nullfree(duri->path); |
474 | 0 | nullfree(duri->query); |
475 | 0 | nullfree(duri->fragment); |
476 | 0 | freestringvec(duri->querylist); |
477 | 0 | freestringvec(duri->fraglist); |
478 | 0 | free(duri); |
479 | 0 | } |
480 | | |
481 | | /* Replace the protocol */ |
482 | | int |
483 | | ncurisetprotocol(NCURI* duri,const char* protocol) |
484 | 0 | { |
485 | 0 | nullfree(duri->protocol); |
486 | 0 | duri->protocol = strdup(protocol); |
487 | 0 | return (NC_NOERR); |
488 | 0 | } |
489 | | |
490 | | /* Replace the host */ |
491 | | int |
492 | | ncurisethost(NCURI* duri,const char* host) |
493 | 0 | { |
494 | 0 | nullfree(duri->host); |
495 | 0 | duri->host = strdup(host); |
496 | 0 | return (NC_NOERR); |
497 | 0 | } |
498 | | |
499 | | /* Replace the path */ |
500 | | int |
501 | | ncurisetpath(NCURI* duri,const char* newpath) |
502 | 0 | { |
503 | 0 | nullfree(duri->path); |
504 | 0 | duri->path = strdup(newpath); |
505 | 0 | return (NC_NOERR); |
506 | 0 | } |
507 | | |
508 | | /* Replace the query */ |
509 | | int |
510 | | ncurisetquery(NCURI* duri,const char* query) |
511 | 0 | { |
512 | 0 | int ret = NC_NOERR; |
513 | 0 | freestringvec(duri->querylist); |
514 | 0 | nullfree(duri->query); |
515 | 0 | duri->query = NULL; |
516 | 0 | duri->querylist = NULL; |
517 | 0 | if(query != NULL && strlen(query) > 0) { |
518 | 0 | NClist* params = nclistnew(); |
519 | 0 | duri->query = strdup(query); |
520 | 0 | ret = parselist(duri->query,params); |
521 | 0 | if(ret != NC_NOERR) |
522 | 0 | {THROW(NC_EURL);} |
523 | 0 | nclistpush(params,NULL); |
524 | 0 | duri->querylist = nclistextract(params); |
525 | 0 | nclistfree(params); |
526 | 0 | } |
527 | 0 | done: |
528 | 0 | return ret; |
529 | 0 | } |
530 | | |
531 | | /* Replace the fragments*/ |
532 | | int |
533 | | ncurisetfragments(NCURI* duri,const char* fragments) |
534 | 0 | { |
535 | 0 | int ret = NC_NOERR; |
536 | 0 | freestringvec(duri->fraglist); |
537 | 0 | nullfree(duri->fragment); |
538 | 0 | duri->fragment = NULL; |
539 | 0 | duri->fraglist = NULL; |
540 | 0 | if(fragments != NULL && strlen(fragments) > 0) { |
541 | 0 | duri->fragment = strdup(fragments); |
542 | 0 | } |
543 | 0 | return ret; |
544 | 0 | } |
545 | | |
546 | | /* Replace the path */ |
547 | | int |
548 | | ncurirebuild(NCURI* duri) |
549 | 0 | { |
550 | 0 | char* surl = ncuribuild(duri,NULL,NULL,NCURIALL); |
551 | 0 | nullfree(duri->uri); |
552 | 0 | duri->uri = surl; |
553 | 0 | return (NC_NOERR); |
554 | 0 | } |
555 | | |
556 | | /* Replace a specific fragment key*/ |
557 | | int |
558 | | ncurisetfragmentkey(NCURI* duri,const char* key, const char* value) |
559 | 0 | { |
560 | 0 | int ret = NC_NOERR; |
561 | 0 | int pos = -1; |
562 | 0 | char* newlist = NULL; |
563 | |
|
564 | 0 | ensurefraglist(duri); |
565 | 0 | pos = ncfind(duri->fraglist, key); |
566 | 0 | if(pos < 0) return NC_EINVAL; /* does not exist */ |
567 | 0 | nullfree(duri->fraglist[pos+1]); |
568 | 0 | duri->fraglist[pos+1] = strdup(value); |
569 | | /* Rebuild the fragment */ |
570 | 0 | if((ret = unparselist((const char**)duri->fraglist,"#",0,&newlist))) goto done; |
571 | 0 | nullfree(duri->fragment); |
572 | 0 | duri->fragment = newlist; newlist = NULL; |
573 | 0 | done: |
574 | 0 | return ret; |
575 | 0 | } |
576 | | |
577 | | /* Replace or add a specific fragment key*/ |
578 | | int |
579 | | ncuriappendfragmentkey(NCURI* duri,const char* key, const char* value) |
580 | 0 | { |
581 | 0 | int ret = NC_NOERR; |
582 | 0 | int len; |
583 | 0 | int pos = -1; |
584 | 0 | char* newlist = NULL; |
585 | |
|
586 | 0 | ensurefraglist(duri); |
587 | 0 | pos = ncfind(duri->fraglist, key); |
588 | 0 | if(pos < 0) { /* does not exist */ |
589 | 0 | if((ret = extendenvv(&duri->fraglist,2,&len))) goto done; |
590 | 0 | duri->fraglist[len] = strdup(key); |
591 | 0 | duri->fraglist[len+1] = nulldup(value); |
592 | 0 | duri->fraglist[len+2] = NULL; |
593 | 0 | } else { |
594 | 0 | nullfree(duri->fraglist[pos+1]); |
595 | 0 | duri->fraglist[pos+1] = strdup(value); |
596 | 0 | } |
597 | | /* Rebuild the fragment */ |
598 | 0 | if((ret = unparselist((const char**)duri->fraglist,"#",0,&newlist))) goto done; |
599 | 0 | nullfree(duri->fragment); |
600 | 0 | duri->fragment = newlist; newlist = NULL; |
601 | 0 | done: |
602 | 0 | return ret; |
603 | 0 | } |
604 | | |
605 | | #if 0 |
606 | | /* Replace the constraints */ |
607 | | int |
608 | | ncurisetconstraints(NCURI* duri,const char* constraints) |
609 | | { |
610 | | char* proj = NULL; |
611 | | char* select = NULL; |
612 | | const char* p; |
613 | | |
614 | | if(duri->constraint != NULL) free(duri->constraint); |
615 | | if(duri->projection != NULL) free(duri->projection); |
616 | | if(duri->selection != NULL) free(duri->selection); |
617 | | duri->constraint = NULL; |
618 | | duri->projection = NULL; |
619 | | duri->selection = NULL; |
620 | | |
621 | | if(constraints == NULL || strlen(constraints)==0) return (NC_ECONSTRAINTS); |
622 | | |
623 | | duri->constraint = nulldup(constraints); |
624 | | if(*duri->constraint == '?') |
625 | | nclshift1(duri->constraint); |
626 | | |
627 | | p = duri->constraint; |
628 | | proj = (char*) p; |
629 | | select = strchr(proj,'&'); |
630 | | if(select != NULL) { |
631 | | size_t plen = (size_t)(select - proj); |
632 | | if(plen == 0) { |
633 | | proj = NULL; |
634 | | } else { |
635 | | proj = (char*)malloc(plen+1); |
636 | | memcpy((void*)proj,p,plen); |
637 | | proj[plen] = EOFCHAR; |
638 | | } |
639 | | select = nulldup(select); |
640 | | } else { |
641 | | proj = nulldup(proj); |
642 | | select = NULL; |
643 | | } |
644 | | duri->projection = proj; |
645 | | duri->selection = select; |
646 | | return NC_NOERR; |
647 | | } |
648 | | #endif |
649 | | |
650 | | /* Construct a complete NC URI. |
651 | | Optionally with the constraints. |
652 | | Optionally with the user parameters. |
653 | | Caller frees returned string. |
654 | | Optionally encode the pieces. |
655 | | */ |
656 | | |
657 | | char* |
658 | | ncuribuild(NCURI* duri, const char* prefix, const char* suffix, int flags) |
659 | 0 | { |
660 | 0 | char* newuri = NULL; |
661 | 0 | NCbytes* buf = ncbytesnew(); |
662 | 0 | const int encodepath = (flags&NCURIENCODEPATH ? 1 : 0); |
663 | 0 | const int encodequery = (flags&NCURIENCODEQUERY ? 1 : 0); |
664 | |
|
665 | 0 | if(prefix != NULL) |
666 | 0 | ncbytescat(buf,prefix); |
667 | |
|
668 | 0 | ncbytescat(buf,duri->protocol); |
669 | 0 | ncbytescat(buf,"://"); /* this will produce file:///... */ |
670 | |
|
671 | 0 | if((flags & NCURIPWD) && duri->user != NULL && duri->password != NULL) { |
672 | | /* The user and password must be encoded */ |
673 | 0 | char* encoded = ncuriencodeonly(duri->user,userpwdallow); |
674 | 0 | ncbytescat(buf,encoded); |
675 | 0 | nullfree(encoded); |
676 | 0 | ncbytescat(buf,":"); |
677 | 0 | encoded = ncuriencodeonly(duri->password,userpwdallow); |
678 | 0 | ncbytescat(buf,encoded); |
679 | 0 | nullfree(encoded); |
680 | 0 | ncbytescat(buf,"@"); |
681 | 0 | } |
682 | 0 | if(duri->host != NULL) ncbytescat(buf,duri->host); |
683 | 0 | if(duri->port != NULL) { |
684 | 0 | ncbytescat(buf,":"); |
685 | 0 | ncbytescat(buf,duri->port); |
686 | 0 | } |
687 | 0 | if((flags & NCURIPATH)) { |
688 | 0 | if(duri->path == NULL) |
689 | 0 | ncbytescat(buf,"/"); |
690 | 0 | else if(encodepath) { |
691 | 0 | char* encoded = ncuriencodeonly(duri->path,pathallow); |
692 | 0 | ncbytescat(buf,encoded); |
693 | 0 | nullfree(encoded); |
694 | 0 | } else |
695 | 0 | ncbytescat(buf,duri->path); |
696 | 0 | } |
697 | | |
698 | | /* The suffix is intended to some kind of path extension (e.g. .dds) |
699 | | so insert here |
700 | | */ |
701 | 0 | if(suffix != NULL) |
702 | 0 | ncbytescat(buf,suffix); |
703 | | |
704 | | /* The query and the querylist are assumed to be unencoded */ |
705 | 0 | if(flags & NCURIQUERY) { |
706 | 0 | ensurequerylist(duri); |
707 | 0 | if(duri->query != NULL) { |
708 | 0 | ncbytescat(buf,"?"); |
709 | 0 | if(encodequery) { |
710 | 0 | char* encoded = ncuriencodeonly(duri->query,queryallow); |
711 | 0 | ncbytescat(buf,encoded); |
712 | 0 | nullfree(encoded); |
713 | 0 | } else |
714 | 0 | ncbytescat(buf,duri->query); |
715 | 0 | } |
716 | 0 | } |
717 | 0 | if(flags & NCURIFRAG) { |
718 | 0 | ensurefraglist(duri); |
719 | 0 | if(duri->fragment != NULL) { |
720 | 0 | ncbytescat(buf,"#"); |
721 | 0 | ncbytescat(buf,duri->fragment); |
722 | 0 | } |
723 | 0 | } |
724 | 0 | ncbytesnull(buf); |
725 | 0 | newuri = ncbytesextract(buf); |
726 | 0 | ncbytesfree(buf); |
727 | 0 | return newuri; |
728 | 0 | } |
729 | | |
730 | | |
731 | | const char* |
732 | | ncurifragmentlookup(NCURI* uri, const char* key) |
733 | 0 | { |
734 | 0 | int i; |
735 | 0 | char* value = NULL; |
736 | 0 | if(uri == NULL || key == NULL) return NULL; |
737 | 0 | ensurefraglist(uri); |
738 | 0 | i = ncfind(uri->fraglist,key); |
739 | 0 | if(i < 0) |
740 | 0 | return NULL; |
741 | 0 | value = uri->fraglist[(2*i)+1]; |
742 | 0 | return value; |
743 | 0 | } |
744 | | |
745 | | const char* |
746 | | ncuriquerylookup(NCURI* uri, const char* key) |
747 | 0 | { |
748 | 0 | int i; |
749 | 0 | char* value = NULL; |
750 | 0 | if(uri == NULL || key == NULL || uri->querylist == NULL) return NULL; |
751 | 0 | i = ncfind(uri->querylist,key); |
752 | 0 | if(i < 0) |
753 | 0 | return NULL; |
754 | 0 | value = uri->querylist[(2*i)+1]; |
755 | 0 | return value; |
756 | 0 | } |
757 | | |
758 | | /* Obtain the complete list of fragment pairs in envv format */ |
759 | | const char** |
760 | | ncurifragmentparams(NCURI* uri) |
761 | 0 | { |
762 | 0 | ensurefraglist(uri); |
763 | 0 | return (const char**)uri->fraglist; |
764 | 0 | } |
765 | | |
766 | | /* Obtain the complete list of query pairs in envv format */ |
767 | | const char** |
768 | | ncuriqueryparams(NCURI* uri) |
769 | 0 | { |
770 | 0 | ensurequerylist(uri); |
771 | 0 | return (const char**)uri->querylist; |
772 | 0 | } |
773 | | |
774 | | #if 0 |
775 | | int |
776 | | ncuriremoveparam(NCURI* uri, const char* key) |
777 | | { |
778 | | char** p; |
779 | | char** q = NULL; |
780 | | |
781 | | if(uri->fraglist == NULL) return NC_NOERR; |
782 | | for(q=uri->fraglist,p=uri->fraglist;*p;) { |
783 | | if(strcmp(key,*p)==0) { |
784 | | p += 2; /* skip this entry */ |
785 | | } else { |
786 | | *q++ = *p++; /* move key */ |
787 | | *q++ = *p++; /* move value */ |
788 | | } |
789 | | } |
790 | | return NC_NOERR; |
791 | | } |
792 | | #endif |
793 | | |
794 | | |
795 | | /* Internal version of lookup; returns the paired index of the key; |
796 | | case insensitive |
797 | | */ |
798 | | static int |
799 | | ncfind(char** params, const char* key) |
800 | 0 | { |
801 | 0 | int i; |
802 | 0 | char** p; |
803 | 0 | if(key == NULL) return -1; |
804 | 0 | if(params == NULL) return -1; |
805 | 0 | for(i=0,p=params;*p;p+=2,i++) { |
806 | 0 | if(strcasecmp(key,*p)==0) return i; |
807 | 0 | } |
808 | 0 | return -1; |
809 | 0 | } |
810 | | |
811 | | |
812 | | #if 0 |
813 | | static void |
814 | | ncparamfree(char** params) |
815 | | { |
816 | | char** p; |
817 | | if(params == NULL) return; |
818 | | for(p=params;*p;p+=2) { |
819 | | free(*p); |
820 | | if(p[1] != NULL) free(p[1]); |
821 | | } |
822 | | free(params); |
823 | | } |
824 | | #endif |
825 | | |
826 | | /* Return the ptr to the first occurrence of |
827 | | any char in the list. Return NULL if no |
828 | | occurrences |
829 | | */ |
830 | | static char* |
831 | | nclocate(char* p, const char* charlist) |
832 | 218 | { |
833 | 2.83k | for(;*p;p++) { |
834 | 2.61k | if(*p == '\\') p++; |
835 | 2.61k | else if(strchr(charlist,*p) != NULL) |
836 | 0 | return p; |
837 | 2.61k | } |
838 | 218 | return NULL; |
839 | 218 | } |
840 | | |
841 | | #if 0 |
842 | | /* Shift every char starting at p 1 place to the left */ |
843 | | static void |
844 | | nclshift1(char* p) |
845 | | { |
846 | | if(p != NULL && *p != EOFCHAR) { |
847 | | char* q = p++; |
848 | | while((*q++=*p++)); |
849 | | } |
850 | | } |
851 | | |
852 | | /* Shift every char starting at p 1 place to the right */ |
853 | | static void |
854 | | ncrshift1(char* p) |
855 | | { |
856 | | char cur; |
857 | | cur = 0; |
858 | | do { |
859 | | char next = *p; |
860 | | *p++ = cur; |
861 | | cur = next; |
862 | | } while(cur != 0); |
863 | | *p = 0; /* make sure we are still null terminated */ |
864 | | } |
865 | | #endif |
866 | | |
867 | | /* Provide % encoders and decoders */ |
868 | | |
869 | | static const char* hexchars = "0123456789abcdefABCDEF"; |
870 | | |
871 | | static void |
872 | | toHex(unsigned int b, char hex[2]) |
873 | 0 | { |
874 | 0 | hex[0] = hexchars[(b >> 4) & 0xf]; |
875 | 0 | hex[1] = hexchars[(b) & 0xf]; |
876 | 0 | } |
877 | | |
878 | | |
879 | | static int |
880 | | fromHex(int c) |
881 | 0 | { |
882 | 0 | if(c >= '0' && c <= '9') return (int) (c - '0'); |
883 | 0 | if(c >= 'a' && c <= 'f') return (int) (10 + (c - 'a')); |
884 | 0 | if(c >= 'A' && c <= 'F') return (int) (10 + (c - 'A')); |
885 | 0 | return 0; |
886 | 0 | } |
887 | | |
888 | | /* |
889 | | Support encode of user and password fields |
890 | | */ |
891 | | char* |
892 | | ncuriencodeuserpwd(const char* s) |
893 | 0 | { |
894 | 0 | return ncuriencodeonly(s,userpwdallow); |
895 | 0 | } |
896 | | |
897 | | /* Return a string representing encoding of input; caller must free; |
898 | | watch out: will encode whole string, so watch what you give it. |
899 | | Allowable argument specifies characters that do not need escaping. |
900 | | */ |
901 | | |
902 | | char* |
903 | | ncuriencodeonly(const char* s, const char* allowable) |
904 | 0 | { |
905 | 0 | size_t slen; |
906 | 0 | char* encoded; |
907 | 0 | const char* inptr; |
908 | 0 | char* outptr; |
909 | |
|
910 | 0 | if(s == NULL) return NULL; |
911 | | |
912 | 0 | slen = strlen(s); |
913 | 0 | encoded = (char*)malloc((3*slen) + 1); /* max possible size */ |
914 | |
|
915 | 0 | for(inptr=s,outptr=encoded;*inptr;) { |
916 | 0 | int c = *inptr++; |
917 | 0 | { |
918 | | /* search allowable */ |
919 | 0 | char* p = strchr(allowable,c); |
920 | 0 | if(p != NULL) { |
921 | 0 | *outptr++ = (char)c; |
922 | 0 | } else { |
923 | 0 | char hex[2]; |
924 | 0 | toHex(c,hex); |
925 | 0 | *outptr++ = '%'; |
926 | 0 | *outptr++ = hex[0]; |
927 | 0 | *outptr++ = hex[1]; |
928 | 0 | } |
929 | 0 | } |
930 | 0 | } |
931 | 0 | *outptr = EOFCHAR; |
932 | 0 | return encoded; |
933 | 0 | } |
934 | | |
935 | | /* Return a string representing decoding of input; caller must free;*/ |
936 | | char* |
937 | | ncuridecode(const char* s) |
938 | 0 | { |
939 | 0 | size_t slen; |
940 | 0 | char* decoded; |
941 | 0 | char* outptr; |
942 | 0 | const char* inptr; |
943 | 0 | unsigned int c; |
944 | |
|
945 | 0 | if (s == NULL) return NULL; |
946 | | |
947 | 0 | slen = strlen(s); |
948 | 0 | decoded = (char*)malloc(slen+1); /* Should be max we need */ |
949 | |
|
950 | 0 | outptr = decoded; |
951 | 0 | inptr = s; |
952 | 0 | while((c = (unsigned int)*inptr++)) { |
953 | 0 | if(c == '%') { |
954 | | /* try to pull two hex more characters */ |
955 | 0 | if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR |
956 | 0 | && strchr(hexchars,inptr[0]) != NULL |
957 | 0 | && strchr(hexchars,inptr[1]) != NULL) { |
958 | | /* test conversion */ |
959 | 0 | int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1])); |
960 | 0 | inptr += 2; /* decode it */ |
961 | 0 | c = (unsigned int)xc; |
962 | 0 | } |
963 | 0 | } |
964 | 0 | *outptr++ = (char)c; |
965 | 0 | } |
966 | 0 | *outptr = EOFCHAR; |
967 | 0 | return decoded; |
968 | 0 | } |
969 | | |
970 | | /* |
971 | | Partially decode a string. Only characters in 'decodeset' |
972 | | are decoded. Return decoded string; caller must free. |
973 | | */ |
974 | | char* |
975 | | ncuridecodepartial(const char* s, const char* decodeset) |
976 | 0 | { |
977 | 0 | size_t slen; |
978 | 0 | char* decoded; |
979 | 0 | char* outptr; |
980 | 0 | const char* inptr; |
981 | 0 | unsigned int c; |
982 | |
|
983 | 0 | if (s == NULL || decodeset == NULL) return NULL; |
984 | | |
985 | 0 | slen = strlen(s); |
986 | 0 | decoded = (char*)malloc(slen+1); /* Should be max we need */ |
987 | |
|
988 | 0 | outptr = decoded; |
989 | 0 | inptr = s; |
990 | 0 | while((c = (unsigned int)*inptr++)) { |
991 | 0 | if(c == '+' && strchr(decodeset,'+') != NULL) |
992 | 0 | *outptr++ = ' '; |
993 | 0 | else if(c == '%') { |
994 | | /* try to pull two hex more characters */ |
995 | 0 | if(inptr[0] != EOFCHAR && inptr[1] != EOFCHAR |
996 | 0 | && strchr(hexchars,inptr[0]) != NULL |
997 | 0 | && strchr(hexchars,inptr[1]) != NULL) { |
998 | | /* test conversion */ |
999 | 0 | int xc = (fromHex(inptr[0]) << 4) | (fromHex(inptr[1])); |
1000 | 0 | if(strchr(decodeset,xc) != NULL) { |
1001 | 0 | inptr += 2; /* decode it */ |
1002 | 0 | c = (unsigned int)xc; |
1003 | 0 | } |
1004 | 0 | } |
1005 | 0 | *outptr++ = (char)c; /* pass either the % or decoded char */ |
1006 | 0 | } else /* Not a % char */ |
1007 | 0 | *outptr++ = (char)c; |
1008 | 0 | } |
1009 | 0 | *outptr = EOFCHAR; |
1010 | 0 | return decoded; |
1011 | 0 | } |
1012 | | |
1013 | | /* Deep clone a uri */ |
1014 | | NCURI* |
1015 | | ncuriclone(NCURI* uri) |
1016 | 0 | { |
1017 | 0 | int stat = NC_NOERR; |
1018 | 0 | NCURI* newuri = NULL; |
1019 | | |
1020 | | /* make sure fragments and query are up to date */ |
1021 | 0 | if((stat=ensurefraglist(uri))) goto done; |
1022 | 0 | if((stat=ensurequerylist(uri))) goto done; |
1023 | | |
1024 | 0 | if((newuri = (NCURI*)calloc(1,sizeof(NCURI)))==NULL) |
1025 | 0 | {stat = NC_ENOMEM; goto done;} |
1026 | 0 | *newuri = *uri; /* copy */ |
1027 | | /* deep clone fields */ |
1028 | | |
1029 | 0 | newuri->uri = nulldup(uri->uri); |
1030 | 0 | newuri->protocol = nulldup(uri->protocol); |
1031 | 0 | newuri->user = nulldup(uri->user); |
1032 | 0 | newuri->password = nulldup(uri->password); |
1033 | 0 | newuri->host = nulldup(uri->host); |
1034 | 0 | newuri->port = nulldup(uri->port); |
1035 | 0 | newuri->path = nulldup(uri->path); |
1036 | 0 | newuri->query = nulldup(uri->query); |
1037 | 0 | newuri->fragment = nulldup(uri->fragment); |
1038 | | /* make these be rebuilt */ |
1039 | 0 | newuri->fraglist = NULL; |
1040 | 0 | newuri->querylist = NULL; |
1041 | 0 | done: |
1042 | 0 | return newuri; |
1043 | 0 | } |
1044 | | |
1045 | | static int |
1046 | | collectprefixparams(char* text, char** nextp) |
1047 | 0 | { |
1048 | 0 | int ret = NC_NOERR; |
1049 | 0 | char* sp; |
1050 | 0 | char* ep; |
1051 | 0 | char* last; |
1052 | |
|
1053 | 0 | if(text == NULL) return NC_EURL; |
1054 | 0 | if(strlen(text) == 0) { |
1055 | 0 | if(nextp) *nextp = text; |
1056 | 0 | return NC_NOERR; |
1057 | 0 | } |
1058 | | /* pass 1: locate last rbracket and nul term the prefix */ |
1059 | 0 | sp = text; |
1060 | 0 | last = NULL; |
1061 | 0 | for(;;) { |
1062 | 0 | if(*sp != LBRACKET) { |
1063 | 0 | if(nextp) *nextp = sp; |
1064 | 0 | break; |
1065 | 0 | } |
1066 | | /* use nclocate because \\ escapes might be present */ |
1067 | 0 | ep = nclocate(sp,RBRACKETSTR); |
1068 | 0 | if(ep == NULL) {ret = NC_EINVAL; goto done;} /* malformed */ |
1069 | 0 | last = ep; /* save this position */ |
1070 | 0 | ep++; /* move past rbracket */ |
1071 | 0 | sp = ep; |
1072 | 0 | } |
1073 | | /* nul terminate */ |
1074 | 0 | if(last != NULL) |
1075 | 0 | terminate(last); |
1076 | | |
1077 | | /* pass 2: convert [] to & */ |
1078 | 0 | sp = text; |
1079 | 0 | for(;;) { |
1080 | 0 | char* p; char* q; |
1081 | | /* by construction, here we are at an LBRACKET: compress it out */ |
1082 | 0 | for(p=sp,q=sp+1;(*p++=*q++);) |
1083 | 0 | ; |
1084 | | /* locate the next RRACKET */ |
1085 | 0 | ep = nclocate(sp,RBRACKETSTR); |
1086 | 0 | if(ep == NULL) break;/* we are done */ |
1087 | | /* convert the BRACKET to '&' */ |
1088 | 0 | *ep = '&'; |
1089 | 0 | ep++; /* move past rbracket */ |
1090 | 0 | sp = ep; |
1091 | 0 | } |
1092 | 0 | done: |
1093 | 0 | return ret; |
1094 | 0 | } |
1095 | | |
1096 | | static int |
1097 | | parselist(const char* text, NClist* list) |
1098 | 0 | { |
1099 | 0 | int ret = NC_NOERR; |
1100 | 0 | char* ptext = NULL; |
1101 | 0 | char* p; |
1102 | 0 | ptext = strdup(text); /* We need to modify */ |
1103 | 0 | p = ptext; /* start of next parameter */ |
1104 | 0 | for(;;) { |
1105 | 0 | char* sp = p; |
1106 | 0 | char* eq; |
1107 | 0 | char* ep; |
1108 | 0 | char* key; |
1109 | 0 | char* value; |
1110 | 0 | if(*p == EOFCHAR) break; /* we are done */ |
1111 | | /* use nclocate because \\ escapes might be present */ |
1112 | 0 | ep = nclocate(sp,"&"); |
1113 | 0 | if(ep != NULL) { |
1114 | 0 | terminate(ep); /* overwrite the trailing ampersand */ |
1115 | 0 | p = ep+1; /* next param */ |
1116 | 0 | } |
1117 | | /* split into key + value */ |
1118 | 0 | eq = strchr(sp,'='); |
1119 | 0 | if(eq != NULL) { /* value is present */ |
1120 | 0 | terminate(eq); eq++; |
1121 | 0 | key = strdup(sp); |
1122 | 0 | value = strdup(eq); |
1123 | 0 | } else {/* there is no value */ |
1124 | 0 | key = strdup(sp); |
1125 | 0 | value = strdup(""); |
1126 | 0 | } |
1127 | 0 | nclistpush(list,key); |
1128 | 0 | nclistpush(list,value); |
1129 | 0 | if(ep == NULL) |
1130 | 0 | break; |
1131 | 0 | } |
1132 | 0 | nullfree(ptext); |
1133 | 0 | return ret; |
1134 | 0 | } |
1135 | | |
1136 | | static int |
1137 | | unparselist(const char** vec, const char* prefix, int encode, char** svecp) |
1138 | 0 | { |
1139 | 0 | int stat = NC_NOERR; |
1140 | 0 | NCbytes* buf = ncbytesnew(); |
1141 | 0 | const char** p; |
1142 | 0 | int first = 1; |
1143 | |
|
1144 | 0 | if(vec == NULL || vec[0] == NULL) goto done; |
1145 | 0 | if(prefix != NULL) ncbytescat(buf,prefix); |
1146 | 0 | for(p=vec;*p;p+=2,first=0) { |
1147 | 0 | if(!first) ncbytescat(buf,"&"); |
1148 | 0 | if(encode) { |
1149 | 0 | char* encoded = ncuriencodeonly(p[0],queryallow); |
1150 | 0 | ncbytescat(buf,encoded); |
1151 | 0 | nullfree(encoded); |
1152 | 0 | } else |
1153 | 0 | ncbytescat(buf,p[0]); |
1154 | 0 | if(p[1] != NULL && strlen(p[1]) > 0) { |
1155 | 0 | ncbytescat(buf,"="); |
1156 | 0 | if(encode) { |
1157 | 0 | char* encoded = ncuriencodeonly(p[1],queryallow); |
1158 | 0 | ncbytescat(buf,encoded); |
1159 | 0 | nullfree(encoded); |
1160 | 0 | } else |
1161 | 0 | ncbytescat(buf,p[1]); |
1162 | 0 | } |
1163 | 0 | } |
1164 | 0 | if(svecp) {*svecp = ncbytesextract(buf);} |
1165 | 0 | done: |
1166 | 0 | ncbytesfree(buf); |
1167 | 0 | return stat; |
1168 | 0 | } |
1169 | | |
1170 | | static int |
1171 | | ensurefraglist(NCURI* uri) |
1172 | 0 | { |
1173 | 0 | int stat = NC_NOERR; |
1174 | 0 | int nofrag = 0; |
1175 | 0 | int nolist = 0; |
1176 | 0 | NClist* fraglist = NULL; |
1177 | 0 | NCbytes* frag = NULL; |
1178 | | |
1179 | 0 | if(uri->fragment == NULL || strlen(uri->fragment) == 0) |
1180 | 0 | {nullfree(uri->fragment); uri->fragment = NULL; nofrag=1;} |
1181 | 0 | if(uri->fraglist == NULL) nolist = 1; |
1182 | 0 | if(nolist && !nofrag) { |
1183 | 0 | fraglist = nclistnew(); |
1184 | 0 | if((stat = parselist(uri->fragment,fraglist))) goto done; |
1185 | 0 | removedups(fraglist); |
1186 | 0 | uri->fraglist = nclistextract(fraglist); |
1187 | 0 | } else if(!nolist && nofrag) { |
1188 | | /* Create the fragment string from fraglist */ |
1189 | 0 | frag = ncbytesnew(); |
1190 | 0 | buildlist((const char**)uri->fraglist,0,frag); /* do not encode */ |
1191 | 0 | uri->fragment = ncbytesextract(frag); |
1192 | 0 | } |
1193 | | |
1194 | 0 | done: |
1195 | 0 | ncbytesfree(frag); |
1196 | 0 | nclistfreeall(fraglist); |
1197 | 0 | return stat; |
1198 | 0 | } |
1199 | | |
1200 | | static int |
1201 | | ensurequerylist(NCURI* uri) |
1202 | 0 | { |
1203 | 0 | int stat = NC_NOERR; |
1204 | 0 | int noquery = 0; |
1205 | 0 | int nolist = 0; |
1206 | 0 | NClist* querylist = NULL; |
1207 | 0 | NCbytes* query = NULL; |
1208 | | |
1209 | 0 | if(uri->query == NULL || strlen(uri->query) == 0) |
1210 | 0 | {nullfree(uri->query); uri->query = NULL; noquery=1;} |
1211 | 0 | if(uri->querylist == NULL) nolist = 1; |
1212 | 0 | if(nolist && !noquery) { |
1213 | 0 | querylist = nclistnew(); |
1214 | 0 | if((stat = parselist(uri->query,querylist))) goto done; |
1215 | 0 | removedups(querylist); |
1216 | 0 | uri->querylist = nclistextract(querylist); |
1217 | 0 | } else if(!nolist && noquery) { |
1218 | | /* Create the query string from querylist */ |
1219 | 0 | query = ncbytesnew(); |
1220 | 0 | buildlist((const char**)uri->querylist,0,query); /* do not encode */ |
1221 | 0 | uri->query = ncbytesextract(query); |
1222 | 0 | } |
1223 | | |
1224 | 0 | done: |
1225 | 0 | ncbytesfree(query); |
1226 | 0 | nclistfreeall(querylist); |
1227 | 0 | return stat; |
1228 | 0 | } |
1229 | | |
1230 | | static void |
1231 | | removedups(NClist* list) |
1232 | 218 | { |
1233 | 218 | int i,j; |
1234 | | |
1235 | 218 | if(nclistlength(list) <= 2) return; /* need at least 2 pairs */ |
1236 | 0 | for(i=0;i<nclistlength(list);i+=2) { |
1237 | | /* look for dups for this entry */ |
1238 | 0 | for(j=nclistlength(list)-2;j>i;j-=2) { |
1239 | 0 | if(strcasecmp(nclistget(list,i),nclistget(list,j))==0 |
1240 | 0 | && strcasecmp(nclistget(list,i+1),nclistget(list,j+1))) { |
1241 | 0 | nclistremove(list,j+1); nclistremove(list,j); |
1242 | 0 | } |
1243 | 0 | } |
1244 | 0 | } |
1245 | | /* NULL terminate the list */ |
1246 | 0 | nclistpush(list,NULL); |
1247 | 0 | } |
1248 | | |
1249 | | static void |
1250 | | buildlist(const char** list, int encode, NCbytes* buf) |
1251 | 0 | { |
1252 | 0 | const char** p; |
1253 | 0 | int first = 1; |
1254 | 0 | for(p=list;*p;p+=2,first=0) { |
1255 | 0 | if(!first) ncbytescat(buf,"&"); |
1256 | 0 | ncbytescat(buf,p[0]); |
1257 | 0 | if(p[1] != NULL && strlen(p[1]) > 0) { |
1258 | 0 | ncbytescat(buf,"="); |
1259 | 0 | if(encode) { |
1260 | 0 | char* encoded = ncuriencodeonly(p[1],queryallow); |
1261 | 0 | ncbytescat(buf,encoded); |
1262 | 0 | nullfree(encoded); |
1263 | 0 | } else |
1264 | 0 | ncbytescat(buf,p[1]); |
1265 | 0 | } |
1266 | 0 | } |
1267 | 0 | } |
1268 | | |
1269 | | static int |
1270 | | extendenvv(char*** envvp, int amount, int* oldlenp) |
1271 | 0 | { |
1272 | 0 | char** envv = *envvp; |
1273 | 0 | char** p; |
1274 | 0 | int len; |
1275 | 0 | for(len=0,p=envv;*p;p++) len++; |
1276 | 0 | *oldlenp = len; |
1277 | 0 | if((envv = (char**)malloc((amount+len+1)*sizeof(char*)))==NULL) return NC_ENOMEM; |
1278 | 0 | memcpy(envv,*envvp,sizeof(char*)*len); |
1279 | 0 | envv[len] = NULL; |
1280 | 0 | nullfree(*envvp); |
1281 | 0 | *envvp = envv; envv = NULL; |
1282 | 0 | return NC_NOERR; |
1283 | 0 | } |
1284 | | |
1285 | | /* Use for gdb debug */ |
1286 | | char* |
1287 | | ncuriunescape(const char* s) |
1288 | 0 | { |
1289 | 0 | return ncuridecodepartial(s,ascii); |
1290 | 0 | } |