VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/uri.cpp@ 57983

Last change on this file since 57983 was 57983, checked in by vboxsync, 10 years ago

Build fix.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 27.6 KB
Line 
1/* $Id: uri.cpp 57983 2015-10-01 11:33:25Z vboxsync $ */
2/** @file
3 * IPRT - Uniform Resource Identifier handling.
4 */
5
6/*
7 * Copyright (C) 2011-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/uri.h>
32
33#include <iprt/assert.h>
34#include <iprt/ctype.h>
35#include <iprt/path.h>
36#include <iprt/string.h>
37
38
39/*********************************************************************************************************************************
40* Defined Constants And Macros *
41*********************************************************************************************************************************/
42/** Internal magic value we use to check if a RTURIPARSED structure has made it thru RTUriParse. */
43#define RTURIPARSED_MAGIC UINT32_C(0x439e0745)
44
45
46/* General URI format:
47
48 foo://example.com:8042/over/there?name=ferret#nose
49 \_/ \______________/\_________/ \_________/ \__/
50 | | | | |
51 scheme authority path query fragment
52 | _____________________|__
53 / \ / \
54 urn:example:animal:ferret:nose
55*/
56
57
58/**
59 * The following defines characters which have to be % escaped:
60 * control = 00-1F
61 * space = ' '
62 * delims = '<' , '>' , '#' , '%' , '"'
63 * unwise = '{' , '}' , '|' , '\' , '^' , '[' , ']' , '`'
64 */
65#define URI_EXCLUDED(a) \
66 ( ((a) >= 0x0 && (a) <= 0x20) \
67 || ((a) >= 0x5B && (a) <= 0x5E) \
68 || ((a) >= 0x7B && (a) <= 0x7D) \
69 || (a) == '<' || (a) == '>' || (a) == '#' \
70 || (a) == '%' || (a) == '"' || (a) == '`' )
71
72static char *rtUriPercentEncodeN(const char *pszString, size_t cchMax)
73{
74 if (!pszString)
75 return NULL;
76
77 int rc = VINF_SUCCESS;
78
79 size_t cbLen = RT_MIN(strlen(pszString), cchMax);
80 /* The new string can be max 3 times in size of the original string. */
81 char *pszNew = RTStrAlloc(cbLen * 3 + 1);
82 if (!pszNew)
83 return NULL;
84
85 char *pszRes = NULL;
86 size_t iIn = 0;
87 size_t iOut = 0;
88 while (iIn < cbLen)
89 {
90 if (URI_EXCLUDED(pszString[iIn]))
91 {
92 char szNum[3] = { 0, 0, 0 };
93 RTStrFormatU8(&szNum[0], 3, pszString[iIn++], 16, 2, 2, RTSTR_F_CAPITAL | RTSTR_F_ZEROPAD);
94 pszNew[iOut++] = '%';
95 pszNew[iOut++] = szNum[0];
96 pszNew[iOut++] = szNum[1];
97 }
98 else
99 pszNew[iOut++] = pszString[iIn++];
100 }
101 if (RT_SUCCESS(rc))
102 {
103 pszNew[iOut] = '\0';
104 if (iOut != iIn)
105 {
106 /* If the source and target strings have different size, recreate
107 * the target string with the correct size. */
108 pszRes = RTStrDupN(pszNew, iOut);
109 RTStrFree(pszNew);
110 }
111 else
112 pszRes = pszNew;
113 }
114 else
115 RTStrFree(pszNew);
116
117 return pszRes;
118}
119
120
121static char *rtUriPercentDecodeN(const char *pszString, size_t cchString)
122{
123 AssertPtrReturn(pszString, NULL);
124 AssertReturn(strlen(pszString) >= cchString, NULL);
125
126 /*
127 * The new string can only get smaller, so use the input length as a
128 * staring buffer size.
129 */
130 char *pszDecoded = RTStrAlloc(cchString + 1);
131 if (pszDecoded)
132 {
133 /*
134 * Knowing that the pszString itself is valid UTF-8, we only have to
135 * validate the escape sequences.
136 */
137 size_t cchLeft = cchString;
138 char const *pchSrc = pszString;
139 char *pchDst = pszDecoded;
140 while (cchLeft > 0)
141 {
142 const char *pchPct = (const char *)memchr(pchSrc, '%', cchLeft);
143 if (pchPct)
144 {
145 size_t cchBefore = pchPct - pchSrc;
146 if (cchBefore)
147 {
148 memcpy(pchDst, pchSrc, cchBefore);
149 pchDst += cchBefore;
150 pchSrc += cchBefore;
151 cchLeft -= cchBefore;
152 }
153
154 char chHigh, chLow;
155 if ( cchLeft >= 3
156 && RT_C_IS_XDIGIT(chHigh = pchSrc[1])
157 && RT_C_IS_XDIGIT(chLow = pchSrc[2]))
158 {
159 uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10;
160 b <<= 4;
161 b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10;
162 *pchDst++ = (char)b;
163 pchSrc += 3;
164 cchLeft -= 3;
165 }
166 else
167 {
168 AssertFailed();
169 *pchDst++ = *pchSrc++;
170 cchLeft--;
171 }
172 }
173 else
174 {
175 memcpy(pchDst, pchSrc, cchLeft);
176 pchDst += cchLeft;
177 pchSrc += cchLeft;
178 cchLeft = 0;
179 break;
180 }
181 }
182
183 *pchDst = '\0';
184
185 /*
186 * If we've got lof space room in the result string, reallocate it.
187 */
188 size_t cchDecoded = pchDst - pszDecoded;
189 Assert(cchDecoded <= cchString);
190 // if (cchString - cchDecoded > 64) - enable later!
191 RTStrRealloc(&pszDecoded, cchDecoded + 1);
192 }
193 return pszDecoded;
194}
195
196
197static int rtUriParse(const char *pszUri, PRTURIPARSED pParsed)
198{
199 /*
200 * Validate the input and clear the output.
201 */
202 AssertPtrReturn(pParsed, VERR_INVALID_POINTER);
203 RT_ZERO(*pParsed);
204 pParsed->uAuthorityPort = UINT32_MAX;
205
206 AssertPtrReturn(pszUri, VERR_INVALID_POINTER);
207
208 size_t const cchUri = strlen(pszUri);
209 if (RT_LIKELY(cchUri >= 3)) { /* likely */ }
210 else return cchUri ? VERR_URI_TOO_SHORT : VERR_URI_EMPTY;
211
212 /*
213 * Validating escaped text sequences is much simpler if we know that
214 * that the base URI string is valid. Also, we don't necessarily trust
215 * the developer calling us to remember to do this.
216 */
217 int rc = RTStrValidateEncoding(pszUri);
218 AssertRCReturn(rc, rc);
219
220 /*
221 * RFC-3986, section 3.1:
222 * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
223 *
224 * The scheme ends with a ':', which we also skip here.
225 */
226 size_t off = 0;
227 char ch = pszUri[off++];
228 if (RT_LIKELY(RT_C_IS_ALPHA(ch))) { /* likely */ }
229 else return VERR_URI_INVALID_SCHEME;
230 for (;;)
231 {
232 ch = pszUri[off];
233 if (ch == ':')
234 break;
235 if (RT_LIKELY(RT_C_IS_ALNUM(ch) || ch == '.' || ch == '-' || ch == '+')) { /* likely */ }
236 else return VERR_URI_INVALID_SCHEME;
237 off++;
238 }
239 pParsed->cchScheme = off;
240
241 /* Require the scheme length to be at least two chars so we won't confuse
242 it with a path starting with a DOS drive letter specification. */
243 if (RT_LIKELY(off >= 2)) { /* likely */ }
244 else return VERR_URI_INVALID_SCHEME;
245
246 off++; /* (skip colon) */
247
248 /*
249 * Find the end of the path, we'll need this several times.
250 * Also, while we're potentially scanning the whole thing, check for '%'.
251 */
252 size_t const offHash = RTStrOffCharOrTerm(&pszUri[off], '#') + off;
253 size_t const offQuestionMark = RTStrOffCharOrTerm(&pszUri[off], '?') + off;
254
255 if (memchr(pszUri, '%', cchUri) != NULL)
256 pParsed->fFlags |= RTURIPARSED_F_CONTAINS_ESCAPED_CHARS;
257
258 /*
259 * RFC-3986, section 3.2:
260 * The authority component is preceeded by a double slash ("//")...
261 */
262 if ( pszUri[off] == '/'
263 && pszUri[off + 1] == '/')
264 {
265 off += 2;
266 pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off;
267 pParsed->fFlags |= RTURIPARSED_F_HAVE_AUTHORITY;
268
269 /*
270 * RFC-3986, section 3.2:
271 * ...and is terminated by the next slash ("/"), question mark ("?"),
272 * or number sign ("#") character, or by the end of the URI.
273 */
274 const char *pszAuthority = &pszUri[off];
275 size_t cchAuthority = RTStrOffCharOrTerm(pszAuthority, '/');
276 cchAuthority = RT_MIN(cchAuthority, offHash - off);
277 cchAuthority = RT_MIN(cchAuthority, offQuestionMark - off);
278 pParsed->cchAuthority = cchAuthority;
279
280 /* The Authority can be empty, like for: file:///usr/bin/grep */
281 if (cchAuthority > 0)
282 {
283 pParsed->cchAuthorityHost = cchAuthority;
284
285 /*
286 * If there is a userinfo part, it is ended by a '@'.
287 */
288 const char *pszAt = (const char *)memchr(pszAuthority, '@', cchAuthority);
289 if (pszAt)
290 {
291 size_t cchTmp = pszAt - pszAuthority;
292 pParsed->offAuthorityHost += cchTmp + 1;
293 pParsed->cchAuthorityHost -= cchTmp + 1;
294
295 /* If there is a password part, it's separated from the username with a colon. */
296 const char *pszColon = (const char *)memchr(pszAuthority, ':', cchTmp);
297 if (pszColon)
298 {
299 pParsed->cchAuthorityUsername = pszColon - pszAuthority;
300 pParsed->offAuthorityPassword = &pszColon[1] - pszUri;
301 pParsed->cchAuthorityPassword = pszAt - &pszColon[1];
302 }
303 else
304 {
305 pParsed->cchAuthorityUsername = cchTmp;
306 pParsed->offAuthorityPassword = off + cchTmp;
307 }
308 }
309
310 /*
311 * If there is a port part, its after the last colon in the host part.
312 */
313 const char *pszColon = (const char *)memrchr(&pszUri[pParsed->offAuthorityHost], ':', pParsed->cchAuthorityHost);
314 if (pszColon)
315 {
316 size_t cchTmp = &pszUri[pParsed->offAuthorityHost + pParsed->cchAuthorityHost] - &pszColon[1];
317 pParsed->cchAuthorityHost -= cchTmp + 1;
318
319 pParsed->uAuthorityPort = 0;
320 while (cchTmp-- > 0)
321 {
322 ch = *++pszColon;
323 if ( RT_C_IS_DIGIT(ch)
324 && pParsed->uAuthorityPort < UINT32_MAX / UINT32_C(10))
325 {
326 pParsed->uAuthorityPort *= 10;
327 pParsed->uAuthorityPort += ch - '0';
328 }
329 else
330 return VERR_URI_INVALID_PORT_NUMBER;
331 }
332 }
333 }
334
335 /* Skip past the authority. */
336 off += cchAuthority;
337 }
338 else
339 pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off;
340
341 /*
342 * RFC-3986, section 3.3: Path
343 * The path is terminated by the first question mark ("?")
344 * or number sign ("#") character, or by the end of the URI.
345 */
346 pParsed->offPath = off;
347 pParsed->cchPath = RT_MIN(offHash, offQuestionMark) - off;
348 off += pParsed->cchPath;
349
350 /*
351 * RFC-3986, section 3.4: Query
352 * The query component is indicated by the first question mark ("?")
353 * character and terminated by a number sign ("#") character or by the
354 * end of the URI.
355 */
356 if ( off == offQuestionMark
357 && off < cchUri)
358 {
359 Assert(pszUri[offQuestionMark] == '?');
360 pParsed->offQuery = ++off;
361 pParsed->cchQuery = offHash - off;
362 off = offHash;
363 }
364 else
365 {
366 Assert(!pszUri[offQuestionMark]);
367 pParsed->offQuery = off;
368 }
369
370 /*
371 * RFC-3986, section 3.5: Fragment
372 * A fragment identifier component is indicated by the presence of a
373 * number sign ("#") character and terminated by the end of the URI.
374 */
375 if ( off == offHash
376 && off < cchUri)
377 {
378 pParsed->offFragment = ++off;
379 pParsed->cchFragment = cchUri - off;
380 }
381 else
382 {
383 Assert(!pszUri[offHash]);
384 pParsed->offFragment = off;
385 }
386
387 /*
388 * If there are any escape sequences, validate them.
389 *
390 * This is reasonably simple as we already know that the string is valid UTF-8
391 * before they get decoded. Thus we only have to validate the escaped sequences.
392 */
393 if (pParsed->fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS)
394 {
395 const char *pchSrc = (const char *)memchr(pszUri, '%', cchUri);
396 AssertReturn(pchSrc, VERR_INTERNAL_ERROR);
397 do
398 {
399 char szUtf8Seq[8];
400 unsigned cchUtf8Seq = 0;
401 unsigned cchNeeded = 0;
402 size_t cchLeft = &pszUri[cchUri] - pchSrc;
403 do
404 {
405 if (cchLeft >= 3)
406 {
407 char chHigh = pchSrc[1];
408 char chLow = pchSrc[2];
409 if ( RT_C_IS_XDIGIT(chHigh)
410 && RT_C_IS_XDIGIT(chLow))
411 {
412 uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10;
413 b <<= 4;
414 b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10;
415
416 if (!(b & 0x80))
417 {
418 /* We don't want the string to be terminated prematurely. */
419 if (RT_LIKELY(b != 0)) { /* likely */ }
420 else return VERR_URI_ESCAPED_ZERO;
421
422 /* Check that we're not expecting more UTF-8 bytes. */
423 if (RT_LIKELY(cchNeeded == 0)) { /* likely */ }
424 else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE;
425 }
426 /* Are we waiting UTF-8 bytes? */
427 else if (cchNeeded > 0)
428 {
429 if (RT_LIKELY(!(b & 0x40))) { /* likely */ }
430 else return VERR_URI_INVALID_ESCAPED_UTF8_CONTINUATION_BYTE;
431
432 szUtf8Seq[cchUtf8Seq++] = (char)b;
433 if (--cchNeeded == 0)
434 {
435 szUtf8Seq[cchUtf8Seq] = '\0';
436 rc = RTStrValidateEncoding(szUtf8Seq);
437 if (RT_FAILURE(rc))
438 return VERR_URI_ESCAPED_CHARS_NOT_VALID_UTF8;
439 cchUtf8Seq = 0;
440 }
441 }
442 /* Start a new UTF-8 sequence. */
443 else
444 {
445 if ((b & 0xf8) == 0xf0)
446 cchNeeded = 3;
447 else if ((b & 0xf0) == 0xe0)
448 cchNeeded = 2;
449 else if ((b & 0xe0) == 0xc0)
450 cchNeeded = 1;
451 else
452 return VERR_URI_INVALID_ESCAPED_UTF8_LEAD_BYTE;
453 szUtf8Seq[0] = (char)b;
454 cchUtf8Seq = 1;
455 }
456 pchSrc += 3;
457 cchLeft -= 3;
458 }
459 else
460 return VERR_URI_INVALID_ESCAPE_SEQ;
461 }
462 else
463 return VERR_URI_INVALID_ESCAPE_SEQ;
464 } while (cchLeft > 0 && pchSrc[0] == '%');
465
466 /* Check that we're not expecting more UTF-8 bytes. */
467 if (RT_LIKELY(cchNeeded == 0)) { /* likely */ }
468 else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE;
469
470 /* next */
471 pchSrc = (const char *)memchr(pchSrc, '%', cchLeft);
472 } while (pchSrc);
473 }
474
475 pParsed->u32Magic = RTURIPARSED_MAGIC;
476 return VINF_SUCCESS;
477}
478
479
480RTDECL(int) RTUriParse(const char *pszUri, PRTURIPARSED pParsed)
481{
482 return rtUriParse(pszUri, pParsed);
483}
484
485
486RTDECL(char *) RTUriParsedScheme(const char *pszUri, PCRTURIPARSED pParsed)
487{
488 AssertPtrReturn(pszUri, NULL);
489 AssertPtrReturn(pParsed, NULL);
490 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
491 return RTStrDupN(pszUri, pParsed->cchScheme);
492}
493
494
495RTDECL(char *) RTUriParsedAuthority(const char *pszUri, PCRTURIPARSED pParsed)
496{
497 AssertPtrReturn(pszUri, NULL);
498 AssertPtrReturn(pParsed, NULL);
499 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
500 if (pParsed->cchAuthority || (pParsed->fFlags & RTURIPARSED_F_HAVE_AUTHORITY))
501 return rtUriPercentDecodeN(&pszUri[pParsed->offAuthority], pParsed->cchAuthority);
502 return NULL;
503}
504
505
506RTDECL(char *) RTUriParsedAuthorityUsername(const char *pszUri, PCRTURIPARSED pParsed)
507{
508 AssertPtrReturn(pszUri, NULL);
509 AssertPtrReturn(pParsed, NULL);
510 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
511 if (pParsed->cchAuthorityUsername)
512 return rtUriPercentDecodeN(&pszUri[pParsed->offAuthorityUsername], pParsed->cchAuthorityUsername);
513 return NULL;
514}
515
516
517RTDECL(char *) RTUriParsedAuthorityPassword(const char *pszUri, PCRTURIPARSED pParsed)
518{
519 AssertPtrReturn(pszUri, NULL);
520 AssertPtrReturn(pParsed, NULL);
521 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
522 if (pParsed->cchAuthorityPassword)
523 return rtUriPercentDecodeN(&pszUri[pParsed->offAuthorityPassword], pParsed->cchAuthorityPassword);
524 return NULL;
525}
526
527
528RTDECL(char *) RTUriParsedAuthorityHost(const char *pszUri, PCRTURIPARSED pParsed)
529{
530 AssertPtrReturn(pszUri, NULL);
531 AssertPtrReturn(pParsed, NULL);
532 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
533 if (pParsed->cchAuthorityHost)
534 return rtUriPercentDecodeN(&pszUri[pParsed->offAuthorityHost], pParsed->cchAuthorityHost);
535 return NULL;
536}
537
538
539RTDECL(uint32_t) RTUriParsedAuthorityPort(const char *pszUri, PCRTURIPARSED pParsed)
540{
541 AssertPtrReturn(pszUri, UINT32_MAX);
542 AssertPtrReturn(pParsed, UINT32_MAX);
543 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, UINT32_MAX);
544 return pParsed->uAuthorityPort;
545}
546
547
548RTDECL(char *) RTUriParsedPath(const char *pszUri, PCRTURIPARSED pParsed)
549{
550 AssertPtrReturn(pszUri, NULL);
551 AssertPtrReturn(pParsed, NULL);
552 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
553 if (pParsed->cchPath)
554 return rtUriPercentDecodeN(&pszUri[pParsed->offPath], pParsed->cchPath);
555 return NULL;
556}
557
558
559RTDECL(char *) RTUriParsedQuery(const char *pszUri, PCRTURIPARSED pParsed)
560{
561 AssertPtrReturn(pszUri, NULL);
562 AssertPtrReturn(pParsed, NULL);
563 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
564 if (pParsed->cchQuery)
565 return rtUriPercentDecodeN(&pszUri[pParsed->offQuery], pParsed->cchQuery);
566 return NULL;
567}
568
569
570RTDECL(char *) RTUriParsedFragment(const char *pszUri, PCRTURIPARSED pParsed)
571{
572 AssertPtrReturn(pszUri, NULL);
573 AssertPtrReturn(pParsed, NULL);
574 AssertReturn(pParsed->u32Magic == RTURIPARSED_MAGIC, NULL);
575 if (pParsed->cchFragment)
576 return rtUriPercentDecodeN(&pszUri[pParsed->offFragment], pParsed->cchFragment);
577 return NULL;
578}
579
580
581RTDECL(char *) RTUriCreate(const char *pszScheme, const char *pszAuthority, const char *pszPath, const char *pszQuery,
582 const char *pszFragment)
583{
584 if (!pszScheme) /* Scheme is minimum requirement */
585 return NULL;
586
587 char *pszResult = 0;
588 char *pszAuthority1 = 0;
589 char *pszPath1 = 0;
590 char *pszQuery1 = 0;
591 char *pszFragment1 = 0;
592
593 do
594 {
595 /* Create the percent encoded strings and calculate the necessary uri
596 * length. */
597 size_t cbSize = strlen(pszScheme) + 1 + 1; /* plus zero byte */
598 if (pszAuthority)
599 {
600 pszAuthority1 = rtUriPercentEncodeN(pszAuthority, RTSTR_MAX);
601 if (!pszAuthority1)
602 break;
603 cbSize += strlen(pszAuthority1) + 2;
604 }
605 if (pszPath)
606 {
607 pszPath1 = rtUriPercentEncodeN(pszPath, RTSTR_MAX);
608 if (!pszPath1)
609 break;
610 cbSize += strlen(pszPath1);
611 }
612 if (pszQuery)
613 {
614 pszQuery1 = rtUriPercentEncodeN(pszQuery, RTSTR_MAX);
615 if (!pszQuery1)
616 break;
617 cbSize += strlen(pszQuery1) + 1;
618 }
619 if (pszFragment)
620 {
621 pszFragment1 = rtUriPercentEncodeN(pszFragment, RTSTR_MAX);
622 if (!pszFragment1)
623 break;
624 cbSize += strlen(pszFragment1) + 1;
625 }
626
627 char *pszTmp = pszResult = (char *)RTStrAlloc(cbSize);
628 if (!pszResult)
629 break;
630 RT_BZERO(pszTmp, cbSize);
631
632 /* Compose the target uri string. */
633 RTStrCatP(&pszTmp, &cbSize, pszScheme);
634 RTStrCatP(&pszTmp, &cbSize, ":");
635 if (pszAuthority1)
636 {
637 RTStrCatP(&pszTmp, &cbSize, "//");
638 RTStrCatP(&pszTmp, &cbSize, pszAuthority1);
639 }
640 if (pszPath1)
641 {
642 RTStrCatP(&pszTmp, &cbSize, pszPath1);
643 }
644 if (pszQuery1)
645 {
646 RTStrCatP(&pszTmp, &cbSize, "?");
647 RTStrCatP(&pszTmp, &cbSize, pszQuery1);
648 }
649 if (pszFragment1)
650 {
651 RTStrCatP(&pszTmp, &cbSize, "#");
652 RTStrCatP(&pszTmp, &cbSize, pszFragment1);
653 }
654 } while (0);
655
656 /* Cleanup */
657 if (pszAuthority1)
658 RTStrFree(pszAuthority1);
659 if (pszPath1)
660 RTStrFree(pszPath1);
661 if (pszQuery1)
662 RTStrFree(pszQuery1);
663 if (pszFragment1)
664 RTStrFree(pszFragment1);
665
666 return pszResult;
667}
668
669
670RTDECL(bool) RTUriIsSchemeMatch(const char *pszUri, const char *pszScheme)
671{
672 AssertPtrReturn(pszUri, false);
673 size_t const cchScheme = strlen(pszScheme);
674 return RTStrNICmp(pszUri, pszScheme, cchScheme) == 0
675 && pszUri[cchScheme] == ':';
676}
677
678
679RTDECL(char *) RTUriFileCreate(const char *pszPath)
680{
681 char *pszResult = NULL;
682 if (pszPath)
683 {
684 /* Check if it's an UNC path. Skip any leading slashes. */
685 while (pszPath)
686 {
687 if ( *pszPath != '\\'
688 && *pszPath != '/')
689 break;
690 pszPath++;
691 }
692
693 /* Create the percent encoded strings and calculate the necessary URI length. */
694 char *pszPath1 = rtUriPercentEncodeN(pszPath, RTSTR_MAX);
695 if (pszPath1)
696 {
697 /* Always change DOS slashes to Unix slashes. */
698 RTPathChangeToUnixSlashes(pszPath1, true); /** @todo Flags? */
699
700 size_t cbSize = 7 /* file:// */ + strlen(pszPath1) + 1; /* plus zero byte */
701 if (pszPath1[0] != '/')
702 ++cbSize;
703 char *pszTmp = pszResult = RTStrAlloc(cbSize);
704 if (pszResult)
705 {
706 /* Compose the target URI string. */
707 *pszTmp = '\0';
708 RTStrCatP(&pszTmp, &cbSize, "file://");
709 if (pszPath1[0] != '/')
710 RTStrCatP(&pszTmp, &cbSize, "/");
711 RTStrCatP(&pszTmp, &cbSize, pszPath1);
712 }
713 RTStrFree(pszPath1);
714 }
715 }
716 else
717 {
718 char *pszResTmp;
719 int cchRes = RTStrAPrintf(&pszResTmp, "file://");
720 if (cchRes)
721 pszResult = pszResTmp;
722 }
723
724 return pszResult;
725}
726
727
728RTDECL(char *) RTUriFilePath(const char *pszUri, uint32_t uFormat)
729{
730 return RTUriFileNPath(pszUri, uFormat, RTSTR_MAX);
731}
732
733
734RTDECL(char *) RTUriFileNPath(const char *pszUri, uint32_t uFormat, size_t cchMax)
735{
736 AssertPtrReturn(pszUri, NULL);
737 AssertReturn(uFormat == URI_FILE_FORMAT_AUTO || uFormat == URI_FILE_FORMAT_UNIX || uFormat == URI_FILE_FORMAT_WIN, NULL);
738
739 /* Auto is based on the current OS. */
740 if (uFormat == URI_FILE_FORMAT_AUTO)
741#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
742 uFormat = URI_FILE_FORMAT_WIN;
743#else
744 uFormat = URI_FILE_FORMAT_UNIX;
745#endif
746
747 /* Check that this is a file URI. */
748 if (RTStrNICmp(pszUri, RT_STR_TUPLE("file:")) != 0)
749 return NULL;
750
751 RTURIPARSED Parsed;
752 int rc = rtUriParse(pszUri, &Parsed);
753 if (RT_SUCCESS(rc))
754 {
755 /* No path detected? Take authority as path then. */
756 if (!Parsed.cchPath)
757 {
758 Parsed.cchPath = Parsed.cchAuthority;
759 Parsed.offPath = Parsed.offAuthority;
760 Parsed.cchAuthority = 0;
761 }
762 }
763
764 if ( RT_SUCCESS(rc)
765 && Parsed.cchPath)
766 {
767 const char *pszPathOff = &pszUri[Parsed.offPath];
768 size_t cbResult = 0;
769
770 /* Skip the leading slash if a DOS drive letter (e.g. "C:") is detected right after it. */
771 if ( Parsed.cchPath >= 3
772 && pszPathOff[0] == '/' /* Leading slash. */
773 && RT_C_IS_ALPHA(pszPathOff[1]) /* Drive letter. */
774 && pszPathOff[2] == ':')
775 {
776 Parsed.offPath++;
777 Parsed.cchPath--;
778 pszPathOff++;
779 }
780
781 if (uFormat == URI_FILE_FORMAT_WIN)
782 {
783 /* Authority given? */
784 if (Parsed.cchAuthority)
785 {
786 /* Include authority as part of UNC path. */
787 cbResult += 2; /* UNC slashes "\\". */
788 cbResult += Parsed.cchAuthority;
789 }
790 }
791
792 cbResult += Parsed.cchPath;
793 cbResult += 1; /* Zero termination. */
794
795 /*
796 * Compose string.
797 */
798 char *pszResult;
799
800 do
801 {
802 char *pszTmp = pszResult = RTStrAlloc(cbResult);
803 if (pszTmp)
804 {
805 size_t cbTmp = cbResult;
806
807 if (uFormat == URI_FILE_FORMAT_WIN)
808 {
809 /* If an authority is given, add the required UNC prefix. */
810 if (Parsed.cchAuthority)
811 {
812 rc = RTStrCatP(&pszTmp, &cbTmp, "\\\\");
813 if (RT_SUCCESS(rc))
814 rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offAuthority], Parsed.cchAuthority);
815 }
816 }
817
818 if (RT_SUCCESS(rc))
819 rc = RTStrCatPEx(&pszTmp, &cbTmp, &pszUri[Parsed.offPath], Parsed.cchPath);
820
821 if (RT_FAILURE(rc))
822 RTStrFree(pszResult);
823 }
824 else
825 rc = VERR_NO_MEMORY;
826
827 } while (0);
828
829 if (RT_SUCCESS(rc))
830 {
831 AssertPtr(pszResult);
832 Assert(cbResult);
833 char *pszPath = rtUriPercentDecodeN(pszResult, cbResult - 1 /* Minus termination */);
834 RTStrFree(pszResult);
835 if (uFormat == URI_FILE_FORMAT_UNIX)
836 return RTPathChangeToUnixSlashes(pszPath, true);
837 Assert(uFormat == URI_FILE_FORMAT_WIN);
838 return RTPathChangeToDosSlashes(pszPath, true);
839 }
840 }
841
842 return NULL;
843}
844
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette