Coverage Report

Created: 2025-07-23 08:13

/src/fontconfig/subprojects/libxml2-2.12.6/uri.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * uri.c: set of generic URI related routines
3
 *
4
 * Reference: RFCs 3986, 2732 and 2373
5
 *
6
 * See Copyright for the status of this software.
7
 *
8
 * daniel@veillard.com
9
 */
10
11
#define IN_LIBXML
12
#include "libxml.h"
13
14
#include <limits.h>
15
#include <string.h>
16
17
#include <libxml/xmlmemory.h>
18
#include <libxml/uri.h>
19
#include <libxml/xmlerror.h>
20
21
#include "private/error.h"
22
23
/**
24
 * MAX_URI_LENGTH:
25
 *
26
 * The definition of the URI regexp in the above RFC has no size limit
27
 * In practice they are usually relatively short except for the
28
 * data URI scheme as defined in RFC 2397. Even for data URI the usual
29
 * maximum size before hitting random practical limits is around 64 KB
30
 * and 4KB is usually a maximum admitted limit for proper operations.
31
 * The value below is more a security limit than anything else and
32
 * really should never be hit by 'normal' operations
33
 * Set to 1 MByte in 2012, this is only enforced on output
34
 */
35
0
#define MAX_URI_LENGTH 1024 * 1024
36
37
11
#define PORT_EMPTY           0
38
0
#define PORT_EMPTY_SERVER   -1
39
40
static void
41
xmlURIErrMemory(const char *extra)
42
0
{
43
0
    if (extra)
44
0
        __xmlRaiseError(NULL, NULL, NULL,
45
0
                        NULL, NULL, XML_FROM_URI,
46
0
                        XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
47
0
                        extra, NULL, NULL, 0, 0,
48
0
                        "Memory allocation failed : %s\n", extra);
49
0
    else
50
0
        __xmlRaiseError(NULL, NULL, NULL,
51
0
                        NULL, NULL, XML_FROM_URI,
52
0
                        XML_ERR_NO_MEMORY, XML_ERR_FATAL, NULL, 0,
53
0
                        NULL, NULL, NULL, 0, 0,
54
0
                        "Memory allocation failed\n");
55
0
}
56
57
static void xmlCleanURI(xmlURIPtr uri);
58
59
/*
60
 * Old rule from 2396 used in legacy handling code
61
 * alpha    = lowalpha | upalpha
62
 */
63
0
#define IS_ALPHA(x) (IS_LOWALPHA(x) || IS_UPALPHA(x))
64
65
66
/*
67
 * lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" |
68
 *            "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" |
69
 *            "u" | "v" | "w" | "x" | "y" | "z"
70
 */
71
72
0
#define IS_LOWALPHA(x) (((x) >= 'a') && ((x) <= 'z'))
73
74
/*
75
 * upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" |
76
 *           "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" |
77
 *           "U" | "V" | "W" | "X" | "Y" | "Z"
78
 */
79
0
#define IS_UPALPHA(x) (((x) >= 'A') && ((x) <= 'Z'))
80
81
#ifdef IS_DIGIT
82
#undef IS_DIGIT
83
#endif
84
/*
85
 * digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"
86
 */
87
0
#define IS_DIGIT(x) (((x) >= '0') && ((x) <= '9'))
88
89
/*
90
 * alphanum = alpha | digit
91
 */
92
93
0
#define IS_ALPHANUM(x) (IS_ALPHA(x) || IS_DIGIT(x))
94
95
/*
96
 * mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")"
97
 */
98
99
0
#define IS_MARK(x) (((x) == '-') || ((x) == '_') || ((x) == '.') ||     \
100
0
    ((x) == '!') || ((x) == '~') || ((x) == '*') || ((x) == '\'') ||    \
101
0
    ((x) == '(') || ((x) == ')'))
102
103
/*
104
 * unwise = "{" | "}" | "|" | "\" | "^" | "`"
105
 */
106
107
#define IS_UNWISE(p)                                                    \
108
0
      (((*(p) == '{')) || ((*(p) == '}')) || ((*(p) == '|')) ||         \
109
0
       ((*(p) == '\\')) || ((*(p) == '^')) || ((*(p) == '[')) ||        \
110
0
       ((*(p) == ']')) || ((*(p) == '`')))
111
/*
112
 * reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | "$" | "," |
113
 *            "[" | "]"
114
 */
115
116
0
#define IS_RESERVED(x) (((x) == ';') || ((x) == '/') || ((x) == '?') || \
117
0
        ((x) == ':') || ((x) == '@') || ((x) == '&') || ((x) == '=') || \
118
0
        ((x) == '+') || ((x) == '$') || ((x) == ',') || ((x) == '[') || \
119
0
        ((x) == ']'))
120
121
/*
122
 * unreserved = alphanum | mark
123
 */
124
125
0
#define IS_UNRESERVED(x) (IS_ALPHANUM(x) || IS_MARK(x))
126
127
/*
128
 * Skip to next pointer char, handle escaped sequences
129
 */
130
131
77
#define NEXT(p) ((*p == '%')? p += 3 : p++)
132
133
/*
134
 * Productions from the spec.
135
 *
136
 *    authority     = server | reg_name
137
 *    reg_name      = 1*( unreserved | escaped | "$" | "," |
138
 *                        ";" | ":" | "@" | "&" | "=" | "+" )
139
 *
140
 * path          = [ abs_path | opaque_part ]
141
 */
142
143
11
#define STRNDUP(s, n) (char *) xmlStrndup((const xmlChar *)(s), (n))
144
145
/************************************************************************
146
 *                  *
147
 *                         RFC 3986 parser        *
148
 *                  *
149
 ************************************************************************/
150
151
88
#define ISA_DIGIT(p) ((*(p) >= '0') && (*(p) <= '9'))
152
242
#define ISA_ALPHA(p) (((*(p) >= 'a') && (*(p) <= 'z')) ||   \
153
242
                      ((*(p) >= 'A') && (*(p) <= 'Z')))
154
#define ISA_HEXDIG(p)             \
155
0
       (ISA_DIGIT(p) || ((*(p) >= 'a') && (*(p) <= 'f')) ||    \
156
0
        ((*(p) >= 'A') && (*(p) <= 'F')))
157
158
/*
159
 *    sub-delims    = "!" / "$" / "&" / "'" / "(" / ")"
160
 *                     / "*" / "+" / "," / ";" / "="
161
 */
162
#define ISA_SUB_DELIM(p)            \
163
110
      (((*(p) == '!')) || ((*(p) == '$')) || ((*(p) == '&')) ||   \
164
11
       ((*(p) == '(')) || ((*(p) == ')')) || ((*(p) == '*')) ||   \
165
11
       ((*(p) == '+')) || ((*(p) == ',')) || ((*(p) == ';')) ||   \
166
11
       ((*(p) == '=')) || ((*(p) == '\'')))
167
168
/*
169
 *    gen-delims    = ":" / "/" / "?" / "#" / "[" / "]" / "@"
170
 */
171
#define ISA_GEN_DELIM(p)            \
172
      (((*(p) == ':')) || ((*(p) == '/')) || ((*(p) == '?')) ||         \
173
       ((*(p) == '#')) || ((*(p) == '[')) || ((*(p) == ']')) ||         \
174
       ((*(p) == '@')))
175
176
/*
177
 *    reserved      = gen-delims / sub-delims
178
 */
179
#define ISA_RESERVED(p) (ISA_GEN_DELIM(p) || (ISA_SUB_DELIM(p)))
180
181
/*
182
 *    unreserved    = ALPHA / DIGIT / "-" / "." / "_" / "~"
183
 */
184
#define ISA_UNRESERVED(p)           \
185
198
      ((ISA_ALPHA(p)) || (ISA_DIGIT(p)) || ((*(p) == '-')) ||   \
186
99
       ((*(p) == '.')) || ((*(p) == '_')) || ((*(p) == '~')))
187
188
/*
189
 *    pct-encoded   = "%" HEXDIG HEXDIG
190
 */
191
#define ISA_PCT_ENCODED(p)            \
192
110
     ((*(p) == '%') && (ISA_HEXDIG(p + 1)) && (ISA_HEXDIG(p + 2)))
193
194
/*
195
 *    pchar         = unreserved / pct-encoded / sub-delims / ":" / "@"
196
 */
197
#define ISA_PCHAR(p)              \
198
176
     (ISA_UNRESERVED(p) || ISA_PCT_ENCODED(p) || ISA_SUB_DELIM(p) ||  \
199
99
      ((*(p) == ':')) || ((*(p) == '@')))
200
201
/**
202
 * xmlParse3986Scheme:
203
 * @uri:  pointer to an URI structure
204
 * @str:  pointer to the string to analyze
205
 *
206
 * Parse an URI scheme
207
 *
208
 * ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
209
 *
210
 * Returns 0 or the error code
211
 */
212
static int
213
11
xmlParse3986Scheme(xmlURIPtr uri, const char **str) {
214
11
    const char *cur;
215
216
11
    if (str == NULL)
217
0
  return(-1);
218
219
11
    cur = *str;
220
11
    if (!ISA_ALPHA(cur))
221
0
  return(2);
222
11
    cur++;
223
66
    while (ISA_ALPHA(cur) || ISA_DIGIT(cur) ||
224
66
           (*cur == '+') || (*cur == '-') || (*cur == '.')) cur++;
225
11
    if (uri != NULL) {
226
11
  if (uri->scheme != NULL) xmlFree(uri->scheme);
227
11
  uri->scheme = STRNDUP(*str, cur - *str);
228
11
    }
229
11
    *str = cur;
230
11
    return(0);
231
11
}
232
233
/**
234
 * xmlParse3986Fragment:
235
 * @uri:  pointer to an URI structure
236
 * @str:  pointer to the string to analyze
237
 *
238
 * Parse the query part of an URI
239
 *
240
 * fragment      = *( pchar / "/" / "?" )
241
 * NOTE: the strict syntax as defined by 3986 does not allow '[' and ']'
242
 *       in the fragment identifier but this is used very broadly for
243
 *       xpointer scheme selection, so we are allowing it here to not break
244
 *       for example all the DocBook processing chains.
245
 *
246
 * Returns 0 or the error code
247
 */
248
static int
249
xmlParse3986Fragment(xmlURIPtr uri, const char **str)
250
0
{
251
0
    const char *cur;
252
253
0
    if (str == NULL)
254
0
        return (-1);
255
256
0
    cur = *str;
257
258
0
    while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
259
0
           (*cur == '[') || (*cur == ']') ||
260
0
           ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
261
0
        NEXT(cur);
262
0
    if (uri != NULL) {
263
0
        if (uri->fragment != NULL)
264
0
            xmlFree(uri->fragment);
265
0
  if (uri->cleanup & 2)
266
0
      uri->fragment = STRNDUP(*str, cur - *str);
267
0
  else
268
0
      uri->fragment = xmlURIUnescapeString(*str, cur - *str, NULL);
269
0
    }
270
0
    *str = cur;
271
0
    return (0);
272
0
}
273
274
/**
275
 * xmlParse3986Query:
276
 * @uri:  pointer to an URI structure
277
 * @str:  pointer to the string to analyze
278
 *
279
 * Parse the query part of an URI
280
 *
281
 * query = *uric
282
 *
283
 * Returns 0 or the error code
284
 */
285
static int
286
xmlParse3986Query(xmlURIPtr uri, const char **str)
287
0
{
288
0
    const char *cur;
289
290
0
    if (str == NULL)
291
0
        return (-1);
292
293
0
    cur = *str;
294
295
0
    while ((ISA_PCHAR(cur)) || (*cur == '/') || (*cur == '?') ||
296
0
           ((uri != NULL) && (uri->cleanup & 1) && (IS_UNWISE(cur))))
297
0
        NEXT(cur);
298
0
    if (uri != NULL) {
299
0
        if (uri->query != NULL)
300
0
            xmlFree(uri->query);
301
0
  if (uri->cleanup & 2)
302
0
      uri->query = STRNDUP(*str, cur - *str);
303
0
  else
304
0
      uri->query = xmlURIUnescapeString(*str, cur - *str, NULL);
305
306
  /* Save the raw bytes of the query as well.
307
   * See: http://mail.gnome.org/archives/xml/2007-April/thread.html#00114
308
   */
309
0
  if (uri->query_raw != NULL)
310
0
      xmlFree (uri->query_raw);
311
0
  uri->query_raw = STRNDUP (*str, cur - *str);
312
0
    }
313
0
    *str = cur;
314
0
    return (0);
315
0
}
316
317
/**
318
 * xmlParse3986Port:
319
 * @uri:  pointer to an URI structure
320
 * @str:  the string to analyze
321
 *
322
 * Parse a port part and fills in the appropriate fields
323
 * of the @uri structure
324
 *
325
 * port          = *DIGIT
326
 *
327
 * Returns 0 or the error code
328
 */
329
static int
330
xmlParse3986Port(xmlURIPtr uri, const char **str)
331
0
{
332
0
    const char *cur = *str;
333
0
    int port = 0;
334
335
0
    if (ISA_DIGIT(cur)) {
336
0
  while (ISA_DIGIT(cur)) {
337
0
            int digit = *cur - '0';
338
339
0
            if (port > INT_MAX / 10)
340
0
                return(1);
341
0
            port *= 10;
342
0
            if (port > INT_MAX - digit)
343
0
                return(1);
344
0
      port += digit;
345
346
0
      cur++;
347
0
  }
348
0
  if (uri != NULL)
349
0
      uri->port = port;
350
0
  *str = cur;
351
0
  return(0);
352
0
    }
353
0
    return(1);
354
0
}
355
356
/**
357
 * xmlParse3986Userinfo:
358
 * @uri:  pointer to an URI structure
359
 * @str:  the string to analyze
360
 *
361
 * Parse an user information part and fills in the appropriate fields
362
 * of the @uri structure
363
 *
364
 * userinfo      = *( unreserved / pct-encoded / sub-delims / ":" )
365
 *
366
 * Returns 0 or the error code
367
 */
368
static int
369
xmlParse3986Userinfo(xmlURIPtr uri, const char **str)
370
0
{
371
0
    const char *cur;
372
373
0
    cur = *str;
374
0
    while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) ||
375
0
           ISA_SUB_DELIM(cur) || (*cur == ':'))
376
0
  NEXT(cur);
377
0
    if (*cur == '@') {
378
0
  if (uri != NULL) {
379
0
      if (uri->user != NULL) xmlFree(uri->user);
380
0
      if (uri->cleanup & 2)
381
0
    uri->user = STRNDUP(*str, cur - *str);
382
0
      else
383
0
    uri->user = xmlURIUnescapeString(*str, cur - *str, NULL);
384
0
  }
385
0
  *str = cur;
386
0
  return(0);
387
0
    }
388
0
    return(1);
389
0
}
390
391
/**
392
 * xmlParse3986DecOctet:
393
 * @str:  the string to analyze
394
 *
395
 *    dec-octet     = DIGIT                 ; 0-9
396
 *                  / %x31-39 DIGIT         ; 10-99
397
 *                  / "1" 2DIGIT            ; 100-199
398
 *                  / "2" %x30-34 DIGIT     ; 200-249
399
 *                  / "25" %x30-35          ; 250-255
400
 *
401
 * Skip a dec-octet.
402
 *
403
 * Returns 0 if found and skipped, 1 otherwise
404
 */
405
static int
406
0
xmlParse3986DecOctet(const char **str) {
407
0
    const char *cur = *str;
408
409
0
    if (!(ISA_DIGIT(cur)))
410
0
        return(1);
411
0
    if (!ISA_DIGIT(cur+1))
412
0
  cur++;
413
0
    else if ((*cur != '0') && (ISA_DIGIT(cur + 1)) && (!ISA_DIGIT(cur+2)))
414
0
  cur += 2;
415
0
    else if ((*cur == '1') && (ISA_DIGIT(cur + 1)) && (ISA_DIGIT(cur + 2)))
416
0
  cur += 3;
417
0
    else if ((*cur == '2') && (*(cur + 1) >= '0') &&
418
0
       (*(cur + 1) <= '4') && (ISA_DIGIT(cur + 2)))
419
0
  cur += 3;
420
0
    else if ((*cur == '2') && (*(cur + 1) == '5') &&
421
0
       (*(cur + 2) >= '0') && (*(cur + 1) <= '5'))
422
0
  cur += 3;
423
0
    else
424
0
        return(1);
425
0
    *str = cur;
426
0
    return(0);
427
0
}
428
/**
429
 * xmlParse3986Host:
430
 * @uri:  pointer to an URI structure
431
 * @str:  the string to analyze
432
 *
433
 * Parse an host part and fills in the appropriate fields
434
 * of the @uri structure
435
 *
436
 * host          = IP-literal / IPv4address / reg-name
437
 * IP-literal    = "[" ( IPv6address / IPvFuture  ) "]"
438
 * IPv4address   = dec-octet "." dec-octet "." dec-octet "." dec-octet
439
 * reg-name      = *( unreserved / pct-encoded / sub-delims )
440
 *
441
 * Returns 0 or the error code
442
 */
443
static int
444
xmlParse3986Host(xmlURIPtr uri, const char **str)
445
0
{
446
0
    const char *cur = *str;
447
0
    const char *host;
448
449
0
    host = cur;
450
    /*
451
     * IPv6 and future addressing scheme are enclosed between brackets
452
     */
453
0
    if (*cur == '[') {
454
0
        cur++;
455
0
  while ((*cur != ']') && (*cur != 0))
456
0
      cur++;
457
0
  if (*cur != ']')
458
0
      return(1);
459
0
  cur++;
460
0
  goto found;
461
0
    }
462
    /*
463
     * try to parse an IPv4
464
     */
465
0
    if (ISA_DIGIT(cur)) {
466
0
        if (xmlParse3986DecOctet(&cur) != 0)
467
0
      goto not_ipv4;
468
0
  if (*cur != '.')
469
0
      goto not_ipv4;
470
0
  cur++;
471
0
        if (xmlParse3986DecOctet(&cur) != 0)
472
0
      goto not_ipv4;
473
0
  if (*cur != '.')
474
0
      goto not_ipv4;
475
0
        if (xmlParse3986DecOctet(&cur) != 0)
476
0
      goto not_ipv4;
477
0
  if (*cur != '.')
478
0
      goto not_ipv4;
479
0
        if (xmlParse3986DecOctet(&cur) != 0)
480
0
      goto not_ipv4;
481
0
  goto found;
482
0
not_ipv4:
483
0
        cur = *str;
484
0
    }
485
    /*
486
     * then this should be a hostname which can be empty
487
     */
488
0
    while (ISA_UNRESERVED(cur) || ISA_PCT_ENCODED(cur) || ISA_SUB_DELIM(cur))
489
0
        NEXT(cur);
490
0
found:
491
0
    if (uri != NULL) {
492
0
  if (uri->authority != NULL) xmlFree(uri->authority);
493
0
  uri->authority = NULL;
494
0
  if (uri->server != NULL) xmlFree(uri->server);
495
0
  if (cur != host) {
496
0
      if (uri->cleanup & 2)
497
0
    uri->server = STRNDUP(host, cur - host);
498
0
      else
499
0
    uri->server = xmlURIUnescapeString(host, cur - host, NULL);
500
0
  } else
501
0
      uri->server = NULL;
502
0
    }
503
0
    *str = cur;
504
0
    return(0);
505
0
}
506
507
/**
508
 * xmlParse3986Authority:
509
 * @uri:  pointer to an URI structure
510
 * @str:  the string to analyze
511
 *
512
 * Parse an authority part and fills in the appropriate fields
513
 * of the @uri structure
514
 *
515
 * authority     = [ userinfo "@" ] host [ ":" port ]
516
 *
517
 * Returns 0 or the error code
518
 */
519
static int
520
xmlParse3986Authority(xmlURIPtr uri, const char **str)
521
0
{
522
0
    const char *cur;
523
0
    int ret;
524
525
0
    cur = *str;
526
    /*
527
     * try to parse an userinfo and check for the trailing @
528
     */
529
0
    ret = xmlParse3986Userinfo(uri, &cur);
530
0
    if ((ret != 0) || (*cur != '@'))
531
0
        cur = *str;
532
0
    else
533
0
        cur++;
534
0
    ret = xmlParse3986Host(uri, &cur);
535
0
    if (ret != 0) return(ret);
536
0
    if (*cur == ':') {
537
0
        cur++;
538
0
        ret = xmlParse3986Port(uri, &cur);
539
0
  if (ret != 0) return(ret);
540
0
    }
541
0
    *str = cur;
542
0
    return(0);
543
0
}
544
545
/**
546
 * xmlParse3986Segment:
547
 * @str:  the string to analyze
548
 * @forbid: an optional forbidden character
549
 * @empty: allow an empty segment
550
 *
551
 * Parse a segment and fills in the appropriate fields
552
 * of the @uri structure
553
 *
554
 * segment       = *pchar
555
 * segment-nz    = 1*pchar
556
 * segment-nz-nc = 1*( unreserved / pct-encoded / sub-delims / "@" )
557
 *               ; non-zero-length segment without any colon ":"
558
 *
559
 * Returns 0 or the error code
560
 */
561
static int
562
xmlParse3986Segment(const char **str, char forbid, int empty)
563
11
{
564
11
    const char *cur;
565
566
11
    cur = *str;
567
11
    if (!ISA_PCHAR(cur)) {
568
0
        if (empty)
569
0
      return(0);
570
0
  return(1);
571
0
    }
572
77
    while (ISA_PCHAR(cur) && (*cur != forbid))
573
66
        NEXT(cur);
574
11
    *str = cur;
575
11
    return (0);
576
11
}
577
578
/**
579
 * xmlParse3986PathAbEmpty:
580
 * @uri:  pointer to an URI structure
581
 * @str:  the string to analyze
582
 *
583
 * Parse an path absolute or empty and fills in the appropriate fields
584
 * of the @uri structure
585
 *
586
 * path-abempty  = *( "/" segment )
587
 *
588
 * Returns 0 or the error code
589
 */
590
static int
591
xmlParse3986PathAbEmpty(xmlURIPtr uri, const char **str)
592
0
{
593
0
    const char *cur;
594
0
    int ret;
595
596
0
    cur = *str;
597
598
0
    while (*cur == '/') {
599
0
        cur++;
600
0
  ret = xmlParse3986Segment(&cur, 0, 1);
601
0
  if (ret != 0) return(ret);
602
0
    }
603
0
    if (uri != NULL) {
604
0
  if (uri->path != NULL) xmlFree(uri->path);
605
0
        if (*str != cur) {
606
0
            if (uri->cleanup & 2)
607
0
                uri->path = STRNDUP(*str, cur - *str);
608
0
            else
609
0
                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
610
0
        } else {
611
0
            uri->path = NULL;
612
0
        }
613
0
    }
614
0
    *str = cur;
615
0
    return (0);
616
0
}
617
618
/**
619
 * xmlParse3986PathAbsolute:
620
 * @uri:  pointer to an URI structure
621
 * @str:  the string to analyze
622
 *
623
 * Parse an path absolute and fills in the appropriate fields
624
 * of the @uri structure
625
 *
626
 * path-absolute = "/" [ segment-nz *( "/" segment ) ]
627
 *
628
 * Returns 0 or the error code
629
 */
630
static int
631
xmlParse3986PathAbsolute(xmlURIPtr uri, const char **str)
632
0
{
633
0
    const char *cur;
634
0
    int ret;
635
636
0
    cur = *str;
637
638
0
    if (*cur != '/')
639
0
        return(1);
640
0
    cur++;
641
0
    ret = xmlParse3986Segment(&cur, 0, 0);
642
0
    if (ret == 0) {
643
0
  while (*cur == '/') {
644
0
      cur++;
645
0
      ret = xmlParse3986Segment(&cur, 0, 1);
646
0
      if (ret != 0) return(ret);
647
0
  }
648
0
    }
649
0
    if (uri != NULL) {
650
0
  if (uri->path != NULL) xmlFree(uri->path);
651
0
        if (cur != *str) {
652
0
            if (uri->cleanup & 2)
653
0
                uri->path = STRNDUP(*str, cur - *str);
654
0
            else
655
0
                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
656
0
        } else {
657
0
            uri->path = NULL;
658
0
        }
659
0
    }
660
0
    *str = cur;
661
0
    return (0);
662
0
}
663
664
/**
665
 * xmlParse3986PathRootless:
666
 * @uri:  pointer to an URI structure
667
 * @str:  the string to analyze
668
 *
669
 * Parse an path without root and fills in the appropriate fields
670
 * of the @uri structure
671
 *
672
 * path-rootless = segment-nz *( "/" segment )
673
 *
674
 * Returns 0 or the error code
675
 */
676
static int
677
xmlParse3986PathRootless(xmlURIPtr uri, const char **str)
678
0
{
679
0
    const char *cur;
680
0
    int ret;
681
682
0
    cur = *str;
683
684
0
    ret = xmlParse3986Segment(&cur, 0, 0);
685
0
    if (ret != 0) return(ret);
686
0
    while (*cur == '/') {
687
0
        cur++;
688
0
  ret = xmlParse3986Segment(&cur, 0, 1);
689
0
  if (ret != 0) return(ret);
690
0
    }
691
0
    if (uri != NULL) {
692
0
  if (uri->path != NULL) xmlFree(uri->path);
693
0
        if (cur != *str) {
694
0
            if (uri->cleanup & 2)
695
0
                uri->path = STRNDUP(*str, cur - *str);
696
0
            else
697
0
                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
698
0
        } else {
699
0
            uri->path = NULL;
700
0
        }
701
0
    }
702
0
    *str = cur;
703
0
    return (0);
704
0
}
705
706
/**
707
 * xmlParse3986PathNoScheme:
708
 * @uri:  pointer to an URI structure
709
 * @str:  the string to analyze
710
 *
711
 * Parse an path which is not a scheme and fills in the appropriate fields
712
 * of the @uri structure
713
 *
714
 * path-noscheme = segment-nz-nc *( "/" segment )
715
 *
716
 * Returns 0 or the error code
717
 */
718
static int
719
xmlParse3986PathNoScheme(xmlURIPtr uri, const char **str)
720
11
{
721
11
    const char *cur;
722
11
    int ret;
723
724
11
    cur = *str;
725
726
11
    ret = xmlParse3986Segment(&cur, ':', 0);
727
11
    if (ret != 0) return(ret);
728
11
    while (*cur == '/') {
729
0
        cur++;
730
0
  ret = xmlParse3986Segment(&cur, 0, 1);
731
0
  if (ret != 0) return(ret);
732
0
    }
733
11
    if (uri != NULL) {
734
11
  if (uri->path != NULL) xmlFree(uri->path);
735
11
        if (cur != *str) {
736
11
            if (uri->cleanup & 2)
737
0
                uri->path = STRNDUP(*str, cur - *str);
738
11
            else
739
11
                uri->path = xmlURIUnescapeString(*str, cur - *str, NULL);
740
11
        } else {
741
0
            uri->path = NULL;
742
0
        }
743
11
    }
744
11
    *str = cur;
745
11
    return (0);
746
11
}
747
748
/**
749
 * xmlParse3986HierPart:
750
 * @uri:  pointer to an URI structure
751
 * @str:  the string to analyze
752
 *
753
 * Parse an hierarchical part and fills in the appropriate fields
754
 * of the @uri structure
755
 *
756
 * hier-part     = "//" authority path-abempty
757
 *                / path-absolute
758
 *                / path-rootless
759
 *                / path-empty
760
 *
761
 * Returns 0 or the error code
762
 */
763
static int
764
xmlParse3986HierPart(xmlURIPtr uri, const char **str)
765
0
{
766
0
    const char *cur;
767
0
    int ret;
768
769
0
    cur = *str;
770
771
0
    if ((*cur == '/') && (*(cur + 1) == '/')) {
772
0
        cur += 2;
773
0
  ret = xmlParse3986Authority(uri, &cur);
774
0
  if (ret != 0) return(ret);
775
        /*
776
         * An empty server is marked with a special URI value.
777
         */
778
0
  if ((uri->server == NULL) && (uri->port == PORT_EMPTY))
779
0
      uri->port = PORT_EMPTY_SERVER;
780
0
  ret = xmlParse3986PathAbEmpty(uri, &cur);
781
0
  if (ret != 0) return(ret);
782
0
  *str = cur;
783
0
  return(0);
784
0
    } else if (*cur == '/') {
785
0
        ret = xmlParse3986PathAbsolute(uri, &cur);
786
0
  if (ret != 0) return(ret);
787
0
    } else if (ISA_PCHAR(cur)) {
788
0
        ret = xmlParse3986PathRootless(uri, &cur);
789
0
  if (ret != 0) return(ret);
790
0
    } else {
791
  /* path-empty is effectively empty */
792
0
  if (uri != NULL) {
793
0
      if (uri->path != NULL) xmlFree(uri->path);
794
0
      uri->path = NULL;
795
0
  }
796
0
    }
797
0
    *str = cur;
798
0
    return (0);
799
0
}
800
801
/**
802
 * xmlParse3986RelativeRef:
803
 * @uri:  pointer to an URI structure
804
 * @str:  the string to analyze
805
 *
806
 * Parse an URI string and fills in the appropriate fields
807
 * of the @uri structure
808
 *
809
 * relative-ref  = relative-part [ "?" query ] [ "#" fragment ]
810
 * relative-part = "//" authority path-abempty
811
 *               / path-absolute
812
 *               / path-noscheme
813
 *               / path-empty
814
 *
815
 * Returns 0 or the error code
816
 */
817
static int
818
11
xmlParse3986RelativeRef(xmlURIPtr uri, const char *str) {
819
11
    int ret;
820
821
11
    if ((*str == '/') && (*(str + 1) == '/')) {
822
0
        str += 2;
823
0
  ret = xmlParse3986Authority(uri, &str);
824
0
  if (ret != 0) return(ret);
825
0
  ret = xmlParse3986PathAbEmpty(uri, &str);
826
0
  if (ret != 0) return(ret);
827
11
    } else if (*str == '/') {
828
0
  ret = xmlParse3986PathAbsolute(uri, &str);
829
0
  if (ret != 0) return(ret);
830
11
    } else if (ISA_PCHAR(str)) {
831
11
        ret = xmlParse3986PathNoScheme(uri, &str);
832
11
  if (ret != 0) return(ret);
833
11
    } else {
834
  /* path-empty is effectively empty */
835
0
  if (uri != NULL) {
836
0
      if (uri->path != NULL) xmlFree(uri->path);
837
0
      uri->path = NULL;
838
0
  }
839
0
    }
840
841
11
    if (*str == '?') {
842
0
  str++;
843
0
  ret = xmlParse3986Query(uri, &str);
844
0
  if (ret != 0) return(ret);
845
0
    }
846
11
    if (*str == '#') {
847
0
  str++;
848
0
  ret = xmlParse3986Fragment(uri, &str);
849
0
  if (ret != 0) return(ret);
850
0
    }
851
11
    if (*str != 0) {
852
0
  xmlCleanURI(uri);
853
0
  return(1);
854
0
    }
855
11
    return(0);
856
11
}
857
858
859
/**
860
 * xmlParse3986URI:
861
 * @uri:  pointer to an URI structure
862
 * @str:  the string to analyze
863
 *
864
 * Parse an URI string and fills in the appropriate fields
865
 * of the @uri structure
866
 *
867
 * scheme ":" hier-part [ "?" query ] [ "#" fragment ]
868
 *
869
 * Returns 0 or the error code
870
 */
871
static int
872
11
xmlParse3986URI(xmlURIPtr uri, const char *str) {
873
11
    int ret;
874
875
11
    ret = xmlParse3986Scheme(uri, &str);
876
11
    if (ret != 0) return(ret);
877
11
    if (*str != ':') {
878
11
  return(1);
879
11
    }
880
0
    str++;
881
0
    ret = xmlParse3986HierPart(uri, &str);
882
0
    if (ret != 0) return(ret);
883
0
    if (*str == '?') {
884
0
  str++;
885
0
  ret = xmlParse3986Query(uri, &str);
886
0
  if (ret != 0) return(ret);
887
0
    }
888
0
    if (*str == '#') {
889
0
  str++;
890
0
  ret = xmlParse3986Fragment(uri, &str);
891
0
  if (ret != 0) return(ret);
892
0
    }
893
0
    if (*str != 0) {
894
0
  xmlCleanURI(uri);
895
0
  return(1);
896
0
    }
897
0
    return(0);
898
0
}
899
900
/**
901
 * xmlParse3986URIReference:
902
 * @uri:  pointer to an URI structure
903
 * @str:  the string to analyze
904
 *
905
 * Parse an URI reference string and fills in the appropriate fields
906
 * of the @uri structure
907
 *
908
 * URI-reference = URI / relative-ref
909
 *
910
 * Returns 0 or the error code
911
 */
912
static int
913
11
xmlParse3986URIReference(xmlURIPtr uri, const char *str) {
914
11
    int ret;
915
916
11
    if (str == NULL)
917
0
  return(-1);
918
11
    xmlCleanURI(uri);
919
920
    /*
921
     * Try first to parse absolute refs, then fallback to relative if
922
     * it fails.
923
     */
924
11
    ret = xmlParse3986URI(uri, str);
925
11
    if (ret != 0) {
926
11
  xmlCleanURI(uri);
927
11
        ret = xmlParse3986RelativeRef(uri, str);
928
11
  if (ret != 0) {
929
0
      xmlCleanURI(uri);
930
0
      return(ret);
931
0
  }
932
11
    }
933
11
    return(0);
934
11
}
935
936
/**
937
 * xmlParseURI:
938
 * @str:  the URI string to analyze
939
 *
940
 * Parse an URI based on RFC 3986
941
 *
942
 * URI-reference = [ absoluteURI | relativeURI ] [ "#" fragment ]
943
 *
944
 * Returns a newly built xmlURIPtr or NULL in case of error
945
 */
946
xmlURIPtr
947
11
xmlParseURI(const char *str) {
948
11
    xmlURIPtr uri;
949
11
    int ret;
950
951
11
    if (str == NULL)
952
0
  return(NULL);
953
11
    uri = xmlCreateURI();
954
11
    if (uri != NULL) {
955
11
  ret = xmlParse3986URIReference(uri, str);
956
11
        if (ret) {
957
0
      xmlFreeURI(uri);
958
0
      return(NULL);
959
0
  }
960
11
    }
961
11
    return(uri);
962
11
}
963
964
/**
965
 * xmlParseURIReference:
966
 * @uri:  pointer to an URI structure
967
 * @str:  the string to analyze
968
 *
969
 * Parse an URI reference string based on RFC 3986 and fills in the
970
 * appropriate fields of the @uri structure
971
 *
972
 * URI-reference = URI / relative-ref
973
 *
974
 * Returns 0 or the error code
975
 */
976
int
977
0
xmlParseURIReference(xmlURIPtr uri, const char *str) {
978
0
    return(xmlParse3986URIReference(uri, str));
979
0
}
980
981
/**
982
 * xmlParseURIRaw:
983
 * @str:  the URI string to analyze
984
 * @raw:  if 1 unescaping of URI pieces are disabled
985
 *
986
 * Parse an URI but allows to keep intact the original fragments.
987
 *
988
 * URI-reference = URI / relative-ref
989
 *
990
 * Returns a newly built xmlURIPtr or NULL in case of error
991
 */
992
xmlURIPtr
993
0
xmlParseURIRaw(const char *str, int raw) {
994
0
    xmlURIPtr uri;
995
0
    int ret;
996
997
0
    if (str == NULL)
998
0
  return(NULL);
999
0
    uri = xmlCreateURI();
1000
0
    if (uri != NULL) {
1001
0
        if (raw) {
1002
0
      uri->cleanup |= 2;
1003
0
  }
1004
0
  ret = xmlParseURIReference(uri, str);
1005
0
        if (ret) {
1006
0
      xmlFreeURI(uri);
1007
0
      return(NULL);
1008
0
  }
1009
0
    }
1010
0
    return(uri);
1011
0
}
1012
1013
/************************************************************************
1014
 *                  *
1015
 *      Generic URI structure functions     *
1016
 *                  *
1017
 ************************************************************************/
1018
1019
/**
1020
 * xmlCreateURI:
1021
 *
1022
 * Simply creates an empty xmlURI
1023
 *
1024
 * Returns the new structure or NULL in case of error
1025
 */
1026
xmlURIPtr
1027
11
xmlCreateURI(void) {
1028
11
    xmlURIPtr ret;
1029
1030
11
    ret = (xmlURIPtr) xmlMalloc(sizeof(xmlURI));
1031
11
    if (ret == NULL) {
1032
0
        xmlURIErrMemory("creating URI structure\n");
1033
0
  return(NULL);
1034
0
    }
1035
11
    memset(ret, 0, sizeof(xmlURI));
1036
11
    ret->port = PORT_EMPTY;
1037
11
    return(ret);
1038
11
}
1039
1040
/**
1041
 * xmlSaveUriRealloc:
1042
 *
1043
 * Function to handle properly a reallocation when saving an URI
1044
 * Also imposes some limit on the length of an URI string output
1045
 */
1046
static xmlChar *
1047
0
xmlSaveUriRealloc(xmlChar *ret, int *max) {
1048
0
    xmlChar *temp;
1049
0
    int tmp;
1050
1051
0
    if (*max > MAX_URI_LENGTH) {
1052
0
        xmlURIErrMemory("reaching arbitrary MAX_URI_LENGTH limit\n");
1053
0
        return(NULL);
1054
0
    }
1055
0
    tmp = *max * 2;
1056
0
    temp = (xmlChar *) xmlRealloc(ret, (tmp + 1));
1057
0
    if (temp == NULL) {
1058
0
        xmlURIErrMemory("saving URI\n");
1059
0
        return(NULL);
1060
0
    }
1061
0
    *max = tmp;
1062
0
    return(temp);
1063
0
}
1064
1065
/**
1066
 * xmlSaveUri:
1067
 * @uri:  pointer to an xmlURI
1068
 *
1069
 * Save the URI as an escaped string
1070
 *
1071
 * Returns a new string (to be deallocated by caller)
1072
 */
1073
xmlChar *
1074
0
xmlSaveUri(xmlURIPtr uri) {
1075
0
    xmlChar *ret = NULL;
1076
0
    xmlChar *temp;
1077
0
    const char *p;
1078
0
    int len;
1079
0
    int max;
1080
1081
0
    if (uri == NULL) return(NULL);
1082
1083
1084
0
    max = 80;
1085
0
    ret = (xmlChar *) xmlMallocAtomic(max + 1);
1086
0
    if (ret == NULL) {
1087
0
        xmlURIErrMemory("saving URI\n");
1088
0
  return(NULL);
1089
0
    }
1090
0
    len = 0;
1091
1092
0
    if (uri->scheme != NULL) {
1093
0
  p = uri->scheme;
1094
0
  while (*p != 0) {
1095
0
      if (len >= max) {
1096
0
                temp = xmlSaveUriRealloc(ret, &max);
1097
0
                if (temp == NULL) goto mem_error;
1098
0
    ret = temp;
1099
0
      }
1100
0
      ret[len++] = *p++;
1101
0
  }
1102
0
  if (len >= max) {
1103
0
            temp = xmlSaveUriRealloc(ret, &max);
1104
0
            if (temp == NULL) goto mem_error;
1105
0
            ret = temp;
1106
0
  }
1107
0
  ret[len++] = ':';
1108
0
    }
1109
0
    if (uri->opaque != NULL) {
1110
0
  p = uri->opaque;
1111
0
  while (*p != 0) {
1112
0
      if (len + 3 >= max) {
1113
0
                temp = xmlSaveUriRealloc(ret, &max);
1114
0
                if (temp == NULL) goto mem_error;
1115
0
                ret = temp;
1116
0
      }
1117
0
      if (IS_RESERVED(*(p)) || IS_UNRESERVED(*(p)))
1118
0
    ret[len++] = *p++;
1119
0
      else {
1120
0
    int val = *(unsigned char *)p++;
1121
0
    int hi = val / 0x10, lo = val % 0x10;
1122
0
    ret[len++] = '%';
1123
0
    ret[len++] = hi + (hi > 9? 'A'-10 : '0');
1124
0
    ret[len++] = lo + (lo > 9? 'A'-10 : '0');
1125
0
      }
1126
0
  }
1127
0
    } else {
1128
0
  if ((uri->server != NULL) || (uri->port != PORT_EMPTY)) {
1129
0
      if (len + 3 >= max) {
1130
0
                temp = xmlSaveUriRealloc(ret, &max);
1131
0
                if (temp == NULL) goto mem_error;
1132
0
                ret = temp;
1133
0
      }
1134
0
      ret[len++] = '/';
1135
0
      ret[len++] = '/';
1136
0
      if (uri->user != NULL) {
1137
0
    p = uri->user;
1138
0
    while (*p != 0) {
1139
0
        if (len + 3 >= max) {
1140
0
                        temp = xmlSaveUriRealloc(ret, &max);
1141
0
                        if (temp == NULL) goto mem_error;
1142
0
                        ret = temp;
1143
0
        }
1144
0
        if ((IS_UNRESERVED(*(p))) ||
1145
0
      ((*(p) == ';')) || ((*(p) == ':')) ||
1146
0
      ((*(p) == '&')) || ((*(p) == '=')) ||
1147
0
      ((*(p) == '+')) || ((*(p) == '$')) ||
1148
0
      ((*(p) == ',')))
1149
0
      ret[len++] = *p++;
1150
0
        else {
1151
0
      int val = *(unsigned char *)p++;
1152
0
      int hi = val / 0x10, lo = val % 0x10;
1153
0
      ret[len++] = '%';
1154
0
      ret[len++] = hi + (hi > 9? 'A'-10 : '0');
1155
0
      ret[len++] = lo + (lo > 9? 'A'-10 : '0');
1156
0
        }
1157
0
    }
1158
0
    if (len + 3 >= max) {
1159
0
                    temp = xmlSaveUriRealloc(ret, &max);
1160
0
                    if (temp == NULL) goto mem_error;
1161
0
                    ret = temp;
1162
0
    }
1163
0
    ret[len++] = '@';
1164
0
      }
1165
0
      if (uri->server != NULL) {
1166
0
    p = uri->server;
1167
0
    while (*p != 0) {
1168
0
        if (len >= max) {
1169
0
      temp = xmlSaveUriRealloc(ret, &max);
1170
0
      if (temp == NULL) goto mem_error;
1171
0
      ret = temp;
1172
0
        }
1173
                    /* TODO: escaping? */
1174
0
        ret[len++] = (xmlChar) *p++;
1175
0
    }
1176
0
      }
1177
0
            if (uri->port > 0) {
1178
0
                if (len + 10 >= max) {
1179
0
                    temp = xmlSaveUriRealloc(ret, &max);
1180
0
                    if (temp == NULL) goto mem_error;
1181
0
                    ret = temp;
1182
0
                }
1183
0
                len += snprintf((char *) &ret[len], max - len, ":%d", uri->port);
1184
0
            }
1185
0
  } else if (uri->authority != NULL) {
1186
0
      if (len + 3 >= max) {
1187
0
                temp = xmlSaveUriRealloc(ret, &max);
1188
0
                if (temp == NULL) goto mem_error;
1189
0
                ret = temp;
1190
0
      }
1191
0
      ret[len++] = '/';
1192
0
      ret[len++] = '/';
1193
0
      p = uri->authority;
1194
0
      while (*p != 0) {
1195
0
    if (len + 3 >= max) {
1196
0
                    temp = xmlSaveUriRealloc(ret, &max);
1197
0
                    if (temp == NULL) goto mem_error;
1198
0
                    ret = temp;
1199
0
    }
1200
0
    if ((IS_UNRESERVED(*(p))) ||
1201
0
                    ((*(p) == '$')) || ((*(p) == ',')) || ((*(p) == ';')) ||
1202
0
                    ((*(p) == ':')) || ((*(p) == '@')) || ((*(p) == '&')) ||
1203
0
                    ((*(p) == '=')) || ((*(p) == '+')))
1204
0
        ret[len++] = *p++;
1205
0
    else {
1206
0
        int val = *(unsigned char *)p++;
1207
0
        int hi = val / 0x10, lo = val % 0x10;
1208
0
        ret[len++] = '%';
1209
0
        ret[len++] = hi + (hi > 9? 'A'-10 : '0');
1210
0
        ret[len++] = lo + (lo > 9? 'A'-10 : '0');
1211
0
    }
1212
0
      }
1213
0
  } else if (uri->scheme != NULL) {
1214
0
      if (len + 3 >= max) {
1215
0
                temp = xmlSaveUriRealloc(ret, &max);
1216
0
                if (temp == NULL) goto mem_error;
1217
0
                ret = temp;
1218
0
      }
1219
0
  }
1220
0
  if (uri->path != NULL) {
1221
0
      p = uri->path;
1222
      /*
1223
       * the colon in file:///d: should not be escaped or
1224
       * Windows accesses fail later.
1225
       */
1226
0
      if ((uri->scheme != NULL) &&
1227
0
    (p[0] == '/') &&
1228
0
    (((p[1] >= 'a') && (p[1] <= 'z')) ||
1229
0
     ((p[1] >= 'A') && (p[1] <= 'Z'))) &&
1230
0
    (p[2] == ':') &&
1231
0
          (xmlStrEqual(BAD_CAST uri->scheme, BAD_CAST "file"))) {
1232
0
    if (len + 3 >= max) {
1233
0
                    temp = xmlSaveUriRealloc(ret, &max);
1234
0
                    if (temp == NULL) goto mem_error;
1235
0
                    ret = temp;
1236
0
    }
1237
0
    ret[len++] = *p++;
1238
0
    ret[len++] = *p++;
1239
0
    ret[len++] = *p++;
1240
0
      }
1241
0
      while (*p != 0) {
1242
0
    if (len + 3 >= max) {
1243
0
                    temp = xmlSaveUriRealloc(ret, &max);
1244
0
                    if (temp == NULL) goto mem_error;
1245
0
                    ret = temp;
1246
0
    }
1247
0
    if ((IS_UNRESERVED(*(p))) || ((*(p) == '/')) ||
1248
0
                    ((*(p) == ';')) || ((*(p) == '@')) || ((*(p) == '&')) ||
1249
0
              ((*(p) == '=')) || ((*(p) == '+')) || ((*(p) == '$')) ||
1250
0
              ((*(p) == ',')))
1251
0
        ret[len++] = *p++;
1252
0
    else {
1253
0
        int val = *(unsigned char *)p++;
1254
0
        int hi = val / 0x10, lo = val % 0x10;
1255
0
        ret[len++] = '%';
1256
0
        ret[len++] = hi + (hi > 9? 'A'-10 : '0');
1257
0
        ret[len++] = lo + (lo > 9? 'A'-10 : '0');
1258
0
    }
1259
0
      }
1260
0
  }
1261
0
  if (uri->query_raw != NULL) {
1262
0
      if (len + 1 >= max) {
1263
0
                temp = xmlSaveUriRealloc(ret, &max);
1264
0
                if (temp == NULL) goto mem_error;
1265
0
                ret = temp;
1266
0
      }
1267
0
      ret[len++] = '?';
1268
0
      p = uri->query_raw;
1269
0
      while (*p != 0) {
1270
0
    if (len + 1 >= max) {
1271
0
                    temp = xmlSaveUriRealloc(ret, &max);
1272
0
                    if (temp == NULL) goto mem_error;
1273
0
                    ret = temp;
1274
0
    }
1275
0
    ret[len++] = *p++;
1276
0
      }
1277
0
  } else if (uri->query != NULL) {
1278
0
      if (len + 3 >= max) {
1279
0
                temp = xmlSaveUriRealloc(ret, &max);
1280
0
                if (temp == NULL) goto mem_error;
1281
0
                ret = temp;
1282
0
      }
1283
0
      ret[len++] = '?';
1284
0
      p = uri->query;
1285
0
      while (*p != 0) {
1286
0
    if (len + 3 >= max) {
1287
0
                    temp = xmlSaveUriRealloc(ret, &max);
1288
0
                    if (temp == NULL) goto mem_error;
1289
0
                    ret = temp;
1290
0
    }
1291
0
    if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p))))
1292
0
        ret[len++] = *p++;
1293
0
    else {
1294
0
        int val = *(unsigned char *)p++;
1295
0
        int hi = val / 0x10, lo = val % 0x10;
1296
0
        ret[len++] = '%';
1297
0
        ret[len++] = hi + (hi > 9? 'A'-10 : '0');
1298
0
        ret[len++] = lo + (lo > 9? 'A'-10 : '0');
1299
0
    }
1300
0
      }
1301
0
  }
1302
0
    }
1303
0
    if (uri->fragment != NULL) {
1304
0
  if (len + 3 >= max) {
1305
0
            temp = xmlSaveUriRealloc(ret, &max);
1306
0
            if (temp == NULL) goto mem_error;
1307
0
            ret = temp;
1308
0
  }
1309
0
  ret[len++] = '#';
1310
0
  p = uri->fragment;
1311
0
  while (*p != 0) {
1312
0
      if (len + 3 >= max) {
1313
0
                temp = xmlSaveUriRealloc(ret, &max);
1314
0
                if (temp == NULL) goto mem_error;
1315
0
                ret = temp;
1316
0
      }
1317
0
      if ((IS_UNRESERVED(*(p))) || (IS_RESERVED(*(p))))
1318
0
    ret[len++] = *p++;
1319
0
      else {
1320
0
    int val = *(unsigned char *)p++;
1321
0
    int hi = val / 0x10, lo = val % 0x10;
1322
0
    ret[len++] = '%';
1323
0
    ret[len++] = hi + (hi > 9? 'A'-10 : '0');
1324
0
    ret[len++] = lo + (lo > 9? 'A'-10 : '0');
1325
0
      }
1326
0
  }
1327
0
    }
1328
0
    if (len >= max) {
1329
0
        temp = xmlSaveUriRealloc(ret, &max);
1330
0
        if (temp == NULL) goto mem_error;
1331
0
        ret = temp;
1332
0
    }
1333
0
    ret[len] = 0;
1334
0
    return(ret);
1335
1336
0
mem_error:
1337
0
    xmlFree(ret);
1338
0
    return(NULL);
1339
0
}
1340
1341
/**
1342
 * xmlPrintURI:
1343
 * @stream:  a FILE* for the output
1344
 * @uri:  pointer to an xmlURI
1345
 *
1346
 * Prints the URI in the stream @stream.
1347
 */
1348
void
1349
0
xmlPrintURI(FILE *stream, xmlURIPtr uri) {
1350
0
    xmlChar *out;
1351
1352
0
    out = xmlSaveUri(uri);
1353
0
    if (out != NULL) {
1354
0
  fprintf(stream, "%s", (char *) out);
1355
0
  xmlFree(out);
1356
0
    }
1357
0
}
1358
1359
/**
1360
 * xmlCleanURI:
1361
 * @uri:  pointer to an xmlURI
1362
 *
1363
 * Make sure the xmlURI struct is free of content
1364
 */
1365
static void
1366
22
xmlCleanURI(xmlURIPtr uri) {
1367
22
    if (uri == NULL) return;
1368
1369
22
    if (uri->scheme != NULL) xmlFree(uri->scheme);
1370
22
    uri->scheme = NULL;
1371
22
    if (uri->server != NULL) xmlFree(uri->server);
1372
22
    uri->server = NULL;
1373
22
    if (uri->user != NULL) xmlFree(uri->user);
1374
22
    uri->user = NULL;
1375
22
    if (uri->path != NULL) xmlFree(uri->path);
1376
22
    uri->path = NULL;
1377
22
    if (uri->fragment != NULL) xmlFree(uri->fragment);
1378
22
    uri->fragment = NULL;
1379
22
    if (uri->opaque != NULL) xmlFree(uri->opaque);
1380
22
    uri->opaque = NULL;
1381
22
    if (uri->authority != NULL) xmlFree(uri->authority);
1382
22
    uri->authority = NULL;
1383
22
    if (uri->query != NULL) xmlFree(uri->query);
1384
22
    uri->query = NULL;
1385
22
    if (uri->query_raw != NULL) xmlFree(uri->query_raw);
1386
22
    uri->query_raw = NULL;
1387
22
}
1388
1389
/**
1390
 * xmlFreeURI:
1391
 * @uri:  pointer to an xmlURI
1392
 *
1393
 * Free up the xmlURI struct
1394
 */
1395
void
1396
11
xmlFreeURI(xmlURIPtr uri) {
1397
11
    if (uri == NULL) return;
1398
1399
11
    if (uri->scheme != NULL) xmlFree(uri->scheme);
1400
11
    if (uri->server != NULL) xmlFree(uri->server);
1401
11
    if (uri->user != NULL) xmlFree(uri->user);
1402
11
    if (uri->path != NULL) xmlFree(uri->path);
1403
11
    if (uri->fragment != NULL) xmlFree(uri->fragment);
1404
11
    if (uri->opaque != NULL) xmlFree(uri->opaque);
1405
11
    if (uri->authority != NULL) xmlFree(uri->authority);
1406
11
    if (uri->query != NULL) xmlFree(uri->query);
1407
11
    if (uri->query_raw != NULL) xmlFree(uri->query_raw);
1408
11
    xmlFree(uri);
1409
11
}
1410
1411
/************************************************************************
1412
 *                  *
1413
 *      Helper functions        *
1414
 *                  *
1415
 ************************************************************************/
1416
1417
/**
1418
 * xmlNormalizeURIPath:
1419
 * @path:  pointer to the path string
1420
 *
1421
 * Applies the 5 normalization steps to a path string--that is, RFC 2396
1422
 * Section 5.2, steps 6.c through 6.g.
1423
 *
1424
 * Normalization occurs directly on the string, no new allocation is done
1425
 *
1426
 * Returns 0 or an error code
1427
 */
1428
int
1429
0
xmlNormalizeURIPath(char *path) {
1430
0
    char *cur, *out;
1431
1432
0
    if (path == NULL)
1433
0
  return(-1);
1434
1435
    /* Skip all initial "/" chars.  We want to get to the beginning of the
1436
     * first non-empty segment.
1437
     */
1438
0
    cur = path;
1439
0
    while (cur[0] == '/')
1440
0
      ++cur;
1441
0
    if (cur[0] == '\0')
1442
0
      return(0);
1443
1444
    /* Keep everything we've seen so far.  */
1445
0
    out = cur;
1446
1447
    /*
1448
     * Analyze each segment in sequence for cases (c) and (d).
1449
     */
1450
0
    while (cur[0] != '\0') {
1451
  /*
1452
   * c) All occurrences of "./", where "." is a complete path segment,
1453
   *    are removed from the buffer string.
1454
   */
1455
0
  if ((cur[0] == '.') && (cur[1] == '/')) {
1456
0
      cur += 2;
1457
      /* '//' normalization should be done at this point too */
1458
0
      while (cur[0] == '/')
1459
0
    cur++;
1460
0
      continue;
1461
0
  }
1462
1463
  /*
1464
   * d) If the buffer string ends with "." as a complete path segment,
1465
   *    that "." is removed.
1466
   */
1467
0
  if ((cur[0] == '.') && (cur[1] == '\0'))
1468
0
      break;
1469
1470
  /* Otherwise keep the segment.  */
1471
0
  while (cur[0] != '/') {
1472
0
            if (cur[0] == '\0')
1473
0
              goto done_cd;
1474
0
      (out++)[0] = (cur++)[0];
1475
0
  }
1476
  /* normalize // */
1477
0
  while ((cur[0] == '/') && (cur[1] == '/'))
1478
0
      cur++;
1479
1480
0
        (out++)[0] = (cur++)[0];
1481
0
    }
1482
0
 done_cd:
1483
0
    out[0] = '\0';
1484
1485
    /* Reset to the beginning of the first segment for the next sequence.  */
1486
0
    cur = path;
1487
0
    while (cur[0] == '/')
1488
0
      ++cur;
1489
0
    if (cur[0] == '\0')
1490
0
  return(0);
1491
1492
    /*
1493
     * Analyze each segment in sequence for cases (e) and (f).
1494
     *
1495
     * e) All occurrences of "<segment>/../", where <segment> is a
1496
     *    complete path segment not equal to "..", are removed from the
1497
     *    buffer string.  Removal of these path segments is performed
1498
     *    iteratively, removing the leftmost matching pattern on each
1499
     *    iteration, until no matching pattern remains.
1500
     *
1501
     * f) If the buffer string ends with "<segment>/..", where <segment>
1502
     *    is a complete path segment not equal to "..", that
1503
     *    "<segment>/.." is removed.
1504
     *
1505
     * To satisfy the "iterative" clause in (e), we need to collapse the
1506
     * string every time we find something that needs to be removed.  Thus,
1507
     * we don't need to keep two pointers into the string: we only need a
1508
     * "current position" pointer.
1509
     */
1510
0
    while (1) {
1511
0
        char *segp, *tmp;
1512
1513
        /* At the beginning of each iteration of this loop, "cur" points to
1514
         * the first character of the segment we want to examine.
1515
         */
1516
1517
        /* Find the end of the current segment.  */
1518
0
        segp = cur;
1519
0
        while ((segp[0] != '/') && (segp[0] != '\0'))
1520
0
          ++segp;
1521
1522
        /* If this is the last segment, we're done (we need at least two
1523
         * segments to meet the criteria for the (e) and (f) cases).
1524
         */
1525
0
        if (segp[0] == '\0')
1526
0
          break;
1527
1528
        /* If the first segment is "..", or if the next segment _isn't_ "..",
1529
         * keep this segment and try the next one.
1530
         */
1531
0
        ++segp;
1532
0
        if (((cur[0] == '.') && (cur[1] == '.') && (segp == cur+3))
1533
0
            || ((segp[0] != '.') || (segp[1] != '.')
1534
0
                || ((segp[2] != '/') && (segp[2] != '\0')))) {
1535
0
          cur = segp;
1536
0
          continue;
1537
0
        }
1538
1539
        /* If we get here, remove this segment and the next one and back up
1540
         * to the previous segment (if there is one), to implement the
1541
         * "iteratively" clause.  It's pretty much impossible to back up
1542
         * while maintaining two pointers into the buffer, so just compact
1543
         * the whole buffer now.
1544
         */
1545
1546
        /* If this is the end of the buffer, we're done.  */
1547
0
        if (segp[2] == '\0') {
1548
0
          cur[0] = '\0';
1549
0
          break;
1550
0
        }
1551
        /* Valgrind complained, strcpy(cur, segp + 3); */
1552
        /* string will overlap, do not use strcpy */
1553
0
        tmp = cur;
1554
0
        segp += 3;
1555
0
        while ((*tmp++ = *segp++) != 0)
1556
0
          ;
1557
1558
        /* If there are no previous segments, then keep going from here.  */
1559
0
        segp = cur;
1560
0
        while ((segp > path) && ((--segp)[0] == '/'))
1561
0
          ;
1562
0
        if (segp == path)
1563
0
          continue;
1564
1565
        /* "segp" is pointing to the end of a previous segment; find it's
1566
         * start.  We need to back up to the previous segment and start
1567
         * over with that to handle things like "foo/bar/../..".  If we
1568
         * don't do this, then on the first pass we'll remove the "bar/..",
1569
         * but be pointing at the second ".." so we won't realize we can also
1570
         * remove the "foo/..".
1571
         */
1572
0
        cur = segp;
1573
0
        while ((cur > path) && (cur[-1] != '/'))
1574
0
          --cur;
1575
0
    }
1576
0
    out[0] = '\0';
1577
1578
    /*
1579
     * g) If the resulting buffer string still begins with one or more
1580
     *    complete path segments of "..", then the reference is
1581
     *    considered to be in error. Implementations may handle this
1582
     *    error by retaining these components in the resolved path (i.e.,
1583
     *    treating them as part of the final URI), by removing them from
1584
     *    the resolved path (i.e., discarding relative levels above the
1585
     *    root), or by avoiding traversal of the reference.
1586
     *
1587
     * We discard them from the final path.
1588
     */
1589
0
    if (path[0] == '/') {
1590
0
      cur = path;
1591
0
      while ((cur[0] == '/') && (cur[1] == '.') && (cur[2] == '.')
1592
0
             && ((cur[3] == '/') || (cur[3] == '\0')))
1593
0
  cur += 3;
1594
1595
0
      if (cur != path) {
1596
0
  out = path;
1597
0
  while (cur[0] != '\0')
1598
0
          (out++)[0] = (cur++)[0];
1599
0
  out[0] = 0;
1600
0
      }
1601
0
    }
1602
1603
0
    return(0);
1604
0
}
1605
1606
0
static int is_hex(char c) {
1607
0
    if (((c >= '0') && (c <= '9')) ||
1608
0
        ((c >= 'a') && (c <= 'f')) ||
1609
0
        ((c >= 'A') && (c <= 'F')))
1610
0
  return(1);
1611
0
    return(0);
1612
0
}
1613
1614
/**
1615
 * xmlURIUnescapeString:
1616
 * @str:  the string to unescape
1617
 * @len:   the length in bytes to unescape (or <= 0 to indicate full string)
1618
 * @target:  optional destination buffer
1619
 *
1620
 * Unescaping routine, but does not check that the string is an URI. The
1621
 * output is a direct unsigned char translation of %XX values (no encoding)
1622
 * Note that the length of the result can only be smaller or same size as
1623
 * the input string.
1624
 *
1625
 * Returns a copy of the string, but unescaped, will return NULL only in case
1626
 * of error
1627
 */
1628
char *
1629
11
xmlURIUnescapeString(const char *str, int len, char *target) {
1630
11
    char *ret, *out;
1631
11
    const char *in;
1632
1633
11
    if (str == NULL)
1634
0
  return(NULL);
1635
11
    if (len <= 0) len = strlen(str);
1636
11
    if (len < 0) return(NULL);
1637
1638
11
    if (target == NULL) {
1639
11
  ret = (char *) xmlMallocAtomic(len + 1);
1640
11
  if (ret == NULL) {
1641
0
            xmlURIErrMemory("unescaping URI value\n");
1642
0
      return(NULL);
1643
0
  }
1644
11
    } else
1645
0
  ret = target;
1646
11
    in = str;
1647
11
    out = ret;
1648
77
    while(len > 0) {
1649
66
  if ((len > 2) && (*in == '%') && (is_hex(in[1])) && (is_hex(in[2]))) {
1650
0
            int c = 0;
1651
0
      in++;
1652
0
      if ((*in >= '0') && (*in <= '9'))
1653
0
          c = (*in - '0');
1654
0
      else if ((*in >= 'a') && (*in <= 'f'))
1655
0
          c = (*in - 'a') + 10;
1656
0
      else if ((*in >= 'A') && (*in <= 'F'))
1657
0
          c = (*in - 'A') + 10;
1658
0
      in++;
1659
0
      if ((*in >= '0') && (*in <= '9'))
1660
0
          c = c * 16 + (*in - '0');
1661
0
      else if ((*in >= 'a') && (*in <= 'f'))
1662
0
          c = c * 16 + (*in - 'a') + 10;
1663
0
      else if ((*in >= 'A') && (*in <= 'F'))
1664
0
          c = c * 16 + (*in - 'A') + 10;
1665
0
      in++;
1666
0
      len -= 3;
1667
            /* Explicit sign change */
1668
0
      *out++ = (char) c;
1669
66
  } else {
1670
66
      *out++ = *in++;
1671
66
      len--;
1672
66
  }
1673
66
    }
1674
11
    *out = 0;
1675
11
    return(ret);
1676
11
}
1677
1678
/**
1679
 * xmlURIEscapeStr:
1680
 * @str:  string to escape
1681
 * @list: exception list string of chars not to escape
1682
 *
1683
 * This routine escapes a string to hex, ignoring reserved characters
1684
 * (a-z, A-Z, 0-9, "@-_.!~*'()") and the characters in the exception list.
1685
 *
1686
 * Returns a new escaped string or NULL in case of error.
1687
 */
1688
xmlChar *
1689
0
xmlURIEscapeStr(const xmlChar *str, const xmlChar *list) {
1690
0
    xmlChar *ret, ch;
1691
0
    xmlChar *temp;
1692
0
    const xmlChar *in;
1693
0
    int len, out;
1694
1695
0
    if (str == NULL)
1696
0
  return(NULL);
1697
0
    if (str[0] == 0)
1698
0
  return(xmlStrdup(str));
1699
0
    len = xmlStrlen(str);
1700
0
    if (!(len > 0)) return(NULL);
1701
1702
0
    len += 20;
1703
0
    ret = (xmlChar *) xmlMallocAtomic(len);
1704
0
    if (ret == NULL) {
1705
0
        xmlURIErrMemory("escaping URI value\n");
1706
0
  return(NULL);
1707
0
    }
1708
0
    in = (const xmlChar *) str;
1709
0
    out = 0;
1710
0
    while(*in != 0) {
1711
0
  if (len - out <= 3) {
1712
0
            temp = xmlSaveUriRealloc(ret, &len);
1713
0
      if (temp == NULL) {
1714
0
                xmlURIErrMemory("escaping URI value\n");
1715
0
    xmlFree(ret);
1716
0
    return(NULL);
1717
0
      }
1718
0
      ret = temp;
1719
0
  }
1720
1721
0
  ch = *in;
1722
1723
0
  if ((ch != '@') && (!IS_UNRESERVED(ch)) && (!xmlStrchr(list, ch))) {
1724
0
      unsigned char val;
1725
0
      ret[out++] = '%';
1726
0
      val = ch >> 4;
1727
0
      if (val <= 9)
1728
0
    ret[out++] = '0' + val;
1729
0
      else
1730
0
    ret[out++] = 'A' + val - 0xA;
1731
0
      val = ch & 0xF;
1732
0
      if (val <= 9)
1733
0
    ret[out++] = '0' + val;
1734
0
      else
1735
0
    ret[out++] = 'A' + val - 0xA;
1736
0
      in++;
1737
0
  } else {
1738
0
      ret[out++] = *in++;
1739
0
  }
1740
1741
0
    }
1742
0
    ret[out] = 0;
1743
0
    return(ret);
1744
0
}
1745
1746
/**
1747
 * xmlURIEscape:
1748
 * @str:  the string of the URI to escape
1749
 *
1750
 * Escaping routine, does not do validity checks !
1751
 * It will try to escape the chars needing this, but this is heuristic
1752
 * based it's impossible to be sure.
1753
 *
1754
 * Returns an copy of the string, but escaped
1755
 *
1756
 * 25 May 2001
1757
 * Uses xmlParseURI and xmlURIEscapeStr to try to escape correctly
1758
 * according to RFC2396.
1759
 *   - Carl Douglas
1760
 */
1761
xmlChar *
1762
xmlURIEscape(const xmlChar * str)
1763
0
{
1764
0
    xmlChar *ret, *segment = NULL;
1765
0
    xmlURIPtr uri;
1766
0
    int ret2;
1767
1768
0
    if (str == NULL)
1769
0
        return (NULL);
1770
1771
0
    uri = xmlCreateURI();
1772
0
    if (uri != NULL) {
1773
  /*
1774
   * Allow escaping errors in the unescaped form
1775
   */
1776
0
        uri->cleanup = 1;
1777
0
        ret2 = xmlParseURIReference(uri, (const char *)str);
1778
0
        if (ret2) {
1779
0
            xmlFreeURI(uri);
1780
0
            return (NULL);
1781
0
        }
1782
0
    }
1783
1784
0
    if (!uri)
1785
0
        return NULL;
1786
1787
0
    ret = NULL;
1788
1789
0
#define NULLCHK(p) if(!p) { \
1790
0
         xmlURIErrMemory("escaping URI value\n"); \
1791
0
         xmlFreeURI(uri); \
1792
0
         xmlFree(ret); \
1793
0
         return NULL; } \
1794
0
1795
0
    if (uri->scheme) {
1796
0
        segment = xmlURIEscapeStr(BAD_CAST uri->scheme, BAD_CAST "+-.");
1797
0
        NULLCHK(segment)
1798
0
        ret = xmlStrcat(ret, segment);
1799
0
        ret = xmlStrcat(ret, BAD_CAST ":");
1800
0
        xmlFree(segment);
1801
0
    }
1802
1803
0
    if (uri->authority) {
1804
0
        segment =
1805
0
            xmlURIEscapeStr(BAD_CAST uri->authority, BAD_CAST "/?;:@");
1806
0
        NULLCHK(segment)
1807
0
        ret = xmlStrcat(ret, BAD_CAST "//");
1808
0
        ret = xmlStrcat(ret, segment);
1809
0
        xmlFree(segment);
1810
0
    }
1811
1812
0
    if (uri->user) {
1813
0
        segment = xmlURIEscapeStr(BAD_CAST uri->user, BAD_CAST ";:&=+$,");
1814
0
        NULLCHK(segment)
1815
0
        ret = xmlStrcat(ret,BAD_CAST "//");
1816
0
        ret = xmlStrcat(ret, segment);
1817
0
        ret = xmlStrcat(ret, BAD_CAST "@");
1818
0
        xmlFree(segment);
1819
0
    }
1820
1821
0
    if (uri->server) {
1822
0
        segment = xmlURIEscapeStr(BAD_CAST uri->server, BAD_CAST "/?;:@");
1823
0
        NULLCHK(segment)
1824
0
        if (uri->user == NULL)
1825
0
            ret = xmlStrcat(ret, BAD_CAST "//");
1826
0
        ret = xmlStrcat(ret, segment);
1827
0
        xmlFree(segment);
1828
0
    }
1829
1830
0
    if (uri->port > 0) {
1831
0
        xmlChar port[11];
1832
1833
0
        snprintf((char *) port, 11, "%d", uri->port);
1834
0
        ret = xmlStrcat(ret, BAD_CAST ":");
1835
0
        ret = xmlStrcat(ret, port);
1836
0
    }
1837
1838
0
    if (uri->path) {
1839
0
        segment =
1840
0
            xmlURIEscapeStr(BAD_CAST uri->path, BAD_CAST ":@&=+$,/?;");
1841
0
        NULLCHK(segment)
1842
0
        ret = xmlStrcat(ret, segment);
1843
0
        xmlFree(segment);
1844
0
    }
1845
1846
0
    if (uri->query_raw) {
1847
0
        ret = xmlStrcat(ret, BAD_CAST "?");
1848
0
        ret = xmlStrcat(ret, BAD_CAST uri->query_raw);
1849
0
    }
1850
0
    else if (uri->query) {
1851
0
        segment =
1852
0
            xmlURIEscapeStr(BAD_CAST uri->query, BAD_CAST ";/?:@&=+,$");
1853
0
        NULLCHK(segment)
1854
0
        ret = xmlStrcat(ret, BAD_CAST "?");
1855
0
        ret = xmlStrcat(ret, segment);
1856
0
        xmlFree(segment);
1857
0
    }
1858
1859
0
    if (uri->opaque) {
1860
0
        segment = xmlURIEscapeStr(BAD_CAST uri->opaque, BAD_CAST "");
1861
0
        NULLCHK(segment)
1862
0
        ret = xmlStrcat(ret, segment);
1863
0
        xmlFree(segment);
1864
0
    }
1865
1866
0
    if (uri->fragment) {
1867
0
        segment = xmlURIEscapeStr(BAD_CAST uri->fragment, BAD_CAST "#");
1868
0
        NULLCHK(segment)
1869
0
        ret = xmlStrcat(ret, BAD_CAST "#");
1870
0
        ret = xmlStrcat(ret, segment);
1871
0
        xmlFree(segment);
1872
0
    }
1873
1874
0
    xmlFreeURI(uri);
1875
0
#undef NULLCHK
1876
1877
0
    return (ret);
1878
0
}
1879
1880
/************************************************************************
1881
 *                  *
1882
 *      Public functions        *
1883
 *                  *
1884
 ************************************************************************/
1885
1886
/**
1887
 * xmlBuildURI:
1888
 * @URI:  the URI instance found in the document
1889
 * @base:  the base value
1890
 *
1891
 * Computes he final URI of the reference done by checking that
1892
 * the given URI is valid, and building the final URI using the
1893
 * base URI. This is processed according to section 5.2 of the
1894
 * RFC 2396
1895
 *
1896
 * 5.2. Resolving Relative References to Absolute Form
1897
 *
1898
 * Returns a new URI string (to be freed by the caller) or NULL in case
1899
 *         of error.
1900
 */
1901
xmlChar *
1902
0
xmlBuildURI(const xmlChar *URI, const xmlChar *base) {
1903
0
    xmlChar *val = NULL;
1904
0
    int ret, len, indx, cur, out;
1905
0
    xmlURIPtr ref = NULL;
1906
0
    xmlURIPtr bas = NULL;
1907
0
    xmlURIPtr res = NULL;
1908
1909
    /*
1910
     * 1) The URI reference is parsed into the potential four components and
1911
     *    fragment identifier, as described in Section 4.3.
1912
     *
1913
     *    NOTE that a completely empty URI is treated by modern browsers
1914
     *    as a reference to "." rather than as a synonym for the current
1915
     *    URI.  Should we do that here?
1916
     */
1917
0
    if (URI == NULL)
1918
0
  ret = -1;
1919
0
    else {
1920
0
  if (*URI) {
1921
0
      ref = xmlCreateURI();
1922
0
      if (ref == NULL)
1923
0
    goto done;
1924
0
      ret = xmlParseURIReference(ref, (const char *) URI);
1925
0
  }
1926
0
  else
1927
0
      ret = 0;
1928
0
    }
1929
0
    if (ret != 0)
1930
0
  goto done;
1931
0
    if ((ref != NULL) && (ref->scheme != NULL)) {
1932
  /*
1933
   * The URI is absolute don't modify.
1934
   */
1935
0
  val = xmlStrdup(URI);
1936
0
  goto done;
1937
0
    }
1938
0
    if (base == NULL)
1939
0
  ret = -1;
1940
0
    else {
1941
0
  bas = xmlCreateURI();
1942
0
  if (bas == NULL)
1943
0
      goto done;
1944
0
  ret = xmlParseURIReference(bas, (const char *) base);
1945
0
    }
1946
0
    if (ret != 0) {
1947
0
  if (ref)
1948
0
      val = xmlSaveUri(ref);
1949
0
  goto done;
1950
0
    }
1951
0
    if (ref == NULL) {
1952
  /*
1953
   * the base fragment must be ignored
1954
   */
1955
0
  if (bas->fragment != NULL) {
1956
0
      xmlFree(bas->fragment);
1957
0
      bas->fragment = NULL;
1958
0
  }
1959
0
  val = xmlSaveUri(bas);
1960
0
  goto done;
1961
0
    }
1962
1963
    /*
1964
     * 2) If the path component is empty and the scheme, authority, and
1965
     *    query components are undefined, then it is a reference to the
1966
     *    current document and we are done.  Otherwise, the reference URI's
1967
     *    query and fragment components are defined as found (or not found)
1968
     *    within the URI reference and not inherited from the base URI.
1969
     *
1970
     *    NOTE that in modern browsers, the parsing differs from the above
1971
     *    in the following aspect:  the query component is allowed to be
1972
     *    defined while still treating this as a reference to the current
1973
     *    document.
1974
     */
1975
0
    res = xmlCreateURI();
1976
0
    if (res == NULL)
1977
0
  goto done;
1978
0
    if ((ref->scheme == NULL) && (ref->path == NULL) &&
1979
0
  ((ref->authority == NULL) && (ref->server == NULL) &&
1980
0
         (ref->port == PORT_EMPTY))) {
1981
0
  if (bas->scheme != NULL)
1982
0
      res->scheme = xmlMemStrdup(bas->scheme);
1983
0
  if (bas->authority != NULL)
1984
0
      res->authority = xmlMemStrdup(bas->authority);
1985
0
  else {
1986
0
      if (bas->server != NULL)
1987
0
    res->server = xmlMemStrdup(bas->server);
1988
0
      if (bas->user != NULL)
1989
0
    res->user = xmlMemStrdup(bas->user);
1990
0
      res->port = bas->port;
1991
0
  }
1992
0
  if (bas->path != NULL)
1993
0
      res->path = xmlMemStrdup(bas->path);
1994
0
  if (ref->query_raw != NULL)
1995
0
      res->query_raw = xmlMemStrdup (ref->query_raw);
1996
0
  else if (ref->query != NULL)
1997
0
      res->query = xmlMemStrdup(ref->query);
1998
0
  else if (bas->query_raw != NULL)
1999
0
      res->query_raw = xmlMemStrdup(bas->query_raw);
2000
0
  else if (bas->query != NULL)
2001
0
      res->query = xmlMemStrdup(bas->query);
2002
0
  if (ref->fragment != NULL)
2003
0
      res->fragment = xmlMemStrdup(ref->fragment);
2004
0
  goto step_7;
2005
0
    }
2006
2007
    /*
2008
     * 3) If the scheme component is defined, indicating that the reference
2009
     *    starts with a scheme name, then the reference is interpreted as an
2010
     *    absolute URI and we are done.  Otherwise, the reference URI's
2011
     *    scheme is inherited from the base URI's scheme component.
2012
     */
2013
0
    if (ref->scheme != NULL) {
2014
0
  val = xmlSaveUri(ref);
2015
0
  goto done;
2016
0
    }
2017
0
    if (bas->scheme != NULL)
2018
0
  res->scheme = xmlMemStrdup(bas->scheme);
2019
2020
0
    if (ref->query_raw != NULL)
2021
0
  res->query_raw = xmlMemStrdup(ref->query_raw);
2022
0
    else if (ref->query != NULL)
2023
0
  res->query = xmlMemStrdup(ref->query);
2024
0
    if (ref->fragment != NULL)
2025
0
  res->fragment = xmlMemStrdup(ref->fragment);
2026
2027
    /*
2028
     * 4) If the authority component is defined, then the reference is a
2029
     *    network-path and we skip to step 7.  Otherwise, the reference
2030
     *    URI's authority is inherited from the base URI's authority
2031
     *    component, which will also be undefined if the URI scheme does not
2032
     *    use an authority component.
2033
     */
2034
0
    if ((ref->authority != NULL) || (ref->server != NULL) ||
2035
0
         (ref->port != PORT_EMPTY)) {
2036
0
  if (ref->authority != NULL)
2037
0
      res->authority = xmlMemStrdup(ref->authority);
2038
0
  else {
2039
0
            if (ref->server != NULL)
2040
0
                res->server = xmlMemStrdup(ref->server);
2041
0
      if (ref->user != NULL)
2042
0
    res->user = xmlMemStrdup(ref->user);
2043
0
            res->port = ref->port;
2044
0
  }
2045
0
  if (ref->path != NULL)
2046
0
      res->path = xmlMemStrdup(ref->path);
2047
0
  goto step_7;
2048
0
    }
2049
0
    if (bas->authority != NULL)
2050
0
  res->authority = xmlMemStrdup(bas->authority);
2051
0
    else if ((bas->server != NULL) || (bas->port != PORT_EMPTY)) {
2052
0
  if (bas->server != NULL)
2053
0
      res->server = xmlMemStrdup(bas->server);
2054
0
  if (bas->user != NULL)
2055
0
      res->user = xmlMemStrdup(bas->user);
2056
0
  res->port = bas->port;
2057
0
    }
2058
2059
    /*
2060
     * 5) If the path component begins with a slash character ("/"), then
2061
     *    the reference is an absolute-path and we skip to step 7.
2062
     */
2063
0
    if ((ref->path != NULL) && (ref->path[0] == '/')) {
2064
0
  res->path = xmlMemStrdup(ref->path);
2065
0
  goto step_7;
2066
0
    }
2067
2068
2069
    /*
2070
     * 6) If this step is reached, then we are resolving a relative-path
2071
     *    reference.  The relative path needs to be merged with the base
2072
     *    URI's path.  Although there are many ways to do this, we will
2073
     *    describe a simple method using a separate string buffer.
2074
     *
2075
     * Allocate a buffer large enough for the result string.
2076
     */
2077
0
    len = 2; /* extra / and 0 */
2078
0
    if (ref->path != NULL)
2079
0
  len += strlen(ref->path);
2080
0
    if (bas->path != NULL)
2081
0
  len += strlen(bas->path);
2082
0
    res->path = (char *) xmlMallocAtomic(len);
2083
0
    if (res->path == NULL) {
2084
0
        xmlURIErrMemory("resolving URI against base\n");
2085
0
  goto done;
2086
0
    }
2087
0
    res->path[0] = 0;
2088
2089
    /*
2090
     * a) All but the last segment of the base URI's path component is
2091
     *    copied to the buffer.  In other words, any characters after the
2092
     *    last (right-most) slash character, if any, are excluded.
2093
     */
2094
0
    cur = 0;
2095
0
    out = 0;
2096
0
    if (bas->path != NULL) {
2097
0
  while (bas->path[cur] != 0) {
2098
0
      while ((bas->path[cur] != 0) && (bas->path[cur] != '/'))
2099
0
    cur++;
2100
0
      if (bas->path[cur] == 0)
2101
0
    break;
2102
2103
0
      cur++;
2104
0
      while (out < cur) {
2105
0
    res->path[out] = bas->path[out];
2106
0
    out++;
2107
0
      }
2108
0
  }
2109
0
    }
2110
0
    res->path[out] = 0;
2111
2112
    /*
2113
     * b) The reference's path component is appended to the buffer
2114
     *    string.
2115
     */
2116
0
    if (ref->path != NULL && ref->path[0] != 0) {
2117
0
  indx = 0;
2118
  /*
2119
   * Ensure the path includes a '/'
2120
   */
2121
0
  if ((out == 0) && ((bas->server != NULL) || bas->port != PORT_EMPTY))
2122
0
      res->path[out++] = '/';
2123
0
  while (ref->path[indx] != 0) {
2124
0
      res->path[out++] = ref->path[indx++];
2125
0
  }
2126
0
    }
2127
0
    res->path[out] = 0;
2128
2129
    /*
2130
     * Steps c) to h) are really path normalization steps
2131
     */
2132
0
    xmlNormalizeURIPath(res->path);
2133
2134
0
step_7:
2135
2136
    /*
2137
     * 7) The resulting URI components, including any inherited from the
2138
     *    base URI, are recombined to give the absolute form of the URI
2139
     *    reference.
2140
     */
2141
0
    val = xmlSaveUri(res);
2142
2143
0
done:
2144
0
    if (ref != NULL)
2145
0
  xmlFreeURI(ref);
2146
0
    if (bas != NULL)
2147
0
  xmlFreeURI(bas);
2148
0
    if (res != NULL)
2149
0
  xmlFreeURI(res);
2150
0
    return(val);
2151
0
}
2152
2153
/**
2154
 * xmlBuildRelativeURI:
2155
 * @URI:  the URI reference under consideration
2156
 * @base:  the base value
2157
 *
2158
 * Expresses the URI of the reference in terms relative to the
2159
 * base.  Some examples of this operation include:
2160
 *     base = "http://site1.com/docs/book1.html"
2161
 *        URI input                        URI returned
2162
 *     docs/pic1.gif                    pic1.gif
2163
 *     docs/img/pic1.gif                img/pic1.gif
2164
 *     img/pic1.gif                     ../img/pic1.gif
2165
 *     http://site1.com/docs/pic1.gif   pic1.gif
2166
 *     http://site2.com/docs/pic1.gif   http://site2.com/docs/pic1.gif
2167
 *
2168
 *     base = "docs/book1.html"
2169
 *        URI input                        URI returned
2170
 *     docs/pic1.gif                    pic1.gif
2171
 *     docs/img/pic1.gif                img/pic1.gif
2172
 *     img/pic1.gif                     ../img/pic1.gif
2173
 *     http://site1.com/docs/pic1.gif   http://site1.com/docs/pic1.gif
2174
 *
2175
 *
2176
 * Note: if the URI reference is really weird or complicated, it may be
2177
 *       worthwhile to first convert it into a "nice" one by calling
2178
 *       xmlBuildURI (using 'base') before calling this routine,
2179
 *       since this routine (for reasonable efficiency) assumes URI has
2180
 *       already been through some validation.
2181
 *
2182
 * Returns a new URI string (to be freed by the caller) or NULL in case
2183
 * error.
2184
 */
2185
xmlChar *
2186
xmlBuildRelativeURI (const xmlChar * URI, const xmlChar * base)
2187
0
{
2188
0
    xmlChar *val = NULL;
2189
0
    int ret;
2190
0
    int ix;
2191
0
    int nbslash = 0;
2192
0
    int len;
2193
0
    xmlURIPtr ref = NULL;
2194
0
    xmlURIPtr bas = NULL;
2195
0
    xmlChar *bptr, *uptr, *vptr;
2196
0
    int remove_path = 0;
2197
2198
0
    if ((URI == NULL) || (*URI == 0))
2199
0
  return NULL;
2200
2201
    /*
2202
     * First parse URI into a standard form
2203
     */
2204
0
    ref = xmlCreateURI ();
2205
0
    if (ref == NULL)
2206
0
  return NULL;
2207
    /* If URI not already in "relative" form */
2208
0
    if (URI[0] != '.') {
2209
0
  ret = xmlParseURIReference (ref, (const char *) URI);
2210
0
  if (ret != 0)
2211
0
      goto done;   /* Error in URI, return NULL */
2212
0
    } else
2213
0
  ref->path = (char *)xmlStrdup(URI);
2214
2215
    /*
2216
     * Next parse base into the same standard form
2217
     */
2218
0
    if ((base == NULL) || (*base == 0)) {
2219
0
  val = xmlStrdup (URI);
2220
0
  goto done;
2221
0
    }
2222
0
    bas = xmlCreateURI ();
2223
0
    if (bas == NULL)
2224
0
  goto done;
2225
0
    if (base[0] != '.') {
2226
0
  ret = xmlParseURIReference (bas, (const char *) base);
2227
0
  if (ret != 0)
2228
0
      goto done;   /* Error in base, return NULL */
2229
0
    } else
2230
0
  bas->path = (char *)xmlStrdup(base);
2231
2232
    /*
2233
     * If the scheme / server on the URI differs from the base,
2234
     * just return the URI
2235
     */
2236
0
    if ((ref->scheme != NULL) &&
2237
0
  ((bas->scheme == NULL) ||
2238
0
   (xmlStrcmp ((xmlChar *)bas->scheme, (xmlChar *)ref->scheme)) ||
2239
0
   (xmlStrcmp ((xmlChar *)bas->server, (xmlChar *)ref->server)) ||
2240
0
         (bas->port != ref->port))) {
2241
0
  val = xmlStrdup (URI);
2242
0
  goto done;
2243
0
    }
2244
0
    if (xmlStrEqual((xmlChar *)bas->path, (xmlChar *)ref->path)) {
2245
0
  val = xmlStrdup(BAD_CAST "");
2246
0
  goto done;
2247
0
    }
2248
0
    if (bas->path == NULL) {
2249
0
  val = xmlStrdup((xmlChar *)ref->path);
2250
0
  goto done;
2251
0
    }
2252
0
    if (ref->path == NULL) {
2253
0
        ref->path = (char *) "/";
2254
0
  remove_path = 1;
2255
0
    }
2256
2257
    /*
2258
     * At this point (at last!) we can compare the two paths
2259
     *
2260
     * First we take care of the special case where either of the
2261
     * two path components may be missing (bug 316224)
2262
     */
2263
0
    bptr = (xmlChar *)bas->path;
2264
0
    {
2265
0
        xmlChar *rptr = (xmlChar *) ref->path;
2266
0
        int pos = 0;
2267
2268
        /*
2269
         * Next we compare the two strings and find where they first differ
2270
         */
2271
0
  if ((*rptr == '.') && (rptr[1] == '/'))
2272
0
            rptr += 2;
2273
0
  if ((*bptr == '.') && (bptr[1] == '/'))
2274
0
            bptr += 2;
2275
0
  else if ((*bptr == '/') && (*rptr != '/'))
2276
0
      bptr++;
2277
0
  while ((bptr[pos] == rptr[pos]) && (bptr[pos] != 0))
2278
0
      pos++;
2279
2280
0
  if (bptr[pos] == rptr[pos]) {
2281
0
      val = xmlStrdup(BAD_CAST "");
2282
0
      goto done;    /* (I can't imagine why anyone would do this) */
2283
0
  }
2284
2285
  /*
2286
   * In URI, "back up" to the last '/' encountered.  This will be the
2287
   * beginning of the "unique" suffix of URI
2288
   */
2289
0
  ix = pos;
2290
0
  for (; ix > 0; ix--) {
2291
0
      if (rptr[ix - 1] == '/')
2292
0
    break;
2293
0
  }
2294
0
  uptr = (xmlChar *)&rptr[ix];
2295
2296
  /*
2297
   * In base, count the number of '/' from the differing point
2298
   */
2299
0
  for (; bptr[ix] != 0; ix++) {
2300
0
      if (bptr[ix] == '/')
2301
0
    nbslash++;
2302
0
  }
2303
2304
  /*
2305
   * e.g: URI="foo/" base="foo/bar" -> "./"
2306
   */
2307
0
  if (nbslash == 0 && !uptr[0]) {
2308
0
      val = xmlStrdup(BAD_CAST "./");
2309
0
      goto done;
2310
0
  }
2311
2312
0
  len = xmlStrlen (uptr) + 1;
2313
0
    }
2314
2315
0
    if (nbslash == 0) {
2316
0
  if (uptr != NULL)
2317
      /* exception characters from xmlSaveUri */
2318
0
      val = xmlURIEscapeStr(uptr, BAD_CAST "/;&=+$,");
2319
0
  goto done;
2320
0
    }
2321
2322
    /*
2323
     * Allocate just enough space for the returned string -
2324
     * length of the remainder of the URI, plus enough space
2325
     * for the "../" groups, plus one for the terminator
2326
     */
2327
0
    val = (xmlChar *) xmlMalloc (len + 3 * nbslash);
2328
0
    if (val == NULL) {
2329
0
        xmlURIErrMemory("building relative URI\n");
2330
0
  goto done;
2331
0
    }
2332
0
    vptr = val;
2333
    /*
2334
     * Put in as many "../" as needed
2335
     */
2336
0
    for (; nbslash>0; nbslash--) {
2337
0
  *vptr++ = '.';
2338
0
  *vptr++ = '.';
2339
0
  *vptr++ = '/';
2340
0
    }
2341
    /*
2342
     * Finish up with the end of the URI
2343
     */
2344
0
    if (uptr != NULL) {
2345
0
        if ((vptr > val) && (len > 0) &&
2346
0
      (uptr[0] == '/') && (vptr[-1] == '/')) {
2347
0
      memcpy (vptr, uptr + 1, len - 1);
2348
0
      vptr[len - 2] = 0;
2349
0
  } else {
2350
0
      memcpy (vptr, uptr, len);
2351
0
      vptr[len - 1] = 0;
2352
0
  }
2353
0
    } else {
2354
0
  vptr[len - 1] = 0;
2355
0
    }
2356
2357
    /* escape the freshly-built path */
2358
0
    vptr = val;
2359
  /* exception characters from xmlSaveUri */
2360
0
    val = xmlURIEscapeStr(vptr, BAD_CAST "/;&=+$,");
2361
0
    xmlFree(vptr);
2362
2363
0
done:
2364
    /*
2365
     * Free the working variables
2366
     */
2367
0
    if (remove_path != 0)
2368
0
        ref->path = NULL;
2369
0
    if (ref != NULL)
2370
0
  xmlFreeURI (ref);
2371
0
    if (bas != NULL)
2372
0
  xmlFreeURI (bas);
2373
2374
0
    return val;
2375
0
}
2376
2377
/**
2378
 * xmlCanonicPath:
2379
 * @path:  the resource locator in a filesystem notation
2380
 *
2381
 * Constructs a canonic path from the specified path.
2382
 *
2383
 * Returns a new canonic path, or a duplicate of the path parameter if the
2384
 * construction fails. The caller is responsible for freeing the memory occupied
2385
 * by the returned string. If there is insufficient memory available, or the
2386
 * argument is NULL, the function returns NULL.
2387
 */
2388
#define IS_WINDOWS_PATH(p)          \
2389
  ((p != NULL) &&           \
2390
   (((p[0] >= 'a') && (p[0] <= 'z')) ||     \
2391
    ((p[0] >= 'A') && (p[0] <= 'Z'))) &&      \
2392
   (p[1] == ':') && ((p[2] == '/') || (p[2] == '\\')))
2393
xmlChar *
2394
xmlCanonicPath(const xmlChar *path)
2395
11
{
2396
/*
2397
 * For Windows implementations, additional work needs to be done to
2398
 * replace backslashes in pathnames with "forward slashes"
2399
 */
2400
#if defined(_WIN32)
2401
    int len = 0;
2402
    char *p = NULL;
2403
#endif
2404
11
    xmlURIPtr uri;
2405
11
    xmlChar *ret;
2406
11
    const xmlChar *absuri;
2407
2408
11
    if (path == NULL)
2409
0
  return(NULL);
2410
2411
#if defined(_WIN32)
2412
    /*
2413
     * We must not change the backslashes to slashes if the the path
2414
     * starts with \\?\
2415
     * Those paths can be up to 32k characters long.
2416
     * Was added specifically for OpenOffice, those paths can't be converted
2417
     * to URIs anyway.
2418
     */
2419
    if ((path[0] == '\\') && (path[1] == '\\') && (path[2] == '?') &&
2420
        (path[3] == '\\') )
2421
  return xmlStrdup((const xmlChar *) path);
2422
#endif
2423
2424
  /* sanitize filename starting with // so it can be used as URI */
2425
11
    if ((path[0] == '/') && (path[1] == '/') && (path[2] != '/'))
2426
0
        path++;
2427
2428
11
    if ((uri = xmlParseURI((const char *) path)) != NULL) {
2429
11
  xmlFreeURI(uri);
2430
11
  return xmlStrdup(path);
2431
11
    }
2432
2433
    /* Check if this is an "absolute uri" */
2434
0
    absuri = xmlStrstr(path, BAD_CAST "://");
2435
0
    if (absuri != NULL) {
2436
0
        int l, j;
2437
0
  unsigned char c;
2438
0
  xmlChar *escURI;
2439
2440
        /*
2441
   * this looks like an URI where some parts have not been
2442
   * escaped leading to a parsing problem.  Check that the first
2443
   * part matches a protocol.
2444
   */
2445
0
  l = absuri - path;
2446
  /* Bypass if first part (part before the '://') is > 20 chars */
2447
0
  if ((l <= 0) || (l > 20))
2448
0
      goto path_processing;
2449
  /* Bypass if any non-alpha characters are present in first part */
2450
0
  for (j = 0;j < l;j++) {
2451
0
      c = path[j];
2452
0
      if (!(((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))))
2453
0
          goto path_processing;
2454
0
  }
2455
2456
  /* Escape all except the characters specified in the supplied path */
2457
0
        escURI = xmlURIEscapeStr(path, BAD_CAST ":/?_.#&;=");
2458
0
  if (escURI != NULL) {
2459
      /* Try parsing the escaped path */
2460
0
      uri = xmlParseURI((const char *) escURI);
2461
      /* If successful, return the escaped string */
2462
0
      if (uri != NULL) {
2463
0
          xmlFreeURI(uri);
2464
0
    return escURI;
2465
0
      }
2466
0
            xmlFree(escURI);
2467
0
  }
2468
0
    }
2469
2470
0
path_processing:
2471
/* For Windows implementations, replace backslashes with 'forward slashes' */
2472
#if defined(_WIN32)
2473
    /*
2474
     * Create a URI structure
2475
     */
2476
    uri = xmlCreateURI();
2477
    if (uri == NULL) {    /* Guard against 'out of memory' */
2478
        return(NULL);
2479
    }
2480
2481
    len = xmlStrlen(path);
2482
    if ((len > 2) && IS_WINDOWS_PATH(path)) {
2483
        /* make the scheme 'file' */
2484
  uri->scheme = (char *) xmlStrdup(BAD_CAST "file");
2485
  /* allocate space for leading '/' + path + string terminator */
2486
  uri->path = xmlMallocAtomic(len + 2);
2487
  if (uri->path == NULL) {
2488
      xmlFreeURI(uri);  /* Guard against 'out of memory' */
2489
      return(NULL);
2490
  }
2491
  /* Put in leading '/' plus path */
2492
  uri->path[0] = '/';
2493
  p = uri->path + 1;
2494
  strncpy(p, (char *) path, len + 1);
2495
    } else {
2496
  uri->path = (char *) xmlStrdup(path);
2497
  if (uri->path == NULL) {
2498
      xmlFreeURI(uri);
2499
      return(NULL);
2500
  }
2501
  p = uri->path;
2502
    }
2503
    /* Now change all occurrences of '\' to '/' */
2504
    while (*p != '\0') {
2505
  if (*p == '\\')
2506
      *p = '/';
2507
  p++;
2508
    }
2509
2510
    if (uri->scheme == NULL) {
2511
  ret = xmlStrdup((const xmlChar *) uri->path);
2512
    } else {
2513
  ret = xmlSaveUri(uri);
2514
    }
2515
2516
    xmlFreeURI(uri);
2517
#else
2518
0
    ret = xmlStrdup((const xmlChar *) path);
2519
0
#endif
2520
0
    return(ret);
2521
0
}
2522
2523
/**
2524
 * xmlPathToURI:
2525
 * @path:  the resource locator in a filesystem notation
2526
 *
2527
 * Constructs an URI expressing the existing path
2528
 *
2529
 * Returns a new URI, or a duplicate of the path parameter if the
2530
 * construction fails. The caller is responsible for freeing the memory
2531
 * occupied by the returned string. If there is insufficient memory available,
2532
 * or the argument is NULL, the function returns NULL.
2533
 */
2534
xmlChar *
2535
xmlPathToURI(const xmlChar *path)
2536
0
{
2537
0
    xmlURIPtr uri;
2538
0
    xmlURI temp;
2539
0
    xmlChar *ret, *cal;
2540
2541
0
    if (path == NULL)
2542
0
        return(NULL);
2543
2544
0
    if ((uri = xmlParseURI((const char *) path)) != NULL) {
2545
0
  xmlFreeURI(uri);
2546
0
  return xmlStrdup(path);
2547
0
    }
2548
0
    cal = xmlCanonicPath(path);
2549
0
    if (cal == NULL)
2550
0
        return(NULL);
2551
#if defined(_WIN32)
2552
    /* xmlCanonicPath can return an URI on Windows (is that the intended behaviour?)
2553
       If 'cal' is a valid URI already then we are done here, as continuing would make
2554
       it invalid. */
2555
    if ((uri = xmlParseURI((const char *) cal)) != NULL) {
2556
  xmlFreeURI(uri);
2557
  return cal;
2558
    }
2559
    /* 'cal' can contain a relative path with backslashes. If that is processed
2560
       by xmlSaveURI, they will be escaped and the external entity loader machinery
2561
       will fail. So convert them to slashes. Misuse 'ret' for walking. */
2562
    ret = cal;
2563
    while (*ret != '\0') {
2564
  if (*ret == '\\')
2565
      *ret = '/';
2566
  ret++;
2567
    }
2568
#endif
2569
0
    memset(&temp, 0, sizeof(temp));
2570
0
    temp.path = (char *) cal;
2571
0
    ret = xmlSaveUri(&temp);
2572
0
    xmlFree(cal);
2573
0
    return(ret);
2574
0
}