1251881Speter/* 2251881Speter * dirent_uri.c: a library to manipulate URIs and directory entries. 3251881Speter * 4251881Speter * ==================================================================== 5251881Speter * Licensed to the Apache Software Foundation (ASF) under one 6251881Speter * or more contributor license agreements. See the NOTICE file 7251881Speter * distributed with this work for additional information 8251881Speter * regarding copyright ownership. The ASF licenses this file 9251881Speter * to you under the Apache License, Version 2.0 (the 10251881Speter * "License"); you may not use this file except in compliance 11251881Speter * with the License. You may obtain a copy of the License at 12251881Speter * 13251881Speter * http://www.apache.org/licenses/LICENSE-2.0 14251881Speter * 15251881Speter * Unless required by applicable law or agreed to in writing, 16251881Speter * software distributed under the License is distributed on an 17251881Speter * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 18251881Speter * KIND, either express or implied. See the License for the 19251881Speter * specific language governing permissions and limitations 20251881Speter * under the License. 21251881Speter * ==================================================================== 22251881Speter */ 23251881Speter 24251881Speter 25251881Speter 26251881Speter#include <string.h> 27251881Speter#include <assert.h> 28251881Speter#include <ctype.h> 29251881Speter 30251881Speter#include <apr_uri.h> 31251881Speter#include <apr_lib.h> 32251881Speter 33251881Speter#include "svn_private_config.h" 34251881Speter#include "svn_string.h" 35251881Speter#include "svn_dirent_uri.h" 36251881Speter#include "svn_path.h" 37251881Speter#include "svn_ctype.h" 38251881Speter 39251881Speter#include "dirent_uri.h" 40362181Sdim#include "private/svn_dirent_uri_private.h" 41251881Speter#include "private/svn_fspath.h" 42269833Speter#include "private/svn_cert.h" 43251881Speter 44251881Speter/* The canonical empty path. Can this be changed? Well, change the empty 45251881Speter test below and the path library will work, not so sure about the fs/wc 46251881Speter libraries. */ 47251881Speter#define SVN_EMPTY_PATH "" 48251881Speter 49251881Speter/* TRUE if s is the canonical empty path, FALSE otherwise */ 50251881Speter#define SVN_PATH_IS_EMPTY(s) ((s)[0] == '\0') 51251881Speter 52251881Speter/* TRUE if s,n is the platform's empty path ("."), FALSE otherwise. Can 53251881Speter this be changed? Well, the path library will work, not so sure about 54251881Speter the OS! */ 55251881Speter#define SVN_PATH_IS_PLATFORM_EMPTY(s,n) ((n) == 1 && (s)[0] == '.') 56251881Speter 57251881Speter/* This check must match the check on top of dirent_uri-tests.c and 58251881Speter path-tests.c */ 59251881Speter#if defined(WIN32) || defined(__CYGWIN__) || defined(__OS2__) 60251881Speter#define SVN_USE_DOS_PATHS 61251881Speter#endif 62251881Speter 63251881Speter/* Path type definition. Used only by internal functions. */ 64251881Spetertypedef enum path_type_t { 65251881Speter type_uri, 66251881Speter type_dirent, 67251881Speter type_relpath 68251881Speter} path_type_t; 69251881Speter 70251881Speter 71251881Speter/**** Forward declarations *****/ 72251881Speter 73251881Speterstatic svn_boolean_t 74251881Speterrelpath_is_canonical(const char *relpath); 75251881Speter 76251881Speter 77251881Speter/**** Internal implementation functions *****/ 78251881Speter 79251881Speter/* Return an internal-style new path based on PATH, allocated in POOL. 80251881Speter * 81251881Speter * "Internal-style" means that separators are all '/'. 82251881Speter */ 83251881Speterstatic const char * 84251881Speterinternal_style(const char *path, apr_pool_t *pool) 85251881Speter{ 86251881Speter#if '/' != SVN_PATH_LOCAL_SEPARATOR 87251881Speter { 88251881Speter char *p = apr_pstrdup(pool, path); 89251881Speter path = p; 90251881Speter 91251881Speter /* Convert all local-style separators to the canonical ones. */ 92251881Speter for (; *p != '\0'; ++p) 93251881Speter if (*p == SVN_PATH_LOCAL_SEPARATOR) 94251881Speter *p = '/'; 95251881Speter } 96251881Speter#endif 97251881Speter 98251881Speter return path; 99251881Speter} 100251881Speter 101251881Speter/* Locale insensitive tolower() for converting parts of dirents and urls 102251881Speter while canonicalizing */ 103251881Speterstatic char 104251881Spetercanonicalize_to_lower(char c) 105251881Speter{ 106251881Speter if (c < 'A' || c > 'Z') 107251881Speter return c; 108251881Speter else 109251881Speter return (char)(c - 'A' + 'a'); 110251881Speter} 111251881Speter 112251881Speter/* Locale insensitive toupper() for converting parts of dirents and urls 113251881Speter while canonicalizing */ 114251881Speterstatic char 115251881Spetercanonicalize_to_upper(char c) 116251881Speter{ 117251881Speter if (c < 'a' || c > 'z') 118251881Speter return c; 119251881Speter else 120251881Speter return (char)(c - 'a' + 'A'); 121251881Speter} 122251881Speter 123251881Speter/* Calculates the length of the dirent absolute or non absolute root in 124251881Speter DIRENT, return 0 if dirent is not rooted */ 125251881Speterstatic apr_size_t 126251881Speterdirent_root_length(const char *dirent, apr_size_t len) 127251881Speter{ 128251881Speter#ifdef SVN_USE_DOS_PATHS 129251881Speter if (len >= 2 && dirent[1] == ':' && 130251881Speter ((dirent[0] >= 'A' && dirent[0] <= 'Z') || 131251881Speter (dirent[0] >= 'a' && dirent[0] <= 'z'))) 132251881Speter { 133251881Speter return (len > 2 && dirent[2] == '/') ? 3 : 2; 134251881Speter } 135251881Speter 136251881Speter if (len > 2 && dirent[0] == '/' && dirent[1] == '/') 137251881Speter { 138251881Speter apr_size_t i = 2; 139251881Speter 140251881Speter while (i < len && dirent[i] != '/') 141251881Speter i++; 142251881Speter 143251881Speter if (i == len) 144251881Speter return len; /* Cygwin drive alias, invalid path on WIN32 */ 145251881Speter 146251881Speter i++; /* Skip '/' */ 147251881Speter 148251881Speter while (i < len && dirent[i] != '/') 149251881Speter i++; 150251881Speter 151251881Speter return i; 152251881Speter } 153251881Speter#endif /* SVN_USE_DOS_PATHS */ 154251881Speter if (len >= 1 && dirent[0] == '/') 155251881Speter return 1; 156251881Speter 157251881Speter return 0; 158251881Speter} 159251881Speter 160251881Speter 161251881Speter/* Return the length of substring necessary to encompass the entire 162251881Speter * previous dirent segment in DIRENT, which should be a LEN byte string. 163251881Speter * 164251881Speter * A trailing slash will not be included in the returned length except 165251881Speter * in the case in which DIRENT is absolute and there are no more 166251881Speter * previous segments. 167251881Speter */ 168251881Speterstatic apr_size_t 169251881Speterdirent_previous_segment(const char *dirent, 170251881Speter apr_size_t len) 171251881Speter{ 172251881Speter if (len == 0) 173251881Speter return 0; 174251881Speter 175251881Speter --len; 176251881Speter while (len > 0 && dirent[len] != '/' 177251881Speter#ifdef SVN_USE_DOS_PATHS 178251881Speter && (dirent[len] != ':' || len != 1) 179251881Speter#endif /* SVN_USE_DOS_PATHS */ 180251881Speter ) 181251881Speter --len; 182251881Speter 183251881Speter /* check if the remaining segment including trailing '/' is a root dirent */ 184251881Speter if (dirent_root_length(dirent, len+1) == len + 1) 185251881Speter return len + 1; 186251881Speter else 187251881Speter return len; 188251881Speter} 189251881Speter 190251881Speter/* Calculates the length occupied by the schema defined root of URI */ 191251881Speterstatic apr_size_t 192251881Speteruri_schema_root_length(const char *uri, apr_size_t len) 193251881Speter{ 194251881Speter apr_size_t i; 195251881Speter 196251881Speter for (i = 0; i < len; i++) 197251881Speter { 198251881Speter if (uri[i] == '/') 199251881Speter { 200251881Speter if (i > 0 && uri[i-1] == ':' && i < len-1 && uri[i+1] == '/') 201251881Speter { 202251881Speter /* We have an absolute uri */ 203251881Speter if (i == 5 && strncmp("file", uri, 4) == 0) 204251881Speter return 7; /* file:// */ 205251881Speter else 206251881Speter { 207251881Speter for (i += 2; i < len; i++) 208251881Speter if (uri[i] == '/') 209251881Speter return i; 210251881Speter 211251881Speter return len; /* Only a hostname is found */ 212251881Speter } 213251881Speter } 214251881Speter else 215251881Speter return 0; 216251881Speter } 217251881Speter } 218251881Speter 219251881Speter return 0; 220251881Speter} 221251881Speter 222251881Speter/* Returns TRUE if svn_dirent_is_absolute(dirent) or when dirent has 223251881Speter a non absolute root. (E.g. '/' or 'F:' on Windows) */ 224251881Speterstatic svn_boolean_t 225251881Speterdirent_is_rooted(const char *dirent) 226251881Speter{ 227251881Speter if (! dirent) 228251881Speter return FALSE; 229251881Speter 230251881Speter /* Root on all systems */ 231251881Speter if (dirent[0] == '/') 232251881Speter return TRUE; 233251881Speter 234251881Speter /* On Windows, dirent is also absolute when it starts with 'H:' or 'H:/' 235251881Speter where 'H' is any letter. */ 236251881Speter#ifdef SVN_USE_DOS_PATHS 237251881Speter if (((dirent[0] >= 'A' && dirent[0] <= 'Z') || 238251881Speter (dirent[0] >= 'a' && dirent[0] <= 'z')) && 239251881Speter (dirent[1] == ':')) 240251881Speter return TRUE; 241251881Speter#endif /* SVN_USE_DOS_PATHS */ 242251881Speter 243251881Speter return FALSE; 244251881Speter} 245251881Speter 246251881Speter/* Return the length of substring necessary to encompass the entire 247251881Speter * previous relpath segment in RELPATH, which should be a LEN byte string. 248251881Speter * 249251881Speter * A trailing slash will not be included in the returned length. 250251881Speter */ 251251881Speterstatic apr_size_t 252251881Speterrelpath_previous_segment(const char *relpath, 253251881Speter apr_size_t len) 254251881Speter{ 255251881Speter if (len == 0) 256251881Speter return 0; 257251881Speter 258251881Speter --len; 259251881Speter while (len > 0 && relpath[len] != '/') 260251881Speter --len; 261251881Speter 262251881Speter return len; 263251881Speter} 264251881Speter 265251881Speter/* Return the length of substring necessary to encompass the entire 266251881Speter * previous uri segment in URI, which should be a LEN byte string. 267251881Speter * 268251881Speter * A trailing slash will not be included in the returned length except 269251881Speter * in the case in which URI is absolute and there are no more 270251881Speter * previous segments. 271251881Speter */ 272251881Speterstatic apr_size_t 273251881Speteruri_previous_segment(const char *uri, 274251881Speter apr_size_t len) 275251881Speter{ 276251881Speter apr_size_t root_length; 277251881Speter apr_size_t i = len; 278251881Speter if (len == 0) 279251881Speter return 0; 280251881Speter 281251881Speter root_length = uri_schema_root_length(uri, len); 282251881Speter 283251881Speter --i; 284251881Speter while (len > root_length && uri[i] != '/') 285251881Speter --i; 286251881Speter 287251881Speter if (i == 0 && len > 1 && *uri == '/') 288251881Speter return 1; 289251881Speter 290251881Speter return i; 291251881Speter} 292251881Speter 293251881Speter/* Return the canonicalized version of PATH, of type TYPE, allocated in 294251881Speter * POOL. 295251881Speter */ 296362181Sdimstatic svn_error_t * 297362181Sdimcanonicalize(const char **canonical_path, 298362181Sdim path_type_t type, const char *path, apr_pool_t *pool) 299251881Speter{ 300251881Speter char *canon, *dst; 301251881Speter const char *src; 302251881Speter apr_size_t seglen; 303251881Speter apr_size_t schemelen = 0; 304251881Speter apr_size_t canon_segments = 0; 305251881Speter svn_boolean_t url = FALSE; 306251881Speter char *schema_data = NULL; 307251881Speter 308251881Speter /* "" is already canonical, so just return it; note that later code 309251881Speter depends on path not being zero-length. */ 310251881Speter if (SVN_PATH_IS_EMPTY(path)) 311251881Speter { 312362181Sdim *canonical_path = ""; 313362181Sdim if (type == type_uri) 314362181Sdim return svn_error_create(SVN_ERR_CANONICALIZATION_FAILED, NULL, 315362181Sdim _("An empty URI can not be canonicalized")); 316362181Sdim else 317362181Sdim return SVN_NO_ERROR; 318251881Speter } 319251881Speter 320251881Speter dst = canon = apr_pcalloc(pool, strlen(path) + 1); 321251881Speter 322251881Speter /* If this is supposed to be an URI, it should start with 323251881Speter "scheme://". We'll copy the scheme, host name, etc. to DST and 324251881Speter set URL = TRUE. */ 325251881Speter src = path; 326251881Speter if (type == type_uri) 327251881Speter { 328362181Sdim if (*src == '/') 329362181Sdim { 330362181Sdim *canonical_path = src; 331362181Sdim return svn_error_create(SVN_ERR_CANONICALIZATION_FAILED, NULL, 332362181Sdim _("A URI can not start with '/'")); 333362181Sdim } 334251881Speter 335251881Speter while (*src && (*src != '/') && (*src != ':')) 336251881Speter src++; 337251881Speter 338251881Speter if (*src == ':' && *(src+1) == '/' && *(src+2) == '/') 339251881Speter { 340251881Speter const char *seg; 341251881Speter 342251881Speter url = TRUE; 343251881Speter 344251881Speter /* Found a scheme, convert to lowercase and copy to dst. */ 345251881Speter src = path; 346251881Speter while (*src != ':') 347251881Speter { 348251881Speter *(dst++) = canonicalize_to_lower((*src++)); 349251881Speter schemelen++; 350251881Speter } 351251881Speter *(dst++) = ':'; 352251881Speter *(dst++) = '/'; 353251881Speter *(dst++) = '/'; 354251881Speter src += 3; 355251881Speter schemelen += 3; 356251881Speter 357251881Speter /* This might be the hostname */ 358251881Speter seg = src; 359251881Speter while (*src && (*src != '/') && (*src != '@')) 360251881Speter src++; 361251881Speter 362251881Speter if (*src == '@') 363251881Speter { 364251881Speter /* Copy the username & password. */ 365251881Speter seglen = src - seg + 1; 366251881Speter memcpy(dst, seg, seglen); 367251881Speter dst += seglen; 368251881Speter src++; 369251881Speter } 370251881Speter else 371251881Speter src = seg; 372251881Speter 373251881Speter /* Found a hostname, convert to lowercase and copy to dst. */ 374251881Speter if (*src == '[') 375251881Speter { 376251881Speter *(dst++) = *(src++); /* Copy '[' */ 377251881Speter 378251881Speter while (*src == ':' 379251881Speter || (*src >= '0' && (*src <= '9')) 380251881Speter || (*src >= 'a' && (*src <= 'f')) 381251881Speter || (*src >= 'A' && (*src <= 'F'))) 382251881Speter { 383251881Speter *(dst++) = canonicalize_to_lower((*src++)); 384251881Speter } 385251881Speter 386251881Speter if (*src == ']') 387251881Speter *(dst++) = *(src++); /* Copy ']' */ 388251881Speter } 389251881Speter else 390251881Speter while (*src && (*src != '/') && (*src != ':')) 391251881Speter *(dst++) = canonicalize_to_lower((*src++)); 392251881Speter 393251881Speter if (*src == ':') 394251881Speter { 395251881Speter /* We probably have a port number: Is it a default portnumber 396251881Speter which doesn't belong in a canonical url? */ 397251881Speter if (src[1] == '8' && src[2] == '0' 398251881Speter && (src[3]== '/'|| !src[3]) 399251881Speter && !strncmp(canon, "http:", 5)) 400251881Speter { 401251881Speter src += 3; 402251881Speter } 403251881Speter else if (src[1] == '4' && src[2] == '4' && src[3] == '3' 404251881Speter && (src[4]== '/'|| !src[4]) 405251881Speter && !strncmp(canon, "https:", 6)) 406251881Speter { 407251881Speter src += 4; 408251881Speter } 409251881Speter else if (src[1] == '3' && src[2] == '6' 410251881Speter && src[3] == '9' && src[4] == '0' 411251881Speter && (src[5]== '/'|| !src[5]) 412251881Speter && !strncmp(canon, "svn:", 4)) 413251881Speter { 414251881Speter src += 5; 415251881Speter } 416251881Speter else if (src[1] == '/' || !src[1]) 417251881Speter { 418251881Speter src += 1; 419251881Speter } 420251881Speter 421251881Speter while (*src && (*src != '/')) 422251881Speter *(dst++) = canonicalize_to_lower((*src++)); 423251881Speter } 424251881Speter 425251881Speter /* Copy trailing slash, or null-terminator. */ 426251881Speter *(dst) = *(src); 427251881Speter 428251881Speter /* Move src and dst forward only if we are not 429251881Speter * at null-terminator yet. */ 430251881Speter if (*src) 431251881Speter { 432251881Speter src++; 433251881Speter dst++; 434251881Speter schema_data = dst; 435251881Speter } 436251881Speter 437251881Speter canon_segments = 1; 438251881Speter } 439251881Speter } 440251881Speter 441251881Speter /* Copy to DST any separator or drive letter that must come before the 442251881Speter first regular path segment. */ 443251881Speter if (! url && type != type_relpath) 444251881Speter { 445251881Speter src = path; 446251881Speter /* If this is an absolute path, then just copy over the initial 447251881Speter separator character. */ 448251881Speter if (*src == '/') 449251881Speter { 450251881Speter *(dst++) = *(src++); 451251881Speter 452251881Speter#ifdef SVN_USE_DOS_PATHS 453251881Speter /* On Windows permit two leading separator characters which means an 454251881Speter * UNC path. */ 455251881Speter if ((type == type_dirent) && *src == '/') 456251881Speter *(dst++) = *(src++); 457251881Speter#endif /* SVN_USE_DOS_PATHS */ 458251881Speter } 459251881Speter#ifdef SVN_USE_DOS_PATHS 460251881Speter /* On Windows the first segment can be a drive letter, which we normalize 461251881Speter to upper case. */ 462251881Speter else if (type == type_dirent && 463251881Speter ((*src >= 'a' && *src <= 'z') || 464251881Speter (*src >= 'A' && *src <= 'Z')) && 465251881Speter (src[1] == ':')) 466251881Speter { 467251881Speter *(dst++) = canonicalize_to_upper(*(src++)); 468251881Speter /* Leave the ':' to be processed as (or as part of) a path segment 469251881Speter by the following code block, so we need not care whether it has 470251881Speter a slash after it. */ 471251881Speter } 472251881Speter#endif /* SVN_USE_DOS_PATHS */ 473251881Speter } 474251881Speter 475251881Speter while (*src) 476251881Speter { 477251881Speter /* Parse each segment, finding the closing '/' (which might look 478251881Speter like '%2F' for URIs). */ 479251881Speter const char *next = src; 480251881Speter apr_size_t slash_len = 0; 481251881Speter 482251881Speter while (*next 483251881Speter && (next[0] != '/') 484251881Speter && (! (type == type_uri && next[0] == '%' && next[1] == '2' && 485251881Speter canonicalize_to_upper(next[2]) == 'F'))) 486251881Speter { 487251881Speter ++next; 488251881Speter } 489251881Speter 490251881Speter /* Record how long our "slash" is. */ 491251881Speter if (next[0] == '/') 492251881Speter slash_len = 1; 493251881Speter else if (type == type_uri && next[0] == '%') 494251881Speter slash_len = 3; 495251881Speter 496251881Speter seglen = next - src; 497251881Speter 498251881Speter if (seglen == 0 499251881Speter || (seglen == 1 && src[0] == '.') 500251881Speter || (type == type_uri && seglen == 3 && src[0] == '%' && src[1] == '2' 501251881Speter && canonicalize_to_upper(src[2]) == 'E')) 502251881Speter { 503251881Speter /* Empty or noop segment, so do nothing. (For URIs, '%2E' 504251881Speter is equivalent to '.'). */ 505251881Speter } 506251881Speter#ifdef SVN_USE_DOS_PATHS 507251881Speter /* If this is the first path segment of a file:// URI and it contains a 508251881Speter windows drive letter, convert the drive letter to upper case. */ 509362181Sdim else if (url && canon_segments == 1 && seglen >= 2 && 510251881Speter (strncmp(canon, "file:", 5) == 0) && 511251881Speter src[0] >= 'a' && src[0] <= 'z' && src[1] == ':') 512251881Speter { 513251881Speter *(dst++) = canonicalize_to_upper(src[0]); 514251881Speter *(dst++) = ':'; 515362181Sdim if (seglen > 2) /* drive relative path */ 516362181Sdim { 517362181Sdim memcpy(dst, src + 2, seglen - 2); 518362181Sdim dst += seglen - 2; 519362181Sdim } 520362181Sdim 521362181Sdim if (slash_len) 522362181Sdim *(dst++) = '/'; 523251881Speter canon_segments++; 524251881Speter } 525251881Speter#endif /* SVN_USE_DOS_PATHS */ 526251881Speter else 527251881Speter { 528251881Speter /* An actual segment, append it to the destination path */ 529251881Speter memcpy(dst, src, seglen); 530251881Speter dst += seglen; 531251881Speter if (slash_len) 532251881Speter *(dst++) = '/'; 533251881Speter canon_segments++; 534251881Speter } 535251881Speter 536251881Speter /* Skip over trailing slash to the next segment. */ 537251881Speter src = next + slash_len; 538251881Speter } 539251881Speter 540251881Speter /* Remove the trailing slash if there was at least one 541251881Speter * canonical segment and the last segment ends with a slash. 542251881Speter * 543251881Speter * But keep in mind that, for URLs, the scheme counts as a 544251881Speter * canonical segment -- so if path is ONLY a scheme (such 545251881Speter * as "https://") we should NOT remove the trailing slash. */ 546251881Speter if ((canon_segments > 0 && *(dst - 1) == '/') 547251881Speter && ! (url && path[schemelen] == '\0')) 548251881Speter { 549251881Speter dst --; 550251881Speter } 551251881Speter 552251881Speter *dst = '\0'; 553251881Speter 554251881Speter#ifdef SVN_USE_DOS_PATHS 555251881Speter /* Skip leading double slashes when there are less than 2 556251881Speter * canon segments. UNC paths *MUST* have two segments. */ 557251881Speter if ((type == type_dirent) && canon[0] == '/' && canon[1] == '/') 558251881Speter { 559251881Speter if (canon_segments < 2) 560362181Sdim { 561362181Sdim *canonical_path = canon + 1; 562362181Sdim return SVN_NO_ERROR; 563362181Sdim } 564251881Speter else 565251881Speter { 566251881Speter /* Now we're sure this is a valid UNC path, convert the server name 567251881Speter (the first path segment) to lowercase as Windows treats it as case 568251881Speter insensitive. 569251881Speter Note: normally the share name is treated as case insensitive too, 570251881Speter but it seems to be possible to configure Samba to treat those as 571251881Speter case sensitive, so better leave that alone. */ 572251881Speter for (dst = canon + 2; *dst && *dst != '/'; dst++) 573251881Speter *dst = canonicalize_to_lower(*dst); 574251881Speter } 575251881Speter } 576251881Speter#endif /* SVN_USE_DOS_PATHS */ 577251881Speter 578251881Speter /* Check the normalization of characters in a uri */ 579251881Speter if (schema_data) 580251881Speter { 581251881Speter int need_extra = 0; 582251881Speter src = schema_data; 583251881Speter 584251881Speter while (*src) 585251881Speter { 586251881Speter switch (*src) 587251881Speter { 588251881Speter case '/': 589251881Speter break; 590251881Speter case '%': 591251881Speter if (!svn_ctype_isxdigit(*(src+1)) || 592251881Speter !svn_ctype_isxdigit(*(src+2))) 593251881Speter need_extra += 2; 594251881Speter else 595251881Speter src += 2; 596251881Speter break; 597251881Speter default: 598251881Speter if (!svn_uri__char_validity[(unsigned char)*src]) 599251881Speter need_extra += 2; 600251881Speter break; 601251881Speter } 602251881Speter src++; 603251881Speter } 604251881Speter 605251881Speter if (need_extra > 0) 606251881Speter { 607251881Speter apr_size_t pre_schema_size = (apr_size_t)(schema_data - canon); 608251881Speter 609251881Speter dst = apr_palloc(pool, (apr_size_t)(src - canon) + need_extra + 1); 610251881Speter memcpy(dst, canon, pre_schema_size); 611251881Speter canon = dst; 612251881Speter 613251881Speter dst += pre_schema_size; 614251881Speter } 615251881Speter else 616251881Speter dst = schema_data; 617251881Speter 618251881Speter src = schema_data; 619251881Speter 620251881Speter while (*src) 621251881Speter { 622251881Speter switch (*src) 623251881Speter { 624251881Speter case '/': 625251881Speter *(dst++) = '/'; 626251881Speter break; 627251881Speter case '%': 628251881Speter if (!svn_ctype_isxdigit(*(src+1)) || 629251881Speter !svn_ctype_isxdigit(*(src+2))) 630251881Speter { 631251881Speter *(dst++) = '%'; 632251881Speter *(dst++) = '2'; 633251881Speter *(dst++) = '5'; 634251881Speter } 635251881Speter else 636251881Speter { 637251881Speter char digitz[3]; 638251881Speter int val; 639251881Speter 640251881Speter digitz[0] = *(++src); 641251881Speter digitz[1] = *(++src); 642251881Speter digitz[2] = 0; 643251881Speter 644251881Speter val = (int)strtol(digitz, NULL, 16); 645251881Speter 646251881Speter if (svn_uri__char_validity[(unsigned char)val]) 647251881Speter *(dst++) = (char)val; 648251881Speter else 649251881Speter { 650251881Speter *(dst++) = '%'; 651251881Speter *(dst++) = canonicalize_to_upper(digitz[0]); 652251881Speter *(dst++) = canonicalize_to_upper(digitz[1]); 653251881Speter } 654251881Speter } 655251881Speter break; 656251881Speter default: 657251881Speter if (!svn_uri__char_validity[(unsigned char)*src]) 658251881Speter { 659251881Speter apr_snprintf(dst, 4, "%%%02X", (unsigned char)*src); 660251881Speter dst += 3; 661251881Speter } 662251881Speter else 663251881Speter *(dst++) = *src; 664251881Speter break; 665251881Speter } 666251881Speter src++; 667251881Speter } 668251881Speter *dst = '\0'; 669251881Speter } 670251881Speter 671362181Sdim *canonical_path = canon; 672362181Sdim return SVN_NO_ERROR; 673251881Speter} 674251881Speter 675251881Speter/* Return the string length of the longest common ancestor of PATH1 and PATH2. 676251881Speter * Pass type_uri for TYPE if PATH1 and PATH2 are URIs, and type_dirent if 677251881Speter * PATH1 and PATH2 are regular paths. 678251881Speter * 679251881Speter * If the two paths do not share a common ancestor, return 0. 680251881Speter * 681251881Speter * New strings are allocated in POOL. 682251881Speter */ 683251881Speterstatic apr_size_t 684251881Speterget_longest_ancestor_length(path_type_t types, 685251881Speter const char *path1, 686251881Speter const char *path2, 687251881Speter apr_pool_t *pool) 688251881Speter{ 689251881Speter apr_size_t path1_len, path2_len; 690251881Speter apr_size_t i = 0; 691251881Speter apr_size_t last_dirsep = 0; 692251881Speter#ifdef SVN_USE_DOS_PATHS 693251881Speter svn_boolean_t unc = FALSE; 694251881Speter#endif 695251881Speter 696251881Speter path1_len = strlen(path1); 697251881Speter path2_len = strlen(path2); 698251881Speter 699251881Speter if (SVN_PATH_IS_EMPTY(path1) || SVN_PATH_IS_EMPTY(path2)) 700251881Speter return 0; 701251881Speter 702251881Speter while (path1[i] == path2[i]) 703251881Speter { 704251881Speter /* Keep track of the last directory separator we hit. */ 705251881Speter if (path1[i] == '/') 706251881Speter last_dirsep = i; 707251881Speter 708251881Speter i++; 709251881Speter 710251881Speter /* If we get to the end of either path, break out. */ 711251881Speter if ((i == path1_len) || (i == path2_len)) 712251881Speter break; 713251881Speter } 714251881Speter 715251881Speter /* two special cases: 716251881Speter 1. '/' is the longest common ancestor of '/' and '/foo' */ 717251881Speter if (i == 1 && path1[0] == '/' && path2[0] == '/') 718251881Speter return 1; 719251881Speter /* 2. '' is the longest common ancestor of any non-matching 720251881Speter * strings 'foo' and 'bar' */ 721251881Speter if (types == type_dirent && i == 0) 722251881Speter return 0; 723251881Speter 724251881Speter /* Handle some windows specific cases */ 725251881Speter#ifdef SVN_USE_DOS_PATHS 726251881Speter if (types == type_dirent) 727251881Speter { 728251881Speter /* don't count the '//' from UNC paths */ 729251881Speter if (last_dirsep == 1 && path1[0] == '/' && path1[1] == '/') 730251881Speter { 731251881Speter last_dirsep = 0; 732251881Speter unc = TRUE; 733251881Speter } 734251881Speter 735251881Speter /* X:/ and X:/foo */ 736251881Speter if (i == 3 && path1[2] == '/' && path1[1] == ':') 737251881Speter return i; 738251881Speter 739251881Speter /* Cannot use SVN_ERR_ASSERT here, so we'll have to crash, sorry. 740251881Speter * Note that this assertion triggers only if the code above has 741251881Speter * been broken. The code below relies on this assertion, because 742251881Speter * it uses [i - 1] as index. */ 743251881Speter assert(i > 0); 744251881Speter 745251881Speter /* X: and X:/ */ 746251881Speter if ((path1[i - 1] == ':' && path2[i] == '/') || 747251881Speter (path2[i - 1] == ':' && path1[i] == '/')) 748251881Speter return 0; 749251881Speter /* X: and X:foo */ 750251881Speter if (path1[i - 1] == ':' || path2[i - 1] == ':') 751251881Speter return i; 752251881Speter } 753251881Speter#endif /* SVN_USE_DOS_PATHS */ 754251881Speter 755251881Speter /* last_dirsep is now the offset of the last directory separator we 756251881Speter crossed before reaching a non-matching byte. i is the offset of 757251881Speter that non-matching byte, and is guaranteed to be <= the length of 758251881Speter whichever path is shorter. 759251881Speter If one of the paths is the common part return that. */ 760251881Speter if (((i == path1_len) && (path2[i] == '/')) 761251881Speter || ((i == path2_len) && (path1[i] == '/')) 762251881Speter || ((i == path1_len) && (i == path2_len))) 763251881Speter return i; 764251881Speter else 765251881Speter { 766251881Speter /* Nothing in common but the root folder '/' or 'X:/' for Windows 767251881Speter dirents. */ 768251881Speter#ifdef SVN_USE_DOS_PATHS 769251881Speter if (! unc) 770251881Speter { 771251881Speter /* X:/foo and X:/bar returns X:/ */ 772251881Speter if ((types == type_dirent) && 773251881Speter last_dirsep == 2 && path1[1] == ':' && path1[2] == '/' 774251881Speter && path2[1] == ':' && path2[2] == '/') 775251881Speter return 3; 776251881Speter#endif /* SVN_USE_DOS_PATHS */ 777251881Speter if (last_dirsep == 0 && path1[0] == '/' && path2[0] == '/') 778251881Speter return 1; 779251881Speter#ifdef SVN_USE_DOS_PATHS 780251881Speter } 781251881Speter#endif 782251881Speter } 783251881Speter 784251881Speter return last_dirsep; 785251881Speter} 786251881Speter 787251881Speter/* Determine whether PATH2 is a child of PATH1. 788251881Speter * 789251881Speter * PATH2 is a child of PATH1 if 790251881Speter * 1) PATH1 is empty, and PATH2 is not empty and not an absolute path. 791251881Speter * or 792251881Speter * 2) PATH2 is has n components, PATH1 has x < n components, 793251881Speter * and PATH1 matches PATH2 in all its x components. 794251881Speter * Components are separated by a slash, '/'. 795251881Speter * 796251881Speter * Pass type_uri for TYPE if PATH1 and PATH2 are URIs, and type_dirent if 797251881Speter * PATH1 and PATH2 are regular paths. 798251881Speter * 799251881Speter * If PATH2 is not a child of PATH1, return NULL. 800251881Speter * 801251881Speter * If PATH2 is a child of PATH1, and POOL is not NULL, allocate a copy 802251881Speter * of the child part of PATH2 in POOL and return a pointer to the 803251881Speter * newly allocated child part. 804251881Speter * 805251881Speter * If PATH2 is a child of PATH1, and POOL is NULL, return a pointer 806251881Speter * pointing to the child part of PATH2. 807251881Speter * */ 808251881Speterstatic const char * 809251881Speteris_child(path_type_t type, const char *path1, const char *path2, 810251881Speter apr_pool_t *pool) 811251881Speter{ 812251881Speter apr_size_t i; 813251881Speter 814251881Speter /* Allow "" and "foo" or "H:foo" to be parent/child */ 815251881Speter if (SVN_PATH_IS_EMPTY(path1)) /* "" is the parent */ 816251881Speter { 817251881Speter if (SVN_PATH_IS_EMPTY(path2)) /* "" not a child */ 818251881Speter return NULL; 819251881Speter 820251881Speter /* check if this is an absolute path */ 821251881Speter if ((type == type_uri) || 822251881Speter (type == type_dirent && dirent_is_rooted(path2))) 823251881Speter return NULL; 824251881Speter else 825251881Speter /* everything else is child */ 826251881Speter return pool ? apr_pstrdup(pool, path2) : path2; 827251881Speter } 828251881Speter 829251881Speter /* Reach the end of at least one of the paths. How should we handle 830251881Speter things like path1:"foo///bar" and path2:"foo/bar/baz"? It doesn't 831251881Speter appear to arise in the current Subversion code, it's not clear to me 832251881Speter if they should be parent/child or not. */ 833251881Speter /* Hmmm... aren't paths assumed to be canonical in this function? 834251881Speter * How can "foo///bar" even happen if the paths are canonical? */ 835251881Speter for (i = 0; path1[i] && path2[i]; i++) 836251881Speter if (path1[i] != path2[i]) 837251881Speter return NULL; 838251881Speter 839251881Speter /* FIXME: This comment does not really match 840251881Speter * the checks made in the code it refers to: */ 841251881Speter /* There are two cases that are parent/child 842251881Speter ... path1[i] == '\0' 843251881Speter .../foo path2[i] == '/' 844251881Speter or 845251881Speter / path1[i] == '\0' 846251881Speter /foo path2[i] != '/' 847251881Speter 848251881Speter Other root paths (like X:/) fall under the former case: 849251881Speter X:/ path1[i] == '\0' 850251881Speter X:/foo path2[i] != '/' 851251881Speter 852251881Speter Check for '//' to avoid matching '/' and '//srv'. 853251881Speter */ 854251881Speter if (path1[i] == '\0' && path2[i]) 855251881Speter { 856251881Speter if (path1[i - 1] == '/' 857251881Speter#ifdef SVN_USE_DOS_PATHS 858251881Speter || ((type == type_dirent) && path1[i - 1] == ':') 859251881Speter#endif 860251881Speter ) 861251881Speter { 862251881Speter if (path2[i] == '/') 863251881Speter /* .../ 864251881Speter * ..../ 865251881Speter * i */ 866251881Speter return NULL; 867251881Speter else 868251881Speter /* .../ 869251881Speter * .../foo 870251881Speter * i */ 871251881Speter return pool ? apr_pstrdup(pool, path2 + i) : path2 + i; 872251881Speter } 873251881Speter else if (path2[i] == '/') 874251881Speter { 875251881Speter if (path2[i + 1]) 876251881Speter /* ... 877251881Speter * .../foo 878251881Speter * i */ 879251881Speter return pool ? apr_pstrdup(pool, path2 + i + 1) : path2 + i + 1; 880251881Speter else 881251881Speter /* ... 882251881Speter * .../ 883251881Speter * i */ 884251881Speter return NULL; 885251881Speter } 886251881Speter } 887251881Speter 888251881Speter /* Otherwise, path2 isn't a child. */ 889251881Speter return NULL; 890251881Speter} 891251881Speter 892251881Speter 893251881Speter/**** Public API functions ****/ 894251881Speter 895251881Speterconst char * 896251881Spetersvn_dirent_internal_style(const char *dirent, apr_pool_t *pool) 897251881Speter{ 898251881Speter return svn_dirent_canonicalize(internal_style(dirent, pool), pool); 899251881Speter} 900251881Speter 901362181Sdimsvn_error_t * 902362181Sdimsvn_dirent_internal_style_safe(const char **internal_style_dirent, 903362181Sdim const char **non_canonical_result, 904362181Sdim const char *dirent, 905362181Sdim apr_pool_t *result_pool, 906362181Sdim apr_pool_t *scratch_pool) 907362181Sdim{ 908362181Sdim return svn_error_trace( 909362181Sdim svn_dirent_canonicalize_safe(internal_style_dirent, 910362181Sdim non_canonical_result, 911362181Sdim internal_style(dirent, scratch_pool), 912362181Sdim result_pool, scratch_pool)); 913362181Sdim} 914362181Sdim 915251881Speterconst char * 916251881Spetersvn_dirent_local_style(const char *dirent, apr_pool_t *pool) 917251881Speter{ 918251881Speter /* Internally, Subversion represents the current directory with the 919251881Speter empty string. But users like to see "." . */ 920251881Speter if (SVN_PATH_IS_EMPTY(dirent)) 921251881Speter return "."; 922251881Speter 923251881Speter#if '/' != SVN_PATH_LOCAL_SEPARATOR 924251881Speter { 925251881Speter char *p = apr_pstrdup(pool, dirent); 926251881Speter dirent = p; 927251881Speter 928251881Speter /* Convert all canonical separators to the local-style ones. */ 929251881Speter for (; *p != '\0'; ++p) 930251881Speter if (*p == '/') 931251881Speter *p = SVN_PATH_LOCAL_SEPARATOR; 932251881Speter } 933251881Speter#endif 934251881Speter 935251881Speter return dirent; 936251881Speter} 937251881Speter 938362181Sdimsvn_error_t * 939362181Sdimsvn_relpath__make_internal(const char **internal_style_relpath, 940362181Sdim const char *relpath, 941362181Sdim apr_pool_t *result_pool, 942362181Sdim apr_pool_t *scratch_pool) 943251881Speter{ 944362181Sdim return svn_error_trace( 945362181Sdim svn_relpath_canonicalize_safe(internal_style_relpath, NULL, 946362181Sdim internal_style(relpath, scratch_pool), 947362181Sdim result_pool, scratch_pool)); 948251881Speter} 949251881Speter 950251881Speter/* We decided against using apr_filepath_root here because of the negative 951251881Speter performance impact (creating a pool and converting strings ). */ 952251881Spetersvn_boolean_t 953251881Spetersvn_dirent_is_root(const char *dirent, apr_size_t len) 954251881Speter{ 955251881Speter#ifdef SVN_USE_DOS_PATHS 956251881Speter /* On Windows and Cygwin, 'H:' or 'H:/' (where 'H' is any letter) 957251881Speter are also root directories */ 958251881Speter if ((len == 2 || ((len == 3) && (dirent[2] == '/'))) && 959251881Speter (dirent[1] == ':') && 960251881Speter ((dirent[0] >= 'A' && dirent[0] <= 'Z') || 961251881Speter (dirent[0] >= 'a' && dirent[0] <= 'z'))) 962251881Speter return TRUE; 963251881Speter 964251881Speter /* On Windows and Cygwin //server/share is a root directory, 965251881Speter and on Cygwin //drive is a drive alias */ 966251881Speter if (len >= 2 && dirent[0] == '/' && dirent[1] == '/' 967251881Speter && dirent[len - 1] != '/') 968251881Speter { 969251881Speter int segments = 0; 970251881Speter apr_size_t i; 971251881Speter for (i = len; i >= 2; i--) 972251881Speter { 973251881Speter if (dirent[i] == '/') 974251881Speter { 975251881Speter segments ++; 976251881Speter if (segments > 1) 977251881Speter return FALSE; 978251881Speter } 979251881Speter } 980251881Speter#ifdef __CYGWIN__ 981251881Speter return (segments <= 1); 982251881Speter#else 983251881Speter return (segments == 1); /* //drive is invalid on plain Windows */ 984251881Speter#endif 985251881Speter } 986251881Speter#endif 987251881Speter 988251881Speter /* directory is root if it's equal to '/' */ 989251881Speter if (len == 1 && dirent[0] == '/') 990251881Speter return TRUE; 991251881Speter 992251881Speter return FALSE; 993251881Speter} 994251881Speter 995251881Spetersvn_boolean_t 996251881Spetersvn_uri_is_root(const char *uri, apr_size_t len) 997251881Speter{ 998251881Speter assert(svn_uri_is_canonical(uri, NULL)); 999251881Speter return (len == uri_schema_root_length(uri, len)); 1000251881Speter} 1001251881Speter 1002251881Speterchar *svn_dirent_join(const char *base, 1003251881Speter const char *component, 1004251881Speter apr_pool_t *pool) 1005251881Speter{ 1006251881Speter apr_size_t blen = strlen(base); 1007251881Speter apr_size_t clen = strlen(component); 1008251881Speter char *dirent; 1009251881Speter int add_separator; 1010251881Speter 1011251881Speter assert(svn_dirent_is_canonical(base, pool)); 1012251881Speter assert(svn_dirent_is_canonical(component, pool)); 1013251881Speter 1014251881Speter /* If the component is absolute, then return it. */ 1015251881Speter if (svn_dirent_is_absolute(component)) 1016251881Speter return apr_pmemdup(pool, component, clen + 1); 1017251881Speter 1018251881Speter /* If either is empty return the other */ 1019251881Speter if (SVN_PATH_IS_EMPTY(base)) 1020251881Speter return apr_pmemdup(pool, component, clen + 1); 1021251881Speter if (SVN_PATH_IS_EMPTY(component)) 1022251881Speter return apr_pmemdup(pool, base, blen + 1); 1023251881Speter 1024251881Speter#ifdef SVN_USE_DOS_PATHS 1025251881Speter if (component[0] == '/') 1026251881Speter { 1027251881Speter /* '/' is drive relative on Windows, not absolute like on Posix */ 1028251881Speter if (dirent_is_rooted(base)) 1029251881Speter { 1030251881Speter /* Join component without '/' to root-of(base) */ 1031251881Speter blen = dirent_root_length(base, blen); 1032251881Speter component++; 1033251881Speter clen--; 1034251881Speter 1035251881Speter if (blen == 2 && base[1] == ':') /* "C:" case */ 1036251881Speter { 1037251881Speter char *root = apr_pmemdup(pool, base, 3); 1038251881Speter root[2] = '/'; /* We don't need the final '\0' */ 1039251881Speter 1040251881Speter base = root; 1041251881Speter blen = 3; 1042251881Speter } 1043251881Speter 1044251881Speter if (clen == 0) 1045251881Speter return apr_pstrndup(pool, base, blen); 1046251881Speter } 1047251881Speter else 1048251881Speter return apr_pmemdup(pool, component, clen + 1); 1049251881Speter } 1050251881Speter else if (dirent_is_rooted(component)) 1051251881Speter return apr_pmemdup(pool, component, clen + 1); 1052251881Speter#endif /* SVN_USE_DOS_PATHS */ 1053251881Speter 1054251881Speter /* if last character of base is already a separator, don't add a '/' */ 1055251881Speter add_separator = 1; 1056251881Speter if (base[blen - 1] == '/' 1057251881Speter#ifdef SVN_USE_DOS_PATHS 1058251881Speter || base[blen - 1] == ':' 1059251881Speter#endif 1060251881Speter ) 1061251881Speter add_separator = 0; 1062251881Speter 1063251881Speter /* Construct the new, combined dirent. */ 1064251881Speter dirent = apr_palloc(pool, blen + add_separator + clen + 1); 1065251881Speter memcpy(dirent, base, blen); 1066251881Speter if (add_separator) 1067251881Speter dirent[blen] = '/'; 1068251881Speter memcpy(dirent + blen + add_separator, component, clen + 1); 1069251881Speter 1070251881Speter return dirent; 1071251881Speter} 1072251881Speter 1073251881Speterchar *svn_dirent_join_many(apr_pool_t *pool, const char *base, ...) 1074251881Speter{ 1075251881Speter#define MAX_SAVED_LENGTHS 10 1076251881Speter apr_size_t saved_lengths[MAX_SAVED_LENGTHS]; 1077251881Speter apr_size_t total_len; 1078251881Speter int nargs; 1079251881Speter va_list va; 1080251881Speter const char *s; 1081251881Speter apr_size_t len; 1082251881Speter char *dirent; 1083251881Speter char *p; 1084251881Speter int add_separator; 1085251881Speter int base_arg = 0; 1086251881Speter 1087251881Speter total_len = strlen(base); 1088251881Speter 1089251881Speter assert(svn_dirent_is_canonical(base, pool)); 1090251881Speter 1091251881Speter /* if last character of base is already a separator, don't add a '/' */ 1092251881Speter add_separator = 1; 1093251881Speter if (total_len == 0 1094251881Speter || base[total_len - 1] == '/' 1095251881Speter#ifdef SVN_USE_DOS_PATHS 1096251881Speter || base[total_len - 1] == ':' 1097251881Speter#endif 1098251881Speter ) 1099251881Speter add_separator = 0; 1100251881Speter 1101251881Speter saved_lengths[0] = total_len; 1102251881Speter 1103251881Speter /* Compute the length of the resulting string. */ 1104251881Speter 1105251881Speter nargs = 0; 1106251881Speter va_start(va, base); 1107251881Speter while ((s = va_arg(va, const char *)) != NULL) 1108251881Speter { 1109251881Speter len = strlen(s); 1110251881Speter 1111251881Speter assert(svn_dirent_is_canonical(s, pool)); 1112251881Speter 1113251881Speter if (SVN_PATH_IS_EMPTY(s)) 1114251881Speter continue; 1115251881Speter 1116251881Speter if (nargs++ < MAX_SAVED_LENGTHS) 1117251881Speter saved_lengths[nargs] = len; 1118251881Speter 1119251881Speter if (dirent_is_rooted(s)) 1120251881Speter { 1121251881Speter total_len = len; 1122251881Speter base_arg = nargs; 1123251881Speter 1124251881Speter#ifdef SVN_USE_DOS_PATHS 1125251881Speter if (!svn_dirent_is_absolute(s)) /* Handle non absolute roots */ 1126251881Speter { 1127251881Speter /* Set new base and skip the current argument */ 1128251881Speter base = s = svn_dirent_join(base, s, pool); 1129251881Speter base_arg++; 1130251881Speter saved_lengths[0] = total_len = len = strlen(s); 1131251881Speter } 1132251881Speter else 1133251881Speter#endif /* SVN_USE_DOS_PATHS */ 1134251881Speter { 1135251881Speter base = ""; /* Don't add base */ 1136251881Speter saved_lengths[0] = 0; 1137251881Speter } 1138251881Speter 1139251881Speter add_separator = 1; 1140251881Speter if (s[len - 1] == '/' 1141251881Speter#ifdef SVN_USE_DOS_PATHS 1142251881Speter || s[len - 1] == ':' 1143251881Speter#endif 1144251881Speter ) 1145251881Speter add_separator = 0; 1146251881Speter } 1147251881Speter else if (nargs <= base_arg + 1) 1148251881Speter { 1149251881Speter total_len += add_separator + len; 1150251881Speter } 1151251881Speter else 1152251881Speter { 1153251881Speter total_len += 1 + len; 1154251881Speter } 1155251881Speter } 1156251881Speter va_end(va); 1157251881Speter 1158251881Speter /* base == "/" and no further components. just return that. */ 1159251881Speter if (add_separator == 0 && total_len == 1) 1160251881Speter return apr_pmemdup(pool, "/", 2); 1161251881Speter 1162251881Speter /* we got the total size. allocate it, with room for a NULL character. */ 1163251881Speter dirent = p = apr_palloc(pool, total_len + 1); 1164251881Speter 1165251881Speter /* if we aren't supposed to skip forward to an absolute component, and if 1166251881Speter this is not an empty base that we are skipping, then copy the base 1167251881Speter into the output. */ 1168251881Speter if (! SVN_PATH_IS_EMPTY(base)) 1169251881Speter { 1170251881Speter memcpy(p, base, len = saved_lengths[0]); 1171251881Speter p += len; 1172251881Speter } 1173251881Speter 1174251881Speter nargs = 0; 1175251881Speter va_start(va, base); 1176251881Speter while ((s = va_arg(va, const char *)) != NULL) 1177251881Speter { 1178251881Speter if (SVN_PATH_IS_EMPTY(s)) 1179251881Speter continue; 1180251881Speter 1181251881Speter if (++nargs < base_arg) 1182251881Speter continue; 1183251881Speter 1184251881Speter if (nargs < MAX_SAVED_LENGTHS) 1185251881Speter len = saved_lengths[nargs]; 1186251881Speter else 1187251881Speter len = strlen(s); 1188251881Speter 1189251881Speter /* insert a separator if we aren't copying in the first component 1190251881Speter (which can happen when base_arg is set). also, don't put in a slash 1191251881Speter if the prior character is a slash (occurs when prior component 1192251881Speter is "/"). */ 1193251881Speter if (p != dirent && 1194251881Speter ( ! (nargs - 1 <= base_arg) || add_separator)) 1195251881Speter *p++ = '/'; 1196251881Speter 1197251881Speter /* copy the new component and advance the pointer */ 1198251881Speter memcpy(p, s, len); 1199251881Speter p += len; 1200251881Speter } 1201251881Speter va_end(va); 1202251881Speter 1203251881Speter *p = '\0'; 1204251881Speter assert((apr_size_t)(p - dirent) == total_len); 1205251881Speter 1206251881Speter return dirent; 1207251881Speter} 1208251881Speter 1209251881Speterchar * 1210251881Spetersvn_relpath_join(const char *base, 1211251881Speter const char *component, 1212251881Speter apr_pool_t *pool) 1213251881Speter{ 1214251881Speter apr_size_t blen = strlen(base); 1215251881Speter apr_size_t clen = strlen(component); 1216251881Speter char *path; 1217251881Speter 1218251881Speter assert(relpath_is_canonical(base)); 1219251881Speter assert(relpath_is_canonical(component)); 1220251881Speter 1221251881Speter /* If either is empty return the other */ 1222251881Speter if (blen == 0) 1223251881Speter return apr_pmemdup(pool, component, clen + 1); 1224251881Speter if (clen == 0) 1225251881Speter return apr_pmemdup(pool, base, blen + 1); 1226251881Speter 1227251881Speter path = apr_palloc(pool, blen + 1 + clen + 1); 1228251881Speter memcpy(path, base, blen); 1229251881Speter path[blen] = '/'; 1230251881Speter memcpy(path + blen + 1, component, clen + 1); 1231251881Speter 1232251881Speter return path; 1233251881Speter} 1234251881Speter 1235251881Speterchar * 1236251881Spetersvn_dirent_dirname(const char *dirent, apr_pool_t *pool) 1237251881Speter{ 1238251881Speter apr_size_t len = strlen(dirent); 1239251881Speter 1240251881Speter assert(svn_dirent_is_canonical(dirent, pool)); 1241251881Speter 1242251881Speter if (len == dirent_root_length(dirent, len)) 1243251881Speter return apr_pstrmemdup(pool, dirent, len); 1244251881Speter else 1245251881Speter return apr_pstrmemdup(pool, dirent, dirent_previous_segment(dirent, len)); 1246251881Speter} 1247251881Speter 1248251881Speterconst char * 1249251881Spetersvn_dirent_basename(const char *dirent, apr_pool_t *pool) 1250251881Speter{ 1251251881Speter apr_size_t len = strlen(dirent); 1252251881Speter apr_size_t start; 1253251881Speter 1254251881Speter assert(!pool || svn_dirent_is_canonical(dirent, pool)); 1255251881Speter 1256251881Speter if (svn_dirent_is_root(dirent, len)) 1257251881Speter return ""; 1258251881Speter else 1259251881Speter { 1260251881Speter start = len; 1261251881Speter while (start > 0 && dirent[start - 1] != '/' 1262251881Speter#ifdef SVN_USE_DOS_PATHS 1263251881Speter && dirent[start - 1] != ':' 1264251881Speter#endif 1265251881Speter ) 1266251881Speter --start; 1267251881Speter } 1268251881Speter 1269251881Speter if (pool) 1270251881Speter return apr_pstrmemdup(pool, dirent + start, len - start); 1271251881Speter else 1272251881Speter return dirent + start; 1273251881Speter} 1274251881Speter 1275251881Spetervoid 1276251881Spetersvn_dirent_split(const char **dirpath, 1277251881Speter const char **base_name, 1278251881Speter const char *dirent, 1279251881Speter apr_pool_t *pool) 1280251881Speter{ 1281251881Speter assert(dirpath != base_name); 1282251881Speter 1283251881Speter if (dirpath) 1284251881Speter *dirpath = svn_dirent_dirname(dirent, pool); 1285251881Speter 1286251881Speter if (base_name) 1287251881Speter *base_name = svn_dirent_basename(dirent, pool); 1288251881Speter} 1289251881Speter 1290251881Speterchar * 1291251881Spetersvn_relpath_dirname(const char *relpath, 1292251881Speter apr_pool_t *pool) 1293251881Speter{ 1294251881Speter apr_size_t len = strlen(relpath); 1295251881Speter 1296251881Speter assert(relpath_is_canonical(relpath)); 1297251881Speter 1298251881Speter return apr_pstrmemdup(pool, relpath, 1299251881Speter relpath_previous_segment(relpath, len)); 1300251881Speter} 1301251881Speter 1302251881Speterconst char * 1303251881Spetersvn_relpath_basename(const char *relpath, 1304251881Speter apr_pool_t *pool) 1305251881Speter{ 1306251881Speter apr_size_t len = strlen(relpath); 1307251881Speter apr_size_t start; 1308251881Speter 1309251881Speter assert(relpath_is_canonical(relpath)); 1310251881Speter 1311251881Speter start = len; 1312251881Speter while (start > 0 && relpath[start - 1] != '/') 1313251881Speter --start; 1314251881Speter 1315251881Speter if (pool) 1316251881Speter return apr_pstrmemdup(pool, relpath + start, len - start); 1317251881Speter else 1318251881Speter return relpath + start; 1319251881Speter} 1320251881Speter 1321251881Spetervoid 1322251881Spetersvn_relpath_split(const char **dirpath, 1323251881Speter const char **base_name, 1324251881Speter const char *relpath, 1325251881Speter apr_pool_t *pool) 1326251881Speter{ 1327251881Speter assert(dirpath != base_name); 1328251881Speter 1329251881Speter if (dirpath) 1330251881Speter *dirpath = svn_relpath_dirname(relpath, pool); 1331251881Speter 1332251881Speter if (base_name) 1333251881Speter *base_name = svn_relpath_basename(relpath, pool); 1334251881Speter} 1335251881Speter 1336289180Speterconst char * 1337289180Spetersvn_relpath_prefix(const char *relpath, 1338289180Speter int max_components, 1339289180Speter apr_pool_t *result_pool) 1340289180Speter{ 1341289180Speter const char *end; 1342289180Speter assert(relpath_is_canonical(relpath)); 1343289180Speter 1344289180Speter if (max_components <= 0) 1345289180Speter return ""; 1346289180Speter 1347289180Speter for (end = relpath; *end; end++) 1348289180Speter { 1349289180Speter if (*end == '/') 1350289180Speter { 1351289180Speter if (!--max_components) 1352289180Speter break; 1353289180Speter } 1354289180Speter } 1355289180Speter 1356289180Speter return apr_pstrmemdup(result_pool, relpath, end-relpath); 1357289180Speter} 1358289180Speter 1359251881Speterchar * 1360251881Spetersvn_uri_dirname(const char *uri, apr_pool_t *pool) 1361251881Speter{ 1362251881Speter apr_size_t len = strlen(uri); 1363251881Speter 1364251881Speter assert(svn_uri_is_canonical(uri, pool)); 1365251881Speter 1366251881Speter if (svn_uri_is_root(uri, len)) 1367251881Speter return apr_pstrmemdup(pool, uri, len); 1368251881Speter else 1369251881Speter return apr_pstrmemdup(pool, uri, uri_previous_segment(uri, len)); 1370251881Speter} 1371251881Speter 1372251881Speterconst char * 1373251881Spetersvn_uri_basename(const char *uri, apr_pool_t *pool) 1374251881Speter{ 1375251881Speter apr_size_t len = strlen(uri); 1376251881Speter apr_size_t start; 1377251881Speter 1378251881Speter assert(svn_uri_is_canonical(uri, NULL)); 1379251881Speter 1380251881Speter if (svn_uri_is_root(uri, len)) 1381251881Speter return ""; 1382251881Speter 1383251881Speter start = len; 1384251881Speter while (start > 0 && uri[start - 1] != '/') 1385251881Speter --start; 1386251881Speter 1387251881Speter return svn_path_uri_decode(uri + start, pool); 1388251881Speter} 1389251881Speter 1390251881Spetervoid 1391251881Spetersvn_uri_split(const char **dirpath, 1392251881Speter const char **base_name, 1393251881Speter const char *uri, 1394251881Speter apr_pool_t *pool) 1395251881Speter{ 1396251881Speter assert(dirpath != base_name); 1397251881Speter 1398251881Speter if (dirpath) 1399251881Speter *dirpath = svn_uri_dirname(uri, pool); 1400251881Speter 1401251881Speter if (base_name) 1402251881Speter *base_name = svn_uri_basename(uri, pool); 1403251881Speter} 1404251881Speter 1405251881Speterchar * 1406251881Spetersvn_dirent_get_longest_ancestor(const char *dirent1, 1407251881Speter const char *dirent2, 1408251881Speter apr_pool_t *pool) 1409251881Speter{ 1410251881Speter return apr_pstrndup(pool, dirent1, 1411251881Speter get_longest_ancestor_length(type_dirent, dirent1, 1412251881Speter dirent2, pool)); 1413251881Speter} 1414251881Speter 1415251881Speterchar * 1416251881Spetersvn_relpath_get_longest_ancestor(const char *relpath1, 1417251881Speter const char *relpath2, 1418251881Speter apr_pool_t *pool) 1419251881Speter{ 1420251881Speter assert(relpath_is_canonical(relpath1)); 1421251881Speter assert(relpath_is_canonical(relpath2)); 1422251881Speter 1423251881Speter return apr_pstrndup(pool, relpath1, 1424251881Speter get_longest_ancestor_length(type_relpath, relpath1, 1425251881Speter relpath2, pool)); 1426251881Speter} 1427251881Speter 1428251881Speterchar * 1429251881Spetersvn_uri_get_longest_ancestor(const char *uri1, 1430251881Speter const char *uri2, 1431251881Speter apr_pool_t *pool) 1432251881Speter{ 1433251881Speter apr_size_t uri_ancestor_len; 1434251881Speter apr_size_t i = 0; 1435251881Speter 1436251881Speter assert(svn_uri_is_canonical(uri1, NULL)); 1437251881Speter assert(svn_uri_is_canonical(uri2, NULL)); 1438251881Speter 1439251881Speter /* Find ':' */ 1440251881Speter while (1) 1441251881Speter { 1442251881Speter /* No shared protocol => no common prefix */ 1443251881Speter if (uri1[i] != uri2[i]) 1444251881Speter return apr_pmemdup(pool, SVN_EMPTY_PATH, 1445251881Speter sizeof(SVN_EMPTY_PATH)); 1446251881Speter 1447251881Speter if (uri1[i] == ':') 1448251881Speter break; 1449251881Speter 1450251881Speter /* They're both URLs, so EOS can't come before ':' */ 1451251881Speter assert((uri1[i] != '\0') && (uri2[i] != '\0')); 1452251881Speter 1453251881Speter i++; 1454251881Speter } 1455251881Speter 1456251881Speter i += 3; /* Advance past '://' */ 1457251881Speter 1458251881Speter uri_ancestor_len = get_longest_ancestor_length(type_uri, uri1 + i, 1459251881Speter uri2 + i, pool); 1460251881Speter 1461251881Speter if (uri_ancestor_len == 0 || 1462251881Speter (uri_ancestor_len == 1 && (uri1 + i)[0] == '/')) 1463251881Speter return apr_pmemdup(pool, SVN_EMPTY_PATH, sizeof(SVN_EMPTY_PATH)); 1464251881Speter else 1465251881Speter return apr_pstrndup(pool, uri1, uri_ancestor_len + i); 1466251881Speter} 1467251881Speter 1468251881Speterconst char * 1469251881Spetersvn_dirent_is_child(const char *parent_dirent, 1470251881Speter const char *child_dirent, 1471251881Speter apr_pool_t *pool) 1472251881Speter{ 1473251881Speter return is_child(type_dirent, parent_dirent, child_dirent, pool); 1474251881Speter} 1475251881Speter 1476251881Speterconst char * 1477251881Spetersvn_dirent_skip_ancestor(const char *parent_dirent, 1478251881Speter const char *child_dirent) 1479251881Speter{ 1480251881Speter apr_size_t len = strlen(parent_dirent); 1481251881Speter apr_size_t root_len; 1482251881Speter 1483251881Speter if (0 != strncmp(parent_dirent, child_dirent, len)) 1484251881Speter return NULL; /* parent_dirent is no ancestor of child_dirent */ 1485251881Speter 1486251881Speter if (child_dirent[len] == 0) 1487251881Speter return ""; /* parent_dirent == child_dirent */ 1488251881Speter 1489251881Speter /* Child == parent + more-characters */ 1490251881Speter 1491251881Speter root_len = dirent_root_length(child_dirent, strlen(child_dirent)); 1492251881Speter if (root_len > len) 1493251881Speter /* Different root, e.g. ("" "/...") or ("//z" "//z/share") */ 1494251881Speter return NULL; 1495251881Speter 1496251881Speter /* Now, child == [root-of-parent] + [rest-of-parent] + more-characters. 1497251881Speter * It must be one of the following forms. 1498251881Speter * 1499251881Speter * rlen parent child bad? rlen=len? c[len]=/? 1500251881Speter * 0 "" "foo" * 1501251881Speter * 0 "b" "bad" ! 1502251881Speter * 0 "b" "b/foo" * 1503251881Speter * 1 "/" "/foo" * 1504251881Speter * 1 "/b" "/bad" ! 1505251881Speter * 1 "/b" "/b/foo" * 1506251881Speter * 2 "a:" "a:foo" * 1507251881Speter * 2 "a:b" "a:bad" ! 1508251881Speter * 2 "a:b" "a:b/foo" * 1509251881Speter * 3 "a:/" "a:/foo" * 1510251881Speter * 3 "a:/b" "a:/bad" ! 1511251881Speter * 3 "a:/b" "a:/b/foo" * 1512251881Speter * 5 "//s/s" "//s/s/foo" * * 1513251881Speter * 5 "//s/s/b" "//s/s/bad" ! 1514251881Speter * 5 "//s/s/b" "//s/s/b/foo" * 1515251881Speter */ 1516251881Speter 1517251881Speter if (child_dirent[len] == '/') 1518251881Speter /* "parent|child" is one of: 1519251881Speter * "[a:]b|/foo" "[a:]/b|/foo" "//s/s|/foo" "//s/s/b|/foo" */ 1520251881Speter return child_dirent + len + 1; 1521251881Speter 1522251881Speter if (root_len == len) 1523251881Speter /* "parent|child" is "|foo" "/|foo" "a:|foo" "a:/|foo" "//s/s|/foo" */ 1524251881Speter return child_dirent + len; 1525251881Speter 1526251881Speter return NULL; 1527251881Speter} 1528251881Speter 1529251881Speterconst char * 1530251881Spetersvn_relpath_skip_ancestor(const char *parent_relpath, 1531251881Speter const char *child_relpath) 1532251881Speter{ 1533251881Speter apr_size_t len = strlen(parent_relpath); 1534251881Speter 1535251881Speter assert(relpath_is_canonical(parent_relpath)); 1536251881Speter assert(relpath_is_canonical(child_relpath)); 1537251881Speter 1538251881Speter if (len == 0) 1539251881Speter return child_relpath; 1540251881Speter 1541251881Speter if (0 != strncmp(parent_relpath, child_relpath, len)) 1542251881Speter return NULL; /* parent_relpath is no ancestor of child_relpath */ 1543251881Speter 1544251881Speter if (child_relpath[len] == 0) 1545251881Speter return ""; /* parent_relpath == child_relpath */ 1546251881Speter 1547251881Speter if (child_relpath[len] == '/') 1548251881Speter return child_relpath + len + 1; 1549251881Speter 1550251881Speter return NULL; 1551251881Speter} 1552251881Speter 1553251881Speter 1554251881Speter/* */ 1555251881Speterstatic const char * 1556251881Speteruri_skip_ancestor(const char *parent_uri, 1557251881Speter const char *child_uri) 1558251881Speter{ 1559251881Speter apr_size_t len = strlen(parent_uri); 1560251881Speter 1561251881Speter assert(svn_uri_is_canonical(parent_uri, NULL)); 1562251881Speter assert(svn_uri_is_canonical(child_uri, NULL)); 1563251881Speter 1564251881Speter if (0 != strncmp(parent_uri, child_uri, len)) 1565251881Speter return NULL; /* parent_uri is no ancestor of child_uri */ 1566251881Speter 1567251881Speter if (child_uri[len] == 0) 1568251881Speter return ""; /* parent_uri == child_uri */ 1569251881Speter 1570251881Speter if (child_uri[len] == '/') 1571251881Speter return child_uri + len + 1; 1572251881Speter 1573251881Speter return NULL; 1574251881Speter} 1575251881Speter 1576251881Speterconst char * 1577251881Spetersvn_uri_skip_ancestor(const char *parent_uri, 1578251881Speter const char *child_uri, 1579251881Speter apr_pool_t *result_pool) 1580251881Speter{ 1581251881Speter const char *result = uri_skip_ancestor(parent_uri, child_uri); 1582251881Speter 1583251881Speter return result ? svn_path_uri_decode(result, result_pool) : NULL; 1584251881Speter} 1585251881Speter 1586251881Spetersvn_boolean_t 1587251881Spetersvn_dirent_is_ancestor(const char *parent_dirent, const char *child_dirent) 1588251881Speter{ 1589251881Speter return svn_dirent_skip_ancestor(parent_dirent, child_dirent) != NULL; 1590251881Speter} 1591251881Speter 1592251881Spetersvn_boolean_t 1593251881Spetersvn_uri__is_ancestor(const char *parent_uri, const char *child_uri) 1594251881Speter{ 1595251881Speter return uri_skip_ancestor(parent_uri, child_uri) != NULL; 1596251881Speter} 1597251881Speter 1598251881Speter 1599251881Spetersvn_boolean_t 1600251881Spetersvn_dirent_is_absolute(const char *dirent) 1601251881Speter{ 1602251881Speter if (! dirent) 1603251881Speter return FALSE; 1604251881Speter 1605251881Speter /* dirent is absolute if it starts with '/' on non-Windows platforms 1606251881Speter or with '//' on Windows platforms */ 1607251881Speter if (dirent[0] == '/' 1608251881Speter#ifdef SVN_USE_DOS_PATHS 1609251881Speter && dirent[1] == '/' /* Single '/' depends on current drive */ 1610251881Speter#endif 1611251881Speter ) 1612251881Speter return TRUE; 1613251881Speter 1614251881Speter /* On Windows, dirent is also absolute when it starts with 'H:/' 1615251881Speter where 'H' is any letter. */ 1616251881Speter#ifdef SVN_USE_DOS_PATHS 1617251881Speter if (((dirent[0] >= 'A' && dirent[0] <= 'Z')) && 1618251881Speter (dirent[1] == ':') && (dirent[2] == '/')) 1619251881Speter return TRUE; 1620251881Speter#endif /* SVN_USE_DOS_PATHS */ 1621251881Speter 1622251881Speter return FALSE; 1623251881Speter} 1624251881Speter 1625251881Spetersvn_error_t * 1626251881Spetersvn_dirent_get_absolute(const char **pabsolute, 1627251881Speter const char *relative, 1628251881Speter apr_pool_t *pool) 1629251881Speter{ 1630251881Speter char *buffer; 1631251881Speter apr_status_t apr_err; 1632251881Speter const char *path_apr; 1633251881Speter 1634251881Speter SVN_ERR_ASSERT(! svn_path_is_url(relative)); 1635251881Speter 1636251881Speter /* Merge the current working directory with the relative dirent. */ 1637251881Speter SVN_ERR(svn_path_cstring_from_utf8(&path_apr, relative, pool)); 1638251881Speter 1639251881Speter apr_err = apr_filepath_merge(&buffer, NULL, 1640251881Speter path_apr, 1641251881Speter APR_FILEPATH_NOTRELATIVE, 1642251881Speter pool); 1643251881Speter if (apr_err) 1644251881Speter { 1645251881Speter /* In some cases when the passed path or its ancestor(s) do not exist 1646251881Speter or no longer exist apr returns an error. 1647251881Speter 1648251881Speter In many of these cases we would like to return a path anyway, when the 1649251881Speter passed path was already a safe absolute path. So check for that now to 1650251881Speter avoid an error. 1651251881Speter 1652251881Speter svn_dirent_is_absolute() doesn't perform the necessary checks to see 1653251881Speter if the path doesn't need post processing to be in the canonical absolute 1654251881Speter format. 1655251881Speter */ 1656251881Speter 1657251881Speter if (svn_dirent_is_absolute(relative) 1658251881Speter && svn_dirent_is_canonical(relative, pool) 1659251881Speter && !svn_path_is_backpath_present(relative)) 1660251881Speter { 1661251881Speter *pabsolute = apr_pstrdup(pool, relative); 1662251881Speter return SVN_NO_ERROR; 1663251881Speter } 1664251881Speter 1665251881Speter return svn_error_createf(SVN_ERR_BAD_FILENAME, 1666251881Speter svn_error_create(apr_err, NULL, NULL), 1667251881Speter _("Couldn't determine absolute path of '%s'"), 1668251881Speter svn_dirent_local_style(relative, pool)); 1669251881Speter } 1670251881Speter 1671251881Speter SVN_ERR(svn_path_cstring_to_utf8(pabsolute, buffer, pool)); 1672251881Speter *pabsolute = svn_dirent_canonicalize(*pabsolute, pool); 1673251881Speter return SVN_NO_ERROR; 1674251881Speter} 1675251881Speter 1676251881Speterconst char * 1677251881Spetersvn_uri_canonicalize(const char *uri, apr_pool_t *pool) 1678251881Speter{ 1679362181Sdim const char *result; 1680362181Sdim svn_error_t *const err = canonicalize(&result, type_uri, uri, pool); 1681362181Sdim if (err) 1682362181Sdim { 1683362181Sdim svn_error_clear(err); 1684362181Sdim SVN_ERR_ASSERT_NO_RETURN(!"URI canonicalization failed"); 1685362181Sdim } 1686362181Sdim return result; 1687251881Speter} 1688251881Speter 1689362181Sdimsvn_error_t * 1690362181Sdimsvn_uri_canonicalize_safe(const char **canonical_uri, 1691362181Sdim const char **non_canonical_result, 1692362181Sdim const char *uri, 1693362181Sdim apr_pool_t *result_pool, 1694362181Sdim apr_pool_t *scratch_pool) 1695362181Sdim{ 1696362181Sdim const char *result = NULL; 1697362181Sdim SVN_ERR(canonicalize(&result, type_uri, uri, result_pool)); 1698362181Sdim if (!svn_uri_is_canonical(result, scratch_pool)) 1699362181Sdim { 1700362181Sdim if (non_canonical_result) 1701362181Sdim *non_canonical_result = result; 1702362181Sdim 1703362181Sdim return svn_error_createf( 1704362181Sdim SVN_ERR_CANONICALIZATION_FAILED, NULL, 1705362181Sdim _("Could not canonicalize URI '%s'" 1706362181Sdim " (the result '%s' is not canonical)"), 1707362181Sdim uri, result); 1708362181Sdim } 1709362181Sdim *canonical_uri = result; 1710362181Sdim return SVN_NO_ERROR; 1711362181Sdim} 1712362181Sdim 1713251881Speterconst char * 1714251881Spetersvn_relpath_canonicalize(const char *relpath, apr_pool_t *pool) 1715251881Speter{ 1716362181Sdim const char *result; 1717362181Sdim svn_error_t *const err = canonicalize(&result, type_relpath, relpath, pool); 1718362181Sdim if (err) 1719362181Sdim { 1720362181Sdim svn_error_clear(err); 1721362181Sdim SVN_ERR_ASSERT_NO_RETURN(!"relpath canonicalization failed"); 1722362181Sdim } 1723362181Sdim return result; 1724251881Speter} 1725251881Speter 1726362181Sdimsvn_error_t * 1727362181Sdimsvn_relpath_canonicalize_safe(const char **canonical_relpath, 1728362181Sdim const char **non_canonical_result, 1729362181Sdim const char *relpath, 1730362181Sdim apr_pool_t *result_pool, 1731362181Sdim apr_pool_t *scratch_pool) 1732251881Speter{ 1733362181Sdim const char *result = NULL; 1734362181Sdim SVN_ERR(canonicalize(&result, type_relpath, relpath, result_pool)); 1735362181Sdim if (!svn_relpath_is_canonical(result)) 1736362181Sdim { 1737362181Sdim if (non_canonical_result) 1738362181Sdim *non_canonical_result = result; 1739251881Speter 1740362181Sdim return svn_error_createf( 1741362181Sdim SVN_ERR_CANONICALIZATION_FAILED, NULL, 1742362181Sdim _("Could not canonicalize relpath '%s'" 1743362181Sdim " (the result '%s' is not canonical)"), 1744362181Sdim relpath, result); 1745362181Sdim } 1746362181Sdim 1747362181Sdim SVN_UNUSED(scratch_pool); 1748362181Sdim *canonical_relpath = result; 1749362181Sdim return SVN_NO_ERROR; 1750362181Sdim} 1751362181Sdim 1752362181Sdimstatic svn_error_t * 1753362181Sdimcanonicalize_dirent(const char **result, const char *dirent, apr_pool_t *pool) 1754362181Sdim{ 1755362181Sdim const char *dst; 1756362181Sdim SVN_ERR(canonicalize(&dst, type_dirent, dirent, pool)); 1757362181Sdim 1758251881Speter#ifdef SVN_USE_DOS_PATHS 1759251881Speter /* Handle a specific case on Windows where path == "X:/". Here we have to 1760251881Speter append the final '/', as svn_path_canonicalize will chop this of. */ 1761251881Speter if (((dirent[0] >= 'A' && dirent[0] <= 'Z') || 1762251881Speter (dirent[0] >= 'a' && dirent[0] <= 'z')) && 1763251881Speter dirent[1] == ':' && dirent[2] == '/' && 1764251881Speter dst[3] == '\0') 1765251881Speter { 1766251881Speter char *dst_slash = apr_pcalloc(pool, 4); 1767251881Speter dst_slash[0] = canonicalize_to_upper(dirent[0]); 1768251881Speter dst_slash[1] = ':'; 1769251881Speter dst_slash[2] = '/'; 1770251881Speter dst_slash[3] = '\0'; 1771251881Speter 1772362181Sdim *result = dst_slash; 1773362181Sdim return SVN_NO_ERROR; 1774251881Speter } 1775251881Speter#endif /* SVN_USE_DOS_PATHS */ 1776251881Speter 1777362181Sdim *result = dst; 1778362181Sdim return SVN_NO_ERROR; 1779251881Speter} 1780251881Speter 1781362181Sdimconst char * 1782362181Sdimsvn_dirent_canonicalize(const char *dirent, apr_pool_t *pool) 1783362181Sdim{ 1784362181Sdim const char *result; 1785362181Sdim svn_error_t *const err = canonicalize_dirent(&result, dirent, pool); 1786362181Sdim if (err) 1787362181Sdim { 1788362181Sdim svn_error_clear(err); 1789362181Sdim SVN_ERR_ASSERT_NO_RETURN(!"dirent canonicalization failed"); 1790362181Sdim } 1791362181Sdim return result; 1792362181Sdim} 1793362181Sdim 1794362181Sdimsvn_error_t * 1795362181Sdimsvn_dirent_canonicalize_safe(const char **canonical_dirent, 1796362181Sdim const char **non_canonical_result, 1797362181Sdim const char *dirent, 1798362181Sdim apr_pool_t *result_pool, 1799362181Sdim apr_pool_t *scratch_pool) 1800362181Sdim{ 1801362181Sdim const char *result = NULL; 1802362181Sdim SVN_ERR(canonicalize_dirent(&result, dirent, result_pool)); 1803362181Sdim if (!svn_dirent_is_canonical(result, scratch_pool)) 1804362181Sdim { 1805362181Sdim if (non_canonical_result) 1806362181Sdim *non_canonical_result = result; 1807362181Sdim 1808362181Sdim return svn_error_createf( 1809362181Sdim SVN_ERR_CANONICALIZATION_FAILED, NULL, 1810362181Sdim _("Could not canonicalize dirent '%s'" 1811362181Sdim " (the result '%s' is not canonical)"), 1812362181Sdim dirent, result); 1813362181Sdim } 1814362181Sdim *canonical_dirent = result; 1815362181Sdim return SVN_NO_ERROR; 1816362181Sdim} 1817362181Sdim 1818251881Spetersvn_boolean_t 1819251881Spetersvn_dirent_is_canonical(const char *dirent, apr_pool_t *scratch_pool) 1820251881Speter{ 1821251881Speter const char *ptr = dirent; 1822251881Speter if (*ptr == '/') 1823251881Speter { 1824251881Speter ptr++; 1825251881Speter#ifdef SVN_USE_DOS_PATHS 1826251881Speter /* Check for UNC paths */ 1827251881Speter if (*ptr == '/') 1828251881Speter { 1829251881Speter /* TODO: Scan hostname and sharename and fall back to part code */ 1830251881Speter 1831251881Speter /* ### Fall back to old implementation */ 1832251881Speter return (strcmp(dirent, svn_dirent_canonicalize(dirent, scratch_pool)) 1833251881Speter == 0); 1834251881Speter } 1835251881Speter#endif /* SVN_USE_DOS_PATHS */ 1836251881Speter } 1837251881Speter#ifdef SVN_USE_DOS_PATHS 1838251881Speter else if (((*ptr >= 'a' && *ptr <= 'z') || (*ptr >= 'A' && *ptr <= 'Z')) && 1839251881Speter (ptr[1] == ':')) 1840251881Speter { 1841251881Speter /* The only canonical drive names are "A:"..."Z:", no lower case */ 1842251881Speter if (*ptr < 'A' || *ptr > 'Z') 1843251881Speter return FALSE; 1844251881Speter 1845251881Speter ptr += 2; 1846251881Speter 1847251881Speter if (*ptr == '/') 1848251881Speter ptr++; 1849251881Speter } 1850251881Speter#endif /* SVN_USE_DOS_PATHS */ 1851251881Speter 1852251881Speter return relpath_is_canonical(ptr); 1853251881Speter} 1854251881Speter 1855251881Speterstatic svn_boolean_t 1856251881Speterrelpath_is_canonical(const char *relpath) 1857251881Speter{ 1858289180Speter const char *dot_pos, *ptr = relpath; 1859289180Speter apr_size_t i, len; 1860289180Speter unsigned pattern = 0; 1861251881Speter 1862251881Speter /* RELPATH is canonical if it has: 1863251881Speter * - no '.' segments 1864251881Speter * - no start and closing '/' 1865251881Speter * - no '//' 1866251881Speter */ 1867251881Speter 1868289180Speter /* invalid beginnings */ 1869251881Speter if (*ptr == '/') 1870251881Speter return FALSE; 1871251881Speter 1872289180Speter if (ptr[0] == '.' && (ptr[1] == '/' || ptr[1] == '\0')) 1873289180Speter return FALSE; 1874251881Speter 1875289180Speter /* valid special cases */ 1876289180Speter len = strlen(ptr); 1877289180Speter if (len < 2) 1878289180Speter return TRUE; 1879251881Speter 1880289180Speter /* invalid endings */ 1881289180Speter if (ptr[len-1] == '/' || (ptr[len-1] == '.' && ptr[len-2] == '/')) 1882289180Speter return FALSE; 1883251881Speter 1884289180Speter /* '.' are rare. So, search for them globally. There will often be no 1885289180Speter * more than one hit. Also note that we already checked for invalid 1886289180Speter * starts and endings, i.e. we only need to check for "/./" 1887289180Speter */ 1888289180Speter for (dot_pos = memchr(ptr, '.', len); 1889289180Speter dot_pos; 1890289180Speter dot_pos = strchr(dot_pos+1, '.')) 1891289180Speter if (dot_pos > ptr && dot_pos[-1] == '/' && dot_pos[1] == '/') 1892289180Speter return FALSE; 1893251881Speter 1894289180Speter /* Now validate the rest of the path. */ 1895289180Speter for (i = 0; i < len - 1; ++i) 1896289180Speter { 1897289180Speter pattern = ((pattern & 0xff) << 8) + (unsigned char)ptr[i]; 1898289180Speter if (pattern == 0x101 * (unsigned char)('/')) 1899289180Speter return FALSE; 1900251881Speter } 1901251881Speter 1902251881Speter return TRUE; 1903251881Speter} 1904251881Speter 1905251881Spetersvn_boolean_t 1906251881Spetersvn_relpath_is_canonical(const char *relpath) 1907251881Speter{ 1908251881Speter return relpath_is_canonical(relpath); 1909251881Speter} 1910251881Speter 1911251881Spetersvn_boolean_t 1912251881Spetersvn_uri_is_canonical(const char *uri, apr_pool_t *scratch_pool) 1913251881Speter{ 1914251881Speter const char *ptr = uri, *seg = uri; 1915251881Speter const char *schema_data = NULL; 1916251881Speter 1917251881Speter /* URI is canonical if it has: 1918251881Speter * - lowercase URL scheme 1919251881Speter * - lowercase URL hostname 1920251881Speter * - no '.' segments 1921251881Speter * - no closing '/' 1922251881Speter * - no '//' 1923251881Speter * - uppercase hex-encoded pair digits ("%AB", not "%ab") 1924251881Speter */ 1925251881Speter 1926251881Speter if (*uri == '\0') 1927251881Speter return FALSE; 1928251881Speter 1929251881Speter if (! svn_path_is_url(uri)) 1930251881Speter return FALSE; 1931251881Speter 1932251881Speter /* Skip the scheme. */ 1933251881Speter while (*ptr && (*ptr != '/') && (*ptr != ':')) 1934251881Speter ptr++; 1935251881Speter 1936251881Speter /* No scheme? No good. */ 1937251881Speter if (! (*ptr == ':' && *(ptr+1) == '/' && *(ptr+2) == '/')) 1938251881Speter return FALSE; 1939251881Speter 1940251881Speter /* Found a scheme, check that it's all lowercase. */ 1941251881Speter ptr = uri; 1942251881Speter while (*ptr != ':') 1943251881Speter { 1944251881Speter if (*ptr >= 'A' && *ptr <= 'Z') 1945251881Speter return FALSE; 1946251881Speter ptr++; 1947251881Speter } 1948251881Speter /* Skip :// */ 1949251881Speter ptr += 3; 1950251881Speter 1951251881Speter /* Scheme only? That works. */ 1952251881Speter if (! *ptr) 1953251881Speter return TRUE; 1954251881Speter 1955251881Speter /* This might be the hostname */ 1956251881Speter seg = ptr; 1957251881Speter while (*ptr && (*ptr != '/') && (*ptr != '@')) 1958251881Speter ptr++; 1959251881Speter 1960251881Speter if (*ptr == '@') 1961251881Speter seg = ptr + 1; 1962251881Speter 1963251881Speter /* Found a hostname, check that it's all lowercase. */ 1964251881Speter ptr = seg; 1965251881Speter 1966251881Speter if (*ptr == '[') 1967251881Speter { 1968251881Speter ptr++; 1969251881Speter while (*ptr == ':' 1970251881Speter || (*ptr >= '0' && *ptr <= '9') 1971251881Speter || (*ptr >= 'a' && *ptr <= 'f')) 1972251881Speter { 1973251881Speter ptr++; 1974251881Speter } 1975251881Speter 1976251881Speter if (*ptr != ']') 1977251881Speter return FALSE; 1978251881Speter ptr++; 1979251881Speter } 1980251881Speter else 1981251881Speter while (*ptr && *ptr != '/' && *ptr != ':') 1982251881Speter { 1983251881Speter if (*ptr >= 'A' && *ptr <= 'Z') 1984251881Speter return FALSE; 1985251881Speter ptr++; 1986251881Speter } 1987251881Speter 1988251881Speter /* Found a portnumber */ 1989251881Speter if (*ptr == ':') 1990251881Speter { 1991251881Speter apr_int64_t port = 0; 1992251881Speter 1993251881Speter ptr++; 1994251881Speter schema_data = ptr; 1995251881Speter 1996251881Speter while (*ptr >= '0' && *ptr <= '9') 1997251881Speter { 1998251881Speter port = 10 * port + (*ptr - '0'); 1999251881Speter ptr++; 2000251881Speter } 2001251881Speter 2002362181Sdim if (ptr == schema_data && (*ptr == '/' || *ptr == '\0')) 2003251881Speter return FALSE; /* Fail on "http://host:" */ 2004251881Speter 2005251881Speter if (port == 80 && strncmp(uri, "http:", 5) == 0) 2006251881Speter return FALSE; 2007251881Speter else if (port == 443 && strncmp(uri, "https:", 6) == 0) 2008251881Speter return FALSE; 2009251881Speter else if (port == 3690 && strncmp(uri, "svn:", 4) == 0) 2010251881Speter return FALSE; 2011362181Sdim 2012362181Sdim while (*ptr && *ptr != '/') 2013362181Sdim ++ptr; /* Allow "http://host:stuff" */ 2014251881Speter } 2015251881Speter 2016251881Speter schema_data = ptr; 2017251881Speter 2018251881Speter#ifdef SVN_USE_DOS_PATHS 2019251881Speter if (schema_data && *ptr == '/') 2020251881Speter { 2021251881Speter /* If this is a file url, ptr now points to the third '/' in 2022251881Speter file:///C:/path. Check that if we have such a URL the drive 2023251881Speter letter is in uppercase. */ 2024251881Speter if (strncmp(uri, "file:", 5) == 0 && 2025251881Speter ! (*(ptr+1) >= 'A' && *(ptr+1) <= 'Z') && 2026251881Speter *(ptr+2) == ':') 2027251881Speter return FALSE; 2028251881Speter } 2029251881Speter#endif /* SVN_USE_DOS_PATHS */ 2030251881Speter 2031251881Speter /* Now validate the rest of the URI. */ 2032257936Speter seg = ptr; 2033257936Speter while (*ptr && (*ptr != '/')) 2034257936Speter ptr++; 2035251881Speter while(1) 2036251881Speter { 2037251881Speter apr_size_t seglen = ptr - seg; 2038251881Speter 2039251881Speter if (seglen == 1 && *seg == '.') 2040251881Speter return FALSE; /* /./ */ 2041251881Speter 2042251881Speter if (*ptr == '/' && *(ptr+1) == '/') 2043251881Speter return FALSE; /* // */ 2044251881Speter 2045251881Speter if (! *ptr && *(ptr - 1) == '/' && ptr - 1 != uri) 2046251881Speter return FALSE; /* foo/ */ 2047251881Speter 2048251881Speter if (! *ptr) 2049251881Speter break; 2050251881Speter 2051251881Speter if (*ptr == '/') 2052251881Speter ptr++; 2053257936Speter 2054251881Speter seg = ptr; 2055251881Speter while (*ptr && (*ptr != '/')) 2056251881Speter ptr++; 2057251881Speter } 2058251881Speter 2059251881Speter ptr = schema_data; 2060251881Speter 2061251881Speter while (*ptr) 2062251881Speter { 2063251881Speter if (*ptr == '%') 2064251881Speter { 2065251881Speter char digitz[3]; 2066251881Speter int val; 2067251881Speter 2068251881Speter /* Can't usesvn_ctype_isxdigit() because lower case letters are 2069251881Speter not in our canonical format */ 2070251881Speter if (((*(ptr+1) < '0' || *(ptr+1) > '9')) 2071251881Speter && (*(ptr+1) < 'A' || *(ptr+1) > 'F')) 2072251881Speter return FALSE; 2073251881Speter else if (((*(ptr+2) < '0' || *(ptr+2) > '9')) 2074251881Speter && (*(ptr+2) < 'A' || *(ptr+2) > 'F')) 2075251881Speter return FALSE; 2076251881Speter 2077251881Speter digitz[0] = *(++ptr); 2078251881Speter digitz[1] = *(++ptr); 2079251881Speter digitz[2] = '\0'; 2080251881Speter val = (int)strtol(digitz, NULL, 16); 2081251881Speter 2082251881Speter if (svn_uri__char_validity[val]) 2083251881Speter return FALSE; /* Should not have been escaped */ 2084251881Speter } 2085251881Speter else if (*ptr != '/' && !svn_uri__char_validity[(unsigned char)*ptr]) 2086251881Speter return FALSE; /* Character should have been escaped */ 2087251881Speter ptr++; 2088251881Speter } 2089251881Speter 2090251881Speter return TRUE; 2091251881Speter} 2092251881Speter 2093251881Spetersvn_error_t * 2094251881Spetersvn_dirent_condense_targets(const char **pcommon, 2095251881Speter apr_array_header_t **pcondensed_targets, 2096251881Speter const apr_array_header_t *targets, 2097251881Speter svn_boolean_t remove_redundancies, 2098251881Speter apr_pool_t *result_pool, 2099251881Speter apr_pool_t *scratch_pool) 2100251881Speter{ 2101251881Speter int i, num_condensed = targets->nelts; 2102251881Speter svn_boolean_t *removed; 2103251881Speter apr_array_header_t *abs_targets; 2104251881Speter 2105251881Speter /* Early exit when there's no data to work on. */ 2106251881Speter if (targets->nelts <= 0) 2107251881Speter { 2108251881Speter *pcommon = NULL; 2109251881Speter if (pcondensed_targets) 2110251881Speter *pcondensed_targets = NULL; 2111251881Speter return SVN_NO_ERROR; 2112251881Speter } 2113251881Speter 2114251881Speter /* Get the absolute path of the first target. */ 2115251881Speter SVN_ERR(svn_dirent_get_absolute(pcommon, 2116251881Speter APR_ARRAY_IDX(targets, 0, const char *), 2117251881Speter scratch_pool)); 2118251881Speter 2119251881Speter /* Early exit when there's only one dirent to work on. */ 2120251881Speter if (targets->nelts == 1) 2121251881Speter { 2122251881Speter *pcommon = apr_pstrdup(result_pool, *pcommon); 2123251881Speter if (pcondensed_targets) 2124251881Speter *pcondensed_targets = apr_array_make(result_pool, 0, 2125251881Speter sizeof(const char *)); 2126251881Speter return SVN_NO_ERROR; 2127251881Speter } 2128251881Speter 2129251881Speter /* Copy the targets array, but with absolute dirents instead of 2130251881Speter relative. Also, find the pcommon argument by finding what is 2131251881Speter common in all of the absolute dirents. NOTE: This is not as 2132251881Speter efficient as it could be. The calculation of the basedir could 2133251881Speter be done in the loop below, which would save some calls to 2134251881Speter svn_dirent_get_longest_ancestor. I decided to do it this way 2135251881Speter because I thought it would be simpler, since this way, we don't 2136251881Speter even do the loop if we don't need to condense the targets. */ 2137251881Speter 2138251881Speter removed = apr_pcalloc(scratch_pool, (targets->nelts * 2139251881Speter sizeof(svn_boolean_t))); 2140251881Speter abs_targets = apr_array_make(scratch_pool, targets->nelts, 2141251881Speter sizeof(const char *)); 2142251881Speter 2143251881Speter APR_ARRAY_PUSH(abs_targets, const char *) = *pcommon; 2144251881Speter 2145251881Speter for (i = 1; i < targets->nelts; ++i) 2146251881Speter { 2147251881Speter const char *rel = APR_ARRAY_IDX(targets, i, const char *); 2148251881Speter const char *absolute; 2149251881Speter SVN_ERR(svn_dirent_get_absolute(&absolute, rel, scratch_pool)); 2150251881Speter APR_ARRAY_PUSH(abs_targets, const char *) = absolute; 2151251881Speter *pcommon = svn_dirent_get_longest_ancestor(*pcommon, absolute, 2152251881Speter scratch_pool); 2153251881Speter } 2154251881Speter 2155251881Speter *pcommon = apr_pstrdup(result_pool, *pcommon); 2156251881Speter 2157251881Speter if (pcondensed_targets != NULL) 2158251881Speter { 2159251881Speter size_t basedir_len; 2160251881Speter 2161251881Speter if (remove_redundancies) 2162251881Speter { 2163251881Speter /* Find the common part of each pair of targets. If 2164251881Speter common part is equal to one of the dirents, the other 2165251881Speter is a child of it, and can be removed. If a target is 2166251881Speter equal to *pcommon, it can also be removed. */ 2167251881Speter 2168251881Speter /* First pass: when one non-removed target is a child of 2169251881Speter another non-removed target, remove the child. */ 2170251881Speter for (i = 0; i < abs_targets->nelts; ++i) 2171251881Speter { 2172251881Speter int j; 2173251881Speter 2174251881Speter if (removed[i]) 2175251881Speter continue; 2176251881Speter 2177251881Speter for (j = i + 1; j < abs_targets->nelts; ++j) 2178251881Speter { 2179251881Speter const char *abs_targets_i; 2180251881Speter const char *abs_targets_j; 2181251881Speter const char *ancestor; 2182251881Speter 2183251881Speter if (removed[j]) 2184251881Speter continue; 2185251881Speter 2186251881Speter abs_targets_i = APR_ARRAY_IDX(abs_targets, i, const char *); 2187251881Speter abs_targets_j = APR_ARRAY_IDX(abs_targets, j, const char *); 2188251881Speter 2189251881Speter ancestor = svn_dirent_get_longest_ancestor 2190251881Speter (abs_targets_i, abs_targets_j, scratch_pool); 2191251881Speter 2192251881Speter if (*ancestor == '\0') 2193251881Speter continue; 2194251881Speter 2195251881Speter if (strcmp(ancestor, abs_targets_i) == 0) 2196251881Speter { 2197251881Speter removed[j] = TRUE; 2198251881Speter num_condensed--; 2199251881Speter } 2200251881Speter else if (strcmp(ancestor, abs_targets_j) == 0) 2201251881Speter { 2202251881Speter removed[i] = TRUE; 2203251881Speter num_condensed--; 2204251881Speter } 2205251881Speter } 2206251881Speter } 2207251881Speter 2208251881Speter /* Second pass: when a target is the same as *pcommon, 2209251881Speter remove the target. */ 2210251881Speter for (i = 0; i < abs_targets->nelts; ++i) 2211251881Speter { 2212251881Speter const char *abs_targets_i = APR_ARRAY_IDX(abs_targets, i, 2213251881Speter const char *); 2214251881Speter 2215251881Speter if ((strcmp(abs_targets_i, *pcommon) == 0) && (! removed[i])) 2216251881Speter { 2217251881Speter removed[i] = TRUE; 2218251881Speter num_condensed--; 2219251881Speter } 2220251881Speter } 2221251881Speter } 2222251881Speter 2223251881Speter /* Now create the return array, and copy the non-removed items */ 2224251881Speter basedir_len = strlen(*pcommon); 2225251881Speter *pcondensed_targets = apr_array_make(result_pool, num_condensed, 2226251881Speter sizeof(const char *)); 2227251881Speter 2228251881Speter for (i = 0; i < abs_targets->nelts; ++i) 2229251881Speter { 2230251881Speter const char *rel_item = APR_ARRAY_IDX(abs_targets, i, const char *); 2231251881Speter 2232251881Speter /* Skip this if it's been removed. */ 2233251881Speter if (removed[i]) 2234251881Speter continue; 2235251881Speter 2236251881Speter /* If a common prefix was found, condensed_targets are given 2237251881Speter relative to that prefix. */ 2238251881Speter if (basedir_len > 0) 2239251881Speter { 2240251881Speter /* Only advance our pointer past a dirent separator if 2241251881Speter REL_ITEM isn't the same as *PCOMMON. 2242251881Speter 2243251881Speter If *PCOMMON is a root dirent, basedir_len will already 2244251881Speter include the closing '/', so never advance the pointer 2245251881Speter here. 2246251881Speter */ 2247251881Speter rel_item += basedir_len; 2248251881Speter if (rel_item[0] && 2249251881Speter ! svn_dirent_is_root(*pcommon, basedir_len)) 2250251881Speter rel_item++; 2251251881Speter } 2252251881Speter 2253251881Speter APR_ARRAY_PUSH(*pcondensed_targets, const char *) 2254251881Speter = apr_pstrdup(result_pool, rel_item); 2255251881Speter } 2256251881Speter } 2257251881Speter 2258251881Speter return SVN_NO_ERROR; 2259251881Speter} 2260251881Speter 2261251881Spetersvn_error_t * 2262251881Spetersvn_uri_condense_targets(const char **pcommon, 2263251881Speter apr_array_header_t **pcondensed_targets, 2264251881Speter const apr_array_header_t *targets, 2265251881Speter svn_boolean_t remove_redundancies, 2266251881Speter apr_pool_t *result_pool, 2267251881Speter apr_pool_t *scratch_pool) 2268251881Speter{ 2269251881Speter int i, num_condensed = targets->nelts; 2270251881Speter apr_array_header_t *uri_targets; 2271251881Speter svn_boolean_t *removed; 2272251881Speter 2273251881Speter /* Early exit when there's no data to work on. */ 2274251881Speter if (targets->nelts <= 0) 2275251881Speter { 2276251881Speter *pcommon = NULL; 2277251881Speter if (pcondensed_targets) 2278251881Speter *pcondensed_targets = NULL; 2279251881Speter return SVN_NO_ERROR; 2280251881Speter } 2281251881Speter 2282251881Speter *pcommon = svn_uri_canonicalize(APR_ARRAY_IDX(targets, 0, const char *), 2283251881Speter scratch_pool); 2284251881Speter 2285251881Speter /* Early exit when there's only one uri to work on. */ 2286251881Speter if (targets->nelts == 1) 2287251881Speter { 2288251881Speter *pcommon = apr_pstrdup(result_pool, *pcommon); 2289251881Speter if (pcondensed_targets) 2290251881Speter *pcondensed_targets = apr_array_make(result_pool, 0, 2291251881Speter sizeof(const char *)); 2292251881Speter return SVN_NO_ERROR; 2293251881Speter } 2294251881Speter 2295251881Speter /* Find the pcommon argument by finding what is common in all of the 2296251881Speter uris. NOTE: This is not as efficient as it could be. The calculation 2297251881Speter of the basedir could be done in the loop below, which would 2298251881Speter save some calls to svn_uri_get_longest_ancestor. I decided to do it 2299251881Speter this way because I thought it would be simpler, since this way, we don't 2300251881Speter even do the loop if we don't need to condense the targets. */ 2301251881Speter 2302251881Speter removed = apr_pcalloc(scratch_pool, (targets->nelts * 2303251881Speter sizeof(svn_boolean_t))); 2304251881Speter uri_targets = apr_array_make(scratch_pool, targets->nelts, 2305251881Speter sizeof(const char *)); 2306251881Speter 2307251881Speter APR_ARRAY_PUSH(uri_targets, const char *) = *pcommon; 2308251881Speter 2309251881Speter for (i = 1; i < targets->nelts; ++i) 2310251881Speter { 2311251881Speter const char *uri = svn_uri_canonicalize( 2312251881Speter APR_ARRAY_IDX(targets, i, const char *), 2313251881Speter scratch_pool); 2314251881Speter APR_ARRAY_PUSH(uri_targets, const char *) = uri; 2315251881Speter 2316251881Speter /* If the commonmost ancestor so far is empty, there's no point 2317251881Speter in continuing to search for a common ancestor at all. But 2318251881Speter we'll keep looping for the sake of canonicalizing the 2319251881Speter targets, I suppose. */ 2320251881Speter if (**pcommon != '\0') 2321251881Speter *pcommon = svn_uri_get_longest_ancestor(*pcommon, uri, 2322251881Speter scratch_pool); 2323251881Speter } 2324251881Speter 2325251881Speter *pcommon = apr_pstrdup(result_pool, *pcommon); 2326251881Speter 2327251881Speter if (pcondensed_targets != NULL) 2328251881Speter { 2329251881Speter size_t basedir_len; 2330251881Speter 2331251881Speter if (remove_redundancies) 2332251881Speter { 2333251881Speter /* Find the common part of each pair of targets. If 2334251881Speter common part is equal to one of the dirents, the other 2335251881Speter is a child of it, and can be removed. If a target is 2336251881Speter equal to *pcommon, it can also be removed. */ 2337251881Speter 2338251881Speter /* First pass: when one non-removed target is a child of 2339251881Speter another non-removed target, remove the child. */ 2340251881Speter for (i = 0; i < uri_targets->nelts; ++i) 2341251881Speter { 2342251881Speter int j; 2343251881Speter 2344251881Speter if (removed[i]) 2345251881Speter continue; 2346251881Speter 2347251881Speter for (j = i + 1; j < uri_targets->nelts; ++j) 2348251881Speter { 2349251881Speter const char *uri_i; 2350251881Speter const char *uri_j; 2351251881Speter const char *ancestor; 2352251881Speter 2353251881Speter if (removed[j]) 2354251881Speter continue; 2355251881Speter 2356251881Speter uri_i = APR_ARRAY_IDX(uri_targets, i, const char *); 2357251881Speter uri_j = APR_ARRAY_IDX(uri_targets, j, const char *); 2358251881Speter 2359251881Speter ancestor = svn_uri_get_longest_ancestor(uri_i, 2360251881Speter uri_j, 2361251881Speter scratch_pool); 2362251881Speter 2363251881Speter if (*ancestor == '\0') 2364251881Speter continue; 2365251881Speter 2366251881Speter if (strcmp(ancestor, uri_i) == 0) 2367251881Speter { 2368251881Speter removed[j] = TRUE; 2369251881Speter num_condensed--; 2370251881Speter } 2371251881Speter else if (strcmp(ancestor, uri_j) == 0) 2372251881Speter { 2373251881Speter removed[i] = TRUE; 2374251881Speter num_condensed--; 2375251881Speter } 2376251881Speter } 2377251881Speter } 2378251881Speter 2379251881Speter /* Second pass: when a target is the same as *pcommon, 2380251881Speter remove the target. */ 2381251881Speter for (i = 0; i < uri_targets->nelts; ++i) 2382251881Speter { 2383251881Speter const char *uri_targets_i = APR_ARRAY_IDX(uri_targets, i, 2384251881Speter const char *); 2385251881Speter 2386251881Speter if ((strcmp(uri_targets_i, *pcommon) == 0) && (! removed[i])) 2387251881Speter { 2388251881Speter removed[i] = TRUE; 2389251881Speter num_condensed--; 2390251881Speter } 2391251881Speter } 2392251881Speter } 2393251881Speter 2394251881Speter /* Now create the return array, and copy the non-removed items */ 2395251881Speter basedir_len = strlen(*pcommon); 2396251881Speter *pcondensed_targets = apr_array_make(result_pool, num_condensed, 2397251881Speter sizeof(const char *)); 2398251881Speter 2399251881Speter for (i = 0; i < uri_targets->nelts; ++i) 2400251881Speter { 2401251881Speter const char *rel_item = APR_ARRAY_IDX(uri_targets, i, const char *); 2402251881Speter 2403251881Speter /* Skip this if it's been removed. */ 2404251881Speter if (removed[i]) 2405251881Speter continue; 2406251881Speter 2407251881Speter /* If a common prefix was found, condensed_targets are given 2408251881Speter relative to that prefix. */ 2409251881Speter if (basedir_len > 0) 2410251881Speter { 2411251881Speter /* Only advance our pointer past a dirent separator if 2412251881Speter REL_ITEM isn't the same as *PCOMMON. 2413251881Speter 2414251881Speter If *PCOMMON is a root dirent, basedir_len will already 2415251881Speter include the closing '/', so never advance the pointer 2416251881Speter here. 2417251881Speter */ 2418251881Speter rel_item += basedir_len; 2419251881Speter if ((rel_item[0] == '/') || 2420251881Speter (rel_item[0] && !svn_uri_is_root(*pcommon, basedir_len))) 2421251881Speter { 2422251881Speter rel_item++; 2423251881Speter } 2424251881Speter } 2425251881Speter 2426251881Speter APR_ARRAY_PUSH(*pcondensed_targets, const char *) 2427251881Speter = svn_path_uri_decode(rel_item, result_pool); 2428251881Speter } 2429251881Speter } 2430251881Speter 2431251881Speter return SVN_NO_ERROR; 2432251881Speter} 2433251881Speter 2434251881Spetersvn_error_t * 2435251881Spetersvn_dirent_is_under_root(svn_boolean_t *under_root, 2436251881Speter const char **result_path, 2437251881Speter const char *base_path, 2438251881Speter const char *path, 2439251881Speter apr_pool_t *result_pool) 2440251881Speter{ 2441251881Speter apr_status_t status; 2442251881Speter char *full_path; 2443251881Speter 2444251881Speter *under_root = FALSE; 2445251881Speter if (result_path) 2446251881Speter *result_path = NULL; 2447251881Speter 2448251881Speter status = apr_filepath_merge(&full_path, 2449251881Speter base_path, 2450251881Speter path, 2451251881Speter APR_FILEPATH_NOTABOVEROOT 2452251881Speter | APR_FILEPATH_SECUREROOTTEST, 2453251881Speter result_pool); 2454251881Speter 2455251881Speter if (status == APR_SUCCESS) 2456251881Speter { 2457251881Speter if (result_path) 2458251881Speter *result_path = svn_dirent_canonicalize(full_path, result_pool); 2459251881Speter *under_root = TRUE; 2460251881Speter return SVN_NO_ERROR; 2461251881Speter } 2462251881Speter else if (status == APR_EABOVEROOT) 2463251881Speter { 2464251881Speter *under_root = FALSE; 2465251881Speter return SVN_NO_ERROR; 2466251881Speter } 2467251881Speter 2468251881Speter return svn_error_wrap_apr(status, NULL); 2469251881Speter} 2470251881Speter 2471251881Spetersvn_error_t * 2472251881Spetersvn_uri_get_dirent_from_file_url(const char **dirent, 2473251881Speter const char *url, 2474251881Speter apr_pool_t *pool) 2475251881Speter{ 2476251881Speter const char *hostname, *path; 2477251881Speter 2478251881Speter SVN_ERR_ASSERT(svn_uri_is_canonical(url, pool)); 2479251881Speter 2480251881Speter /* Verify that the URL is well-formed (loosely) */ 2481251881Speter 2482251881Speter /* First, check for the "file://" prefix. */ 2483251881Speter if (strncmp(url, "file://", 7) != 0) 2484251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 2485251881Speter _("Local URL '%s' does not contain 'file://' " 2486251881Speter "prefix"), url); 2487251881Speter 2488251881Speter /* Find the HOSTNAME portion and the PATH portion of the URL. The host 2489289180Speter name is between the "file://" prefix and the next occurrence of '/'. We 2490251881Speter are considering everything from that '/' until the end of the URL to be 2491251881Speter the absolute path portion of the URL. 2492251881Speter If we got just "file://", treat it the same as "file:///". */ 2493251881Speter hostname = url + 7; 2494251881Speter path = strchr(hostname, '/'); 2495251881Speter if (path) 2496251881Speter hostname = apr_pstrmemdup(pool, hostname, path - hostname); 2497251881Speter else 2498251881Speter path = "/"; 2499251881Speter 2500251881Speter /* URI-decode HOSTNAME, and set it to NULL if it is "" or "localhost". */ 2501251881Speter if (*hostname == '\0') 2502251881Speter hostname = NULL; 2503251881Speter else 2504251881Speter { 2505251881Speter hostname = svn_path_uri_decode(hostname, pool); 2506251881Speter if (strcmp(hostname, "localhost") == 0) 2507251881Speter hostname = NULL; 2508251881Speter } 2509251881Speter 2510251881Speter /* Duplicate the URL, starting at the top of the path. 2511251881Speter At the same time, we URI-decode the path. */ 2512251881Speter#ifdef SVN_USE_DOS_PATHS 2513251881Speter /* On Windows, we'll typically have to skip the leading / if the 2514251881Speter path starts with a drive letter. Like most Web browsers, We 2515251881Speter support two variants of this scheme: 2516251881Speter 2517251881Speter file:///X:/path and 2518251881Speter file:///X|/path 2519251881Speter 2520251881Speter Note that, at least on WinNT and above, file:////./X:/path will 2521251881Speter also work, so we must make sure the transformation doesn't break 2522251881Speter that, and file:///path (that looks within the current drive 2523251881Speter only) should also keep working. 2524251881Speter If we got a non-empty hostname other than localhost, we convert this 2525251881Speter into an UNC path. In this case, we obviously don't strip the slash 2526251881Speter even if the path looks like it starts with a drive letter. 2527251881Speter */ 2528251881Speter { 2529251881Speter static const char valid_drive_letters[] = 2530251881Speter "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; 2531251881Speter /* Casting away const! */ 2532251881Speter char *dup_path = (char *)svn_path_uri_decode(path, pool); 2533251881Speter 2534251881Speter /* This check assumes ':' and '|' are already decoded! */ 2535251881Speter if (!hostname && dup_path[1] && strchr(valid_drive_letters, dup_path[1]) 2536251881Speter && (dup_path[2] == ':' || dup_path[2] == '|')) 2537251881Speter { 2538251881Speter /* Skip the leading slash. */ 2539251881Speter ++dup_path; 2540251881Speter 2541251881Speter if (dup_path[1] == '|') 2542251881Speter dup_path[1] = ':'; 2543251881Speter 2544362181Sdim if (dup_path[2] == '/' || dup_path[2] == '\\' || dup_path[2] == '\0') 2545251881Speter { 2546362181Sdim /* Dirents have upper case drive letters in their canonical form */ 2547362181Sdim dup_path[0] = canonicalize_to_upper(dup_path[0]); 2548362181Sdim 2549251881Speter if (dup_path[2] == '\0') 2550251881Speter { 2551251881Speter /* A valid dirent for the driveroot must be like "C:/" instead of 2552251881Speter just "C:" or svn_dirent_join() will use the current directory 2553251881Speter on the drive instead */ 2554251881Speter char *new_path = apr_pcalloc(pool, 4); 2555251881Speter new_path[0] = dup_path[0]; 2556251881Speter new_path[1] = ':'; 2557251881Speter new_path[2] = '/'; 2558251881Speter new_path[3] = '\0'; 2559251881Speter dup_path = new_path; 2560251881Speter } 2561362181Sdim else 2562362181Sdim dup_path[2] = '/'; /* Ensure not relative for '\' after drive! */ 2563251881Speter } 2564251881Speter } 2565251881Speter if (hostname) 2566251881Speter { 2567251881Speter if (dup_path[0] == '/' && dup_path[1] == '\0') 2568251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 2569251881Speter _("Local URL '%s' contains only a hostname, " 2570251881Speter "no path"), url); 2571251881Speter 2572251881Speter /* We still know that the path starts with a slash. */ 2573289180Speter *dirent = apr_pstrcat(pool, "//", hostname, dup_path, SVN_VA_NULL); 2574251881Speter } 2575251881Speter else 2576251881Speter *dirent = dup_path; 2577251881Speter } 2578251881Speter#else /* !SVN_USE_DOS_PATHS */ 2579251881Speter /* Currently, the only hostnames we are allowing on non-Win32 platforms 2580251881Speter are the empty string and 'localhost'. */ 2581251881Speter if (hostname) 2582251881Speter return svn_error_createf(SVN_ERR_RA_ILLEGAL_URL, NULL, 2583251881Speter _("Local URL '%s' contains unsupported hostname"), 2584251881Speter url); 2585251881Speter 2586251881Speter *dirent = svn_path_uri_decode(path, pool); 2587251881Speter#endif /* SVN_USE_DOS_PATHS */ 2588251881Speter return SVN_NO_ERROR; 2589251881Speter} 2590251881Speter 2591251881Spetersvn_error_t * 2592251881Spetersvn_uri_get_file_url_from_dirent(const char **url, 2593251881Speter const char *dirent, 2594251881Speter apr_pool_t *pool) 2595251881Speter{ 2596251881Speter assert(svn_dirent_is_canonical(dirent, pool)); 2597251881Speter 2598251881Speter SVN_ERR(svn_dirent_get_absolute(&dirent, dirent, pool)); 2599251881Speter 2600251881Speter dirent = svn_path_uri_encode(dirent, pool); 2601251881Speter 2602251881Speter#ifndef SVN_USE_DOS_PATHS 2603251881Speter if (dirent[0] == '/' && dirent[1] == '\0') 2604251881Speter dirent = NULL; /* "file://" is the canonical form of "file:///" */ 2605251881Speter 2606289180Speter *url = apr_pstrcat(pool, "file://", dirent, SVN_VA_NULL); 2607251881Speter#else 2608251881Speter if (dirent[0] == '/') 2609251881Speter { 2610251881Speter /* Handle UNC paths //server/share -> file://server/share */ 2611251881Speter assert(dirent[1] == '/'); /* Expect UNC, not non-absolute */ 2612251881Speter 2613289180Speter *url = apr_pstrcat(pool, "file:", dirent, SVN_VA_NULL); 2614251881Speter } 2615251881Speter else 2616251881Speter { 2617289180Speter char *uri = apr_pstrcat(pool, "file:///", dirent, SVN_VA_NULL); 2618251881Speter apr_size_t len = 8 /* strlen("file:///") */ + strlen(dirent); 2619251881Speter 2620251881Speter /* "C:/" is a canonical dirent on Windows, 2621251881Speter but "file:///C:/" is not a canonical uri */ 2622251881Speter if (uri[len-1] == '/') 2623251881Speter uri[len-1] = '\0'; 2624251881Speter 2625251881Speter *url = uri; 2626251881Speter } 2627251881Speter#endif 2628251881Speter 2629251881Speter return SVN_NO_ERROR; 2630251881Speter} 2631251881Speter 2632251881Speter 2633251881Speter 2634251881Speter/* -------------- The fspath API (see private/svn_fspath.h) -------------- */ 2635251881Speter 2636251881Spetersvn_boolean_t 2637251881Spetersvn_fspath__is_canonical(const char *fspath) 2638251881Speter{ 2639251881Speter return fspath[0] == '/' && relpath_is_canonical(fspath + 1); 2640251881Speter} 2641251881Speter 2642251881Speter 2643251881Speterconst char * 2644251881Spetersvn_fspath__canonicalize(const char *fspath, 2645251881Speter apr_pool_t *pool) 2646251881Speter{ 2647251881Speter if ((fspath[0] == '/') && (fspath[1] == '\0')) 2648251881Speter return "/"; 2649251881Speter 2650251881Speter return apr_pstrcat(pool, "/", svn_relpath_canonicalize(fspath, pool), 2651289180Speter SVN_VA_NULL); 2652251881Speter} 2653251881Speter 2654251881Speter 2655251881Spetersvn_boolean_t 2656251881Spetersvn_fspath__is_root(const char *fspath, apr_size_t len) 2657251881Speter{ 2658251881Speter /* directory is root if it's equal to '/' */ 2659251881Speter return (len == 1 && fspath[0] == '/'); 2660251881Speter} 2661251881Speter 2662251881Speter 2663251881Speterconst char * 2664251881Spetersvn_fspath__skip_ancestor(const char *parent_fspath, 2665251881Speter const char *child_fspath) 2666251881Speter{ 2667251881Speter assert(svn_fspath__is_canonical(parent_fspath)); 2668251881Speter assert(svn_fspath__is_canonical(child_fspath)); 2669251881Speter 2670251881Speter return svn_relpath_skip_ancestor(parent_fspath + 1, child_fspath + 1); 2671251881Speter} 2672251881Speter 2673251881Speter 2674251881Speterconst char * 2675251881Spetersvn_fspath__dirname(const char *fspath, 2676251881Speter apr_pool_t *pool) 2677251881Speter{ 2678251881Speter assert(svn_fspath__is_canonical(fspath)); 2679251881Speter 2680251881Speter if (fspath[0] == '/' && fspath[1] == '\0') 2681251881Speter return apr_pstrdup(pool, fspath); 2682251881Speter else 2683251881Speter return apr_pstrcat(pool, "/", svn_relpath_dirname(fspath + 1, pool), 2684289180Speter SVN_VA_NULL); 2685251881Speter} 2686251881Speter 2687251881Speter 2688251881Speterconst char * 2689251881Spetersvn_fspath__basename(const char *fspath, 2690251881Speter apr_pool_t *pool) 2691251881Speter{ 2692251881Speter const char *result; 2693251881Speter assert(svn_fspath__is_canonical(fspath)); 2694251881Speter 2695251881Speter result = svn_relpath_basename(fspath + 1, pool); 2696251881Speter 2697251881Speter assert(strchr(result, '/') == NULL); 2698251881Speter return result; 2699251881Speter} 2700251881Speter 2701251881Spetervoid 2702251881Spetersvn_fspath__split(const char **dirpath, 2703251881Speter const char **base_name, 2704251881Speter const char *fspath, 2705251881Speter apr_pool_t *result_pool) 2706251881Speter{ 2707251881Speter assert(dirpath != base_name); 2708251881Speter 2709251881Speter if (dirpath) 2710251881Speter *dirpath = svn_fspath__dirname(fspath, result_pool); 2711251881Speter 2712251881Speter if (base_name) 2713251881Speter *base_name = svn_fspath__basename(fspath, result_pool); 2714251881Speter} 2715251881Speter 2716251881Speterchar * 2717251881Spetersvn_fspath__join(const char *fspath, 2718251881Speter const char *relpath, 2719251881Speter apr_pool_t *result_pool) 2720251881Speter{ 2721251881Speter char *result; 2722251881Speter assert(svn_fspath__is_canonical(fspath)); 2723251881Speter assert(svn_relpath_is_canonical(relpath)); 2724251881Speter 2725251881Speter if (relpath[0] == '\0') 2726251881Speter result = apr_pstrdup(result_pool, fspath); 2727251881Speter else if (fspath[1] == '\0') 2728289180Speter result = apr_pstrcat(result_pool, "/", relpath, SVN_VA_NULL); 2729251881Speter else 2730289180Speter result = apr_pstrcat(result_pool, fspath, "/", relpath, SVN_VA_NULL); 2731251881Speter 2732251881Speter assert(svn_fspath__is_canonical(result)); 2733251881Speter return result; 2734251881Speter} 2735251881Speter 2736251881Speterchar * 2737251881Spetersvn_fspath__get_longest_ancestor(const char *fspath1, 2738251881Speter const char *fspath2, 2739251881Speter apr_pool_t *result_pool) 2740251881Speter{ 2741251881Speter char *result; 2742251881Speter assert(svn_fspath__is_canonical(fspath1)); 2743251881Speter assert(svn_fspath__is_canonical(fspath2)); 2744251881Speter 2745251881Speter result = apr_pstrcat(result_pool, "/", 2746251881Speter svn_relpath_get_longest_ancestor(fspath1 + 1, 2747251881Speter fspath2 + 1, 2748251881Speter result_pool), 2749289180Speter SVN_VA_NULL); 2750251881Speter 2751251881Speter assert(svn_fspath__is_canonical(result)); 2752251881Speter return result; 2753251881Speter} 2754251881Speter 2755251881Speter 2756251881Speter 2757251881Speter 2758251881Speter/* -------------- The urlpath API (see private/svn_fspath.h) ------------- */ 2759251881Speter 2760251881Speterconst char * 2761251881Spetersvn_urlpath__canonicalize(const char *uri, 2762251881Speter apr_pool_t *pool) 2763251881Speter{ 2764251881Speter if (svn_path_is_url(uri)) 2765251881Speter { 2766251881Speter uri = svn_uri_canonicalize(uri, pool); 2767251881Speter } 2768251881Speter else 2769251881Speter { 2770251881Speter uri = svn_fspath__canonicalize(uri, pool); 2771251881Speter /* Do a little dance to normalize hex encoding. */ 2772251881Speter uri = svn_path_uri_decode(uri, pool); 2773251881Speter uri = svn_path_uri_encode(uri, pool); 2774251881Speter } 2775251881Speter return uri; 2776251881Speter} 2777269833Speter 2778269833Speter 2779269833Speter/* -------------- The cert API (see private/svn_cert.h) ------------- */ 2780269833Speter 2781269833Spetersvn_boolean_t 2782269833Spetersvn_cert__match_dns_identity(svn_string_t *pattern, svn_string_t *hostname) 2783269833Speter{ 2784269833Speter apr_size_t pattern_pos = 0, hostname_pos = 0; 2785269833Speter 2786269833Speter /* support leading wildcards that composed of the only character in the 2787269833Speter * left-most label. */ 2788269833Speter if (pattern->len >= 2 && 2789269833Speter pattern->data[pattern_pos] == '*' && 2790269833Speter pattern->data[pattern_pos + 1] == '.') 2791269833Speter { 2792269833Speter while (hostname_pos < hostname->len && 2793269833Speter hostname->data[hostname_pos] != '.') 2794269833Speter { 2795269833Speter hostname_pos++; 2796269833Speter } 2797269833Speter /* Assume that the wildcard must match something. Rule 2 says 2798269833Speter * that *.example.com should not match example.com. If the wildcard 2799269833Speter * ends up not matching anything then it matches .example.com which 2800269833Speter * seems to be essentially the same as just example.com */ 2801269833Speter if (hostname_pos == 0) 2802269833Speter return FALSE; 2803269833Speter 2804269833Speter pattern_pos++; 2805269833Speter } 2806269833Speter 2807269833Speter while (pattern_pos < pattern->len && hostname_pos < hostname->len) 2808269833Speter { 2809269833Speter char pattern_c = pattern->data[pattern_pos]; 2810269833Speter char hostname_c = hostname->data[hostname_pos]; 2811269833Speter 2812269833Speter /* fold case as described in RFC 4343. 2813269833Speter * Note: We actually convert to lowercase, since our URI 2814269833Speter * canonicalization code converts to lowercase and generally 2815269833Speter * most certs are issued with lowercase DNS names, meaning 2816269833Speter * this avoids the fold operation in most cases. The RFC 2817269833Speter * suggests the opposite transformation, but doesn't require 2818269833Speter * any specific implementation in any case. It is critical 2819269833Speter * that this folding be locale independent so you can't use 2820269833Speter * tolower(). */ 2821269833Speter pattern_c = canonicalize_to_lower(pattern_c); 2822269833Speter hostname_c = canonicalize_to_lower(hostname_c); 2823269833Speter 2824269833Speter if (pattern_c != hostname_c) 2825269833Speter { 2826269833Speter /* doesn't match */ 2827269833Speter return FALSE; 2828269833Speter } 2829269833Speter else 2830269833Speter { 2831269833Speter /* characters match so skip both */ 2832269833Speter pattern_pos++; 2833269833Speter hostname_pos++; 2834269833Speter } 2835269833Speter } 2836269833Speter 2837269833Speter /* ignore a trailing period on the hostname since this has no effect on the 2838269833Speter * security of the matching. See the following for the long explanation as 2839269833Speter * to why: 2840269833Speter * https://bugzilla.mozilla.org/show_bug.cgi?id=134402#c28 2841269833Speter */ 2842269833Speter if (pattern_pos == pattern->len && 2843269833Speter hostname_pos == hostname->len - 1 && 2844269833Speter hostname->data[hostname_pos] == '.') 2845269833Speter hostname_pos++; 2846269833Speter 2847269833Speter if (pattern_pos != pattern->len || hostname_pos != hostname->len) 2848269833Speter { 2849269833Speter /* end didn't match */ 2850269833Speter return FALSE; 2851269833Speter } 2852269833Speter 2853269833Speter return TRUE; 2854269833Speter} 2855