Coverage Report

Created: 2023-05-28 06:42

/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
}