Coverage Report

Created: 2025-06-09 07:42

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