diff options
Diffstat (limited to 'xbmc/utils/URIUtils.cpp')
| -rw-r--r-- | xbmc/utils/URIUtils.cpp | 1441 |
1 files changed, 1441 insertions, 0 deletions
diff --git a/xbmc/utils/URIUtils.cpp b/xbmc/utils/URIUtils.cpp new file mode 100644 index 0000000..738c946 --- /dev/null +++ b/xbmc/utils/URIUtils.cpp | |||
| @@ -0,0 +1,1441 @@ | |||
| 1 | /* | ||
| 2 | * Copyright (C) 2005-2018 Team Kodi | ||
| 3 | * This file is part of Kodi - https://kodi.tv | ||
| 4 | * | ||
| 5 | * SPDX-License-Identifier: GPL-2.0-or-later | ||
| 6 | * See LICENSES/README.md for more information. | ||
| 7 | */ | ||
| 8 | |||
| 9 | #include "network/Network.h" | ||
| 10 | #include "URIUtils.h" | ||
| 11 | #include "FileItem.h" | ||
| 12 | #include "filesystem/MultiPathDirectory.h" | ||
| 13 | #include "filesystem/SpecialProtocol.h" | ||
| 14 | #include "filesystem/StackDirectory.h" | ||
| 15 | #include "network/DNSNameCache.h" | ||
| 16 | #include "pvr/channels/PVRChannelsPath.h" | ||
| 17 | #include "settings/AdvancedSettings.h" | ||
| 18 | #include "URL.h" | ||
| 19 | #include "utils/FileExtensionProvider.h" | ||
| 20 | #include "ServiceBroker.h" | ||
| 21 | #include "StringUtils.h" | ||
| 22 | #include "utils/log.h" | ||
| 23 | |||
| 24 | #if defined(TARGET_WINDOWS) | ||
| 25 | #include "platform/win32/CharsetConverter.h" | ||
| 26 | #endif | ||
| 27 | |||
| 28 | #include <algorithm> | ||
| 29 | #include <cassert> | ||
| 30 | #include <netinet/in.h> | ||
| 31 | #include <arpa/inet.h> | ||
| 32 | |||
| 33 | using namespace PVR; | ||
| 34 | using namespace XFILE; | ||
| 35 | |||
| 36 | const CAdvancedSettings* URIUtils::m_advancedSettings = nullptr; | ||
| 37 | |||
| 38 | void URIUtils::RegisterAdvancedSettings(const CAdvancedSettings& advancedSettings) | ||
| 39 | { | ||
| 40 | m_advancedSettings = &advancedSettings; | ||
| 41 | } | ||
| 42 | |||
| 43 | void URIUtils::UnregisterAdvancedSettings() | ||
| 44 | { | ||
| 45 | m_advancedSettings = nullptr; | ||
| 46 | } | ||
| 47 | |||
| 48 | /* returns filename extension including period of filename */ | ||
| 49 | std::string URIUtils::GetExtension(const CURL& url) | ||
| 50 | { | ||
| 51 | return URIUtils::GetExtension(url.GetFileName()); | ||
| 52 | } | ||
| 53 | |||
| 54 | std::string URIUtils::GetExtension(const std::string& strFileName) | ||
| 55 | { | ||
| 56 | if (IsURL(strFileName)) | ||
| 57 | { | ||
| 58 | CURL url(strFileName); | ||
| 59 | return GetExtension(url.GetFileName()); | ||
| 60 | } | ||
| 61 | |||
| 62 | size_t period = strFileName.find_last_of("./\\"); | ||
| 63 | if (period == std::string::npos || strFileName[period] != '.') | ||
| 64 | return std::string(); | ||
| 65 | |||
| 66 | return strFileName.substr(period); | ||
| 67 | } | ||
| 68 | |||
| 69 | bool URIUtils::HasExtension(const std::string& strFileName) | ||
| 70 | { | ||
| 71 | if (IsURL(strFileName)) | ||
| 72 | { | ||
| 73 | CURL url(strFileName); | ||
| 74 | return HasExtension(url.GetFileName()); | ||
| 75 | } | ||
| 76 | |||
| 77 | size_t iPeriod = strFileName.find_last_of("./\\"); | ||
| 78 | return iPeriod != std::string::npos && strFileName[iPeriod] == '.'; | ||
| 79 | } | ||
| 80 | |||
| 81 | bool URIUtils::HasExtension(const CURL& url, const std::string& strExtensions) | ||
| 82 | { | ||
| 83 | return HasExtension(url.GetFileName(), strExtensions); | ||
| 84 | } | ||
| 85 | |||
| 86 | bool URIUtils::HasExtension(const std::string& strFileName, const std::string& strExtensions) | ||
| 87 | { | ||
| 88 | if (IsURL(strFileName)) | ||
| 89 | { | ||
| 90 | CURL url(strFileName); | ||
| 91 | return HasExtension(url.GetFileName(), strExtensions); | ||
| 92 | } | ||
| 93 | |||
| 94 | // Search backwards so that '.' can be used as a search terminator. | ||
| 95 | std::string::const_reverse_iterator itExtensions = strExtensions.rbegin(); | ||
| 96 | while (itExtensions != strExtensions.rend()) | ||
| 97 | { | ||
| 98 | // Iterate backwards over strFileName untill we hit a '.' or a mismatch | ||
| 99 | for (std::string::const_reverse_iterator itFileName = strFileName.rbegin(); | ||
| 100 | itFileName != strFileName.rend() && itExtensions != strExtensions.rend() && | ||
| 101 | tolower(*itFileName) == *itExtensions; | ||
| 102 | ++itFileName, ++itExtensions) | ||
| 103 | { | ||
| 104 | if (*itExtensions == '.') | ||
| 105 | return true; // Match | ||
| 106 | } | ||
| 107 | |||
| 108 | // No match. Look for more extensions to try. | ||
| 109 | while (itExtensions != strExtensions.rend() && *itExtensions != '|') | ||
| 110 | ++itExtensions; | ||
| 111 | |||
| 112 | while (itExtensions != strExtensions.rend() && *itExtensions == '|') | ||
| 113 | ++itExtensions; | ||
| 114 | } | ||
| 115 | |||
| 116 | return false; | ||
| 117 | } | ||
| 118 | |||
| 119 | void URIUtils::RemoveExtension(std::string& strFileName) | ||
| 120 | { | ||
| 121 | if(IsURL(strFileName)) | ||
| 122 | { | ||
| 123 | CURL url(strFileName); | ||
| 124 | strFileName = url.GetFileName(); | ||
| 125 | RemoveExtension(strFileName); | ||
| 126 | url.SetFileName(strFileName); | ||
| 127 | strFileName = url.Get(); | ||
| 128 | return; | ||
| 129 | } | ||
| 130 | |||
| 131 | size_t period = strFileName.find_last_of("./\\"); | ||
| 132 | if (period != std::string::npos && strFileName[period] == '.') | ||
| 133 | { | ||
| 134 | std::string strExtension = strFileName.substr(period); | ||
| 135 | StringUtils::ToLower(strExtension); | ||
| 136 | strExtension += "|"; | ||
| 137 | |||
| 138 | std::string strFileMask; | ||
| 139 | strFileMask = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions(); | ||
| 140 | strFileMask += "|" + CServiceBroker::GetFileExtensionProvider().GetMusicExtensions(); | ||
| 141 | strFileMask += "|" + CServiceBroker::GetFileExtensionProvider().GetVideoExtensions(); | ||
| 142 | strFileMask += "|" + CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions(); | ||
| 143 | #if defined(TARGET_DARWIN) | ||
| 144 | strFileMask += "|.py|.xml|.milk|.xbt|.cdg|.app|.applescript|.workflow"; | ||
| 145 | #else | ||
| 146 | strFileMask += "|.py|.xml|.milk|.xbt|.cdg"; | ||
| 147 | #endif | ||
| 148 | strFileMask += "|"; | ||
| 149 | |||
| 150 | if (strFileMask.find(strExtension) != std::string::npos) | ||
| 151 | strFileName.erase(period); | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | std::string URIUtils::ReplaceExtension(const std::string& strFile, | ||
| 156 | const std::string& strNewExtension) | ||
| 157 | { | ||
| 158 | if(IsURL(strFile)) | ||
| 159 | { | ||
| 160 | CURL url(strFile); | ||
| 161 | url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension)); | ||
| 162 | return url.Get(); | ||
| 163 | } | ||
| 164 | |||
| 165 | std::string strChangedFile; | ||
| 166 | std::string strExtension = GetExtension(strFile); | ||
| 167 | if ( strExtension.size() ) | ||
| 168 | { | ||
| 169 | strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ; | ||
| 170 | strChangedFile += strNewExtension; | ||
| 171 | } | ||
| 172 | else | ||
| 173 | { | ||
| 174 | strChangedFile = strFile; | ||
| 175 | strChangedFile += strNewExtension; | ||
| 176 | } | ||
| 177 | return strChangedFile; | ||
| 178 | } | ||
| 179 | |||
| 180 | std::string URIUtils::GetFileName(const CURL& url) | ||
| 181 | { | ||
| 182 | return GetFileName(url.GetFileName()); | ||
| 183 | } | ||
| 184 | |||
| 185 | /* returns a filename given an url */ | ||
| 186 | /* handles both / and \, and options in urls*/ | ||
| 187 | std::string URIUtils::GetFileName(const std::string& strFileNameAndPath) | ||
| 188 | { | ||
| 189 | if(IsURL(strFileNameAndPath)) | ||
| 190 | { | ||
| 191 | CURL url(strFileNameAndPath); | ||
| 192 | return GetFileName(url.GetFileName()); | ||
| 193 | } | ||
| 194 | |||
| 195 | /* find the last slash */ | ||
| 196 | const size_t slash = strFileNameAndPath.find_last_of("/\\"); | ||
| 197 | return strFileNameAndPath.substr(slash+1); | ||
| 198 | } | ||
| 199 | |||
| 200 | void URIUtils::Split(const std::string& strFileNameAndPath, | ||
| 201 | std::string& strPath, std::string& strFileName) | ||
| 202 | { | ||
| 203 | //Splits a full filename in path and file. | ||
| 204 | //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext | ||
| 205 | //Trailing slash will be preserved | ||
| 206 | strFileName = ""; | ||
| 207 | strPath = ""; | ||
| 208 | int i = strFileNameAndPath.size() - 1; | ||
| 209 | while (i > 0) | ||
| 210 | { | ||
| 211 | char ch = strFileNameAndPath[i]; | ||
| 212 | // Only break on ':' if it's a drive separator for DOS (ie d:foo) | ||
| 213 | if (ch == '/' || ch == '\\' || (ch == ':' && i == 1)) break; | ||
| 214 | else i--; | ||
| 215 | } | ||
| 216 | if (i == 0) | ||
| 217 | i--; | ||
| 218 | |||
| 219 | // take left including the directory separator | ||
| 220 | strPath = strFileNameAndPath.substr(0, i+1); | ||
| 221 | // everything to the right of the directory separator | ||
| 222 | strFileName = strFileNameAndPath.substr(i+1); | ||
| 223 | |||
| 224 | // if actual uri, ignore options | ||
| 225 | if (IsURL(strFileNameAndPath)) | ||
| 226 | { | ||
| 227 | i = strFileName.size() - 1; | ||
| 228 | while (i > 0) | ||
| 229 | { | ||
| 230 | char ch = strFileName[i]; | ||
| 231 | if (ch == '?' || ch == '|') break; | ||
| 232 | else i--; | ||
| 233 | } | ||
| 234 | if (i > 0) | ||
| 235 | strFileName = strFileName.substr(0, i); | ||
| 236 | } | ||
| 237 | } | ||
| 238 | |||
| 239 | std::vector<std::string> URIUtils::SplitPath(const std::string& strPath) | ||
| 240 | { | ||
| 241 | CURL url(strPath); | ||
| 242 | |||
| 243 | // silly std::string can't take a char in the constructor | ||
| 244 | std::string sep(1, url.GetDirectorySeparator()); | ||
| 245 | |||
| 246 | // split the filename portion of the URL up into separate dirs | ||
| 247 | std::vector<std::string> dirs = StringUtils::Split(url.GetFileName(), sep); | ||
| 248 | |||
| 249 | // we start with the root path | ||
| 250 | std::string dir = url.GetWithoutFilename(); | ||
| 251 | |||
| 252 | if (!dir.empty()) | ||
| 253 | dirs.insert(dirs.begin(), dir); | ||
| 254 | |||
| 255 | // we don't need empty token on the end | ||
| 256 | if (dirs.size() > 1 && dirs.back().empty()) | ||
| 257 | dirs.erase(dirs.end() - 1); | ||
| 258 | |||
| 259 | return dirs; | ||
| 260 | } | ||
| 261 | |||
| 262 | void URIUtils::GetCommonPath(std::string& strParent, const std::string& strPath) | ||
| 263 | { | ||
| 264 | // find the common path of parent and path | ||
| 265 | unsigned int j = 1; | ||
| 266 | while (j <= std::min(strParent.size(), strPath.size()) && | ||
| 267 | StringUtils::CompareNoCase(strParent, strPath, j) == 0) | ||
| 268 | j++; | ||
| 269 | strParent.erase(j - 1); | ||
| 270 | // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be | ||
| 271 | if (!HasSlashAtEnd(strParent)) | ||
| 272 | { | ||
| 273 | strParent = GetDirectory(strParent); | ||
| 274 | AddSlashAtEnd(strParent); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | |||
| 278 | bool URIUtils::HasParentInHostname(const CURL& url) | ||
| 279 | { | ||
| 280 | return url.IsProtocol("zip") || url.IsProtocol("apk") || url.IsProtocol("bluray") || | ||
| 281 | url.IsProtocol("udf") || url.IsProtocol("iso9660") || url.IsProtocol("xbt") || | ||
| 282 | (CServiceBroker::IsBinaryAddonCacheUp() && | ||
| 283 | CServiceBroker::GetFileExtensionProvider().EncodedHostName(url.GetProtocol())); | ||
| 284 | } | ||
| 285 | |||
| 286 | bool URIUtils::HasEncodedHostname(const CURL& url) | ||
| 287 | { | ||
| 288 | return HasParentInHostname(url) | ||
| 289 | || url.IsProtocol("musicsearch") | ||
| 290 | || url.IsProtocol( "image"); | ||
| 291 | } | ||
| 292 | |||
| 293 | bool URIUtils::HasEncodedFilename(const CURL& url) | ||
| 294 | { | ||
| 295 | const std::string prot2 = url.GetTranslatedProtocol(); | ||
| 296 | |||
| 297 | // For now assume only (quasi) http internet streams use URL encoding | ||
| 298 | return CURL::IsProtocolEqual(prot2, "http") || | ||
| 299 | CURL::IsProtocolEqual(prot2, "https"); | ||
| 300 | } | ||
| 301 | |||
| 302 | std::string URIUtils::GetParentPath(const std::string& strPath) | ||
| 303 | { | ||
| 304 | std::string strReturn; | ||
| 305 | GetParentPath(strPath, strReturn); | ||
| 306 | return strReturn; | ||
| 307 | } | ||
| 308 | |||
| 309 | bool URIUtils::GetParentPath(const std::string& strPath, std::string& strParent) | ||
| 310 | { | ||
| 311 | strParent.clear(); | ||
| 312 | |||
| 313 | CURL url(strPath); | ||
| 314 | std::string strFile = url.GetFileName(); | ||
| 315 | if ( URIUtils::HasParentInHostname(url) && strFile.empty()) | ||
| 316 | { | ||
| 317 | strFile = url.GetHostName(); | ||
| 318 | return GetParentPath(strFile, strParent); | ||
| 319 | } | ||
| 320 | else if (url.IsProtocol("stack")) | ||
| 321 | { | ||
| 322 | CStackDirectory dir; | ||
| 323 | CFileItemList items; | ||
| 324 | if (!dir.GetDirectory(url, items)) | ||
| 325 | return false; | ||
| 326 | CURL url2(GetDirectory(items[0]->GetPath())); | ||
| 327 | if (HasParentInHostname(url2)) | ||
| 328 | GetParentPath(url2.Get(), strParent); | ||
| 329 | else | ||
| 330 | strParent = url2.Get(); | ||
| 331 | for( int i=1;i<items.Size();++i) | ||
| 332 | { | ||
| 333 | items[i]->m_strDVDLabel = GetDirectory(items[i]->GetPath()); | ||
| 334 | if (HasParentInHostname(url2)) | ||
| 335 | items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel)); | ||
| 336 | else | ||
| 337 | items[i]->SetPath(items[i]->m_strDVDLabel); | ||
| 338 | |||
| 339 | GetCommonPath(strParent,items[i]->GetPath()); | ||
| 340 | } | ||
| 341 | return true; | ||
| 342 | } | ||
| 343 | else if (url.IsProtocol("multipath")) | ||
| 344 | { | ||
| 345 | // get the parent path of the first item | ||
| 346 | return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent); | ||
| 347 | } | ||
| 348 | else if (url.IsProtocol("plugin")) | ||
| 349 | { | ||
| 350 | if (!url.GetOptions().empty()) | ||
| 351 | { | ||
| 352 | //! @todo Make a new python call to get the plugin content type and remove this temporary hack | ||
| 353 | // When a plugin provides multiple types, it has "plugin://addon.id/?content_type=xxx" root URL | ||
| 354 | if (url.GetFileName().empty() && url.HasOption("content_type") && url.GetOptions().find('&') == std::string::npos) | ||
| 355 | url.SetHostName(""); | ||
| 356 | // | ||
| 357 | url.SetOptions(""); | ||
| 358 | strParent = url.Get(); | ||
| 359 | return true; | ||
| 360 | } | ||
| 361 | if (!url.GetFileName().empty()) | ||
| 362 | { | ||
| 363 | url.SetFileName(""); | ||
| 364 | strParent = url.Get(); | ||
| 365 | return true; | ||
| 366 | } | ||
| 367 | if (!url.GetHostName().empty()) | ||
| 368 | { | ||
| 369 | url.SetHostName(""); | ||
| 370 | strParent = url.Get(); | ||
| 371 | return true; | ||
| 372 | } | ||
| 373 | return true; // already at root | ||
| 374 | } | ||
| 375 | else if (url.IsProtocol("special")) | ||
| 376 | { | ||
| 377 | if (HasSlashAtEnd(strFile)) | ||
| 378 | strFile.erase(strFile.size() - 1); | ||
| 379 | if(strFile.rfind('/') == std::string::npos) | ||
| 380 | return false; | ||
| 381 | } | ||
| 382 | else if (strFile.empty()) | ||
| 383 | { | ||
| 384 | if (!url.GetHostName().empty()) | ||
| 385 | { | ||
| 386 | // we have an share with only server or workgroup name | ||
| 387 | // set hostname to "" and return true to get back to root | ||
| 388 | url.SetHostName(""); | ||
| 389 | strParent = url.Get(); | ||
| 390 | return true; | ||
| 391 | } | ||
| 392 | return false; | ||
| 393 | } | ||
| 394 | |||
| 395 | if (HasSlashAtEnd(strFile) ) | ||
| 396 | { | ||
| 397 | strFile.erase(strFile.size() - 1); | ||
| 398 | } | ||
| 399 | |||
| 400 | size_t iPos = strFile.rfind('/'); | ||
| 401 | #ifndef TARGET_POSIX | ||
| 402 | if (iPos == std::string::npos) | ||
| 403 | { | ||
| 404 | iPos = strFile.rfind('\\'); | ||
| 405 | } | ||
| 406 | #endif | ||
| 407 | if (iPos == std::string::npos) | ||
| 408 | { | ||
| 409 | url.SetFileName(""); | ||
| 410 | strParent = url.Get(); | ||
| 411 | return true; | ||
| 412 | } | ||
| 413 | |||
| 414 | strFile.erase(iPos); | ||
| 415 | |||
| 416 | AddSlashAtEnd(strFile); | ||
| 417 | |||
| 418 | url.SetFileName(strFile); | ||
| 419 | strParent = url.Get(); | ||
| 420 | return true; | ||
| 421 | } | ||
| 422 | |||
| 423 | std::string URIUtils::GetBasePath(const std::string& strPath) | ||
| 424 | { | ||
| 425 | std::string strCheck(strPath); | ||
| 426 | if (IsStack(strPath)) | ||
| 427 | strCheck = CStackDirectory::GetFirstStackedFile(strPath); | ||
| 428 | |||
| 429 | std::string strDirectory = GetDirectory(strCheck); | ||
| 430 | if (IsInRAR(strCheck)) | ||
| 431 | { | ||
| 432 | std::string strPath=strDirectory; | ||
| 433 | GetParentPath(strPath, strDirectory); | ||
| 434 | } | ||
| 435 | if (IsStack(strPath)) | ||
| 436 | { | ||
| 437 | strCheck = strDirectory; | ||
| 438 | RemoveSlashAtEnd(strCheck); | ||
| 439 | if (GetFileName(strCheck).size() == 3 && StringUtils::StartsWithNoCase(GetFileName(strCheck), "cd")) | ||
| 440 | strDirectory = GetDirectory(strCheck); | ||
| 441 | } | ||
| 442 | return strDirectory; | ||
| 443 | } | ||
| 444 | |||
| 445 | std::string URLEncodePath(const std::string& strPath) | ||
| 446 | { | ||
| 447 | std::vector<std::string> segments = StringUtils::Split(strPath, "/"); | ||
| 448 | for (std::vector<std::string>::iterator i = segments.begin(); i != segments.end(); ++i) | ||
| 449 | *i = CURL::Encode(*i); | ||
| 450 | |||
| 451 | return StringUtils::Join(segments, "/"); | ||
| 452 | } | ||
| 453 | |||
| 454 | std::string URLDecodePath(const std::string& strPath) | ||
| 455 | { | ||
| 456 | std::vector<std::string> segments = StringUtils::Split(strPath, "/"); | ||
| 457 | for (std::vector<std::string>::iterator i = segments.begin(); i != segments.end(); ++i) | ||
| 458 | *i = CURL::Decode(*i); | ||
| 459 | |||
| 460 | return StringUtils::Join(segments, "/"); | ||
| 461 | } | ||
| 462 | |||
| 463 | std::string URIUtils::ChangeBasePath(const std::string &fromPath, const std::string &fromFile, const std::string &toPath, const bool &bAddPath /* = true */) | ||
| 464 | { | ||
| 465 | std::string toFile = fromFile; | ||
| 466 | |||
| 467 | // Convert back slashes to forward slashes, if required | ||
| 468 | if (IsDOSPath(fromPath) && !IsDOSPath(toPath)) | ||
| 469 | StringUtils::Replace(toFile, "\\", "/"); | ||
| 470 | |||
| 471 | // Handle difference in URL encoded vs. not encoded | ||
| 472 | if ( HasEncodedFilename(CURL(fromPath)) | ||
| 473 | && !HasEncodedFilename(CURL(toPath)) ) | ||
| 474 | { | ||
| 475 | toFile = URLDecodePath(toFile); // Decode path | ||
| 476 | } | ||
| 477 | else if (!HasEncodedFilename(CURL(fromPath)) | ||
| 478 | && HasEncodedFilename(CURL(toPath)) ) | ||
| 479 | { | ||
| 480 | toFile = URLEncodePath(toFile); // Encode path | ||
| 481 | } | ||
| 482 | |||
| 483 | // Convert forward slashes to back slashes, if required | ||
| 484 | if (!IsDOSPath(fromPath) && IsDOSPath(toPath)) | ||
| 485 | StringUtils::Replace(toFile, "/", "\\"); | ||
| 486 | |||
| 487 | if (bAddPath) | ||
| 488 | return AddFileToFolder(toPath, toFile); | ||
| 489 | |||
| 490 | return toFile; | ||
| 491 | } | ||
| 492 | |||
| 493 | CURL URIUtils::SubstitutePath(const CURL& url, bool reverse /* = false */) | ||
| 494 | { | ||
| 495 | const std::string pathToUrl = url.Get(); | ||
| 496 | return CURL(SubstitutePath(pathToUrl, reverse)); | ||
| 497 | } | ||
| 498 | |||
| 499 | std::string URIUtils::SubstitutePath(const std::string& strPath, bool reverse /* = false */) | ||
| 500 | { | ||
| 501 | if (!m_advancedSettings) | ||
| 502 | { | ||
| 503 | // path substitution not needed / not working during Kodi bootstrap. | ||
| 504 | return strPath; | ||
| 505 | } | ||
| 506 | |||
| 507 | for (const auto& pathPair : m_advancedSettings->m_pathSubstitutions) | ||
| 508 | { | ||
| 509 | const std::string fromPath = reverse ? pathPair.second : pathPair.first; | ||
| 510 | const std::string toPath = reverse ? pathPair.first : pathPair.second; | ||
| 511 | |||
| 512 | if (strncmp(strPath.c_str(), fromPath.c_str(), HasSlashAtEnd(fromPath) ? fromPath.size() - 1 : fromPath.size()) == 0) | ||
| 513 | { | ||
| 514 | if (strPath.size() > fromPath.size()) | ||
| 515 | { | ||
| 516 | std::string strSubPathAndFileName = strPath.substr(fromPath.size()); | ||
| 517 | return ChangeBasePath(fromPath, strSubPathAndFileName, toPath); // Fix encoding + slash direction | ||
| 518 | } | ||
| 519 | else | ||
| 520 | { | ||
| 521 | return toPath; | ||
| 522 | } | ||
| 523 | } | ||
| 524 | } | ||
| 525 | return strPath; | ||
| 526 | } | ||
| 527 | |||
| 528 | bool URIUtils::IsProtocol(const std::string& url, const std::string &type) | ||
| 529 | { | ||
| 530 | return StringUtils::StartsWithNoCase(url, type + "://"); | ||
| 531 | } | ||
| 532 | |||
| 533 | bool URIUtils::PathHasParent(std::string path, std::string parent, bool translate /* = false */) | ||
| 534 | { | ||
| 535 | if (translate) | ||
| 536 | { | ||
| 537 | path = CSpecialProtocol::TranslatePath(path); | ||
| 538 | parent = CSpecialProtocol::TranslatePath(parent); | ||
| 539 | } | ||
| 540 | |||
| 541 | if (parent.empty()) | ||
| 542 | return false; | ||
| 543 | |||
| 544 | if (path == parent) | ||
| 545 | return true; | ||
| 546 | |||
| 547 | // Make sure parent has a trailing slash | ||
| 548 | AddSlashAtEnd(parent); | ||
| 549 | |||
| 550 | return StringUtils::StartsWith(path, parent); | ||
| 551 | } | ||
| 552 | |||
| 553 | bool URIUtils::PathEquals(std::string path1, std::string path2, bool ignoreTrailingSlash /* = false */, bool ignoreURLOptions /* = false */) | ||
| 554 | { | ||
| 555 | if (ignoreURLOptions) | ||
| 556 | { | ||
| 557 | path1 = CURL(path1).GetWithoutOptions(); | ||
| 558 | path2 = CURL(path2).GetWithoutOptions(); | ||
| 559 | } | ||
| 560 | |||
| 561 | if (ignoreTrailingSlash) | ||
| 562 | { | ||
| 563 | RemoveSlashAtEnd(path1); | ||
| 564 | RemoveSlashAtEnd(path2); | ||
| 565 | } | ||
| 566 | |||
| 567 | return (path1 == path2); | ||
| 568 | } | ||
| 569 | |||
| 570 | bool URIUtils::IsRemote(const std::string& strFile) | ||
| 571 | { | ||
| 572 | if (IsCDDA(strFile) || IsISO9660(strFile)) | ||
| 573 | return false; | ||
| 574 | |||
| 575 | if (IsStack(strFile)) | ||
| 576 | return IsRemote(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 577 | |||
| 578 | if (IsSpecial(strFile)) | ||
| 579 | return IsRemote(CSpecialProtocol::TranslatePath(strFile)); | ||
| 580 | |||
| 581 | if(IsMultiPath(strFile)) | ||
| 582 | { // virtual paths need to be checked separately | ||
| 583 | std::vector<std::string> paths; | ||
| 584 | if (CMultiPathDirectory::GetPaths(strFile, paths)) | ||
| 585 | { | ||
| 586 | for (unsigned int i = 0; i < paths.size(); i++) | ||
| 587 | if (IsRemote(paths[i])) return true; | ||
| 588 | } | ||
| 589 | return false; | ||
| 590 | } | ||
| 591 | |||
| 592 | CURL url(strFile); | ||
| 593 | if(HasParentInHostname(url)) | ||
| 594 | return IsRemote(url.GetHostName()); | ||
| 595 | |||
| 596 | if (IsAddonsPath(strFile)) | ||
| 597 | return false; | ||
| 598 | |||
| 599 | if (IsSourcesPath(strFile)) | ||
| 600 | return false; | ||
| 601 | |||
| 602 | if (IsVideoDb(strFile) || IsMusicDb(strFile)) | ||
| 603 | return false; | ||
| 604 | |||
| 605 | if (IsLibraryFolder(strFile)) | ||
| 606 | return false; | ||
| 607 | |||
| 608 | if (IsPlugin(strFile)) | ||
| 609 | return false; | ||
| 610 | |||
| 611 | if (IsAndroidApp(strFile)) | ||
| 612 | return false; | ||
| 613 | |||
| 614 | if (!url.IsLocal()) | ||
| 615 | return true; | ||
| 616 | |||
| 617 | return false; | ||
| 618 | } | ||
| 619 | |||
| 620 | bool URIUtils::IsOnDVD(const std::string& strFile) | ||
| 621 | { | ||
| 622 | if (IsProtocol(strFile, "dvd")) | ||
| 623 | return true; | ||
| 624 | |||
| 625 | if (IsProtocol(strFile, "udf")) | ||
| 626 | return true; | ||
| 627 | |||
| 628 | if (IsProtocol(strFile, "iso9660")) | ||
| 629 | return true; | ||
| 630 | |||
| 631 | if (IsProtocol(strFile, "cdda")) | ||
| 632 | return true; | ||
| 633 | |||
| 634 | #if defined(TARGET_WINDOWS_STORE) | ||
| 635 | CLog::Log(LOGDEBUG, "%s is not implemented", __FUNCTION__); | ||
| 636 | #elif defined(TARGET_WINDOWS_DESKTOP) | ||
| 637 | using KODI::PLATFORM::WINDOWS::ToW; | ||
| 638 | if (strFile.size() >= 2 && strFile.substr(1, 1) == ":") | ||
| 639 | return (GetDriveType(ToW(strFile.substr(0, 3)).c_str()) == DRIVE_CDROM); | ||
| 640 | #endif | ||
| 641 | return false; | ||
| 642 | } | ||
| 643 | |||
| 644 | bool URIUtils::IsOnLAN(const std::string& strPath) | ||
| 645 | { | ||
| 646 | if(IsMultiPath(strPath)) | ||
| 647 | return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath)); | ||
| 648 | |||
| 649 | if(IsStack(strPath)) | ||
| 650 | return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath)); | ||
| 651 | |||
| 652 | if(IsSpecial(strPath)) | ||
| 653 | return IsOnLAN(CSpecialProtocol::TranslatePath(strPath)); | ||
| 654 | |||
| 655 | if(IsPlugin(strPath)) | ||
| 656 | return false; | ||
| 657 | |||
| 658 | if(IsUPnP(strPath)) | ||
| 659 | return true; | ||
| 660 | |||
| 661 | CURL url(strPath); | ||
| 662 | if (HasParentInHostname(url)) | ||
| 663 | return IsOnLAN(url.GetHostName()); | ||
| 664 | |||
| 665 | if(!IsRemote(strPath)) | ||
| 666 | return false; | ||
| 667 | |||
| 668 | std::string host = url.GetHostName(); | ||
| 669 | |||
| 670 | return IsHostOnLAN(host); | ||
| 671 | } | ||
| 672 | |||
| 673 | static bool addr_match(uint32_t addr, const char* target, const char* submask) | ||
| 674 | { | ||
| 675 | uint32_t addr2 = ntohl(inet_addr(target)); | ||
| 676 | uint32_t mask = ntohl(inet_addr(submask)); | ||
| 677 | return (addr & mask) == (addr2 & mask); | ||
| 678 | } | ||
| 679 | |||
| 680 | bool URIUtils::IsHostOnLAN(const std::string& host, bool offLineCheck) | ||
| 681 | { | ||
| 682 | if(host.length() == 0) | ||
| 683 | return false; | ||
| 684 | |||
| 685 | // assume a hostname without dot's | ||
| 686 | // is local (smb netbios hostnames) | ||
| 687 | if(host.find('.') == std::string::npos) | ||
| 688 | return true; | ||
| 689 | |||
| 690 | uint32_t address = ntohl(inet_addr(host.c_str())); | ||
| 691 | if(address == INADDR_NONE) | ||
| 692 | { | ||
| 693 | std::string ip; | ||
| 694 | if(CDNSNameCache::Lookup(host, ip)) | ||
| 695 | address = ntohl(inet_addr(ip.c_str())); | ||
| 696 | } | ||
| 697 | |||
| 698 | if(address != INADDR_NONE) | ||
| 699 | { | ||
| 700 | if (offLineCheck) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network | ||
| 701 | { | ||
| 702 | if ( | ||
| 703 | addr_match(address, "192.168.0.0", "255.255.0.0") || | ||
| 704 | addr_match(address, "10.0.0.0", "255.0.0.0") || | ||
| 705 | addr_match(address, "172.16.0.0", "255.240.0.0") | ||
| 706 | ) | ||
| 707 | return true; | ||
| 708 | } | ||
| 709 | // check if we are on the local subnet | ||
| 710 | if (!CServiceBroker::GetNetwork().GetFirstConnectedInterface()) | ||
| 711 | return false; | ||
| 712 | |||
| 713 | if (CServiceBroker::GetNetwork().HasInterfaceForIP(address)) | ||
| 714 | return true; | ||
| 715 | } | ||
| 716 | |||
| 717 | return false; | ||
| 718 | } | ||
| 719 | |||
| 720 | bool URIUtils::IsMultiPath(const std::string& strPath) | ||
| 721 | { | ||
| 722 | return IsProtocol(strPath, "multipath"); | ||
| 723 | } | ||
| 724 | |||
| 725 | bool URIUtils::IsHD(const std::string& strFileName) | ||
| 726 | { | ||
| 727 | CURL url(strFileName); | ||
| 728 | |||
| 729 | if (IsStack(strFileName)) | ||
| 730 | return IsHD(CStackDirectory::GetFirstStackedFile(strFileName)); | ||
| 731 | |||
| 732 | if (IsSpecial(strFileName)) | ||
| 733 | return IsHD(CSpecialProtocol::TranslatePath(strFileName)); | ||
| 734 | |||
| 735 | if (HasParentInHostname(url)) | ||
| 736 | return IsHD(url.GetHostName()); | ||
| 737 | |||
| 738 | return url.GetProtocol().empty() || url.IsProtocol("file") || url.IsProtocol("win-lib"); | ||
| 739 | } | ||
| 740 | |||
| 741 | bool URIUtils::IsDVD(const std::string& strFile) | ||
| 742 | { | ||
| 743 | std::string strFileLow = strFile; | ||
| 744 | StringUtils::ToLower(strFileLow); | ||
| 745 | if (strFileLow.find("video_ts.ifo") != std::string::npos && IsOnDVD(strFile)) | ||
| 746 | return true; | ||
| 747 | |||
| 748 | #if defined(TARGET_WINDOWS) | ||
| 749 | if (IsProtocol(strFile, "dvd")) | ||
| 750 | return true; | ||
| 751 | |||
| 752 | if(strFile.size() < 2 || (strFile.substr(1) != ":\\" && strFile.substr(1) != ":")) | ||
| 753 | return false; | ||
| 754 | |||
| 755 | #ifndef TARGET_WINDOWS_STORE | ||
| 756 | if(GetDriveType(KODI::PLATFORM::WINDOWS::ToW(strFile).c_str()) == DRIVE_CDROM) | ||
| 757 | return true; | ||
| 758 | #endif | ||
| 759 | #else | ||
| 760 | if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" ) | ||
| 761 | return true; | ||
| 762 | #endif | ||
| 763 | |||
| 764 | return false; | ||
| 765 | } | ||
| 766 | |||
| 767 | bool URIUtils::IsStack(const std::string& strFile) | ||
| 768 | { | ||
| 769 | return IsProtocol(strFile, "stack"); | ||
| 770 | } | ||
| 771 | |||
| 772 | bool URIUtils::IsRAR(const std::string& strFile) | ||
| 773 | { | ||
| 774 | std::string strExtension = GetExtension(strFile); | ||
| 775 | |||
| 776 | if (strExtension == ".001" && !StringUtils::EndsWithNoCase(strFile, ".ts.001")) | ||
| 777 | return true; | ||
| 778 | |||
| 779 | if (StringUtils::EqualsNoCase(strExtension, ".cbr")) | ||
| 780 | return true; | ||
| 781 | |||
| 782 | if (StringUtils::EqualsNoCase(strExtension, ".rar")) | ||
| 783 | return true; | ||
| 784 | |||
| 785 | return false; | ||
| 786 | } | ||
| 787 | |||
| 788 | bool URIUtils::IsInArchive(const std::string &strFile) | ||
| 789 | { | ||
| 790 | CURL url(strFile); | ||
| 791 | |||
| 792 | bool archiveProto = url.IsProtocol("archive") && !url.GetFileName().empty(); | ||
| 793 | return archiveProto || IsInZIP(strFile) || IsInRAR(strFile) || IsInAPK(strFile); | ||
| 794 | } | ||
| 795 | |||
| 796 | bool URIUtils::IsInAPK(const std::string& strFile) | ||
| 797 | { | ||
| 798 | CURL url(strFile); | ||
| 799 | |||
| 800 | return url.IsProtocol("apk") && !url.GetFileName().empty(); | ||
| 801 | } | ||
| 802 | |||
| 803 | bool URIUtils::IsInZIP(const std::string& strFile) | ||
| 804 | { | ||
| 805 | CURL url(strFile); | ||
| 806 | |||
| 807 | if (url.GetFileName().empty()) | ||
| 808 | return false; | ||
| 809 | |||
| 810 | if (url.IsProtocol("archive")) | ||
| 811 | return IsZIP(url.GetHostName()); | ||
| 812 | |||
| 813 | return url.IsProtocol("zip"); | ||
| 814 | } | ||
| 815 | |||
| 816 | bool URIUtils::IsInRAR(const std::string& strFile) | ||
| 817 | { | ||
| 818 | CURL url(strFile); | ||
| 819 | |||
| 820 | if (url.GetFileName().empty()) | ||
| 821 | return false; | ||
| 822 | |||
| 823 | if (url.IsProtocol("archive")) | ||
| 824 | return IsRAR(url.GetHostName()); | ||
| 825 | |||
| 826 | return url.IsProtocol("rar"); | ||
| 827 | } | ||
| 828 | |||
| 829 | bool URIUtils::IsAPK(const std::string& strFile) | ||
| 830 | { | ||
| 831 | return HasExtension(strFile, ".apk"); | ||
| 832 | } | ||
| 833 | |||
| 834 | bool URIUtils::IsZIP(const std::string& strFile) // also checks for comic books! | ||
| 835 | { | ||
| 836 | return HasExtension(strFile, ".zip|.cbz"); | ||
| 837 | } | ||
| 838 | |||
| 839 | bool URIUtils::IsArchive(const std::string& strFile) | ||
| 840 | { | ||
| 841 | return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr"); | ||
| 842 | } | ||
| 843 | |||
| 844 | bool URIUtils::IsSpecial(const std::string& strFile) | ||
| 845 | { | ||
| 846 | if (IsStack(strFile)) | ||
| 847 | return IsSpecial(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 848 | |||
| 849 | return IsProtocol(strFile, "special"); | ||
| 850 | } | ||
| 851 | |||
| 852 | bool URIUtils::IsPlugin(const std::string& strFile) | ||
| 853 | { | ||
| 854 | CURL url(strFile); | ||
| 855 | return url.IsProtocol("plugin"); | ||
| 856 | } | ||
| 857 | |||
| 858 | bool URIUtils::IsScript(const std::string& strFile) | ||
| 859 | { | ||
| 860 | CURL url(strFile); | ||
| 861 | return url.IsProtocol("script"); | ||
| 862 | } | ||
| 863 | |||
| 864 | bool URIUtils::IsAddonsPath(const std::string& strFile) | ||
| 865 | { | ||
| 866 | CURL url(strFile); | ||
| 867 | return url.IsProtocol("addons"); | ||
| 868 | } | ||
| 869 | |||
| 870 | bool URIUtils::IsSourcesPath(const std::string& strPath) | ||
| 871 | { | ||
| 872 | CURL url(strPath); | ||
| 873 | return url.IsProtocol("sources"); | ||
| 874 | } | ||
| 875 | |||
| 876 | bool URIUtils::IsCDDA(const std::string& strFile) | ||
| 877 | { | ||
| 878 | return IsProtocol(strFile, "cdda"); | ||
| 879 | } | ||
| 880 | |||
| 881 | bool URIUtils::IsISO9660(const std::string& strFile) | ||
| 882 | { | ||
| 883 | return IsProtocol(strFile, "iso9660"); | ||
| 884 | } | ||
| 885 | |||
| 886 | bool URIUtils::IsSmb(const std::string& strFile) | ||
| 887 | { | ||
| 888 | if (IsStack(strFile)) | ||
| 889 | return IsSmb(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 890 | |||
| 891 | if (IsSpecial(strFile)) | ||
| 892 | return IsSmb(CSpecialProtocol::TranslatePath(strFile)); | ||
| 893 | |||
| 894 | CURL url(strFile); | ||
| 895 | if (HasParentInHostname(url)) | ||
| 896 | return IsSmb(url.GetHostName()); | ||
| 897 | |||
| 898 | return IsProtocol(strFile, "smb"); | ||
| 899 | } | ||
| 900 | |||
| 901 | bool URIUtils::IsURL(const std::string& strFile) | ||
| 902 | { | ||
| 903 | return strFile.find("://") != std::string::npos; | ||
| 904 | } | ||
| 905 | |||
| 906 | bool URIUtils::IsFTP(const std::string& strFile) | ||
| 907 | { | ||
| 908 | if (IsStack(strFile)) | ||
| 909 | return IsFTP(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 910 | |||
| 911 | if (IsSpecial(strFile)) | ||
| 912 | return IsFTP(CSpecialProtocol::TranslatePath(strFile)); | ||
| 913 | |||
| 914 | CURL url(strFile); | ||
| 915 | if (HasParentInHostname(url)) | ||
| 916 | return IsFTP(url.GetHostName()); | ||
| 917 | |||
| 918 | return IsProtocol(strFile, "ftp") || | ||
| 919 | IsProtocol(strFile, "ftps"); | ||
| 920 | } | ||
| 921 | |||
| 922 | bool URIUtils::IsHTTP(const std::string& strFile) | ||
| 923 | { | ||
| 924 | if (IsStack(strFile)) | ||
| 925 | return IsHTTP(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 926 | |||
| 927 | if (IsSpecial(strFile)) | ||
| 928 | return IsHTTP(CSpecialProtocol::TranslatePath(strFile)); | ||
| 929 | |||
| 930 | CURL url(strFile); | ||
| 931 | if (HasParentInHostname(url)) | ||
| 932 | return IsHTTP(url.GetHostName()); | ||
| 933 | |||
| 934 | return IsProtocol(strFile, "http") || | ||
| 935 | IsProtocol(strFile, "https"); | ||
| 936 | } | ||
| 937 | |||
| 938 | bool URIUtils::IsUDP(const std::string& strFile) | ||
| 939 | { | ||
| 940 | if (IsStack(strFile)) | ||
| 941 | return IsUDP(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 942 | |||
| 943 | return IsProtocol(strFile, "udp"); | ||
| 944 | } | ||
| 945 | |||
| 946 | bool URIUtils::IsTCP(const std::string& strFile) | ||
| 947 | { | ||
| 948 | if (IsStack(strFile)) | ||
| 949 | return IsTCP(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 950 | |||
| 951 | return IsProtocol(strFile, "tcp"); | ||
| 952 | } | ||
| 953 | |||
| 954 | bool URIUtils::IsPVR(const std::string& strFile) | ||
| 955 | { | ||
| 956 | if (IsStack(strFile)) | ||
| 957 | return IsPVR(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 958 | |||
| 959 | return IsProtocol(strFile, "pvr"); | ||
| 960 | } | ||
| 961 | |||
| 962 | bool URIUtils::IsPVRChannel(const std::string& strFile) | ||
| 963 | { | ||
| 964 | if (IsStack(strFile)) | ||
| 965 | return IsPVRChannel(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 966 | |||
| 967 | return IsProtocol(strFile, "pvr") && CPVRChannelsPath(strFile).IsChannel(); | ||
| 968 | } | ||
| 969 | |||
| 970 | bool URIUtils::IsPVRChannelGroup(const std::string& strFile) | ||
| 971 | { | ||
| 972 | if (IsStack(strFile)) | ||
| 973 | return IsPVRChannelGroup(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 974 | |||
| 975 | return IsProtocol(strFile, "pvr") && CPVRChannelsPath(strFile).IsChannelGroup(); | ||
| 976 | } | ||
| 977 | |||
| 978 | bool URIUtils::IsPVRGuideItem(const std::string& strFile) | ||
| 979 | { | ||
| 980 | if (IsStack(strFile)) | ||
| 981 | return IsPVRGuideItem(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 982 | |||
| 983 | return StringUtils::StartsWithNoCase(strFile, "pvr://guide"); | ||
| 984 | } | ||
| 985 | |||
| 986 | bool URIUtils::IsDAV(const std::string& strFile) | ||
| 987 | { | ||
| 988 | if (IsStack(strFile)) | ||
| 989 | return IsDAV(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 990 | |||
| 991 | if (IsSpecial(strFile)) | ||
| 992 | return IsDAV(CSpecialProtocol::TranslatePath(strFile)); | ||
| 993 | |||
| 994 | CURL url(strFile); | ||
| 995 | if (HasParentInHostname(url)) | ||
| 996 | return IsDAV(url.GetHostName()); | ||
| 997 | |||
| 998 | return IsProtocol(strFile, "dav") || | ||
| 999 | IsProtocol(strFile, "davs"); | ||
| 1000 | } | ||
| 1001 | |||
| 1002 | bool URIUtils::IsInternetStream(const std::string &path, bool bStrictCheck /* = false */) | ||
| 1003 | { | ||
| 1004 | const CURL pathToUrl(path); | ||
| 1005 | return IsInternetStream(pathToUrl, bStrictCheck); | ||
| 1006 | } | ||
| 1007 | |||
| 1008 | bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */) | ||
| 1009 | { | ||
| 1010 | if (url.GetProtocol().empty()) | ||
| 1011 | return false; | ||
| 1012 | |||
| 1013 | // there's nothing to stop internet streams from being stacked | ||
| 1014 | if (url.IsProtocol("stack")) | ||
| 1015 | return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get())); | ||
| 1016 | |||
| 1017 | // Special case these | ||
| 1018 | //! @todo sftp special case has to be handled by vfs addon | ||
| 1019 | if (url.IsProtocol("ftp") || url.IsProtocol("ftps") || | ||
| 1020 | url.IsProtocol("dav") || url.IsProtocol("davs") || | ||
| 1021 | url.IsProtocol("sftp")) | ||
| 1022 | return bStrictCheck; | ||
| 1023 | |||
| 1024 | std::string protocol = url.GetTranslatedProtocol(); | ||
| 1025 | if (CURL::IsProtocolEqual(protocol, "http") || CURL::IsProtocolEqual(protocol, "https") || | ||
| 1026 | CURL::IsProtocolEqual(protocol, "tcp") || CURL::IsProtocolEqual(protocol, "udp") || | ||
| 1027 | CURL::IsProtocolEqual(protocol, "rtp") || CURL::IsProtocolEqual(protocol, "sdp") || | ||
| 1028 | CURL::IsProtocolEqual(protocol, "mms") || CURL::IsProtocolEqual(protocol, "mmst") || | ||
| 1029 | CURL::IsProtocolEqual(protocol, "mmsh") || CURL::IsProtocolEqual(protocol, "rtsp") || | ||
| 1030 | CURL::IsProtocolEqual(protocol, "rtmp") || CURL::IsProtocolEqual(protocol, "rtmpt") || | ||
| 1031 | CURL::IsProtocolEqual(protocol, "rtmpe") || CURL::IsProtocolEqual(protocol, "rtmpte") || | ||
| 1032 | CURL::IsProtocolEqual(protocol, "rtmps")) | ||
| 1033 | return true; | ||
| 1034 | |||
| 1035 | return false; | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | bool URIUtils::IsUPnP(const std::string& strFile) | ||
| 1039 | { | ||
| 1040 | return IsProtocol(strFile, "upnp"); | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | bool URIUtils::IsLiveTV(const std::string& strFile) | ||
| 1044 | { | ||
| 1045 | std::string strFileWithoutSlash(strFile); | ||
| 1046 | RemoveSlashAtEnd(strFileWithoutSlash); | ||
| 1047 | |||
| 1048 | if (StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && | ||
| 1049 | !StringUtils::StartsWith(strFileWithoutSlash, "pvr://recordings")) | ||
| 1050 | return true; | ||
| 1051 | |||
| 1052 | return false; | ||
| 1053 | } | ||
| 1054 | |||
| 1055 | bool URIUtils::IsPVRRecording(const std::string& strFile) | ||
| 1056 | { | ||
| 1057 | std::string strFileWithoutSlash(strFile); | ||
| 1058 | RemoveSlashAtEnd(strFileWithoutSlash); | ||
| 1059 | |||
| 1060 | return StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && | ||
| 1061 | StringUtils::StartsWith(strFile, "pvr://recordings"); | ||
| 1062 | } | ||
| 1063 | |||
| 1064 | bool URIUtils::IsPVRRecordingFileOrFolder(const std::string& strFile) | ||
| 1065 | { | ||
| 1066 | return StringUtils::StartsWith(strFile, "pvr://recordings"); | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | bool URIUtils::IsMusicDb(const std::string& strFile) | ||
| 1070 | { | ||
| 1071 | return IsProtocol(strFile, "musicdb"); | ||
| 1072 | } | ||
| 1073 | |||
| 1074 | bool URIUtils::IsNfs(const std::string& strFile) | ||
| 1075 | { | ||
| 1076 | if (IsStack(strFile)) | ||
| 1077 | return IsNfs(CStackDirectory::GetFirstStackedFile(strFile)); | ||
| 1078 | |||
| 1079 | if (IsSpecial(strFile)) | ||
| 1080 | return IsNfs(CSpecialProtocol::TranslatePath(strFile)); | ||
| 1081 | |||
| 1082 | CURL url(strFile); | ||
| 1083 | if (HasParentInHostname(url)) | ||
| 1084 | return IsNfs(url.GetHostName()); | ||
| 1085 | |||
| 1086 | return IsProtocol(strFile, "nfs"); | ||
| 1087 | } | ||
| 1088 | |||
| 1089 | bool URIUtils::IsVideoDb(const std::string& strFile) | ||
| 1090 | { | ||
| 1091 | return IsProtocol(strFile, "videodb"); | ||
| 1092 | } | ||
| 1093 | |||
| 1094 | bool URIUtils::IsBluray(const std::string& strFile) | ||
| 1095 | { | ||
| 1096 | return IsProtocol(strFile, "bluray"); | ||
| 1097 | } | ||
| 1098 | |||
| 1099 | bool URIUtils::IsAndroidApp(const std::string &path) | ||
| 1100 | { | ||
| 1101 | return IsProtocol(path, "androidapp"); | ||
| 1102 | } | ||
| 1103 | |||
| 1104 | bool URIUtils::IsLibraryFolder(const std::string& strFile) | ||
| 1105 | { | ||
| 1106 | CURL url(strFile); | ||
| 1107 | return url.IsProtocol("library"); | ||
| 1108 | } | ||
| 1109 | |||
| 1110 | bool URIUtils::IsLibraryContent(const std::string &strFile) | ||
| 1111 | { | ||
| 1112 | return (IsProtocol(strFile, "library") || | ||
| 1113 | IsProtocol(strFile, "videodb") || | ||
| 1114 | IsProtocol(strFile, "musicdb") || | ||
| 1115 | StringUtils::EndsWith(strFile, ".xsp")); | ||
| 1116 | } | ||
| 1117 | |||
| 1118 | bool URIUtils::IsDOSPath(const std::string &path) | ||
| 1119 | { | ||
| 1120 | if (path.size() > 1 && path[1] == ':' && isalpha(path[0])) | ||
| 1121 | return true; | ||
| 1122 | |||
| 1123 | // windows network drives | ||
| 1124 | if (path.size() > 1 && path[0] == '\\' && path[1] == '\\') | ||
| 1125 | return true; | ||
| 1126 | |||
| 1127 | return false; | ||
| 1128 | } | ||
| 1129 | |||
| 1130 | std::string URIUtils::AppendSlash(std::string strFolder) | ||
| 1131 | { | ||
| 1132 | AddSlashAtEnd(strFolder); | ||
| 1133 | return strFolder; | ||
| 1134 | } | ||
| 1135 | |||
| 1136 | void URIUtils::AddSlashAtEnd(std::string& strFolder) | ||
| 1137 | { | ||
| 1138 | if (IsURL(strFolder)) | ||
| 1139 | { | ||
| 1140 | CURL url(strFolder); | ||
| 1141 | std::string file = url.GetFileName(); | ||
| 1142 | if(!file.empty() && file != strFolder) | ||
| 1143 | { | ||
| 1144 | AddSlashAtEnd(file); | ||
| 1145 | url.SetFileName(file); | ||
| 1146 | strFolder = url.Get(); | ||
| 1147 | } | ||
| 1148 | return; | ||
| 1149 | } | ||
| 1150 | |||
| 1151 | if (!HasSlashAtEnd(strFolder)) | ||
| 1152 | { | ||
| 1153 | if (IsDOSPath(strFolder)) | ||
| 1154 | strFolder += '\\'; | ||
| 1155 | else | ||
| 1156 | strFolder += '/'; | ||
| 1157 | } | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */) | ||
| 1161 | { | ||
| 1162 | if (strFile.empty()) return false; | ||
| 1163 | if (checkURL && IsURL(strFile)) | ||
| 1164 | { | ||
| 1165 | CURL url(strFile); | ||
| 1166 | std::string file = url.GetFileName(); | ||
| 1167 | return file.empty() || HasSlashAtEnd(file, false); | ||
| 1168 | } | ||
| 1169 | char kar = strFile.c_str()[strFile.size() - 1]; | ||
| 1170 | |||
| 1171 | if (kar == '/' || kar == '\\') | ||
| 1172 | return true; | ||
| 1173 | |||
| 1174 | return false; | ||
| 1175 | } | ||
| 1176 | |||
| 1177 | void URIUtils::RemoveSlashAtEnd(std::string& strFolder) | ||
| 1178 | { | ||
| 1179 | // performance optimization. pvr guide items are mass objects, uri never has a slash at end, and this method is quite expensive... | ||
| 1180 | if (IsPVRGuideItem(strFolder)) | ||
| 1181 | return; | ||
| 1182 | |||
| 1183 | if (IsURL(strFolder)) | ||
| 1184 | { | ||
| 1185 | CURL url(strFolder); | ||
| 1186 | std::string file = url.GetFileName(); | ||
| 1187 | if (!file.empty() && file != strFolder) | ||
| 1188 | { | ||
| 1189 | RemoveSlashAtEnd(file); | ||
| 1190 | url.SetFileName(file); | ||
| 1191 | strFolder = url.Get(); | ||
| 1192 | return; | ||
| 1193 | } | ||
| 1194 | if(url.GetHostName().empty()) | ||
| 1195 | return; | ||
| 1196 | } | ||
| 1197 | |||
| 1198 | while (HasSlashAtEnd(strFolder)) | ||
| 1199 | strFolder.erase(strFolder.size()-1, 1); | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | bool URIUtils::CompareWithoutSlashAtEnd(const std::string& strPath1, const std::string& strPath2) | ||
| 1203 | { | ||
| 1204 | std::string strc1 = strPath1, strc2 = strPath2; | ||
| 1205 | RemoveSlashAtEnd(strc1); | ||
| 1206 | RemoveSlashAtEnd(strc2); | ||
| 1207 | return StringUtils::EqualsNoCase(strc1, strc2); | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | |||
| 1211 | std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/) | ||
| 1212 | { | ||
| 1213 | const size_t len = path.length(); | ||
| 1214 | if (startFrom >= len) | ||
| 1215 | return path; | ||
| 1216 | |||
| 1217 | std::string result(path, 0, startFrom); | ||
| 1218 | result.reserve(len); | ||
| 1219 | |||
| 1220 | const char* const str = path.c_str(); | ||
| 1221 | size_t pos = startFrom; | ||
| 1222 | do | ||
| 1223 | { | ||
| 1224 | if (str[pos] == '\\' || str[pos] == '/') | ||
| 1225 | { | ||
| 1226 | result.push_back(slashCharacter); // append one slash | ||
| 1227 | pos++; | ||
| 1228 | // skip any following slashes | ||
| 1229 | while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun | ||
| 1230 | pos++; | ||
| 1231 | } | ||
| 1232 | else | ||
| 1233 | result.push_back(str[pos++]); // append current char and advance pos to next char | ||
| 1234 | |||
| 1235 | } while (pos < len); | ||
| 1236 | |||
| 1237 | return result; | ||
| 1238 | } | ||
| 1239 | |||
| 1240 | |||
| 1241 | std::string URIUtils::CanonicalizePath(const std::string& path, const char slashCharacter /*= '\\'*/) | ||
| 1242 | { | ||
| 1243 | assert(slashCharacter == '\\' || slashCharacter == '/'); | ||
| 1244 | |||
| 1245 | if (path.empty()) | ||
| 1246 | return path; | ||
| 1247 | |||
| 1248 | const std::string slashStr(1, slashCharacter); | ||
| 1249 | std::vector<std::string> pathVec, resultVec; | ||
| 1250 | StringUtils::Tokenize(path, pathVec, slashStr); | ||
| 1251 | |||
| 1252 | for (std::vector<std::string>::const_iterator it = pathVec.begin(); it != pathVec.end(); ++it) | ||
| 1253 | { | ||
| 1254 | if (*it == ".") | ||
| 1255 | { /* skip - do nothing */ } | ||
| 1256 | else if (*it == ".." && !resultVec.empty() && resultVec.back() != "..") | ||
| 1257 | resultVec.pop_back(); | ||
| 1258 | else | ||
| 1259 | resultVec.push_back(*it); | ||
| 1260 | } | ||
| 1261 | |||
| 1262 | std::string result; | ||
| 1263 | if (path[0] == slashCharacter) | ||
| 1264 | result.push_back(slashCharacter); // add slash at the begin | ||
| 1265 | |||
| 1266 | result += StringUtils::Join(resultVec, slashStr); | ||
| 1267 | |||
| 1268 | if (path[path.length() - 1] == slashCharacter && !result.empty() && result[result.length() - 1] != slashCharacter) | ||
| 1269 | result.push_back(slashCharacter); // add slash at the end if result isn't empty and result isn't "/" | ||
| 1270 | |||
| 1271 | return result; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | std::string URIUtils::AddFileToFolder(const std::string& strFolder, | ||
| 1275 | const std::string& strFile) | ||
| 1276 | { | ||
| 1277 | if (IsURL(strFolder)) | ||
| 1278 | { | ||
| 1279 | CURL url(strFolder); | ||
| 1280 | if (url.GetFileName() != strFolder) | ||
| 1281 | { | ||
| 1282 | url.SetFileName(AddFileToFolder(url.GetFileName(), strFile)); | ||
| 1283 | return url.Get(); | ||
| 1284 | } | ||
| 1285 | } | ||
| 1286 | |||
| 1287 | std::string strResult = strFolder; | ||
| 1288 | if (!strResult.empty()) | ||
| 1289 | AddSlashAtEnd(strResult); | ||
| 1290 | |||
| 1291 | // Remove any slash at the start of the file | ||
| 1292 | if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\')) | ||
| 1293 | strResult += strFile.substr(1); | ||
| 1294 | else | ||
| 1295 | strResult += strFile; | ||
| 1296 | |||
| 1297 | // correct any slash directions | ||
| 1298 | if (!IsDOSPath(strFolder)) | ||
| 1299 | StringUtils::Replace(strResult, '\\', '/'); | ||
| 1300 | else | ||
| 1301 | StringUtils::Replace(strResult, '/', '\\'); | ||
| 1302 | |||
| 1303 | return strResult; | ||
| 1304 | } | ||
| 1305 | |||
| 1306 | std::string URIUtils::GetDirectory(const std::string &strFilePath) | ||
| 1307 | { | ||
| 1308 | // Will from a full filename return the directory the file resides in. | ||
| 1309 | // Keeps the final slash at end and possible |option=foo options. | ||
| 1310 | |||
| 1311 | size_t iPosSlash = strFilePath.find_last_of("/\\"); | ||
| 1312 | if (iPosSlash == std::string::npos) | ||
| 1313 | return ""; // No slash, so no path (ignore any options) | ||
| 1314 | |||
| 1315 | size_t iPosBar = strFilePath.rfind('|'); | ||
| 1316 | if (iPosBar == std::string::npos) | ||
| 1317 | return strFilePath.substr(0, iPosSlash + 1); // Only path | ||
| 1318 | |||
| 1319 | return strFilePath.substr(0, iPosSlash + 1) + strFilePath.substr(iPosBar); // Path + options | ||
| 1320 | } | ||
| 1321 | |||
| 1322 | CURL URIUtils::CreateArchivePath(const std::string& type, | ||
| 1323 | const CURL& archiveUrl, | ||
| 1324 | const std::string& pathInArchive, | ||
| 1325 | const std::string& password) | ||
| 1326 | { | ||
| 1327 | CURL url; | ||
| 1328 | url.SetProtocol(type); | ||
| 1329 | if (!password.empty()) | ||
| 1330 | url.SetUserName(password); | ||
| 1331 | url.SetHostName(archiveUrl.Get()); | ||
| 1332 | |||
| 1333 | /* NOTE: on posix systems, the replacement of \ with / is incorrect. | ||
| 1334 | Ideally this would not be done. We need to check that the ZipManager | ||
| 1335 | code (and elsewhere) doesn't pass in non-posix paths. | ||
| 1336 | */ | ||
| 1337 | std::string strBuffer(pathInArchive); | ||
| 1338 | StringUtils::Replace(strBuffer, '\\', '/'); | ||
| 1339 | StringUtils::TrimLeft(strBuffer, "/"); | ||
| 1340 | url.SetFileName(strBuffer); | ||
| 1341 | |||
| 1342 | return url; | ||
| 1343 | } | ||
| 1344 | |||
| 1345 | std::string URIUtils::GetRealPath(const std::string &path) | ||
| 1346 | { | ||
| 1347 | if (path.empty()) | ||
| 1348 | return path; | ||
| 1349 | |||
| 1350 | CURL url(path); | ||
| 1351 | url.SetHostName(GetRealPath(url.GetHostName())); | ||
| 1352 | url.SetFileName(resolvePath(url.GetFileName())); | ||
| 1353 | |||
| 1354 | return url.Get(); | ||
| 1355 | } | ||
| 1356 | |||
| 1357 | std::string URIUtils::resolvePath(const std::string &path) | ||
| 1358 | { | ||
| 1359 | if (path.empty()) | ||
| 1360 | return path; | ||
| 1361 | |||
| 1362 | size_t posSlash = path.find('/'); | ||
| 1363 | size_t posBackslash = path.find('\\'); | ||
| 1364 | std::string delim = posSlash < posBackslash ? "/" : "\\"; | ||
| 1365 | std::vector<std::string> parts = StringUtils::Split(path, delim); | ||
| 1366 | std::vector<std::string> realParts; | ||
| 1367 | |||
| 1368 | for (std::vector<std::string>::const_iterator part = parts.begin(); part != parts.end(); ++part) | ||
| 1369 | { | ||
| 1370 | if (part->empty() || part->compare(".") == 0) | ||
| 1371 | continue; | ||
| 1372 | |||
| 1373 | // go one level back up | ||
| 1374 | if (part->compare("..") == 0) | ||
| 1375 | { | ||
| 1376 | if (!realParts.empty()) | ||
| 1377 | realParts.pop_back(); | ||
| 1378 | continue; | ||
| 1379 | } | ||
| 1380 | |||
| 1381 | realParts.push_back(*part); | ||
| 1382 | } | ||
| 1383 | |||
| 1384 | std::string realPath; | ||
| 1385 | // re-add any / or \ at the beginning | ||
| 1386 | for (std::string::const_iterator itPath = path.begin(); itPath != path.end(); ++itPath) | ||
| 1387 | { | ||
| 1388 | if (*itPath != delim.at(0)) | ||
| 1389 | break; | ||
| 1390 | |||
| 1391 | realPath += delim; | ||
| 1392 | } | ||
| 1393 | // put together the path | ||
| 1394 | realPath += StringUtils::Join(realParts, delim); | ||
| 1395 | // re-add any / or \ at the end | ||
| 1396 | if (path.at(path.size() - 1) == delim.at(0) && | ||
| 1397 | realPath.size() > 0 && realPath.at(realPath.size() - 1) != delim.at(0)) | ||
| 1398 | realPath += delim; | ||
| 1399 | |||
| 1400 | return realPath; | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | bool URIUtils::UpdateUrlEncoding(std::string &strFilename) | ||
| 1404 | { | ||
| 1405 | if (strFilename.empty()) | ||
| 1406 | return false; | ||
| 1407 | |||
| 1408 | CURL url(strFilename); | ||
| 1409 | // if this is a stack:// URL we need to work with its filename | ||
| 1410 | if (URIUtils::IsStack(strFilename)) | ||
| 1411 | { | ||
| 1412 | std::vector<std::string> files; | ||
| 1413 | if (!CStackDirectory::GetPaths(strFilename, files)) | ||
| 1414 | return false; | ||
| 1415 | |||
| 1416 | for (std::vector<std::string>::iterator file = files.begin(); file != files.end(); ++file) | ||
| 1417 | UpdateUrlEncoding(*file); | ||
| 1418 | |||
| 1419 | std::string stackPath; | ||
| 1420 | if (!CStackDirectory::ConstructStackPath(files, stackPath)) | ||
| 1421 | return false; | ||
| 1422 | |||
| 1423 | url.Parse(stackPath); | ||
| 1424 | } | ||
| 1425 | // if the protocol has an encoded hostname we need to work with its hostname | ||
| 1426 | else if (URIUtils::HasEncodedHostname(url)) | ||
| 1427 | { | ||
| 1428 | std::string hostname = url.GetHostName(); | ||
| 1429 | UpdateUrlEncoding(hostname); | ||
| 1430 | url.SetHostName(hostname); | ||
| 1431 | } | ||
| 1432 | else | ||
| 1433 | return false; | ||
| 1434 | |||
| 1435 | std::string newFilename = url.Get(); | ||
| 1436 | if (newFilename == strFilename) | ||
| 1437 | return false; | ||
| 1438 | |||
| 1439 | strFilename = newFilename; | ||
| 1440 | return true; | ||
| 1441 | } | ||
