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