2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
9 #include "network/Network.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"
19 #include "utils/FileExtensionProvider.h"
20 #include "ServiceBroker.h"
21 #include "StringUtils.h"
22 #include "utils/log.h"
24 #if defined(TARGET_WINDOWS)
25 #include "platform/win32/CharsetConverter.h"
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
34 using namespace XFILE
;
36 const CAdvancedSettings
* URIUtils::m_advancedSettings
= nullptr;
38 void URIUtils::RegisterAdvancedSettings(const CAdvancedSettings
& advancedSettings
)
40 m_advancedSettings
= &advancedSettings
;
43 void URIUtils::UnregisterAdvancedSettings()
45 m_advancedSettings
= nullptr;
48 /* returns filename extension including period of filename */
49 std::string
URIUtils::GetExtension(const CURL
& url
)
51 return URIUtils::GetExtension(url
.GetFileName());
54 std::string
URIUtils::GetExtension(const std::string
& strFileName
)
56 if (IsURL(strFileName
))
58 CURL
url(strFileName
);
59 return GetExtension(url
.GetFileName());
62 size_t period
= strFileName
.find_last_of("./\\");
63 if (period
== std::string::npos
|| strFileName
[period
] != '.')
66 return strFileName
.substr(period
);
69 bool URIUtils::HasPluginPath(const CFileItem
& item
)
71 return IsPlugin(item
.GetPath()) || IsPlugin(item
.GetDynPath());
74 bool URIUtils::HasExtension(const std::string
& strFileName
)
76 if (IsURL(strFileName
))
78 CURL
url(strFileName
);
79 return HasExtension(url
.GetFileName());
82 size_t iPeriod
= strFileName
.find_last_of("./\\");
83 return iPeriod
!= std::string::npos
&& strFileName
[iPeriod
] == '.';
86 bool URIUtils::HasExtension(const CURL
& url
, const std::string
& strExtensions
)
88 return HasExtension(url
.GetFileName(), strExtensions
);
91 bool URIUtils::HasExtension(const std::string
& strFileName
, const std::string
& strExtensions
)
93 if (IsURL(strFileName
))
95 const CURL
url(strFileName
);
96 return HasExtension(url
.GetFileName(), strExtensions
);
99 const size_t pos
= strFileName
.find_last_of("./\\");
100 if (pos
== std::string::npos
|| strFileName
[pos
] != '.')
103 const std::string extensionLower
= StringUtils::ToLower(strFileName
.substr(pos
));
105 const std::vector
<std::string
> extensionsLower
=
106 StringUtils::Split(StringUtils::ToLower(strExtensions
), '|');
108 for (const auto& ext
: extensionsLower
)
110 if (StringUtils::EndsWith(ext
, extensionLower
))
117 void URIUtils::RemoveExtension(std::string
& strFileName
)
119 if(IsURL(strFileName
))
121 CURL
url(strFileName
);
122 strFileName
= url
.GetFileName();
123 RemoveExtension(strFileName
);
124 url
.SetFileName(strFileName
);
125 strFileName
= url
.Get();
129 size_t period
= strFileName
.find_last_of("./\\");
130 if (period
!= std::string::npos
&& strFileName
[period
] == '.')
132 std::string strExtension
= strFileName
.substr(period
);
133 StringUtils::ToLower(strExtension
);
136 std::string strFileMask
;
137 strFileMask
= CServiceBroker::GetFileExtensionProvider().GetPictureExtensions();
138 strFileMask
+= "|" + CServiceBroker::GetFileExtensionProvider().GetMusicExtensions();
139 strFileMask
+= "|" + CServiceBroker::GetFileExtensionProvider().GetVideoExtensions();
140 strFileMask
+= "|" + CServiceBroker::GetFileExtensionProvider().GetSubtitleExtensions();
141 #if defined(TARGET_DARWIN)
142 strFileMask
+= "|.py|.xml|.milk|.xbt|.cdg|.app|.applescript|.workflow";
144 strFileMask
+= "|.py|.xml|.milk|.xbt|.cdg";
148 if (strFileMask
.find(strExtension
) != std::string::npos
)
149 strFileName
.erase(period
);
153 std::string
URIUtils::ReplaceExtension(const std::string
& strFile
,
154 const std::string
& strNewExtension
)
159 url
.SetFileName(ReplaceExtension(url
.GetFileName(), strNewExtension
));
163 std::string strChangedFile
;
164 std::string strExtension
= GetExtension(strFile
);
165 if ( strExtension
.size() )
167 strChangedFile
= strFile
.substr(0, strFile
.size() - strExtension
.size()) ;
168 strChangedFile
+= strNewExtension
;
172 strChangedFile
= strFile
;
173 strChangedFile
+= strNewExtension
;
175 return strChangedFile
;
178 std::string
URIUtils::GetFileName(const CURL
& url
)
180 return GetFileName(url
.GetFileName());
183 /* returns a filename given an url */
184 /* handles both / and \, and options in urls*/
185 std::string
URIUtils::GetFileName(const std::string
& strFileNameAndPath
)
187 if(IsURL(strFileNameAndPath
))
189 CURL
url(strFileNameAndPath
);
190 return GetFileName(url
.GetFileName());
193 /* find the last slash */
194 const size_t slash
= strFileNameAndPath
.find_last_of("/\\");
195 return strFileNameAndPath
.substr(slash
+1);
198 void URIUtils::Split(const std::string
& strFileNameAndPath
,
199 std::string
& strPath
, std::string
& strFileName
)
201 //Splits a full filename in path and file.
202 //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext
203 //Trailing slash will be preserved
206 int i
= strFileNameAndPath
.size() - 1;
209 char ch
= strFileNameAndPath
[i
];
210 // Only break on ':' if it's a drive separator for DOS (ie d:foo)
211 if (ch
== '/' || ch
== '\\' || (ch
== ':' && i
== 1)) break;
217 // take left including the directory separator
218 strPath
= strFileNameAndPath
.substr(0, i
+1);
219 // everything to the right of the directory separator
220 strFileName
= strFileNameAndPath
.substr(i
+1);
222 // if actual uri, ignore options
223 if (IsURL(strFileNameAndPath
))
225 i
= strFileName
.size() - 1;
228 char ch
= strFileName
[i
];
229 if (ch
== '?' || ch
== '|') break;
233 strFileName
= strFileName
.substr(0, i
);
237 std::vector
<std::string
> URIUtils::SplitPath(const std::string
& strPath
)
241 // silly std::string can't take a char in the constructor
242 std::string
sep(1, url
.GetDirectorySeparator());
244 // split the filename portion of the URL up into separate dirs
245 std::vector
<std::string
> dirs
= StringUtils::Split(url
.GetFileName(), sep
);
247 // we start with the root path
248 std::string dir
= url
.GetWithoutFilename();
251 dirs
.insert(dirs
.begin(), dir
);
253 // we don't need empty token on the end
254 if (dirs
.size() > 1 && dirs
.back().empty())
255 dirs
.erase(dirs
.end() - 1);
260 void URIUtils::GetCommonPath(std::string
& strParent
, const std::string
& strPath
)
262 // find the common path of parent and path
264 while (j
<= std::min(strParent
.size(), strPath
.size()) &&
265 StringUtils::CompareNoCase(strParent
, strPath
, j
) == 0)
267 strParent
.erase(j
- 1);
268 // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be
269 if (!HasSlashAtEnd(strParent
))
271 strParent
= GetDirectory(strParent
);
272 AddSlashAtEnd(strParent
);
276 bool URIUtils::HasParentInHostname(const CURL
& url
)
278 return url
.IsProtocol("zip") || url
.IsProtocol("apk") || url
.IsProtocol("bluray") ||
279 url
.IsProtocol("udf") || url
.IsProtocol("iso9660") || url
.IsProtocol("xbt") ||
280 (CServiceBroker::IsAddonInterfaceUp() &&
281 CServiceBroker::GetFileExtensionProvider().EncodedHostName(url
.GetProtocol()));
284 bool URIUtils::HasEncodedHostname(const CURL
& url
)
286 return HasParentInHostname(url
)
287 || url
.IsProtocol("musicsearch")
288 || url
.IsProtocol( "image");
291 bool URIUtils::HasEncodedFilename(const CURL
& url
)
293 const std::string prot2
= url
.GetTranslatedProtocol();
295 // For now assume only (quasi) http internet streams use URL encoding
296 return CURL::IsProtocolEqual(prot2
, "http") ||
297 CURL::IsProtocolEqual(prot2
, "https");
300 std::string
URIUtils::GetParentPath(const std::string
& strPath
)
302 std::string strReturn
;
303 GetParentPath(strPath
, strReturn
);
307 bool URIUtils::GetParentPath(const std::string
& strPath
, std::string
& strParent
)
312 std::string strFile
= url
.GetFileName();
313 if ( URIUtils::HasParentInHostname(url
) && strFile
.empty())
315 strFile
= url
.GetHostName();
316 return GetParentPath(strFile
, strParent
);
318 else if (url
.IsProtocol("stack"))
322 if (!dir
.GetDirectory(url
, items
))
324 CURL
url2(GetDirectory(items
[0]->GetPath()));
325 if (HasParentInHostname(url2
))
326 GetParentPath(url2
.Get(), strParent
);
328 strParent
= url2
.Get();
329 for( int i
=1;i
<items
.Size();++i
)
331 items
[i
]->m_strDVDLabel
= GetDirectory(items
[i
]->GetPath());
332 if (HasParentInHostname(url2
))
333 items
[i
]->SetPath(GetParentPath(items
[i
]->m_strDVDLabel
));
335 items
[i
]->SetPath(items
[i
]->m_strDVDLabel
);
337 GetCommonPath(strParent
,items
[i
]->GetPath());
341 else if (url
.IsProtocol("multipath"))
343 // get the parent path of the first item
344 return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath
), strParent
);
346 else if (url
.IsProtocol("plugin"))
348 if (!url
.GetOptions().empty())
350 //! @todo Make a new python call to get the plugin content type and remove this temporary hack
351 // When a plugin provides multiple types, it has "plugin://addon.id/?content_type=xxx" root URL
352 if (url
.GetFileName().empty() && url
.HasOption("content_type") && url
.GetOptions().find('&') == std::string::npos
)
356 strParent
= url
.Get();
359 if (!url
.GetFileName().empty())
362 strParent
= url
.Get();
365 if (!url
.GetHostName().empty())
368 strParent
= url
.Get();
371 return true; // already at root
373 else if (url
.IsProtocol("special"))
375 if (HasSlashAtEnd(strFile
))
376 strFile
.erase(strFile
.size() - 1);
377 if(strFile
.rfind('/') == std::string::npos
)
380 else if (strFile
.empty())
382 if (!url
.GetHostName().empty())
384 // we have an share with only server or workgroup name
385 // set hostname to "" and return true to get back to root
387 strParent
= url
.Get();
393 if (HasSlashAtEnd(strFile
) )
395 strFile
.erase(strFile
.size() - 1);
398 size_t iPos
= strFile
.rfind('/');
400 if (iPos
== std::string::npos
)
402 iPos
= strFile
.rfind('\\');
405 if (iPos
== std::string::npos
)
408 strParent
= url
.Get();
414 AddSlashAtEnd(strFile
);
416 url
.SetFileName(strFile
);
417 strParent
= url
.Get();
421 std::string
URIUtils::GetBasePath(const std::string
& strPath
)
423 std::string
strCheck(strPath
);
424 if (IsStack(strPath
))
425 strCheck
= CStackDirectory::GetFirstStackedFile(strPath
);
427 std::string strDirectory
= GetDirectory(strCheck
);
428 if (IsInRAR(strCheck
))
430 std::string strPath
=strDirectory
;
431 GetParentPath(strPath
, strDirectory
);
433 if (IsStack(strPath
))
435 strCheck
= strDirectory
;
436 RemoveSlashAtEnd(strCheck
);
437 if (GetFileName(strCheck
).size() == 3 && StringUtils::StartsWithNoCase(GetFileName(strCheck
), "cd"))
438 strDirectory
= GetDirectory(strCheck
);
443 std::string
URLEncodePath(const std::string
& strPath
)
445 std::vector
<std::string
> segments
= StringUtils::Split(strPath
, "/");
446 for (std::vector
<std::string
>::iterator i
= segments
.begin(); i
!= segments
.end(); ++i
)
447 *i
= CURL::Encode(*i
);
449 return StringUtils::Join(segments
, "/");
452 std::string
URLDecodePath(const std::string
& strPath
)
454 std::vector
<std::string
> segments
= StringUtils::Split(strPath
, "/");
455 for (std::vector
<std::string
>::iterator i
= segments
.begin(); i
!= segments
.end(); ++i
)
456 *i
= CURL::Decode(*i
);
458 return StringUtils::Join(segments
, "/");
461 std::string
URIUtils::ChangeBasePath(const std::string
&fromPath
, const std::string
&fromFile
, const std::string
&toPath
, const bool &bAddPath
/* = true */)
463 std::string toFile
= fromFile
;
465 // Convert back slashes to forward slashes, if required
466 if (IsDOSPath(fromPath
) && !IsDOSPath(toPath
))
467 StringUtils::Replace(toFile
, "\\", "/");
469 // Handle difference in URL encoded vs. not encoded
470 if ( HasEncodedFilename(CURL(fromPath
))
471 && !HasEncodedFilename(CURL(toPath
)) )
473 toFile
= URLDecodePath(toFile
); // Decode path
475 else if (!HasEncodedFilename(CURL(fromPath
))
476 && HasEncodedFilename(CURL(toPath
)) )
478 toFile
= URLEncodePath(toFile
); // Encode path
481 // Convert forward slashes to back slashes, if required
482 if (!IsDOSPath(fromPath
) && IsDOSPath(toPath
))
483 StringUtils::Replace(toFile
, "/", "\\");
486 return AddFileToFolder(toPath
, toFile
);
491 CURL
URIUtils::SubstitutePath(const CURL
& url
, bool reverse
/* = false */)
493 const std::string pathToUrl
= url
.Get();
494 return CURL(SubstitutePath(pathToUrl
, reverse
));
497 std::string
URIUtils::SubstitutePath(const std::string
& strPath
, bool reverse
/* = false */)
499 if (!m_advancedSettings
)
501 // path substitution not needed / not working during Kodi bootstrap.
505 for (const auto& pathPair
: m_advancedSettings
->m_pathSubstitutions
)
507 const std::string fromPath
= reverse
? pathPair
.second
: pathPair
.first
;
508 std::string toPath
= reverse
? pathPair
.first
: pathPair
.second
;
510 if (strncmp(strPath
.c_str(), fromPath
.c_str(), HasSlashAtEnd(fromPath
) ? fromPath
.size() - 1 : fromPath
.size()) == 0)
512 if (strPath
.size() > fromPath
.size())
514 std::string strSubPathAndFileName
= strPath
.substr(fromPath
.size());
515 return ChangeBasePath(fromPath
, strSubPathAndFileName
, toPath
); // Fix encoding + slash direction
526 bool URIUtils::IsProtocol(const std::string
& url
, const std::string
&type
)
528 return StringUtils::StartsWithNoCase(url
, type
+ "://");
531 bool URIUtils::PathHasParent(std::string path
, std::string parent
, bool translate
/* = false */)
535 path
= CSpecialProtocol::TranslatePath(path
);
536 parent
= CSpecialProtocol::TranslatePath(parent
);
545 // Make sure parent has a trailing slash
546 AddSlashAtEnd(parent
);
548 return StringUtils::StartsWith(path
, parent
);
551 bool URIUtils::PathEquals(std::string path1
, std::string path2
, bool ignoreTrailingSlash
/* = false */, bool ignoreURLOptions
/* = false */)
553 if (ignoreURLOptions
)
555 path1
= CURL(path1
).GetWithoutOptions();
556 path2
= CURL(path2
).GetWithoutOptions();
559 if (ignoreTrailingSlash
)
561 RemoveSlashAtEnd(path1
);
562 RemoveSlashAtEnd(path2
);
565 return (path1
== path2
);
568 bool URIUtils::IsRemote(const std::string
& strFile
)
570 if (IsCDDA(strFile
) || IsISO9660(strFile
))
573 if (IsStack(strFile
))
574 return IsRemote(CStackDirectory::GetFirstStackedFile(strFile
));
576 if (IsSpecial(strFile
))
577 return IsRemote(CSpecialProtocol::TranslatePath(strFile
));
579 if(IsMultiPath(strFile
))
580 { // virtual paths need to be checked separately
581 std::vector
<std::string
> paths
;
582 if (CMultiPathDirectory::GetPaths(strFile
, paths
))
584 for (unsigned int i
= 0; i
< paths
.size(); i
++)
585 if (IsRemote(paths
[i
])) return true;
591 if(HasParentInHostname(url
))
592 return IsRemote(url
.GetHostName());
594 if (IsAddonsPath(strFile
))
597 if (IsSourcesPath(strFile
))
600 if (IsVideoDb(strFile
) || IsMusicDb(strFile
))
603 if (IsLibraryFolder(strFile
))
606 if (IsPlugin(strFile
))
609 if (IsAndroidApp(strFile
))
618 bool URIUtils::IsOnDVD(const std::string
& strFile
)
620 if (IsProtocol(strFile
, "dvd"))
623 if (IsProtocol(strFile
, "udf"))
626 if (IsProtocol(strFile
, "iso9660"))
629 if (IsProtocol(strFile
, "cdda"))
632 #if defined(TARGET_WINDOWS_STORE)
633 CLog::Log(LOGDEBUG
, "{} is not implemented", __FUNCTION__
);
634 #elif defined(TARGET_WINDOWS_DESKTOP)
635 using KODI::PLATFORM::WINDOWS::ToW
;
636 if (strFile
.size() >= 2 && strFile
.substr(1, 1) == ":")
637 return (GetDriveType(ToW(strFile
.substr(0, 3)).c_str()) == DRIVE_CDROM
);
642 bool URIUtils::IsOnLAN(const std::string
& strPath
)
644 if(IsMultiPath(strPath
))
645 return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath
));
648 return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath
));
650 if(IsSpecial(strPath
))
651 return IsOnLAN(CSpecialProtocol::TranslatePath(strPath
));
653 if(IsPlugin(strPath
))
660 if (HasParentInHostname(url
))
661 return IsOnLAN(url
.GetHostName());
663 if(!IsRemote(strPath
))
666 const std::string
& host
= url
.GetHostName();
668 return IsHostOnLAN(host
);
671 static bool addr_match(uint32_t addr
, const char* target
, const char* submask
)
673 uint32_t addr2
= ntohl(inet_addr(target
));
674 uint32_t mask
= ntohl(inet_addr(submask
));
675 return (addr
& mask
) == (addr2
& mask
);
678 bool URIUtils::IsHostOnLAN(const std::string
& host
, bool offLineCheck
)
680 if(host
.length() == 0)
683 // assume a hostname without dot's
684 // is local (smb netbios hostnames)
685 if(host
.find('.') == std::string::npos
)
688 uint32_t address
= ntohl(inet_addr(host
.c_str()));
689 if(address
== INADDR_NONE
)
692 if(CDNSNameCache::Lookup(host
, ip
))
693 address
= ntohl(inet_addr(ip
.c_str()));
696 if(address
!= INADDR_NONE
)
698 if (offLineCheck
) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network
701 addr_match(address
, "192.168.0.0", "255.255.0.0") ||
702 addr_match(address
, "10.0.0.0", "255.0.0.0") ||
703 addr_match(address
, "172.16.0.0", "255.240.0.0")
707 // check if we are on the local subnet
708 if (!CServiceBroker::GetNetwork().GetFirstConnectedInterface())
711 if (CServiceBroker::GetNetwork().HasInterfaceForIP(address
))
718 bool URIUtils::IsMultiPath(const std::string
& strPath
)
720 return IsProtocol(strPath
, "multipath");
723 bool URIUtils::IsHD(const std::string
& strFileName
)
725 CURL
url(strFileName
);
727 if (IsStack(strFileName
))
728 return IsHD(CStackDirectory::GetFirstStackedFile(strFileName
));
730 if (IsSpecial(strFileName
))
731 return IsHD(CSpecialProtocol::TranslatePath(strFileName
));
733 if (HasParentInHostname(url
))
734 return IsHD(url
.GetHostName());
736 return url
.GetProtocol().empty() || url
.IsProtocol("file") || url
.IsProtocol("win-lib");
739 bool URIUtils::IsDVD(const std::string
& strFile
)
741 std::string strFileLow
= strFile
;
742 StringUtils::ToLower(strFileLow
);
743 if (strFileLow
.find("video_ts.ifo") != std::string::npos
&& IsOnDVD(strFile
))
746 #if defined(TARGET_WINDOWS)
747 if (IsProtocol(strFile
, "dvd"))
750 if(strFile
.size() < 2 || (strFile
.substr(1) != ":\\" && strFile
.substr(1) != ":"))
753 #ifndef TARGET_WINDOWS_STORE
754 if(GetDriveType(KODI::PLATFORM::WINDOWS::ToW(strFile
).c_str()) == DRIVE_CDROM
)
758 if (strFileLow
== "iso9660://" || strFileLow
== "udf://" || strFileLow
== "dvd://1" )
765 bool URIUtils::IsStack(const std::string
& strFile
)
767 return IsProtocol(strFile
, "stack");
770 bool URIUtils::IsFavourite(const std::string
& strFile
)
772 return IsProtocol(strFile
, "favourites");
775 bool URIUtils::IsRAR(const std::string
& strFile
)
777 std::string strExtension
= GetExtension(strFile
);
779 if (strExtension
== ".001" && !StringUtils::EndsWithNoCase(strFile
, ".ts.001"))
782 if (StringUtils::EqualsNoCase(strExtension
, ".cbr"))
785 if (StringUtils::EqualsNoCase(strExtension
, ".rar"))
791 bool URIUtils::IsInArchive(const std::string
&strFile
)
795 bool archiveProto
= url
.IsProtocol("archive") && !url
.GetFileName().empty();
796 return archiveProto
|| IsInZIP(strFile
) || IsInRAR(strFile
) || IsInAPK(strFile
);
799 bool URIUtils::IsInAPK(const std::string
& strFile
)
803 return url
.IsProtocol("apk") && !url
.GetFileName().empty();
806 bool URIUtils::IsInZIP(const std::string
& strFile
)
810 if (url
.GetFileName().empty())
813 if (url
.IsProtocol("archive"))
814 return IsZIP(url
.GetHostName());
816 return url
.IsProtocol("zip");
819 bool URIUtils::IsInRAR(const std::string
& strFile
)
823 if (url
.GetFileName().empty())
826 if (url
.IsProtocol("archive"))
827 return IsRAR(url
.GetHostName());
829 return url
.IsProtocol("rar");
832 bool URIUtils::IsAPK(const std::string
& strFile
)
834 return HasExtension(strFile
, ".apk");
837 bool URIUtils::IsZIP(const std::string
& strFile
) // also checks for comic books!
839 return HasExtension(strFile
, ".zip|.cbz");
842 bool URIUtils::IsArchive(const std::string
& strFile
)
844 return HasExtension(strFile
, ".zip|.rar|.apk|.cbz|.cbr");
847 bool URIUtils::IsSpecial(const std::string
& strFile
)
849 if (IsStack(strFile
))
850 return IsSpecial(CStackDirectory::GetFirstStackedFile(strFile
));
852 return IsProtocol(strFile
, "special");
855 bool URIUtils::IsPlugin(const std::string
& strFile
)
858 return url
.IsProtocol("plugin");
861 bool URIUtils::IsScript(const std::string
& strFile
)
864 return url
.IsProtocol("script");
867 bool URIUtils::IsAddonsPath(const std::string
& strFile
)
870 return url
.IsProtocol("addons");
873 bool URIUtils::IsSourcesPath(const std::string
& strPath
)
876 return url
.IsProtocol("sources");
879 bool URIUtils::IsCDDA(const std::string
& strFile
)
881 return IsProtocol(strFile
, "cdda");
884 bool URIUtils::IsISO9660(const std::string
& strFile
)
886 return IsProtocol(strFile
, "iso9660");
889 bool URIUtils::IsSmb(const std::string
& strFile
)
891 if (IsStack(strFile
))
892 return IsSmb(CStackDirectory::GetFirstStackedFile(strFile
));
894 if (IsSpecial(strFile
))
895 return IsSmb(CSpecialProtocol::TranslatePath(strFile
));
898 if (HasParentInHostname(url
))
899 return IsSmb(url
.GetHostName());
901 return IsProtocol(strFile
, "smb");
904 bool URIUtils::IsURL(const std::string
& strFile
)
906 return strFile
.find("://") != std::string::npos
;
909 bool URIUtils::IsFTP(const std::string
& strFile
)
911 if (IsStack(strFile
))
912 return IsFTP(CStackDirectory::GetFirstStackedFile(strFile
));
914 if (IsSpecial(strFile
))
915 return IsFTP(CSpecialProtocol::TranslatePath(strFile
));
918 if (HasParentInHostname(url
))
919 return IsFTP(url
.GetHostName());
921 return IsProtocol(strFile
, "ftp") ||
922 IsProtocol(strFile
, "ftps");
925 bool URIUtils::IsHTTP(const std::string
& strFile
, bool bTranslate
/* = false */)
927 if (IsStack(strFile
))
928 return IsHTTP(CStackDirectory::GetFirstStackedFile(strFile
));
930 if (IsSpecial(strFile
))
931 return IsHTTP(CSpecialProtocol::TranslatePath(strFile
));
934 if (HasParentInHostname(url
))
935 return IsHTTP(url
.GetHostName());
937 const std::string strProtocol
= (bTranslate
? url
.GetTranslatedProtocol() : url
.GetProtocol());
939 return (strProtocol
== "http" || strProtocol
== "https");
942 bool URIUtils::IsUDP(const std::string
& strFile
)
944 if (IsStack(strFile
))
945 return IsUDP(CStackDirectory::GetFirstStackedFile(strFile
));
947 return IsProtocol(strFile
, "udp");
950 bool URIUtils::IsTCP(const std::string
& strFile
)
952 if (IsStack(strFile
))
953 return IsTCP(CStackDirectory::GetFirstStackedFile(strFile
));
955 return IsProtocol(strFile
, "tcp");
958 bool URIUtils::IsPVR(const std::string
& strFile
)
960 if (IsStack(strFile
))
961 return IsPVR(CStackDirectory::GetFirstStackedFile(strFile
));
963 return IsProtocol(strFile
, "pvr");
966 bool URIUtils::IsPVRChannel(const std::string
& strFile
)
968 if (IsStack(strFile
))
969 return IsPVRChannel(CStackDirectory::GetFirstStackedFile(strFile
));
971 return IsProtocol(strFile
, "pvr") && CPVRChannelsPath(strFile
).IsChannel();
974 bool URIUtils::IsPVRChannelGroup(const std::string
& strFile
)
976 if (IsStack(strFile
))
977 return IsPVRChannelGroup(CStackDirectory::GetFirstStackedFile(strFile
));
979 return IsProtocol(strFile
, "pvr") && CPVRChannelsPath(strFile
).IsChannelGroup();
982 bool URIUtils::IsPVRGuideItem(const std::string
& strFile
)
984 if (IsStack(strFile
))
985 return IsPVRGuideItem(CStackDirectory::GetFirstStackedFile(strFile
));
987 return StringUtils::StartsWithNoCase(strFile
, "pvr://guide");
990 bool URIUtils::IsDAV(const std::string
& strFile
)
992 if (IsStack(strFile
))
993 return IsDAV(CStackDirectory::GetFirstStackedFile(strFile
));
995 if (IsSpecial(strFile
))
996 return IsDAV(CSpecialProtocol::TranslatePath(strFile
));
999 if (HasParentInHostname(url
))
1000 return IsDAV(url
.GetHostName());
1002 return IsProtocol(strFile
, "dav") ||
1003 IsProtocol(strFile
, "davs");
1006 bool URIUtils::IsInternetStream(const std::string
&path
, bool bStrictCheck
/* = false */)
1008 const CURL
pathToUrl(path
);
1009 return IsInternetStream(pathToUrl
, bStrictCheck
);
1012 bool URIUtils::IsInternetStream(const CURL
& url
, bool bStrictCheck
/* = false */)
1014 if (url
.GetProtocol().empty())
1017 // there's nothing to stop internet streams from being stacked
1018 if (url
.IsProtocol("stack"))
1019 return IsInternetStream(CStackDirectory::GetFirstStackedFile(url
.Get()), bStrictCheck
);
1021 // Only consider "streamed" filesystems internet streams when being strict
1022 if (bStrictCheck
&& IsStreamedFilesystem(url
.Get()))
1025 // Check for true internetstreams
1026 const std::string
& protocol
= url
.GetProtocol();
1027 if (CURL::IsProtocolEqual(protocol
, "http") || CURL::IsProtocolEqual(protocol
, "https") ||
1028 CURL::IsProtocolEqual(protocol
, "tcp") || CURL::IsProtocolEqual(protocol
, "udp") ||
1029 CURL::IsProtocolEqual(protocol
, "rtp") || CURL::IsProtocolEqual(protocol
, "sdp") ||
1030 CURL::IsProtocolEqual(protocol
, "mms") || CURL::IsProtocolEqual(protocol
, "mmst") ||
1031 CURL::IsProtocolEqual(protocol
, "mmsh") || CURL::IsProtocolEqual(protocol
, "rtsp") ||
1032 CURL::IsProtocolEqual(protocol
, "rtmp") || CURL::IsProtocolEqual(protocol
, "rtmpt") ||
1033 CURL::IsProtocolEqual(protocol
, "rtmpe") || CURL::IsProtocolEqual(protocol
, "rtmpte") ||
1034 CURL::IsProtocolEqual(protocol
, "rtmps") || CURL::IsProtocolEqual(protocol
, "shout") ||
1035 CURL::IsProtocolEqual(protocol
, "rss") || CURL::IsProtocolEqual(protocol
, "rsss"))
1041 bool URIUtils::IsStreamedFilesystem(const std::string
& strPath
)
1045 if (url
.GetProtocol().empty())
1048 if (url
.IsProtocol("stack"))
1049 return IsStreamedFilesystem(CStackDirectory::GetFirstStackedFile(strPath
));
1051 if (IsUPnP(strPath
) || IsFTP(strPath
) || IsHTTP(strPath
, true))
1054 //! @todo sftp/ssh special case has to be handled by vfs addon
1055 if (url
.IsProtocol("sftp") || url
.IsProtocol("ssh"))
1061 bool URIUtils::IsNetworkFilesystem(const std::string
& strPath
)
1065 if (url
.GetProtocol().empty())
1068 if (url
.IsProtocol("stack"))
1069 return IsNetworkFilesystem(CStackDirectory::GetFirstStackedFile(strPath
));
1071 if (IsStreamedFilesystem(strPath
))
1074 if (IsSmb(strPath
) || IsNfs(strPath
))
1080 bool URIUtils::IsUPnP(const std::string
& strFile
)
1082 return IsProtocol(strFile
, "upnp");
1085 bool URIUtils::IsLiveTV(const std::string
& strFile
)
1087 std::string
strFileWithoutSlash(strFile
);
1088 RemoveSlashAtEnd(strFileWithoutSlash
);
1090 if (StringUtils::EndsWithNoCase(strFileWithoutSlash
, ".pvr") &&
1091 !StringUtils::StartsWith(strFileWithoutSlash
, "pvr://recordings"))
1097 bool URIUtils::IsPVRRecording(const std::string
& strFile
)
1099 std::string
strFileWithoutSlash(strFile
);
1100 RemoveSlashAtEnd(strFileWithoutSlash
);
1102 return StringUtils::EndsWithNoCase(strFileWithoutSlash
, ".pvr") &&
1103 StringUtils::StartsWith(strFile
, "pvr://recordings");
1106 bool URIUtils::IsPVRRecordingFileOrFolder(const std::string
& strFile
)
1108 return StringUtils::StartsWith(strFile
, "pvr://recordings");
1111 bool URIUtils::IsPVRTVRecordingFileOrFolder(const std::string
& strFile
)
1113 return StringUtils::StartsWith(strFile
, "pvr://recordings/tv");
1116 bool URIUtils::IsPVRRadioRecordingFileOrFolder(const std::string
& strFile
)
1118 return StringUtils::StartsWith(strFile
, "pvr://recordings/radio");
1121 bool URIUtils::IsMusicDb(const std::string
& strFile
)
1123 return IsProtocol(strFile
, "musicdb");
1126 bool URIUtils::IsNfs(const std::string
& strFile
)
1128 if (IsStack(strFile
))
1129 return IsNfs(CStackDirectory::GetFirstStackedFile(strFile
));
1131 if (IsSpecial(strFile
))
1132 return IsNfs(CSpecialProtocol::TranslatePath(strFile
));
1135 if (HasParentInHostname(url
))
1136 return IsNfs(url
.GetHostName());
1138 return IsProtocol(strFile
, "nfs");
1141 bool URIUtils::IsVideoDb(const std::string
& strFile
)
1143 return IsProtocol(strFile
, "videodb");
1146 bool URIUtils::IsBluray(const std::string
& strFile
)
1148 return IsProtocol(strFile
, "bluray");
1151 bool URIUtils::IsAndroidApp(const std::string
&path
)
1153 return IsProtocol(path
, "androidapp");
1156 bool URIUtils::IsLibraryFolder(const std::string
& strFile
)
1159 return url
.IsProtocol("library");
1162 bool URIUtils::IsLibraryContent(const std::string
&strFile
)
1164 return (IsProtocol(strFile
, "library") ||
1165 IsProtocol(strFile
, "videodb") ||
1166 IsProtocol(strFile
, "musicdb") ||
1167 StringUtils::EndsWith(strFile
, ".xsp"));
1170 bool URIUtils::IsDOSPath(const std::string
&path
)
1172 if (path
.size() > 1 && path
[1] == ':' && isalpha(path
[0]))
1175 // windows network drives
1176 if (path
.size() > 1 && path
[0] == '\\' && path
[1] == '\\')
1182 std::string
URIUtils::AppendSlash(std::string strFolder
)
1184 AddSlashAtEnd(strFolder
);
1188 void URIUtils::AddSlashAtEnd(std::string
& strFolder
)
1190 if (IsURL(strFolder
))
1192 CURL
url(strFolder
);
1193 std::string file
= url
.GetFileName();
1194 if(!file
.empty() && file
!= strFolder
)
1196 AddSlashAtEnd(file
);
1197 url
.SetFileName(file
);
1198 strFolder
= url
.Get();
1203 if (!HasSlashAtEnd(strFolder
))
1205 if (IsDOSPath(strFolder
))
1212 bool URIUtils::HasSlashAtEnd(const std::string
& strFile
, bool checkURL
/* = false */)
1214 if (strFile
.empty()) return false;
1215 if (checkURL
&& IsURL(strFile
))
1218 const std::string
& file
= url
.GetFileName();
1219 return file
.empty() || HasSlashAtEnd(file
, false);
1221 char kar
= strFile
.c_str()[strFile
.size() - 1];
1223 if (kar
== '/' || kar
== '\\')
1229 void URIUtils::RemoveSlashAtEnd(std::string
& strFolder
)
1231 // performance optimization. pvr guide items are mass objects, uri never has a slash at end, and this method is quite expensive...
1232 if (IsPVRGuideItem(strFolder
))
1235 if (IsURL(strFolder
))
1237 CURL
url(strFolder
);
1238 std::string file
= url
.GetFileName();
1239 if (!file
.empty() && file
!= strFolder
)
1241 RemoveSlashAtEnd(file
);
1242 url
.SetFileName(file
);
1243 strFolder
= url
.Get();
1246 if(url
.GetHostName().empty())
1250 while (HasSlashAtEnd(strFolder
))
1251 strFolder
.erase(strFolder
.size()-1, 1);
1254 bool URIUtils::CompareWithoutSlashAtEnd(const std::string
& strPath1
, const std::string
& strPath2
)
1256 std::string strc1
= strPath1
, strc2
= strPath2
;
1257 RemoveSlashAtEnd(strc1
);
1258 RemoveSlashAtEnd(strc2
);
1259 return StringUtils::EqualsNoCase(strc1
, strc2
);
1263 std::string
URIUtils::FixSlashesAndDups(const std::string
& path
, const char slashCharacter
/* = '/' */, const size_t startFrom
/*= 0*/)
1265 const size_t len
= path
.length();
1266 if (startFrom
>= len
)
1269 std::string
result(path
, 0, startFrom
);
1270 result
.reserve(len
);
1272 const char* const str
= path
.c_str();
1273 size_t pos
= startFrom
;
1276 if (str
[pos
] == '\\' || str
[pos
] == '/')
1278 result
.push_back(slashCharacter
); // append one slash
1280 // skip any following slashes
1281 while (str
[pos
] == '\\' || str
[pos
] == '/') // str is null-terminated, no need to check for buffer overrun
1285 result
.push_back(str
[pos
++]); // append current char and advance pos to next char
1287 } while (pos
< len
);
1293 std::string
URIUtils::CanonicalizePath(const std::string
& path
, const char slashCharacter
/*= '\\'*/)
1295 assert(slashCharacter
== '\\' || slashCharacter
== '/');
1300 const std::string
slashStr(1, slashCharacter
);
1301 std::vector
<std::string
> pathVec
, resultVec
;
1302 StringUtils::Tokenize(path
, pathVec
, slashStr
);
1304 for (std::vector
<std::string
>::const_iterator it
= pathVec
.begin(); it
!= pathVec
.end(); ++it
)
1307 { /* skip - do nothing */ }
1308 else if (*it
== ".." && !resultVec
.empty() && resultVec
.back() != "..")
1309 resultVec
.pop_back();
1311 resultVec
.push_back(*it
);
1315 if (path
[0] == slashCharacter
)
1316 result
.push_back(slashCharacter
); // add slash at the begin
1318 result
+= StringUtils::Join(resultVec
, slashStr
);
1320 if (path
[path
.length() - 1] == slashCharacter
&& !result
.empty() && result
[result
.length() - 1] != slashCharacter
)
1321 result
.push_back(slashCharacter
); // add slash at the end if result isn't empty and result isn't "/"
1326 std::string
URIUtils::AddFileToFolder(const std::string
& strFolder
,
1327 const std::string
& strFile
)
1329 if (IsURL(strFolder
))
1331 CURL
url(strFolder
);
1332 if (url
.GetFileName() != strFolder
)
1334 url
.SetFileName(AddFileToFolder(url
.GetFileName(), strFile
));
1339 std::string strResult
= strFolder
;
1340 if (!strResult
.empty())
1341 AddSlashAtEnd(strResult
);
1343 // Remove any slash at the start of the file
1344 if (strFile
.size() && (strFile
[0] == '/' || strFile
[0] == '\\'))
1345 strResult
+= strFile
.substr(1);
1347 strResult
+= strFile
;
1349 // correct any slash directions
1350 if (!IsDOSPath(strFolder
))
1351 StringUtils::Replace(strResult
, '\\', '/');
1353 StringUtils::Replace(strResult
, '/', '\\');
1358 std::string
URIUtils::GetDirectory(const std::string
&strFilePath
)
1360 // Will from a full filename return the directory the file resides in.
1361 // Keeps the final slash at end and possible |option=foo options.
1363 size_t iPosSlash
= strFilePath
.find_last_of("/\\");
1364 if (iPosSlash
== std::string::npos
)
1365 return ""; // No slash, so no path (ignore any options)
1367 size_t iPosBar
= strFilePath
.rfind('|');
1368 if (iPosBar
== std::string::npos
)
1369 return strFilePath
.substr(0, iPosSlash
+ 1); // Only path
1371 return strFilePath
.substr(0, iPosSlash
+ 1) + strFilePath
.substr(iPosBar
); // Path + options
1374 CURL
URIUtils::CreateArchivePath(const std::string
& type
,
1375 const CURL
& archiveUrl
,
1376 const std::string
& pathInArchive
,
1377 const std::string
& password
)
1380 url
.SetProtocol(type
);
1381 if (!password
.empty())
1382 url
.SetUserName(password
);
1383 url
.SetHostName(archiveUrl
.Get());
1385 /* NOTE: on posix systems, the replacement of \ with / is incorrect.
1386 Ideally this would not be done. We need to check that the ZipManager
1387 code (and elsewhere) doesn't pass in non-posix paths.
1389 std::string
strBuffer(pathInArchive
);
1390 StringUtils::Replace(strBuffer
, '\\', '/');
1391 StringUtils::TrimLeft(strBuffer
, "/");
1392 url
.SetFileName(strBuffer
);
1397 std::string
URIUtils::GetRealPath(const std::string
&path
)
1403 url
.SetHostName(GetRealPath(url
.GetHostName()));
1404 url
.SetFileName(resolvePath(url
.GetFileName()));
1409 std::string
URIUtils::resolvePath(const std::string
&path
)
1414 size_t posSlash
= path
.find('/');
1415 size_t posBackslash
= path
.find('\\');
1416 std::string delim
= posSlash
< posBackslash
? "/" : "\\";
1417 std::vector
<std::string
> parts
= StringUtils::Split(path
, delim
);
1418 std::vector
<std::string
> realParts
;
1420 for (std::vector
<std::string
>::const_iterator part
= parts
.begin(); part
!= parts
.end(); ++part
)
1422 if (part
->empty() || part
->compare(".") == 0)
1425 // go one level back up
1426 if (part
->compare("..") == 0)
1428 if (!realParts
.empty())
1429 realParts
.pop_back();
1433 realParts
.push_back(*part
);
1436 std::string realPath
;
1437 // re-add any / or \ at the beginning
1438 for (std::string::const_iterator itPath
= path
.begin(); itPath
!= path
.end(); ++itPath
)
1440 if (*itPath
!= delim
.at(0))
1445 // put together the path
1446 realPath
+= StringUtils::Join(realParts
, delim
);
1447 // re-add any / or \ at the end
1448 if (path
.at(path
.size() - 1) == delim
.at(0) &&
1449 realPath
.size() > 0 && realPath
.at(realPath
.size() - 1) != delim
.at(0))
1455 bool URIUtils::UpdateUrlEncoding(std::string
&strFilename
)
1457 if (strFilename
.empty())
1460 CURL
url(strFilename
);
1461 // if this is a stack:// URL we need to work with its filename
1462 if (URIUtils::IsStack(strFilename
))
1464 std::vector
<std::string
> files
;
1465 if (!CStackDirectory::GetPaths(strFilename
, files
))
1468 for (std::vector
<std::string
>::iterator file
= files
.begin(); file
!= files
.end(); ++file
)
1469 UpdateUrlEncoding(*file
);
1471 std::string stackPath
;
1472 if (!CStackDirectory::ConstructStackPath(files
, stackPath
))
1475 url
.Parse(stackPath
);
1477 // if the protocol has an encoded hostname we need to work with its hostname
1478 else if (URIUtils::HasEncodedHostname(url
))
1480 std::string hostname
= url
.GetHostName();
1481 UpdateUrlEncoding(hostname
);
1482 url
.SetHostName(hostname
);
1487 std::string newFilename
= url
.Get();
1488 if (newFilename
== strFilename
)
1491 strFilename
= newFilename
;