[PVR][Estuary] Timer settings dialog: Show client name in timer type selection dialog...
[xbmc.git] / xbmc / utils / URIUtils.cpp
blobb2b9b23bc03fff9ef25e6078342beace1690dbd7
1 /*
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.
7 */
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"
24 #if defined(TARGET_WINDOWS)
25 #include "platform/win32/CharsetConverter.h"
26 #endif
28 #include <algorithm>
29 #include <cassert>
30 #include <netinet/in.h>
31 #include <arpa/inet.h>
33 using namespace PVR;
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] != '.')
64 return std::string();
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] != '.')
101 return false;
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))
111 return true;
114 return false;
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();
126 return;
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);
134 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";
143 #else
144 strFileMask += "|.py|.xml|.milk|.xbt|.cdg";
145 #endif
146 strFileMask += "|";
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)
156 if(IsURL(strFile))
158 CURL url(strFile);
159 url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
160 return url.Get();
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;
170 else
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
204 strFileName = "";
205 strPath = "";
206 int i = strFileNameAndPath.size() - 1;
207 while (i > 0)
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;
212 else i--;
214 if (i == 0)
215 i--;
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;
226 while (i > 0)
228 char ch = strFileName[i];
229 if (ch == '?' || ch == '|') break;
230 else i--;
232 if (i > 0)
233 strFileName = strFileName.substr(0, i);
237 std::vector<std::string> URIUtils::SplitPath(const std::string& strPath)
239 CURL url(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();
250 if (!dir.empty())
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);
257 return dirs;
260 void URIUtils::GetCommonPath(std::string& strParent, const std::string& strPath)
262 // find the common path of parent and path
263 unsigned int j = 1;
264 while (j <= std::min(strParent.size(), strPath.size()) &&
265 StringUtils::CompareNoCase(strParent, strPath, j) == 0)
266 j++;
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);
304 return strReturn;
307 bool URIUtils::GetParentPath(const std::string& strPath, std::string& strParent)
309 strParent.clear();
311 CURL url(strPath);
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"))
320 CStackDirectory dir;
321 CFileItemList items;
322 if (!dir.GetDirectory(url, items))
323 return false;
324 CURL url2(GetDirectory(items[0]->GetPath()));
325 if (HasParentInHostname(url2))
326 GetParentPath(url2.Get(), strParent);
327 else
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));
334 else
335 items[i]->SetPath(items[i]->m_strDVDLabel);
337 GetCommonPath(strParent,items[i]->GetPath());
339 return true;
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)
353 url.SetHostName("");
355 url.SetOptions("");
356 strParent = url.Get();
357 return true;
359 if (!url.GetFileName().empty())
361 url.SetFileName("");
362 strParent = url.Get();
363 return true;
365 if (!url.GetHostName().empty())
367 url.SetHostName("");
368 strParent = url.Get();
369 return true;
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)
378 return false;
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
386 url.SetHostName("");
387 strParent = url.Get();
388 return true;
390 return false;
393 if (HasSlashAtEnd(strFile) )
395 strFile.erase(strFile.size() - 1);
398 size_t iPos = strFile.rfind('/');
399 #ifndef TARGET_POSIX
400 if (iPos == std::string::npos)
402 iPos = strFile.rfind('\\');
404 #endif
405 if (iPos == std::string::npos)
407 url.SetFileName("");
408 strParent = url.Get();
409 return true;
412 strFile.erase(iPos);
414 AddSlashAtEnd(strFile);
416 url.SetFileName(strFile);
417 strParent = url.Get();
418 return true;
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);
440 return strDirectory;
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, "/", "\\");
485 if (bAddPath)
486 return AddFileToFolder(toPath, toFile);
488 return 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.
502 return strPath;
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
517 else
519 return toPath;
523 return strPath;
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 */)
533 if (translate)
535 path = CSpecialProtocol::TranslatePath(path);
536 parent = CSpecialProtocol::TranslatePath(parent);
539 if (parent.empty())
540 return false;
542 if (path == parent)
543 return true;
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))
571 return false;
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;
587 return false;
590 CURL url(strFile);
591 if(HasParentInHostname(url))
592 return IsRemote(url.GetHostName());
594 if (IsAddonsPath(strFile))
595 return false;
597 if (IsSourcesPath(strFile))
598 return false;
600 if (IsVideoDb(strFile) || IsMusicDb(strFile))
601 return false;
603 if (IsLibraryFolder(strFile))
604 return false;
606 if (IsPlugin(strFile))
607 return false;
609 if (IsAndroidApp(strFile))
610 return false;
612 if (!url.IsLocal())
613 return true;
615 return false;
618 bool URIUtils::IsOnDVD(const std::string& strFile)
620 if (IsProtocol(strFile, "dvd"))
621 return true;
623 if (IsProtocol(strFile, "udf"))
624 return true;
626 if (IsProtocol(strFile, "iso9660"))
627 return true;
629 if (IsProtocol(strFile, "cdda"))
630 return true;
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);
638 #endif
639 return false;
642 bool URIUtils::IsOnLAN(const std::string& strPath)
644 if(IsMultiPath(strPath))
645 return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
647 if(IsStack(strPath))
648 return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
650 if(IsSpecial(strPath))
651 return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
653 if(IsPlugin(strPath))
654 return false;
656 if(IsUPnP(strPath))
657 return true;
659 CURL url(strPath);
660 if (HasParentInHostname(url))
661 return IsOnLAN(url.GetHostName());
663 if(!IsRemote(strPath))
664 return false;
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)
681 return false;
683 // assume a hostname without dot's
684 // is local (smb netbios hostnames)
685 if(host.find('.') == std::string::npos)
686 return true;
688 uint32_t address = ntohl(inet_addr(host.c_str()));
689 if(address == INADDR_NONE)
691 std::string ip;
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
700 if (
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")
705 return true;
707 // check if we are on the local subnet
708 if (!CServiceBroker::GetNetwork().GetFirstConnectedInterface())
709 return false;
711 if (CServiceBroker::GetNetwork().HasInterfaceForIP(address))
712 return true;
715 return false;
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))
744 return true;
746 #if defined(TARGET_WINDOWS)
747 if (IsProtocol(strFile, "dvd"))
748 return true;
750 if(strFile.size() < 2 || (strFile.substr(1) != ":\\" && strFile.substr(1) != ":"))
751 return false;
753 #ifndef TARGET_WINDOWS_STORE
754 if(GetDriveType(KODI::PLATFORM::WINDOWS::ToW(strFile).c_str()) == DRIVE_CDROM)
755 return true;
756 #endif
757 #else
758 if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
759 return true;
760 #endif
762 return false;
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"))
780 return true;
782 if (StringUtils::EqualsNoCase(strExtension, ".cbr"))
783 return true;
785 if (StringUtils::EqualsNoCase(strExtension, ".rar"))
786 return true;
788 return false;
791 bool URIUtils::IsInArchive(const std::string &strFile)
793 CURL url(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)
801 CURL url(strFile);
803 return url.IsProtocol("apk") && !url.GetFileName().empty();
806 bool URIUtils::IsInZIP(const std::string& strFile)
808 CURL url(strFile);
810 if (url.GetFileName().empty())
811 return false;
813 if (url.IsProtocol("archive"))
814 return IsZIP(url.GetHostName());
816 return url.IsProtocol("zip");
819 bool URIUtils::IsInRAR(const std::string& strFile)
821 CURL url(strFile);
823 if (url.GetFileName().empty())
824 return false;
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)
857 CURL url(strFile);
858 return url.IsProtocol("plugin");
861 bool URIUtils::IsScript(const std::string& strFile)
863 CURL url(strFile);
864 return url.IsProtocol("script");
867 bool URIUtils::IsAddonsPath(const std::string& strFile)
869 CURL url(strFile);
870 return url.IsProtocol("addons");
873 bool URIUtils::IsSourcesPath(const std::string& strPath)
875 CURL url(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));
897 CURL url(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));
917 CURL url(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));
933 CURL url(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));
998 CURL url(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())
1015 return false;
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()))
1023 return true;
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"))
1036 return true;
1038 return false;
1041 bool URIUtils::IsStreamedFilesystem(const std::string& strPath)
1043 CURL url(strPath);
1045 if (url.GetProtocol().empty())
1046 return false;
1048 if (url.IsProtocol("stack"))
1049 return IsStreamedFilesystem(CStackDirectory::GetFirstStackedFile(strPath));
1051 if (IsUPnP(strPath) || IsFTP(strPath) || IsHTTP(strPath, true))
1052 return true;
1054 //! @todo sftp/ssh special case has to be handled by vfs addon
1055 if (url.IsProtocol("sftp") || url.IsProtocol("ssh"))
1056 return true;
1058 return false;
1061 bool URIUtils::IsNetworkFilesystem(const std::string& strPath)
1063 CURL url(strPath);
1065 if (url.GetProtocol().empty())
1066 return false;
1068 if (url.IsProtocol("stack"))
1069 return IsNetworkFilesystem(CStackDirectory::GetFirstStackedFile(strPath));
1071 if (IsStreamedFilesystem(strPath))
1072 return true;
1074 if (IsSmb(strPath) || IsNfs(strPath))
1075 return true;
1077 return false;
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"))
1092 return true;
1094 return false;
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));
1134 CURL url(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)
1158 CURL url(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]))
1173 return true;
1175 // windows network drives
1176 if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
1177 return true;
1179 return false;
1182 std::string URIUtils::AppendSlash(std::string strFolder)
1184 AddSlashAtEnd(strFolder);
1185 return 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();
1200 return;
1203 if (!HasSlashAtEnd(strFolder))
1205 if (IsDOSPath(strFolder))
1206 strFolder += '\\';
1207 else
1208 strFolder += '/';
1212 bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
1214 if (strFile.empty()) return false;
1215 if (checkURL && IsURL(strFile))
1217 CURL url(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 == '\\')
1224 return true;
1226 return false;
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))
1233 return;
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();
1244 return;
1246 if(url.GetHostName().empty())
1247 return;
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)
1267 return path;
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
1279 pos++;
1280 // skip any following slashes
1281 while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
1282 pos++;
1284 else
1285 result.push_back(str[pos++]); // append current char and advance pos to next char
1287 } while (pos < len);
1289 return result;
1293 std::string URIUtils::CanonicalizePath(const std::string& path, const char slashCharacter /*= '\\'*/)
1295 assert(slashCharacter == '\\' || slashCharacter == '/');
1297 if (path.empty())
1298 return path;
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)
1306 if (*it == ".")
1307 { /* skip - do nothing */ }
1308 else if (*it == ".." && !resultVec.empty() && resultVec.back() != "..")
1309 resultVec.pop_back();
1310 else
1311 resultVec.push_back(*it);
1314 std::string result;
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 "/"
1323 return result;
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));
1335 return url.Get();
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);
1346 else
1347 strResult += strFile;
1349 // correct any slash directions
1350 if (!IsDOSPath(strFolder))
1351 StringUtils::Replace(strResult, '\\', '/');
1352 else
1353 StringUtils::Replace(strResult, '/', '\\');
1355 return 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)
1379 CURL url;
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);
1394 return url;
1397 std::string URIUtils::GetRealPath(const std::string &path)
1399 if (path.empty())
1400 return path;
1402 CURL url(path);
1403 url.SetHostName(GetRealPath(url.GetHostName()));
1404 url.SetFileName(resolvePath(url.GetFileName()));
1406 return url.Get();
1409 std::string URIUtils::resolvePath(const std::string &path)
1411 if (path.empty())
1412 return 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)
1423 continue;
1425 // go one level back up
1426 if (part->compare("..") == 0)
1428 if (!realParts.empty())
1429 realParts.pop_back();
1430 continue;
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))
1441 break;
1443 realPath += delim;
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))
1450 realPath += delim;
1452 return realPath;
1455 bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
1457 if (strFilename.empty())
1458 return false;
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))
1466 return false;
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))
1473 return false;
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);
1484 else
1485 return false;
1487 std::string newFilename = url.Get();
1488 if (newFilename == strFilename)
1489 return false;
1491 strFilename = newFilename;
1492 return true;