2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "network/Network.h"
23 #include "Application.h"
25 #include "filesystem/MultiPathDirectory.h"
26 #include "filesystem/MythDirectory.h"
27 #include "filesystem/SpecialProtocol.h"
28 #include "filesystem/StackDirectory.h"
29 #include "network/DNSNameCache.h"
30 #include "settings/AdvancedSettings.h"
31 #include "settings/MediaSettings.h"
33 #include "StringUtils.h"
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
39 using namespace XFILE;
41 bool URIUtils::IsInPath(const CStdString &uri, const CStdString &baseURI)
43 CStdString uriPath = CSpecialProtocol::TranslatePath(uri);
44 CStdString basePath = CSpecialProtocol::TranslatePath(baseURI);
45 return StringUtils::StartsWith(uriPath, basePath);
48 /* returns filename extension including period of filename */
49 CStdString URIUtils::GetExtension(const CStdString& strFileName)
51 if (IsURL(strFileName))
53 CURL url(strFileName);
54 return GetExtension(url.GetFileName());
57 size_t period = strFileName.find_last_of("./\\");
58 if (period == string::npos || strFileName[period] != '.')
61 return strFileName.substr(period);
64 bool URIUtils::HasExtension(const CStdString& strFileName)
66 if (IsURL(strFileName))
68 CURL url(strFileName);
69 return HasExtension(url.GetFileName());
72 size_t iPeriod = strFileName.find_last_of("./\\");
73 return iPeriod != string::npos && strFileName[iPeriod] == '.';
76 bool URIUtils::HasExtension(const CStdString& strFileName, const CStdString& strExtensions)
78 if (IsURL(strFileName))
80 CURL url(strFileName);
81 return HasExtension(url.GetFileName(), strExtensions);
84 // Search backwards so that '.' can be used as a search terminator.
85 CStdString::const_reverse_iterator itExtensions = strExtensions.rbegin();
86 while (itExtensions != strExtensions.rend())
88 // Iterate backwards over strFileName untill we hit a '.' or a mismatch
89 for (CStdString::const_reverse_iterator itFileName = strFileName.rbegin();
90 itFileName != strFileName.rend() && itExtensions != strExtensions.rend() &&
91 tolower(*itFileName) == *itExtensions;
92 ++itFileName, ++itExtensions)
94 if (*itExtensions == '.')
98 // No match. Look for more extensions to try.
99 while (itExtensions != strExtensions.rend() && *itExtensions != '|')
102 while (itExtensions != strExtensions.rend() && *itExtensions == '|')
109 void URIUtils::RemoveExtension(CStdString& strFileName)
111 if(IsURL(strFileName))
113 CURL url(strFileName);
114 strFileName = url.GetFileName();
115 RemoveExtension(strFileName);
116 url.SetFileName(strFileName);
117 strFileName = url.Get();
121 size_t period = strFileName.find_last_of("./\\");
122 if (period != string::npos && strFileName[period] == '.')
124 CStdString strExtension = strFileName.substr(period);
125 StringUtils::ToLower(strExtension);
128 CStdString strFileMask;
129 strFileMask = g_advancedSettings.m_pictureExtensions;
130 strFileMask += "|" + g_advancedSettings.m_musicExtensions;
131 strFileMask += "|" + g_advancedSettings.m_videoExtensions;
132 strFileMask += "|" + g_advancedSettings.m_subtitlesExtensions;
133 #if defined(TARGET_DARWIN)
134 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg|.app|.applescript|.workflow";
136 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg";
140 if (strFileMask.find(strExtension) != std::string::npos)
141 strFileName.erase(period);
145 CStdString URIUtils::ReplaceExtension(const CStdString& strFile,
146 const CStdString& strNewExtension)
151 url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
155 CStdString strChangedFile;
156 CStdString strExtension = GetExtension(strFile);
157 if ( strExtension.size() )
159 strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ;
160 strChangedFile += strNewExtension;
164 strChangedFile = strFile;
165 strChangedFile += strNewExtension;
167 return strChangedFile;
170 /* returns a filename given an url */
171 /* handles both / and \, and options in urls*/
172 const CStdString URIUtils::GetFileName(const CStdString& strFileNameAndPath)
174 if(IsURL(strFileNameAndPath))
176 CURL url(strFileNameAndPath);
177 return GetFileName(url.GetFileName());
180 /* find the last slash */
181 const size_t slash = strFileNameAndPath.find_last_of("/\\");
182 return strFileNameAndPath.substr(slash+1);
185 void URIUtils::Split(const CStdString& strFileNameAndPath,
186 CStdString& strPath, CStdString& strFileName)
188 std::string strPathT, strFileNameT;
189 Split(strFileNameAndPath, strPathT, strFileNameT);
191 strFileName = strFileNameT;
194 void URIUtils::Split(const std::string& strFileNameAndPath,
195 std::string& strPath, std::string& strFileName)
197 //Splits a full filename in path and file.
198 //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext
199 //Trailing slash will be preserved
202 int i = strFileNameAndPath.size() - 1;
205 char ch = strFileNameAndPath[i];
206 // Only break on ':' if it's a drive separator for DOS (ie d:foo)
207 if (ch == '/' || ch == '\\' || (ch == ':' && i == 1)) break;
213 // take left including the directory separator
214 strPath = strFileNameAndPath.substr(0, i+1);
215 // everything to the right of the directory separator
216 strFileName = strFileNameAndPath.substr(i+1);
219 CStdStringArray URIUtils::SplitPath(const CStdString& strPath)
223 // silly CStdString can't take a char in the constructor
224 CStdString sep(1, url.GetDirectorySeparator());
226 // split the filename portion of the URL up into separate dirs
227 CStdStringArray dirs;
228 StringUtils::SplitString(url.GetFileName(), sep, dirs);
230 // we start with the root path
231 CStdString dir = url.GetWithoutFilename();
234 dirs.insert(dirs.begin(), dir);
236 // we don't need empty token on the end
237 if (dirs.size() > 1 && dirs.back().empty())
238 dirs.erase(dirs.end() - 1);
243 void URIUtils::GetCommonPath(CStdString& strParent, const CStdString& strPath)
245 // find the common path of parent and path
247 while (j <= min(strParent.size(), strPath.size()) && strnicmp(strParent.c_str(), strPath.c_str(), j) == 0)
249 strParent.erase(j - 1);
250 // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be
251 if (!HasSlashAtEnd(strParent))
253 strParent = GetDirectory(strParent);
254 AddSlashAtEnd(strParent);
258 bool URIUtils::ProtocolHasParentInHostname(const CStdString& prot)
260 return prot.Equals("zip")
261 || prot.Equals("rar")
262 || prot.Equals("apk")
263 || prot.Equals("bluray")
264 || prot.Equals("udf");
267 bool URIUtils::ProtocolHasEncodedHostname(const CStdString& prot)
269 return ProtocolHasParentInHostname(prot)
270 || prot.Equals("musicsearch")
271 || prot.Equals("image");
274 bool URIUtils::ProtocolHasEncodedFilename(const CStdString& prot)
276 CStdString prot2 = CURL::TranslateProtocol(prot);
278 // For now assume only (quasi) http internet streams use URL encoding
279 return prot2 == "http" ||
283 CStdString URIUtils::GetParentPath(const CStdString& strPath)
285 CStdString strReturn;
286 GetParentPath(strPath, strReturn);
290 bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent)
295 CStdString strFile = url.GetFileName();
296 if ( URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFile.empty())
298 strFile = url.GetHostName();
299 return GetParentPath(strFile, strParent);
301 else if (url.GetProtocol() == "stack")
305 dir.GetDirectory(strPath,items);
306 items[0]->m_strDVDLabel = GetDirectory(items[0]->GetPath());
307 if (StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "rar://") || StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "zip://"))
308 GetParentPath(items[0]->m_strDVDLabel, strParent);
310 strParent = items[0]->m_strDVDLabel;
311 for( int i=1;i<items.Size();++i)
313 items[i]->m_strDVDLabel = GetDirectory(items[i]->GetPath());
314 if (StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "rar://") || StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "zip://"))
315 items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel));
317 items[i]->SetPath(items[i]->m_strDVDLabel);
319 GetCommonPath(strParent,items[i]->GetPath());
323 else if (url.GetProtocol() == "multipath")
325 // get the parent path of the first item
326 return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent);
328 else if (url.GetProtocol() == "plugin")
330 if (!url.GetOptions().empty())
333 strParent = url.Get();
336 if (!url.GetFileName().empty())
339 strParent = url.Get();
342 if (!url.GetHostName().empty())
345 strParent = url.Get();
348 return true; // already at root
350 else if (url.GetProtocol() == "special")
352 if (HasSlashAtEnd(strFile))
353 strFile.erase(strFile.size() - 1);
354 if(strFile.rfind('/') == std::string::npos)
357 else if (strFile.size() == 0)
359 if (url.GetHostName().size() > 0)
361 // we have an share with only server or workgroup name
362 // set hostname to "" and return true to get back to root
364 strParent = url.Get();
370 if (HasSlashAtEnd(strFile) )
372 strFile.erase(strFile.size() - 1);
375 size_t iPos = strFile.rfind('/');
377 if (iPos == std::string::npos)
379 iPos = strFile.rfind('\\');
382 if (iPos == std::string::npos)
385 strParent = url.Get();
391 AddSlashAtEnd(strFile);
393 url.SetFileName(strFile);
394 strParent = url.Get();
398 CStdString URIUtils::SubstitutePath(const CStdString& strPath, bool reverse /* = false */)
400 for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
401 i != g_advancedSettings.m_pathSubstitutions.end(); i++)
405 if (strncmp(strPath.c_str(), i->first.c_str(), HasSlashAtEnd(i->first.c_str()) ? i->first.size()-1 : i->first.size()) == 0)
407 if (strPath.size() > i->first.size())
408 return URIUtils::AddFileToFolder(i->second, strPath.substr(i->first.size()));
415 if (strncmp(strPath.c_str(), i->second.c_str(), HasSlashAtEnd(i->second.c_str()) ? i->second.size()-1 : i->second.size()) == 0)
417 if (strPath.size() > i->second.size())
418 return URIUtils::AddFileToFolder(i->first, strPath.substr(i->second.size()));
427 bool URIUtils::IsRemote(const CStdString& strFile)
429 if (IsCDDA(strFile) || IsISO9660(strFile))
432 if (IsSpecial(strFile))
433 return IsRemote(CSpecialProtocol::TranslatePath(strFile));
436 return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
438 if(IsMultiPath(strFile))
439 { // virtual paths need to be checked separately
440 vector<CStdString> paths;
441 if (CMultiPathDirectory::GetPaths(strFile, paths))
443 for (unsigned int i = 0; i < paths.size(); i++)
444 if (IsRemote(paths[i])) return true;
450 if(ProtocolHasParentInHostname(url.GetProtocol()))
451 return IsRemote(url.GetHostName());
459 bool URIUtils::IsOnDVD(const CStdString& strFile)
461 #ifdef TARGET_WINDOWS
462 if (strFile.size() >= 2 && strFile.substr(1,1) == ":")
463 return (GetDriveType(strFile.substr(0, 3).c_str()) == DRIVE_CDROM);
466 if (StringUtils::StartsWith(strFile, "dvd:"))
469 if (StringUtils::StartsWith(strFile, "udf:"))
472 if (StringUtils::StartsWith(strFile, "iso9660:"))
475 if (StringUtils::StartsWith(strFile, "cdda:"))
481 bool URIUtils::IsOnLAN(const CStdString& strPath)
483 if(IsMultiPath(strPath))
484 return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
487 return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
489 if(IsSpecial(strPath))
490 return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
495 if(IsPlugin(strPath))
498 if(IsTuxBox(strPath))
505 if (ProtocolHasParentInHostname(url.GetProtocol()))
506 return IsOnLAN(url.GetHostName());
508 if(!IsRemote(strPath))
511 CStdString host = url.GetHostName();
513 return IsHostOnLAN(host);
516 static bool addr_match(uint32_t addr, const char* target, const char* submask)
518 uint32_t addr2 = ntohl(inet_addr(target));
519 uint32_t mask = ntohl(inet_addr(submask));
520 return (addr & mask) == (addr2 & mask);
523 bool URIUtils::IsHostOnLAN(const CStdString& host, bool offLineCheck)
525 if(host.length() == 0)
528 // assume a hostname without dot's
529 // is local (smb netbios hostnames)
530 if(host.find('.') == string::npos)
533 uint32_t address = ntohl(inet_addr(host.c_str()));
534 if(address == INADDR_NONE)
537 if(CDNSNameCache::Lookup(host, ip))
538 address = ntohl(inet_addr(ip.c_str()));
541 if(address != INADDR_NONE)
543 if (offLineCheck) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network
546 addr_match(address, "192.168.0.0", "255.255.0.0") ||
547 addr_match(address, "10.0.0.0", "255.0.0.0") ||
548 addr_match(address, "172.16.0.0", "255.240.0.0")
552 // check if we are on the local subnet
553 if (!g_application.getNetwork().GetFirstConnectedInterface())
556 if (g_application.getNetwork().HasInterfaceForIP(address))
563 bool URIUtils::IsMultiPath(const CStdString& strPath)
565 return StringUtils::StartsWithNoCase(strPath, "multipath:");
568 bool URIUtils::IsHD(const CStdString& strFileName)
570 CURL url(strFileName);
572 if (IsSpecial(strFileName))
573 return IsHD(CSpecialProtocol::TranslatePath(strFileName));
575 if(IsStack(strFileName))
576 return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
578 if (ProtocolHasParentInHostname(url.GetProtocol()))
579 return IsHD(url.GetHostName());
581 return url.GetProtocol().empty() || url.GetProtocol() == "file";
584 bool URIUtils::IsDVD(const CStdString& strFile)
586 CStdString strFileLow = strFile;
587 StringUtils::ToLower(strFileLow);
588 if (strFileLow.find("video_ts.ifo") != std::string::npos && IsOnDVD(strFile))
591 #if defined(TARGET_WINDOWS)
592 if (StringUtils::StartsWithNoCase(strFile, "dvd://"))
595 if(strFile.size() < 2 || (strFile.substr(1) != ":\\" && strFile.substr(1) != ":"))
598 if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
601 if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
608 bool URIUtils::IsStack(const CStdString& strFile)
610 return StringUtils::StartsWithNoCase(strFile, "stack:");
613 bool URIUtils::IsRAR(const CStdString& strFile)
615 CStdString strExtension = GetExtension(strFile);
617 if (strExtension.Equals(".001") && !StringUtils::EndsWithNoCase(strFile, ".ts.001"))
620 if (StringUtils::EqualsNoCase(strExtension, ".cbr"))
623 if (StringUtils::EqualsNoCase(strExtension, ".rar"))
629 bool URIUtils::IsInArchive(const CStdString &strFile)
631 return IsInZIP(strFile) || IsInRAR(strFile) || IsInAPK(strFile);
634 bool URIUtils::IsInAPK(const CStdString& strFile)
638 return url.GetProtocol() == "apk" && url.GetFileName() != "";
641 bool URIUtils::IsInZIP(const CStdString& strFile)
645 return url.GetProtocol() == "zip" && url.GetFileName() != "";
648 bool URIUtils::IsInRAR(const CStdString& strFile)
652 return url.GetProtocol() == "rar" && url.GetFileName() != "";
655 bool URIUtils::IsAPK(const CStdString& strFile)
657 return HasExtension(strFile, ".apk");
660 bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
662 return HasExtension(strFile, ".zip|.cbz");
665 bool URIUtils::IsArchive(const CStdString& strFile)
667 return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr");
670 bool URIUtils::IsSpecial(const CStdString& strFile)
672 CStdString strFile2(strFile);
674 if (IsStack(strFile))
675 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
677 return StringUtils::StartsWithNoCase(strFile2, "special:");
680 bool URIUtils::IsPlugin(const CStdString& strFile)
683 return url.GetProtocol().Equals("plugin");
686 bool URIUtils::IsScript(const CStdString& strFile)
689 return url.GetProtocol().Equals("script");
692 bool URIUtils::IsAddonsPath(const CStdString& strFile)
695 return url.GetProtocol().Equals("addons");
698 bool URIUtils::IsSourcesPath(const CStdString& strPath)
701 return url.GetProtocol().Equals("sources");
704 bool URIUtils::IsCDDA(const CStdString& strFile)
706 return StringUtils::StartsWithNoCase(strFile, "cdda:");
709 bool URIUtils::IsISO9660(const CStdString& strFile)
711 return StringUtils::StartsWithNoCase(strFile, "iso9660:");
714 bool URIUtils::IsSmb(const CStdString& strFile)
716 CStdString strFile2(strFile);
718 if (IsStack(strFile))
719 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
721 return StringUtils::StartsWithNoCase(strFile2, "smb:");
724 bool URIUtils::IsURL(const CStdString& strFile)
726 return strFile.find("://") != std::string::npos;
729 bool URIUtils::IsFTP(const CStdString& strFile)
731 CStdString strFile2(strFile);
733 if (IsStack(strFile))
734 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
736 return StringUtils::StartsWithNoCase(strFile2, "ftp:") ||
737 StringUtils::StartsWithNoCase(strFile2, "ftps:");
740 bool URIUtils::IsDAV(const CStdString& strFile)
742 CStdString strFile2(strFile);
744 if (IsStack(strFile))
745 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
747 return StringUtils::StartsWithNoCase(strFile2, "dav:") ||
748 StringUtils::StartsWithNoCase(strFile2, "davs:");
751 bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
753 CStdString strProtocol = url.GetProtocol();
755 if (strProtocol.empty())
758 // there's nothing to stop internet streams from being stacked
759 if (strProtocol == "stack")
760 return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
762 CStdString strProtocol2 = url.GetTranslatedProtocol();
764 // Special case these
765 if (strProtocol == "ftp" || strProtocol == "ftps" ||
766 strProtocol == "dav" || strProtocol == "davs")
769 if (strProtocol2 == "http" || strProtocol2 == "https" ||
770 strProtocol2 == "tcp" || strProtocol2 == "udp" ||
771 strProtocol2 == "rtp" || strProtocol2 == "sdp" ||
772 strProtocol2 == "mms" || strProtocol2 == "mmst" ||
773 strProtocol2 == "mmsh" || strProtocol2 == "rtsp" ||
774 strProtocol2 == "rtmp" || strProtocol2 == "rtmpt" ||
775 strProtocol2 == "rtmpe" || strProtocol2 == "rtmpte" ||
776 strProtocol2 == "rtmps")
782 bool URIUtils::IsDAAP(const CStdString& strFile)
784 return StringUtils::StartsWithNoCase(strFile, "daap:");
787 bool URIUtils::IsUPnP(const CStdString& strFile)
789 return StringUtils::StartsWithNoCase(strFile, "upnp:");
792 bool URIUtils::IsTuxBox(const CStdString& strFile)
794 return StringUtils::StartsWithNoCase(strFile, "tuxbox:");
797 bool URIUtils::IsMythTV(const CStdString& strFile)
799 return StringUtils::StartsWithNoCase(strFile, "myth:");
802 bool URIUtils::IsHDHomeRun(const CStdString& strFile)
804 return StringUtils::StartsWithNoCase(strFile, "hdhomerun:");
807 bool URIUtils::IsSlingbox(const CStdString& strFile)
809 return StringUtils::StartsWithNoCase(strFile, "sling:");
812 bool URIUtils::IsVTP(const CStdString& strFile)
814 return StringUtils::StartsWithNoCase(strFile, "vtp:");
817 bool URIUtils::IsHTSP(const CStdString& strFile)
819 return StringUtils::StartsWithNoCase(strFile, "htsp:");
822 bool URIUtils::IsLiveTV(const CStdString& strFile)
824 CStdString strFileWithoutSlash(strFile);
825 RemoveSlashAtEnd(strFileWithoutSlash);
829 || IsHDHomeRun(strFile)
830 || IsSlingbox(strFile)
832 || StringUtils::StartsWithNoCase(strFile, "sap:")
833 ||(StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && !StringUtils::StartsWithNoCase(strFileWithoutSlash, "pvr://recordings")))
836 if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
842 bool URIUtils::IsPVRRecording(const CStdString& strFile)
844 CStdString strFileWithoutSlash(strFile);
845 RemoveSlashAtEnd(strFileWithoutSlash);
847 return StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") &&
848 StringUtils::StartsWithNoCase(strFile, "pvr://recordings");
851 bool URIUtils::IsMusicDb(const CStdString& strFile)
853 return StringUtils::StartsWithNoCase(strFile, "musicdb:");
856 bool URIUtils::IsNfs(const CStdString& strFile)
858 CStdString strFile2(strFile);
860 if (IsStack(strFile))
861 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
863 return StringUtils::StartsWithNoCase(strFile2, "nfs:");
866 bool URIUtils::IsAfp(const CStdString& strFile)
868 CStdString strFile2(strFile);
870 if (IsStack(strFile))
871 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
873 return StringUtils::StartsWithNoCase(strFile2, "afp:");
877 bool URIUtils::IsVideoDb(const CStdString& strFile)
879 return StringUtils::StartsWithNoCase(strFile, "videodb:");
882 bool URIUtils::IsBluray(const CStdString& strFile)
884 return StringUtils::StartsWithNoCase(strFile, "bluray:");
887 bool URIUtils::IsAndroidApp(const CStdString &path)
889 return StringUtils::StartsWithNoCase(path, "androidapp:");
892 bool URIUtils::IsLibraryFolder(const CStdString& strFile)
895 return url.GetProtocol().Equals("library");
898 bool URIUtils::IsDOSPath(const CStdString &path)
900 if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
903 // windows network drives
904 if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
910 void URIUtils::AddSlashAtEnd(std::string& strFolder)
912 if (IsURL(strFolder))
915 std::string file = url.GetFileName();
916 if(!file.empty() && file != strFolder)
919 url.SetFileName(file);
920 strFolder = url.Get();
925 if (!HasSlashAtEnd(strFolder))
927 if (IsDOSPath(strFolder))
934 bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
936 if (strFile.empty()) return false;
937 if (checkURL && IsURL(strFile))
940 CStdString file = url.GetFileName();
941 return file.empty() || HasSlashAtEnd(file, false);
943 char kar = strFile.c_str()[strFile.size() - 1];
945 if (kar == '/' || kar == '\\')
951 void URIUtils::RemoveSlashAtEnd(std::string& strFolder)
953 if (IsURL(strFolder))
956 std::string file = url.GetFileName();
957 if (!file.empty() && file != strFolder)
959 RemoveSlashAtEnd(file);
960 url.SetFileName(file);
961 strFolder = url.Get();
964 if(url.GetHostName().empty())
968 while (HasSlashAtEnd(strFolder))
969 strFolder.erase(strFolder.size()-1, 1);
972 bool URIUtils::CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2)
974 CStdString strc1 = strPath1, strc2 = strPath2;
975 RemoveSlashAtEnd(strc1);
976 RemoveSlashAtEnd(strc2);
977 return strc1.Equals(strc2);
981 std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/)
983 const size_t len = path.length();
984 if (startFrom >= len)
987 std::string result(path, 0, startFrom);
990 const char* const str = path.c_str();
991 size_t pos = startFrom;
994 if (str[pos] == '\\' || str[pos] == '/')
996 result.push_back(slashCharacter); // append one slash
998 // skip any following slashes
999 while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
1003 result.push_back(str[pos++]); // append current char and advance pos to next char
1005 } while (pos < len);
1011 CStdString URIUtils::AddFileToFolder(const CStdString& strFolder,
1012 const CStdString& strFile)
1014 if (IsURL(strFolder))
1016 CURL url(strFolder);
1017 if (url.GetFileName() != strFolder)
1019 url.SetFileName(AddFileToFolder(url.GetFileName(), strFile));
1024 CStdString strResult = strFolder;
1025 if (!strResult.empty())
1026 AddSlashAtEnd(strResult);
1028 // Remove any slash at the start of the file
1029 if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
1030 strResult += strFile.substr(1);
1032 strResult += strFile;
1034 // correct any slash directions
1035 if (!IsDOSPath(strFolder))
1036 StringUtils::Replace(strResult, '\\', '/');
1038 StringUtils::Replace(strResult, '/', '\\');
1043 CStdString URIUtils::GetDirectory(const CStdString &strFilePath)
1045 // Will from a full filename return the directory the file resides in.
1046 // Keeps the final slash at end and possible |option=foo options.
1048 size_t iPosSlash = strFilePath.find_last_of("/\\");
1049 if (iPosSlash == string::npos)
1050 return ""; // No slash, so no path (ignore any options)
1052 size_t iPosBar = strFilePath.rfind('|');
1053 if (iPosBar == string::npos)
1054 return strFilePath.substr(0, iPosSlash + 1); // Only path
1056 return strFilePath.substr(0, iPosSlash + 1) + strFilePath.substr(iPosBar); // Path + options
1059 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
1060 const CStdString& strType,
1061 const CStdString& strArchivePath,
1062 const CStdString& strFilePathInArchive,
1063 const CStdString& strPwd)
1065 CStdString strBuffer;
1067 strUrlPath = strType+"://";
1069 if( !strPwd.empty() )
1072 CURL::Encode(strBuffer);
1073 strUrlPath += strBuffer;
1077 strBuffer = strArchivePath;
1078 CURL::Encode(strBuffer);
1080 strUrlPath += strBuffer;
1082 strBuffer = strFilePathInArchive;
1083 StringUtils::Replace(strBuffer, '\\', '/');
1084 StringUtils::TrimLeft(strBuffer, "/");
1087 strUrlPath += strBuffer;
1089 #if 0 // options are not used
1090 strBuffer = strCachePath;
1091 CURL::Encode(strBuffer);
1093 strUrlPath += "?cache=";
1094 strUrlPath += strBuffer;
1096 strBuffer = StringUtils::Format("%i", wOptions);
1097 strUrlPath += "&flags=";
1098 strUrlPath += strBuffer;
1102 string URIUtils::GetRealPath(const string &path)
1108 url.SetHostName(GetRealPath(url.GetHostName()));
1109 url.SetFileName(resolvePath(url.GetFileName()));
1114 std::string URIUtils::resolvePath(const std::string &path)
1119 size_t posSlash = path.find('/');
1120 size_t posBackslash = path.find('\\');
1121 string delim = posSlash < posBackslash ? "/" : "\\";
1122 vector<string> parts = StringUtils::Split(path, delim);
1123 vector<string> realParts;
1125 for (vector<string>::const_iterator part = parts.begin(); part != parts.end(); part++)
1127 if (part->empty() || part->compare(".") == 0)
1130 // go one level back up
1131 if (part->compare("..") == 0)
1133 if (!realParts.empty())
1134 realParts.pop_back();
1138 realParts.push_back(*part);
1141 CStdString realPath;
1143 // re-add any / or \ at the beginning
1144 while (path.at(i) == delim.at(0))
1149 // put together the path
1150 realPath += StringUtils::Join(realParts, delim);
1151 // re-add any / or \ at the end
1152 if (path.at(path.size() - 1) == delim.at(0) && realPath.at(realPath.size() - 1) != delim.at(0))
1158 bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
1160 if (strFilename.empty())
1163 CURL url(strFilename);
1164 // if this is a stack:// URL we need to work with its filename
1165 if (URIUtils::IsStack(strFilename))
1167 vector<CStdString> files;
1168 if (!CStackDirectory::GetPaths(strFilename, files))
1171 for (vector<CStdString>::iterator file = files.begin(); file != files.end(); file++)
1173 std::string filePath = *file;
1174 UpdateUrlEncoding(filePath);
1178 CStdString stackPath;
1179 if (!CStackDirectory::ConstructStackPath(files, stackPath))
1182 url.Parse(stackPath);
1184 // if the protocol has an encoded hostname we need to work with its hostname
1185 else if (URIUtils::ProtocolHasEncodedHostname(url.GetProtocol()))
1187 std::string hostname = url.GetHostName();
1188 UpdateUrlEncoding(hostname);
1189 url.SetHostName(hostname);
1194 std::string newFilename = url.Get();
1195 if (newFilename == strFilename)
1198 strFilename = newFilename;