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 (strncmp(uriPath.c_str(), basePath.c_str(), basePath.GetLength()) == 0);
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 int iPos = strFileName.ReverseFind(".");
125 CStdString strExtension = GetExtension(strFileName);
126 strExtension.ToLower();
129 CStdString strFileMask;
130 strFileMask = g_advancedSettings.m_pictureExtensions;
131 strFileMask += "|" + g_advancedSettings.m_musicExtensions;
132 strFileMask += "|" + g_advancedSettings.m_videoExtensions;
133 strFileMask += "|" + g_advancedSettings.m_subtitlesExtensions;
134 #if defined(TARGET_DARWIN)
135 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg|.app|.applescript|.workflow";
137 strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg";
141 if (strFileMask.Find(strExtension) >= 0)
142 strFileName = strFileName.Left(iPos);
146 CStdString URIUtils::ReplaceExtension(const CStdString& strFile,
147 const CStdString& strNewExtension)
152 url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
156 CStdString strChangedFile;
157 CStdString strExtension = GetExtension(strFile);
158 if ( strExtension.size() )
160 strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ;
161 strChangedFile += strNewExtension;
165 strChangedFile = strFile;
166 strChangedFile += strNewExtension;
168 return strChangedFile;
171 /* returns a filename given an url */
172 /* handles both / and \, and options in urls*/
173 const CStdString URIUtils::GetFileName(const CStdString& strFileNameAndPath)
175 if(IsURL(strFileNameAndPath))
177 CURL url(strFileNameAndPath);
178 return GetFileName(url.GetFileName());
181 /* find any slashes */
182 const int slash1 = strFileNameAndPath.find_last_of('/');
183 const int slash2 = strFileNameAndPath.find_last_of('\\');
185 /* select the last one */
192 return strFileNameAndPath.substr(slash+1);
195 void URIUtils::Split(const CStdString& strFileNameAndPath,
196 CStdString& strPath, CStdString& strFileName)
198 std::string strPathT, strFileNameT;
199 Split(strFileNameAndPath, strPathT, strFileNameT);
201 strFileName = strFileNameT;
204 void URIUtils::Split(const std::string& strFileNameAndPath,
205 std::string& strPath, std::string& strFileName)
207 //Splits a full filename in path and file.
208 //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext
209 //Trailing slash will be preserved
212 int i = strFileNameAndPath.size() - 1;
215 char ch = strFileNameAndPath[i];
216 // Only break on ':' if it's a drive separator for DOS (ie d:foo)
217 if (ch == '/' || ch == '\\' || (ch == ':' && i == 1)) break;
223 // take left including the directory separator
224 strPath = strFileNameAndPath.substr(0, i+1);
225 // everything to the right of the directory separator
226 strFileName = strFileNameAndPath.substr(i+1);
229 CStdStringArray URIUtils::SplitPath(const CStdString& strPath)
233 // silly CStdString can't take a char in the constructor
234 CStdString sep(1, url.GetDirectorySeparator());
236 // split the filename portion of the URL up into separate dirs
237 CStdStringArray dirs;
238 StringUtils::SplitString(url.GetFileName(), sep, dirs);
240 // we start with the root path
241 CStdString dir = url.GetWithoutFilename();
244 dirs.insert(dirs.begin(), dir);
246 // we don't need empty token on the end
247 if (dirs.size() > 1 && dirs.back().IsEmpty())
248 dirs.erase(dirs.end() - 1);
253 void URIUtils::GetCommonPath(CStdString& strParent, const CStdString& strPath)
255 // find the common path of parent and path
257 while (j <= min(strParent.size(), strPath.size()) && strnicmp(strParent.c_str(), strPath.c_str(), j) == 0)
259 strParent = strParent.Left(j - 1);
260 // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be
261 if (!HasSlashAtEnd(strParent))
263 strParent = GetDirectory(strParent);
264 AddSlashAtEnd(strParent);
268 bool URIUtils::ProtocolHasParentInHostname(const CStdString& prot)
270 return prot.Equals("zip")
271 || prot.Equals("rar")
272 || prot.Equals("apk")
273 || prot.Equals("bluray")
274 || prot.Equals("udf");
277 bool URIUtils::ProtocolHasEncodedHostname(const CStdString& prot)
279 return ProtocolHasParentInHostname(prot)
280 || prot.Equals("musicsearch")
281 || prot.Equals("image");
284 bool URIUtils::ProtocolHasEncodedFilename(const CStdString& prot)
286 CStdString prot2 = CURL::TranslateProtocol(prot);
288 // For now assume only (quasi) http internet streams use URL encoding
289 return prot2 == "http" ||
293 CStdString URIUtils::GetParentPath(const CStdString& strPath)
295 CStdString strReturn;
296 GetParentPath(strPath, strReturn);
300 bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent)
305 CStdString strFile = url.GetFileName();
306 if ( URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFile.IsEmpty())
308 strFile = url.GetHostName();
309 return GetParentPath(strFile, strParent);
311 else if (url.GetProtocol() == "stack")
315 dir.GetDirectory(strPath,items);
316 items[0]->m_strDVDLabel = GetDirectory(items[0]->GetPath());
317 if (StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "rar://") || StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "zip://"))
318 GetParentPath(items[0]->m_strDVDLabel, strParent);
320 strParent = items[0]->m_strDVDLabel;
321 for( int i=1;i<items.Size();++i)
323 items[i]->m_strDVDLabel = GetDirectory(items[i]->GetPath());
324 if (StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "rar://") || StringUtils::StartsWithNoCase(items[0]->m_strDVDLabel, "zip://"))
325 items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel));
327 items[i]->SetPath(items[i]->m_strDVDLabel);
329 GetCommonPath(strParent,items[i]->GetPath());
333 else if (url.GetProtocol() == "multipath")
335 // get the parent path of the first item
336 return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent);
338 else if (url.GetProtocol() == "plugin")
340 if (!url.GetOptions().IsEmpty())
343 strParent = url.Get();
346 if (!url.GetFileName().IsEmpty())
349 strParent = url.Get();
352 if (!url.GetHostName().IsEmpty())
355 strParent = url.Get();
358 return true; // already at root
360 else if (url.GetProtocol() == "special")
362 if (HasSlashAtEnd(strFile) )
363 strFile = strFile.Left(strFile.size() - 1);
364 if(strFile.ReverseFind('/') < 0)
367 else if (strFile.size() == 0)
369 if (url.GetHostName().size() > 0)
371 // we have an share with only server or workgroup name
372 // set hostname to "" and return true to get back to root
374 strParent = url.Get();
380 if (HasSlashAtEnd(strFile) )
382 strFile = strFile.Left(strFile.size() - 1);
385 int iPos = strFile.ReverseFind('/');
389 iPos = strFile.ReverseFind('\\');
395 strParent = url.Get();
399 strFile = strFile.Left(iPos);
401 AddSlashAtEnd(strFile);
403 url.SetFileName(strFile);
404 strParent = url.Get();
408 CStdString URIUtils::SubstitutePath(const CStdString& strPath, bool reverse /* = false */)
410 for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
411 i != g_advancedSettings.m_pathSubstitutions.end(); i++)
415 if (strncmp(strPath.c_str(), i->first.c_str(), HasSlashAtEnd(i->first.c_str()) ? i->first.size()-1 : i->first.size()) == 0)
417 if (strPath.size() > i->first.size())
418 return URIUtils::AddFileToFolder(i->second, strPath.Mid(i->first.size()));
425 if (strncmp(strPath.c_str(), i->second.c_str(), HasSlashAtEnd(i->second.c_str()) ? i->second.size()-1 : i->second.size()) == 0)
427 if (strPath.size() > i->second.size())
428 return URIUtils::AddFileToFolder(i->first, strPath.Mid(i->second.size()));
437 bool URIUtils::IsRemote(const CStdString& strFile)
439 if (IsCDDA(strFile) || IsISO9660(strFile))
442 if (IsSpecial(strFile))
443 return IsRemote(CSpecialProtocol::TranslatePath(strFile));
446 return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
448 if(IsMultiPath(strFile))
449 { // virtual paths need to be checked separately
450 vector<CStdString> paths;
451 if (CMultiPathDirectory::GetPaths(strFile, paths))
453 for (unsigned int i = 0; i < paths.size(); i++)
454 if (IsRemote(paths[i])) return true;
460 if(ProtocolHasParentInHostname(url.GetProtocol()))
461 return IsRemote(url.GetHostName());
469 bool URIUtils::IsOnDVD(const CStdString& strFile)
471 #ifdef TARGET_WINDOWS
472 if (strFile.Mid(1,1) == ":")
473 return (GetDriveType(strFile.Left(3)) == DRIVE_CDROM);
476 if (strFile.Left(4).CompareNoCase("dvd:") == 0)
479 if (strFile.Left(4).CompareNoCase("udf:") == 0)
482 if (strFile.Left(8).CompareNoCase("iso9660:") == 0)
485 if (strFile.Left(5).CompareNoCase("cdda:") == 0)
491 bool URIUtils::IsOnLAN(const CStdString& strPath)
493 if(IsMultiPath(strPath))
494 return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
497 return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
499 if(IsSpecial(strPath))
500 return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
505 if(IsPlugin(strPath))
508 if(IsTuxBox(strPath))
515 if (ProtocolHasParentInHostname(url.GetProtocol()))
516 return IsOnLAN(url.GetHostName());
518 if(!IsRemote(strPath))
521 CStdString host = url.GetHostName();
523 return IsHostOnLAN(host);
526 static bool addr_match(uint32_t addr, const char* target, const char* submask)
528 uint32_t addr2 = ntohl(inet_addr(target));
529 uint32_t mask = ntohl(inet_addr(submask));
530 return (addr & mask) == (addr2 & mask);
533 bool URIUtils::IsHostOnLAN(const CStdString& host, bool offLineCheck)
535 if(host.length() == 0)
538 // assume a hostname without dot's
539 // is local (smb netbios hostnames)
540 if(host.find('.') == string::npos)
543 uint32_t address = ntohl(inet_addr(host.c_str()));
544 if(address == INADDR_NONE)
547 if(CDNSNameCache::Lookup(host, ip))
548 address = ntohl(inet_addr(ip.c_str()));
551 if(address != INADDR_NONE)
553 if (offLineCheck) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network
556 addr_match(address, "192.168.0.0", "255.255.0.0") ||
557 addr_match(address, "10.0.0.0", "255.0.0.0") ||
558 addr_match(address, "172.16.0.0", "255.240.0.0")
562 // check if we are on the local subnet
563 if (!g_application.getNetwork().GetFirstConnectedInterface())
566 if (g_application.getNetwork().HasInterfaceForIP(address))
573 bool URIUtils::IsMultiPath(const CStdString& strPath)
575 return StringUtils::StartsWithNoCase(strPath, "multipath:");
578 bool URIUtils::IsHD(const CStdString& strFileName)
580 CURL url(strFileName);
582 if (IsSpecial(strFileName))
583 return IsHD(CSpecialProtocol::TranslatePath(strFileName));
585 if(IsStack(strFileName))
586 return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
588 if (ProtocolHasParentInHostname(url.GetProtocol()))
589 return IsHD(url.GetHostName());
591 return url.GetProtocol().IsEmpty() || url.GetProtocol() == "file";
594 bool URIUtils::IsDVD(const CStdString& strFile)
596 CStdString strFileLow = strFile;
597 strFileLow.MakeLower();
598 if (strFileLow.Find("video_ts.ifo") != -1 && IsOnDVD(strFile))
601 #if defined(TARGET_WINDOWS)
602 if (StringUtils::StartsWithNoCase(strFile, "dvd://"))
605 if(strFile.Mid(1) != ":\\"
606 && strFile.Mid(1) != ":")
609 if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
612 if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
619 bool URIUtils::IsStack(const CStdString& strFile)
621 return StringUtils::StartsWithNoCase(strFile, "stack:");
624 bool URIUtils::IsRAR(const CStdString& strFile)
626 CStdString strExtension = GetExtension(strFile);
628 if (strExtension.Equals(".001") && strFile.Mid(strFile.length()-7,7).CompareNoCase(".ts.001"))
631 if (strExtension.CompareNoCase(".cbr") == 0)
634 if (strExtension.CompareNoCase(".rar") == 0)
640 bool URIUtils::IsInArchive(const CStdString &strFile)
642 return IsInZIP(strFile) || IsInRAR(strFile) || IsInAPK(strFile);
645 bool URIUtils::IsInAPK(const CStdString& strFile)
649 return url.GetProtocol() == "apk" && url.GetFileName() != "";
652 bool URIUtils::IsInZIP(const CStdString& strFile)
656 return url.GetProtocol() == "zip" && url.GetFileName() != "";
659 bool URIUtils::IsInRAR(const CStdString& strFile)
663 return url.GetProtocol() == "rar" && url.GetFileName() != "";
666 bool URIUtils::IsAPK(const CStdString& strFile)
668 return HasExtension(strFile, ".apk");
671 bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
673 return HasExtension(strFile, ".zip|.cbz");
676 bool URIUtils::IsArchive(const CStdString& strFile)
678 return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr");
681 bool URIUtils::IsSpecial(const CStdString& strFile)
683 CStdString strFile2(strFile);
685 if (IsStack(strFile))
686 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
688 return StringUtils::StartsWithNoCase(strFile2, "special:");
691 bool URIUtils::IsPlugin(const CStdString& strFile)
694 return url.GetProtocol().Equals("plugin");
697 bool URIUtils::IsScript(const CStdString& strFile)
700 return url.GetProtocol().Equals("script");
703 bool URIUtils::IsAddonsPath(const CStdString& strFile)
706 return url.GetProtocol().Equals("addons");
709 bool URIUtils::IsSourcesPath(const CStdString& strPath)
712 return url.GetProtocol().Equals("sources");
715 bool URIUtils::IsCDDA(const CStdString& strFile)
717 return StringUtils::StartsWithNoCase(strFile, "cdda:");
720 bool URIUtils::IsISO9660(const CStdString& strFile)
722 return StringUtils::StartsWithNoCase(strFile, "iso9660:");
725 bool URIUtils::IsSmb(const CStdString& strFile)
727 CStdString strFile2(strFile);
729 if (IsStack(strFile))
730 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
732 return StringUtils::StartsWithNoCase(strFile2, "smb:");
735 bool URIUtils::IsURL(const CStdString& strFile)
737 return strFile.Find("://") >= 0;
740 bool URIUtils::IsFTP(const CStdString& strFile)
742 CStdString strFile2(strFile);
744 if (IsStack(strFile))
745 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
747 return StringUtils::StartsWithNoCase(strFile2, "ftp:") ||
748 StringUtils::StartsWithNoCase(strFile2, "ftps:");
751 bool URIUtils::IsDAV(const CStdString& strFile)
753 CStdString strFile2(strFile);
755 if (IsStack(strFile))
756 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
758 return StringUtils::StartsWithNoCase(strFile2, "dav:") ||
759 StringUtils::StartsWithNoCase(strFile2, "davs:");
762 bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
764 CStdString strProtocol = url.GetProtocol();
766 if (strProtocol.IsEmpty())
769 // there's nothing to stop internet streams from being stacked
770 if (strProtocol == "stack")
771 return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
773 CStdString strProtocol2 = url.GetTranslatedProtocol();
775 // Special case these
776 if (strProtocol == "ftp" || strProtocol == "ftps" ||
777 strProtocol == "dav" || strProtocol == "davs")
780 if (strProtocol2 == "http" || strProtocol2 == "https" ||
781 strProtocol2 == "tcp" || strProtocol2 == "udp" ||
782 strProtocol2 == "rtp" || strProtocol2 == "sdp" ||
783 strProtocol2 == "mms" || strProtocol2 == "mmst" ||
784 strProtocol2 == "mmsh" || strProtocol2 == "rtsp" ||
785 strProtocol2 == "rtmp" || strProtocol2 == "rtmpt" ||
786 strProtocol2 == "rtmpe" || strProtocol2 == "rtmpte" ||
787 strProtocol2 == "rtmps")
793 bool URIUtils::IsDAAP(const CStdString& strFile)
795 return StringUtils::StartsWithNoCase(strFile, "daap:");
798 bool URIUtils::IsUPnP(const CStdString& strFile)
800 return StringUtils::StartsWithNoCase(strFile, "upnp:");
803 bool URIUtils::IsTuxBox(const CStdString& strFile)
805 return StringUtils::StartsWithNoCase(strFile, "tuxbox:");
808 bool URIUtils::IsMythTV(const CStdString& strFile)
810 return StringUtils::StartsWithNoCase(strFile, "myth:");
813 bool URIUtils::IsHDHomeRun(const CStdString& strFile)
815 return StringUtils::StartsWithNoCase(strFile, "hdhomerun:");
818 bool URIUtils::IsSlingbox(const CStdString& strFile)
820 return StringUtils::StartsWithNoCase(strFile, "sling:");
823 bool URIUtils::IsVTP(const CStdString& strFile)
825 return StringUtils::StartsWithNoCase(strFile, "vtp:");
828 bool URIUtils::IsHTSP(const CStdString& strFile)
830 return StringUtils::StartsWithNoCase(strFile, "htsp:");
833 bool URIUtils::IsLiveTV(const CStdString& strFile)
835 CStdString strFileWithoutSlash(strFile);
836 RemoveSlashAtEnd(strFileWithoutSlash);
840 || IsHDHomeRun(strFile)
841 || IsSlingbox(strFile)
843 || StringUtils::StartsWithNoCase(strFile, "sap:")
844 ||(StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && !StringUtils::StartsWithNoCase(strFileWithoutSlash, "pvr://recordings")))
847 if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
853 bool URIUtils::IsPVRRecording(const CStdString& strFile)
855 CStdString strFileWithoutSlash(strFile);
856 RemoveSlashAtEnd(strFileWithoutSlash);
858 return StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") &&
859 StringUtils::StartsWithNoCase(strFile, "pvr://recordings");
862 bool URIUtils::IsMusicDb(const CStdString& strFile)
864 return StringUtils::StartsWithNoCase(strFile, "musicdb:");
867 bool URIUtils::IsNfs(const CStdString& strFile)
869 CStdString strFile2(strFile);
871 if (IsStack(strFile))
872 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
874 return StringUtils::StartsWithNoCase(strFile2, "nfs:");
877 bool URIUtils::IsAfp(const CStdString& strFile)
879 CStdString strFile2(strFile);
881 if (IsStack(strFile))
882 strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
884 return StringUtils::StartsWithNoCase(strFile2, "afp:");
888 bool URIUtils::IsVideoDb(const CStdString& strFile)
890 return StringUtils::StartsWithNoCase(strFile, "videodb:");
893 bool URIUtils::IsBluray(const CStdString& strFile)
895 return StringUtils::StartsWithNoCase(strFile, "bluray:");
898 bool URIUtils::IsAndroidApp(const CStdString &path)
900 return StringUtils::StartsWithNoCase(path, "androidapp:");
903 bool URIUtils::IsLibraryFolder(const CStdString& strFile)
906 return url.GetProtocol().Equals("library");
909 bool URIUtils::IsDOSPath(const CStdString &path)
911 if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
914 // windows network drives
915 if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
921 void URIUtils::AddSlashAtEnd(std::string& strFolder)
923 if (IsURL(strFolder))
926 std::string file = url.GetFileName();
927 if(!file.empty() && file != strFolder)
930 url.SetFileName(file);
931 strFolder = url.Get();
936 if (!HasSlashAtEnd(strFolder))
938 if (IsDOSPath(strFolder))
945 bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
947 if (strFile.empty()) return false;
948 if (checkURL && IsURL(strFile))
951 CStdString file = url.GetFileName();
952 return file.IsEmpty() || HasSlashAtEnd(file, false);
954 char kar = strFile.c_str()[strFile.size() - 1];
956 if (kar == '/' || kar == '\\')
962 void URIUtils::RemoveSlashAtEnd(std::string& strFolder)
964 if (IsURL(strFolder))
967 std::string file = url.GetFileName();
968 if (!file.empty() && file != strFolder)
970 RemoveSlashAtEnd(file);
971 url.SetFileName(file);
972 strFolder = url.Get();
975 if(url.GetHostName().IsEmpty())
979 while (HasSlashAtEnd(strFolder))
980 strFolder.erase(strFolder.size()-1, 1);
983 bool URIUtils::CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2)
985 CStdString strc1 = strPath1, strc2 = strPath2;
986 RemoveSlashAtEnd(strc1);
987 RemoveSlashAtEnd(strc2);
988 return strc1.Equals(strc2);
992 std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/)
994 const size_t len = path.length();
995 if (startFrom >= len)
998 std::string result(path, 0, startFrom);
1001 const char* const str = path.c_str();
1002 size_t pos = startFrom;
1005 if (str[pos] == '\\' || str[pos] == '/')
1007 result.push_back(slashCharacter); // append one slash
1009 // skip any following slashes
1010 while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
1014 result.push_back(str[pos++]); // append current char and advance pos to next char
1016 } while (pos < len);
1022 CStdString URIUtils::AddFileToFolder(const CStdString& strFolder,
1023 const CStdString& strFile)
1025 if (IsURL(strFolder))
1027 CURL url(strFolder);
1028 if (url.GetFileName() != strFolder)
1030 url.SetFileName(AddFileToFolder(url.GetFileName(), strFile));
1035 CStdString strResult = strFolder;
1036 if (!strResult.IsEmpty())
1037 AddSlashAtEnd(strResult);
1039 // Remove any slash at the start of the file
1040 if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
1041 strResult += strFile.Mid(1);
1043 strResult += strFile;
1045 // correct any slash directions
1046 if (!IsDOSPath(strFolder))
1047 strResult.Replace('\\', '/');
1049 strResult.Replace('/', '\\');
1054 CStdString URIUtils::GetDirectory(const CStdString &strFilePath)
1056 // Will from a full filename return the directory the file resides in.
1057 // Keeps the final slash at end and possible |option=foo options.
1059 size_t iPosSlash = strFilePath.find_last_of("/\\");
1060 if (iPosSlash == string::npos)
1061 return ""; // No slash, so no path (ignore any options)
1063 size_t iPosBar = strFilePath.rfind('|');
1064 if (iPosBar == string::npos)
1065 return strFilePath.Left(iPosSlash + 1); // Only path
1067 return strFilePath.Left(iPosSlash + 1) + strFilePath.Mid(iPosBar); // Path + options
1070 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
1071 const CStdString& strType,
1072 const CStdString& strArchivePath,
1073 const CStdString& strFilePathInArchive,
1074 const CStdString& strPwd)
1076 CStdString strBuffer;
1078 strUrlPath = strType+"://";
1080 if( !strPwd.IsEmpty() )
1083 CURL::Encode(strBuffer);
1084 strUrlPath += strBuffer;
1088 strBuffer = strArchivePath;
1089 CURL::Encode(strBuffer);
1091 strUrlPath += strBuffer;
1093 strBuffer = strFilePathInArchive;
1094 strBuffer.Replace('\\', '/');
1095 strBuffer.TrimLeft('/');
1098 strUrlPath += strBuffer;
1100 #if 0 // options are not used
1101 strBuffer = strCachePath;
1102 CURL::Encode(strBuffer);
1104 strUrlPath += "?cache=";
1105 strUrlPath += strBuffer;
1107 strBuffer = StringUtils::Format("%i", wOptions);
1108 strUrlPath += "&flags=";
1109 strUrlPath += strBuffer;
1113 string URIUtils::GetRealPath(const string &path)
1119 url.SetHostName(GetRealPath(url.GetHostName()));
1120 url.SetFileName(resolvePath(url.GetFileName()));
1125 std::string URIUtils::resolvePath(const std::string &path)
1130 size_t posSlash = path.find('/');
1131 size_t posBackslash = path.find('\\');
1132 string delim = posSlash < posBackslash ? "/" : "\\";
1133 vector<string> parts = StringUtils::Split(path, delim);
1134 vector<string> realParts;
1136 for (vector<string>::const_iterator part = parts.begin(); part != parts.end(); part++)
1138 if (part->empty() || part->compare(".") == 0)
1141 // go one level back up
1142 if (part->compare("..") == 0)
1144 if (!realParts.empty())
1145 realParts.pop_back();
1149 realParts.push_back(*part);
1152 CStdString realPath;
1154 // re-add any / or \ at the beginning
1155 while (path.at(i) == delim.at(0))
1160 // put together the path
1161 realPath += StringUtils::Join(realParts, delim);
1162 // re-add any / or \ at the end
1163 if (path.at(path.size() - 1) == delim.at(0) && realPath.at(realPath.size() - 1) != delim.at(0))
1169 bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
1171 if (strFilename.empty())
1174 CURL url(strFilename);
1175 // if this is a stack:// URL we need to work with its filename
1176 if (URIUtils::IsStack(strFilename))
1178 vector<CStdString> files;
1179 if (!CStackDirectory::GetPaths(strFilename, files))
1182 for (vector<CStdString>::iterator file = files.begin(); file != files.end(); file++)
1184 std::string filePath = *file;
1185 UpdateUrlEncoding(filePath);
1189 CStdString stackPath;
1190 if (!CStackDirectory::ConstructStackPath(files, stackPath))
1193 url.Parse(stackPath);
1195 // if the protocol has an encoded hostname we need to work with its hostname
1196 else if (URIUtils::ProtocolHasEncodedHostname(url.GetProtocol()))
1198 std::string hostname = url.GetHostName();
1199 UpdateUrlEncoding(hostname);
1200 url.SetHostName(hostname);
1205 std::string newFilename = url.Get();
1206 if (newFilename == strFilename)
1209 strFilename = newFilename;