summaryrefslogtreecommitdiffstats
path: root/xbmc/utils/URIUtils.cpp
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2020-10-19 00:52:24 +0200
committermanuel <manuel@mausz.at>2020-10-19 00:52:24 +0200
commitbe933ef2241d79558f91796cc5b3a161f72ebf9c (patch)
treefe3ab2f130e20c99001f2d7a81d610c78c96a3f4 /xbmc/utils/URIUtils.cpp
parent5f8335c1e49ce108ef3481863833c98efa00411b (diff)
downloadkodi-pvr-build-be933ef2241d79558f91796cc5b3a161f72ebf9c.tar.gz
kodi-pvr-build-be933ef2241d79558f91796cc5b3a161f72ebf9c.tar.bz2
kodi-pvr-build-be933ef2241d79558f91796cc5b3a161f72ebf9c.zip
sync with upstream
Diffstat (limited to 'xbmc/utils/URIUtils.cpp')
-rw-r--r--xbmc/utils/URIUtils.cpp1441
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
33using namespace PVR;
34using namespace XFILE;
35
36const CAdvancedSettings* URIUtils::m_advancedSettings = nullptr;
37
38void URIUtils::RegisterAdvancedSettings(const CAdvancedSettings& advancedSettings)
39{
40 m_advancedSettings = &advancedSettings;
41}
42
43void URIUtils::UnregisterAdvancedSettings()
44{
45 m_advancedSettings = nullptr;
46}
47
48/* returns filename extension including period of filename */
49std::string URIUtils::GetExtension(const CURL& url)
50{
51 return URIUtils::GetExtension(url.GetFileName());
52}
53
54std::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
69bool 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
81bool URIUtils::HasExtension(const CURL& url, const std::string& strExtensions)
82{
83 return HasExtension(url.GetFileName(), strExtensions);
84}
85
86bool 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
119void 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
155std::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
180std::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*/
187std::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
200void 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
239std::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
262void 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
278bool 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
286bool URIUtils::HasEncodedHostname(const CURL& url)
287{
288 return HasParentInHostname(url)
289 || url.IsProtocol("musicsearch")
290 || url.IsProtocol( "image");
291}
292
293bool 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
302std::string URIUtils::GetParentPath(const std::string& strPath)
303{
304 std::string strReturn;
305 GetParentPath(strPath, strReturn);
306 return strReturn;
307}
308
309bool 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
423std::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
445std::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
454std::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
463std::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
493CURL URIUtils::SubstitutePath(const CURL& url, bool reverse /* = false */)
494{
495 const std::string pathToUrl = url.Get();
496 return CURL(SubstitutePath(pathToUrl, reverse));
497}
498
499std::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
528bool URIUtils::IsProtocol(const std::string& url, const std::string &type)
529{
530 return StringUtils::StartsWithNoCase(url, type + "://");
531}
532
533bool 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
553bool 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
570bool 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
620bool 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
644bool 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
673static 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
680bool 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
720bool URIUtils::IsMultiPath(const std::string& strPath)
721{
722 return IsProtocol(strPath, "multipath");
723}
724
725bool 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
741bool 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
767bool URIUtils::IsStack(const std::string& strFile)
768{
769 return IsProtocol(strFile, "stack");
770}
771
772bool 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
788bool 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
796bool URIUtils::IsInAPK(const std::string& strFile)
797{
798 CURL url(strFile);
799
800 return url.IsProtocol("apk") && !url.GetFileName().empty();
801}
802
803bool 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
816bool 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
829bool URIUtils::IsAPK(const std::string& strFile)
830{
831 return HasExtension(strFile, ".apk");
832}
833
834bool URIUtils::IsZIP(const std::string& strFile) // also checks for comic books!
835{
836 return HasExtension(strFile, ".zip|.cbz");
837}
838
839bool URIUtils::IsArchive(const std::string& strFile)
840{
841 return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr");
842}
843
844bool 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
852bool URIUtils::IsPlugin(const std::string& strFile)
853{
854 CURL url(strFile);
855 return url.IsProtocol("plugin");
856}
857
858bool URIUtils::IsScript(const std::string& strFile)
859{
860 CURL url(strFile);
861 return url.IsProtocol("script");
862}
863
864bool URIUtils::IsAddonsPath(const std::string& strFile)
865{
866 CURL url(strFile);
867 return url.IsProtocol("addons");
868}
869
870bool URIUtils::IsSourcesPath(const std::string& strPath)
871{
872 CURL url(strPath);
873 return url.IsProtocol("sources");
874}
875
876bool URIUtils::IsCDDA(const std::string& strFile)
877{
878 return IsProtocol(strFile, "cdda");
879}
880
881bool URIUtils::IsISO9660(const std::string& strFile)
882{
883 return IsProtocol(strFile, "iso9660");
884}
885
886bool 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
901bool URIUtils::IsURL(const std::string& strFile)
902{
903 return strFile.find("://") != std::string::npos;
904}
905
906bool 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
922bool 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
938bool 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
946bool 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
954bool 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
962bool 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
970bool 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
978bool 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
986bool 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
1002bool URIUtils::IsInternetStream(const std::string &path, bool bStrictCheck /* = false */)
1003{
1004 const CURL pathToUrl(path);
1005 return IsInternetStream(pathToUrl, bStrictCheck);
1006}
1007
1008bool 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
1038bool URIUtils::IsUPnP(const std::string& strFile)
1039{
1040 return IsProtocol(strFile, "upnp");
1041}
1042
1043bool 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
1055bool 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
1064bool URIUtils::IsPVRRecordingFileOrFolder(const std::string& strFile)
1065{
1066 return StringUtils::StartsWith(strFile, "pvr://recordings");
1067}
1068
1069bool URIUtils::IsMusicDb(const std::string& strFile)
1070{
1071 return IsProtocol(strFile, "musicdb");
1072}
1073
1074bool 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
1089bool URIUtils::IsVideoDb(const std::string& strFile)
1090{
1091 return IsProtocol(strFile, "videodb");
1092}
1093
1094bool URIUtils::IsBluray(const std::string& strFile)
1095{
1096 return IsProtocol(strFile, "bluray");
1097}
1098
1099bool URIUtils::IsAndroidApp(const std::string &path)
1100{
1101 return IsProtocol(path, "androidapp");
1102}
1103
1104bool URIUtils::IsLibraryFolder(const std::string& strFile)
1105{
1106 CURL url(strFile);
1107 return url.IsProtocol("library");
1108}
1109
1110bool 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
1118bool 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
1130std::string URIUtils::AppendSlash(std::string strFolder)
1131{
1132 AddSlashAtEnd(strFolder);
1133 return strFolder;
1134}
1135
1136void 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
1160bool 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
1177void 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
1202bool 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
1211std::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
1241std::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
1274std::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
1306std::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
1322CURL 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
1345std::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
1357std::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
1403bool 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}