fix compile warnings
[vuplus_xbmc] / xbmc / utils / URIUtils.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
21 #include "network/Network.h"
22 #include "URIUtils.h"
23 #include "Application.h"
24 #include "FileItem.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"
32 #include "URL.h"
33 #include "StringUtils.h"
34
35 #include <netinet/in.h>
36 #include <arpa/inet.h>
37
38 using namespace std;
39 using namespace XFILE;
40
41 bool URIUtils::IsInPath(const CStdString &uri, const CStdString &baseURI)
42 {
43   CStdString uriPath = CSpecialProtocol::TranslatePath(uri);
44   CStdString basePath = CSpecialProtocol::TranslatePath(baseURI);
45   return StringUtils::StartsWith(uriPath, basePath);
46 }
47
48 /* returns filename extension including period of filename */
49 CStdString URIUtils::GetExtension(const CStdString& strFileName)
50 {
51   if (IsURL(strFileName))
52   {
53     CURL url(strFileName);
54     return GetExtension(url.GetFileName());
55   }
56
57   size_t period = strFileName.find_last_of("./\\");
58   if (period == string::npos || strFileName[period] != '.')
59     return CStdString();
60
61   return strFileName.substr(period);
62 }
63
64 bool URIUtils::HasExtension(const CStdString& strFileName)
65 {
66   if (IsURL(strFileName))
67   {
68     CURL url(strFileName);
69     return HasExtension(url.GetFileName());
70   }
71
72   size_t iPeriod = strFileName.find_last_of("./\\");
73   return iPeriod != string::npos && strFileName[iPeriod] == '.';
74 }
75
76 bool URIUtils::HasExtension(const CStdString& strFileName, const CStdString& strExtensions)
77 {
78   if (IsURL(strFileName))
79   {
80     CURL url(strFileName);
81     return HasExtension(url.GetFileName(), strExtensions);
82   }
83
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())
87   {
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)
93     {
94       if (*itExtensions == '.')
95         return true; // Match
96     }
97
98     // No match. Look for more extensions to try.
99     while (itExtensions != strExtensions.rend() && *itExtensions != '|')
100       ++itExtensions;
101
102     while (itExtensions != strExtensions.rend() && *itExtensions == '|')
103       ++itExtensions;
104   }
105
106   return false;
107 }
108
109 void URIUtils::RemoveExtension(CStdString& strFileName)
110 {
111   if(IsURL(strFileName))
112   {
113     CURL url(strFileName);
114     strFileName = url.GetFileName();
115     RemoveExtension(strFileName);
116     url.SetFileName(strFileName);
117     strFileName = url.Get();
118     return;
119   }
120
121   size_t period = strFileName.find_last_of("./\\");
122   if (period != string::npos && strFileName[period] == '.')
123   {
124     CStdString strExtension = strFileName.substr(period);
125     StringUtils::ToLower(strExtension);
126     strExtension += "|";
127
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";
135 #else
136     strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg";
137 #endif
138     strFileMask += "|";
139
140     if (strFileMask.find(strExtension) != std::string::npos)
141       strFileName.erase(period);
142   }
143 }
144
145 CStdString URIUtils::ReplaceExtension(const CStdString& strFile,
146                                       const CStdString& strNewExtension)
147 {
148   if(IsURL(strFile))
149   {
150     CURL url(strFile);
151     url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
152     return url.Get();
153   }
154
155   CStdString strChangedFile;
156   CStdString strExtension = GetExtension(strFile);
157   if ( strExtension.size() )
158   {
159     strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ;
160     strChangedFile += strNewExtension;
161   }
162   else
163   {
164     strChangedFile = strFile;
165     strChangedFile += strNewExtension;
166   }
167   return strChangedFile;
168 }
169
170 /* returns a filename given an url */
171 /* handles both / and \, and options in urls*/
172 const CStdString URIUtils::GetFileName(const CStdString& strFileNameAndPath)
173 {
174   if(IsURL(strFileNameAndPath))
175   {
176     CURL url(strFileNameAndPath);
177     return GetFileName(url.GetFileName());
178   }
179
180   /* find the last slash */
181   const size_t slash = strFileNameAndPath.find_last_of("/\\");
182   return strFileNameAndPath.substr(slash+1);
183 }
184
185 void URIUtils::Split(const CStdString& strFileNameAndPath,
186                      CStdString& strPath, CStdString& strFileName)
187 {
188   std::string strPathT, strFileNameT;
189   Split(strFileNameAndPath, strPathT, strFileNameT);
190   strPath = strPathT;
191   strFileName = strFileNameT;
192 }
193
194 void URIUtils::Split(const std::string& strFileNameAndPath,
195                      std::string& strPath, std::string& strFileName)
196 {
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
200   strFileName = "";
201   strPath = "";
202   int i = strFileNameAndPath.size() - 1;
203   while (i > 0)
204   {
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;
208     else i--;
209   }
210   if (i == 0)
211     i--;
212
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);
217 }
218
219 CStdStringArray URIUtils::SplitPath(const CStdString& strPath)
220 {
221   CURL url(strPath);
222
223   // silly CStdString can't take a char in the constructor
224   CStdString sep(1, url.GetDirectorySeparator());
225
226   // split the filename portion of the URL up into separate dirs
227   CStdStringArray dirs;
228   StringUtils::SplitString(url.GetFileName(), sep, dirs);
229   
230   // we start with the root path
231   CStdString dir = url.GetWithoutFilename();
232   
233   if (!dir.empty())
234     dirs.insert(dirs.begin(), dir);
235
236   // we don't need empty token on the end
237   if (dirs.size() > 1 && dirs.back().empty())
238     dirs.erase(dirs.end() - 1);
239
240   return dirs;
241 }
242
243 void URIUtils::GetCommonPath(CStdString& strParent, const CStdString& strPath)
244 {
245   // find the common path of parent and path
246   unsigned int j = 1;
247   while (j <= min(strParent.size(), strPath.size()) && strnicmp(strParent.c_str(), strPath.c_str(), j) == 0)
248     j++;
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))
252   {
253     strParent = GetDirectory(strParent);
254     AddSlashAtEnd(strParent);
255   }
256 }
257
258 bool URIUtils::ProtocolHasParentInHostname(const CStdString& prot)
259 {
260   return prot.Equals("zip")
261       || prot.Equals("rar")
262       || prot.Equals("apk")
263       || prot.Equals("bluray")
264       || prot.Equals("udf");
265 }
266
267 bool URIUtils::ProtocolHasEncodedHostname(const CStdString& prot)
268 {
269   return ProtocolHasParentInHostname(prot)
270       || prot.Equals("musicsearch")
271       || prot.Equals("image");
272 }
273
274 bool URIUtils::ProtocolHasEncodedFilename(const CStdString& prot)
275 {
276   CStdString prot2 = CURL::TranslateProtocol(prot);
277
278   // For now assume only (quasi) http internet streams use URL encoding
279   return prot2 == "http"  ||
280          prot2 == "https";
281 }
282
283 CStdString URIUtils::GetParentPath(const CStdString& strPath)
284 {
285   CStdString strReturn;
286   GetParentPath(strPath, strReturn);
287   return strReturn;
288 }
289
290 bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent)
291 {
292   strParent = "";
293
294   CURL url(strPath);
295   CStdString strFile = url.GetFileName();
296   if ( URIUtils::ProtocolHasParentInHostname(url.GetProtocol()) && strFile.empty())
297   {
298     strFile = url.GetHostName();
299     return GetParentPath(strFile, strParent);
300   }
301   else if (url.GetProtocol() == "stack")
302   {
303     CStackDirectory dir;
304     CFileItemList items;
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);
309     else
310       strParent = items[0]->m_strDVDLabel;
311     for( int i=1;i<items.Size();++i)
312     {
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));
316       else
317         items[i]->SetPath(items[i]->m_strDVDLabel);
318
319       GetCommonPath(strParent,items[i]->GetPath());
320     }
321     return true;
322   }
323   else if (url.GetProtocol() == "multipath")
324   {
325     // get the parent path of the first item
326     return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent);
327   }
328   else if (url.GetProtocol() == "plugin")
329   {
330     if (!url.GetOptions().empty())
331     {
332       url.SetOptions("");
333       strParent = url.Get();
334       return true;
335     }
336     if (!url.GetFileName().empty())
337     {
338       url.SetFileName("");
339       strParent = url.Get();
340       return true;
341     }
342     if (!url.GetHostName().empty())
343     {
344       url.SetHostName("");
345       strParent = url.Get();
346       return true;
347     }
348     return true;  // already at root
349   }
350   else if (url.GetProtocol() == "special")
351   {
352     if (HasSlashAtEnd(strFile))
353       strFile.erase(strFile.size() - 1);
354     if(strFile.rfind('/') == std::string::npos)
355       return false;
356   }
357   else if (strFile.size() == 0)
358   {
359     if (url.GetHostName().size() > 0)
360     {
361       // we have an share with only server or workgroup name
362       // set hostname to "" and return true to get back to root
363       url.SetHostName("");
364       strParent = url.Get();
365       return true;
366     }
367     return false;
368   }
369
370   if (HasSlashAtEnd(strFile) )
371   {
372     strFile.erase(strFile.size() - 1);
373   }
374
375   size_t iPos = strFile.rfind('/');
376 #ifndef TARGET_POSIX
377   if (iPos == std::string::npos)
378   {
379     iPos = strFile.rfind('\\');
380   }
381 #endif
382   if (iPos == std::string::npos)
383   {
384     url.SetFileName("");
385     strParent = url.Get();
386     return true;
387   }
388
389   strFile.erase(iPos);
390
391   AddSlashAtEnd(strFile);
392
393   url.SetFileName(strFile);
394   strParent = url.Get();
395   return true;
396 }
397
398 std::string URLEncodePath(const std::string& strPath)
399 {
400   vector<string> segments = StringUtils::Split(strPath, "/");
401   for (vector<string>::iterator i = segments.begin(); i != segments.end(); ++i)
402     *i = CURL::Encode(*i);
403
404   return StringUtils::Join(segments, "/");
405 }
406
407 std::string URLDecodePath(const std::string& strPath)
408 {
409   vector<string> segments = StringUtils::Split(strPath, "/");
410   for (vector<string>::iterator i = segments.begin(); i != segments.end(); ++i)
411     *i = CURL::Decode(*i);
412
413   return StringUtils::Join(segments, "/");
414 }
415
416 std::string URIUtils::ChangeBasePath(const std::string &fromPath, const std::string &fromFile, const std::string &toPath)
417 {
418   std::string toFile = fromFile;
419
420   // Convert back slashes to forward slashes, if required
421   if (IsDOSPath(fromPath) && !IsDOSPath(toPath))
422     StringUtils::Replace(toFile, "\\", "/");
423
424   // Handle difference in URL encoded vs. not encoded
425   if ( ProtocolHasEncodedFilename(CURL(fromPath).GetProtocol() )
426    && !ProtocolHasEncodedFilename(CURL(toPath).GetProtocol() ) )
427   {
428     toFile = URLDecodePath(toFile); // Decode path
429   }
430   else if (!ProtocolHasEncodedFilename(CURL(fromPath).GetProtocol() )
431          && ProtocolHasEncodedFilename(CURL(toPath).GetProtocol() ) )
432   {
433     toFile = URLEncodePath(toFile); // Encode path
434   }
435
436   // Convert forward slashes to back slashes, if required
437   if (!IsDOSPath(fromPath) && IsDOSPath(toPath))
438     StringUtils::Replace(toFile, "/", "\\");
439
440   return AddFileToFolder(toPath, toFile);
441 }
442
443 CStdString URIUtils::SubstitutePath(const CStdString& strPath, bool reverse /* = false */)
444 {
445   for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
446       i != g_advancedSettings.m_pathSubstitutions.end(); i++)
447   {
448     CStdString fromPath;
449     CStdString toPath;
450
451     if (!reverse)
452     {
453       fromPath = i->first;  // Fake path
454       toPath = i->second;   // Real path
455     }
456     else
457     {
458       fromPath = i->second; // Real path
459       toPath = i->first;    // Fake path
460     }
461
462     if (strncmp(strPath.c_str(), fromPath.c_str(), HasSlashAtEnd(fromPath) ? fromPath.size() - 1 : fromPath.size()) == 0)
463     {
464       if (strPath.size() > fromPath.size())
465       {
466         CStdString strSubPathAndFileName = strPath.substr(fromPath.size());
467         return ChangeBasePath(fromPath, strSubPathAndFileName, toPath); // Fix encoding + slash direction
468       }
469       else
470       {
471         return toPath;
472       }
473     }
474   }
475   return strPath;
476 }
477
478 bool URIUtils::IsRemote(const CStdString& strFile)
479 {
480   if (IsCDDA(strFile) || IsISO9660(strFile))
481     return false;
482
483   if (IsStack(strFile))
484     return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
485
486   if (IsSpecial(strFile))
487     return IsRemote(CSpecialProtocol::TranslatePath(strFile));
488
489   if(IsMultiPath(strFile))
490   { // virtual paths need to be checked separately
491     vector<CStdString> paths;
492     if (CMultiPathDirectory::GetPaths(strFile, paths))
493     {
494       for (unsigned int i = 0; i < paths.size(); i++)
495         if (IsRemote(paths[i])) return true;
496     }
497     return false;
498   }
499
500   CURL url(strFile);
501   if(ProtocolHasParentInHostname(url.GetProtocol()))
502     return IsRemote(url.GetHostName());
503
504   if (!url.IsLocal())
505     return true;
506
507   return false;
508 }
509
510 bool URIUtils::IsOnDVD(const CStdString& strFile)
511 {
512 #ifdef TARGET_WINDOWS
513   if (strFile.size() >= 2 && strFile.substr(1,1) == ":")
514     return (GetDriveType(strFile.substr(0, 3).c_str()) == DRIVE_CDROM);
515 #endif
516
517   if (StringUtils::StartsWith(strFile, "dvd:"))
518     return true;
519
520   if (StringUtils::StartsWith(strFile, "udf:"))
521     return true;
522
523   if (StringUtils::StartsWith(strFile, "iso9660:"))
524     return true;
525
526   if (StringUtils::StartsWith(strFile, "cdda:"))
527     return true;
528
529   return false;
530 }
531
532 bool URIUtils::IsOnLAN(const CStdString& strPath)
533 {
534   if(IsMultiPath(strPath))
535     return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
536
537   if(IsStack(strPath))
538     return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
539
540   if(IsSpecial(strPath))
541     return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
542
543   if(IsDAAP(strPath))
544     return true;
545   
546   if(IsPlugin(strPath))
547     return false;
548
549   if(IsTuxBox(strPath))
550     return true;
551
552   if(IsUPnP(strPath))
553     return true;
554
555   CURL url(strPath);
556   if (ProtocolHasParentInHostname(url.GetProtocol()))
557     return IsOnLAN(url.GetHostName());
558
559   if(!IsRemote(strPath))
560     return false;
561
562   CStdString host = url.GetHostName();
563
564   return IsHostOnLAN(host);
565 }
566
567 static bool addr_match(uint32_t addr, const char* target, const char* submask)
568 {
569   uint32_t addr2 = ntohl(inet_addr(target));
570   uint32_t mask = ntohl(inet_addr(submask));
571   return (addr & mask) == (addr2 & mask);
572 }
573
574 bool URIUtils::IsHostOnLAN(const CStdString& host, bool offLineCheck)
575 {
576   if(host.length() == 0)
577     return false;
578
579   // assume a hostname without dot's
580   // is local (smb netbios hostnames)
581   if(host.find('.') == string::npos)
582     return true;
583
584   uint32_t address = ntohl(inet_addr(host.c_str()));
585   if(address == INADDR_NONE)
586   {
587     CStdString ip;
588     if(CDNSNameCache::Lookup(host, ip))
589       address = ntohl(inet_addr(ip.c_str()));
590   }
591
592   if(address != INADDR_NONE)
593   {
594     if (offLineCheck) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network
595     {
596       if (
597         addr_match(address, "192.168.0.0", "255.255.0.0") ||
598         addr_match(address, "10.0.0.0", "255.0.0.0") ||
599         addr_match(address, "172.16.0.0", "255.240.0.0")
600         )
601         return true;
602     }
603     // check if we are on the local subnet
604     if (!g_application.getNetwork().GetFirstConnectedInterface())
605       return false;
606
607     if (g_application.getNetwork().HasInterfaceForIP(address))
608       return true;
609   }
610
611   return false;
612 }
613
614 bool URIUtils::IsMultiPath(const CStdString& strPath)
615 {
616   return StringUtils::StartsWithNoCase(strPath, "multipath:");
617 }
618
619 bool URIUtils::IsHD(const CStdString& strFileName)
620 {
621   CURL url(strFileName);
622
623   if (IsStack(strFileName))
624     return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
625
626   if (IsSpecial(strFileName))
627     return IsHD(CSpecialProtocol::TranslatePath(strFileName));
628
629   if (ProtocolHasParentInHostname(url.GetProtocol()))
630     return IsHD(url.GetHostName());
631
632   return url.GetProtocol().empty() || url.GetProtocol() == "file";
633 }
634
635 bool URIUtils::IsDVD(const CStdString& strFile)
636 {
637   CStdString strFileLow = strFile;
638   StringUtils::ToLower(strFileLow);
639   if (strFileLow.find("video_ts.ifo") != std::string::npos && IsOnDVD(strFile))
640     return true;
641
642 #if defined(TARGET_WINDOWS)
643   if (StringUtils::StartsWithNoCase(strFile, "dvd://"))
644     return true;
645
646   if(strFile.size() < 2 || (strFile.substr(1) != ":\\" && strFile.substr(1) != ":"))
647     return false;
648
649   if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
650     return true;
651 #else
652   if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
653     return true;
654 #endif
655
656   return false;
657 }
658
659 bool URIUtils::IsStack(const CStdString& strFile)
660 {
661   return StringUtils::StartsWithNoCase(strFile, "stack:");
662 }
663
664 bool URIUtils::IsRAR(const CStdString& strFile)
665 {
666   CStdString strExtension = GetExtension(strFile);
667
668   if (strExtension.Equals(".001") && !StringUtils::EndsWithNoCase(strFile, ".ts.001"))
669     return true;
670
671   if (StringUtils::EqualsNoCase(strExtension, ".cbr"))
672     return true;
673
674   if (StringUtils::EqualsNoCase(strExtension, ".rar"))
675     return true;
676
677   return false;
678 }
679
680 bool URIUtils::IsInArchive(const CStdString &strFile)
681 {
682   return IsInZIP(strFile) || IsInRAR(strFile) || IsInAPK(strFile);
683 }
684
685 bool URIUtils::IsInAPK(const CStdString& strFile)
686 {
687   CURL url(strFile);
688
689   return url.GetProtocol() == "apk" && url.GetFileName() != "";
690 }
691
692 bool URIUtils::IsInZIP(const CStdString& strFile)
693 {
694   CURL url(strFile);
695
696   return url.GetProtocol() == "zip" && url.GetFileName() != "";
697 }
698
699 bool URIUtils::IsInRAR(const CStdString& strFile)
700 {
701   CURL url(strFile);
702
703   return url.GetProtocol() == "rar" && url.GetFileName() != "";
704 }
705
706 bool URIUtils::IsAPK(const CStdString& strFile)
707 {
708   return HasExtension(strFile, ".apk");
709 }
710
711 bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
712 {
713   return HasExtension(strFile, ".zip|.cbz");
714 }
715
716 bool URIUtils::IsArchive(const CStdString& strFile)
717 {
718   return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr");
719 }
720
721 bool URIUtils::IsSpecial(const CStdString& strFile)
722 {
723   CStdString strFile2(strFile);
724
725   if (IsStack(strFile))
726     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
727
728   return StringUtils::StartsWithNoCase(strFile2, "special:");
729 }
730
731 bool URIUtils::IsPlugin(const CStdString& strFile)
732 {
733   CURL url(strFile);
734   return url.GetProtocol().Equals("plugin");
735 }
736
737 bool URIUtils::IsScript(const CStdString& strFile)
738 {
739   CURL url(strFile);
740   return url.GetProtocol().Equals("script");
741 }
742
743 bool URIUtils::IsAddonsPath(const CStdString& strFile)
744 {
745   CURL url(strFile);
746   return url.GetProtocol().Equals("addons");
747 }
748
749 bool URIUtils::IsSourcesPath(const CStdString& strPath)
750 {
751   CURL url(strPath);
752   return url.GetProtocol().Equals("sources");
753 }
754
755 bool URIUtils::IsCDDA(const CStdString& strFile)
756 {
757   return StringUtils::StartsWithNoCase(strFile, "cdda:");
758 }
759
760 bool URIUtils::IsISO9660(const CStdString& strFile)
761 {
762   return StringUtils::StartsWithNoCase(strFile, "iso9660:");
763 }
764
765 bool URIUtils::IsSmb(const CStdString& strFile)
766 {
767   CStdString strFile2(strFile);
768
769   if (IsStack(strFile))
770     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
771
772   return StringUtils::StartsWithNoCase(strFile2, "smb:");
773 }
774
775 bool URIUtils::IsURL(const CStdString& strFile)
776 {
777   return strFile.find("://") != std::string::npos;
778 }
779
780 bool URIUtils::IsFTP(const CStdString& strFile)
781 {
782   CStdString strFile2(strFile);
783
784   if (IsStack(strFile))
785     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
786
787   return StringUtils::StartsWithNoCase(strFile2, "ftp:")  ||
788          StringUtils::StartsWithNoCase(strFile2, "ftps:");
789 }
790
791 bool URIUtils::IsDAV(const CStdString& strFile)
792 {
793   CStdString strFile2(strFile);
794
795   if (IsStack(strFile))
796     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
797
798   return StringUtils::StartsWithNoCase(strFile2, "dav:")  ||
799          StringUtils::StartsWithNoCase(strFile2, "davs:");
800 }
801
802 bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
803 {
804   CStdString strProtocol = url.GetProtocol();
805   
806   if (strProtocol.empty())
807     return false;
808
809   // there's nothing to stop internet streams from being stacked
810   if (strProtocol == "stack")
811     return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
812
813   CStdString strProtocol2 = url.GetTranslatedProtocol();
814
815   // Special case these
816   if (strProtocol  == "ftp"   || strProtocol  == "ftps"   ||
817       strProtocol  == "dav"   || strProtocol  == "davs")
818     return bStrictCheck;
819
820   if (strProtocol2 == "http"  || strProtocol2 == "https"  ||
821       strProtocol2 == "tcp"   || strProtocol2 == "udp"    ||
822       strProtocol2 == "rtp"   || strProtocol2 == "sdp"    ||
823       strProtocol2 == "mms"   || strProtocol2 == "mmst"   ||
824       strProtocol2 == "mmsh"  || strProtocol2 == "rtsp"   ||
825       strProtocol2 == "rtmp"  || strProtocol2 == "rtmpt"  ||
826       strProtocol2 == "rtmpe" || strProtocol2 == "rtmpte" ||
827       strProtocol2 == "rtmps")
828     return true;
829
830   return false;
831 }
832
833 bool URIUtils::IsDAAP(const CStdString& strFile)
834 {
835   return StringUtils::StartsWithNoCase(strFile, "daap:");
836 }
837
838 bool URIUtils::IsUPnP(const CStdString& strFile)
839 {
840   return StringUtils::StartsWithNoCase(strFile, "upnp:");
841 }
842
843 bool URIUtils::IsTuxBox(const CStdString& strFile)
844 {
845   return StringUtils::StartsWithNoCase(strFile, "tuxbox:");
846 }
847
848 bool URIUtils::IsMythTV(const CStdString& strFile)
849 {
850   return StringUtils::StartsWithNoCase(strFile, "myth:");
851 }
852
853 bool URIUtils::IsHDHomeRun(const CStdString& strFile)
854 {
855   return StringUtils::StartsWithNoCase(strFile, "hdhomerun:");
856 }
857
858 bool URIUtils::IsSlingbox(const CStdString& strFile)
859 {
860   return StringUtils::StartsWithNoCase(strFile, "sling:");
861 }
862
863 bool URIUtils::IsVTP(const CStdString& strFile)
864 {
865   return StringUtils::StartsWithNoCase(strFile, "vtp:");
866 }
867
868 bool URIUtils::IsHTSP(const CStdString& strFile)
869 {
870   return StringUtils::StartsWithNoCase(strFile, "htsp:");
871 }
872
873 bool URIUtils::IsLiveTV(const CStdString& strFile)
874 {
875   CStdString strFileWithoutSlash(strFile);
876   RemoveSlashAtEnd(strFileWithoutSlash);
877
878   if(IsTuxBox(strFile)
879   || IsVTP(strFile)
880   || IsHDHomeRun(strFile)
881   || IsSlingbox(strFile)
882   || IsHTSP(strFile)
883   || StringUtils::StartsWithNoCase(strFile, "sap:")
884   ||(StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && !StringUtils::StartsWithNoCase(strFileWithoutSlash, "pvr://recordings")))
885     return true;
886
887   if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
888     return true;
889
890   return false;
891 }
892
893 bool URIUtils::IsPVRRecording(const CStdString& strFile)
894 {
895   CStdString strFileWithoutSlash(strFile);
896   RemoveSlashAtEnd(strFileWithoutSlash);
897
898   return StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") &&
899          StringUtils::StartsWithNoCase(strFile, "pvr://recordings");
900 }
901
902 bool URIUtils::IsMusicDb(const CStdString& strFile)
903 {
904   return StringUtils::StartsWithNoCase(strFile, "musicdb:");
905 }
906
907 bool URIUtils::IsNfs(const CStdString& strFile)
908 {
909   CStdString strFile2(strFile);
910   
911   if (IsStack(strFile))
912     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
913   
914   return StringUtils::StartsWithNoCase(strFile2, "nfs:");
915 }
916
917 bool URIUtils::IsAfp(const CStdString& strFile)
918 {
919   CStdString strFile2(strFile);
920   
921   if (IsStack(strFile))
922     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
923   
924   return StringUtils::StartsWithNoCase(strFile2, "afp:");
925 }
926
927
928 bool URIUtils::IsVideoDb(const CStdString& strFile)
929 {
930   return StringUtils::StartsWithNoCase(strFile, "videodb:");
931 }
932
933 bool URIUtils::IsBluray(const CStdString& strFile)
934 {
935   return StringUtils::StartsWithNoCase(strFile, "bluray:");
936 }
937
938 bool URIUtils::IsAndroidApp(const CStdString &path)
939 {
940   return StringUtils::StartsWithNoCase(path, "androidapp:");
941 }
942
943 bool URIUtils::IsLibraryFolder(const CStdString& strFile)
944 {
945   CURL url(strFile);
946   return url.GetProtocol().Equals("library");
947 }
948
949 bool URIUtils::IsLibraryContent(const std::string &strFile)
950 {
951   return (StringUtils::StartsWith(strFile, "library://") ||
952           StringUtils::StartsWith(strFile, "videodb://") ||
953           StringUtils::StartsWith(strFile, "musicdb://") ||
954           StringUtils::EndsWith(strFile, ".xsp"));
955 }
956
957 bool URIUtils::IsDOSPath(const CStdString &path)
958 {
959   if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
960     return true;
961
962   // windows network drives
963   if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
964     return true;
965
966   return false;
967 }
968
969 void URIUtils::AddSlashAtEnd(std::string& strFolder)
970 {
971   if (IsURL(strFolder))
972   {
973     CURL url(strFolder);
974     std::string file = url.GetFileName();
975     if(!file.empty() && file != strFolder)
976     {
977       AddSlashAtEnd(file);
978       url.SetFileName(file);
979       strFolder = url.Get();
980     }
981     return;
982   }
983
984   if (!HasSlashAtEnd(strFolder))
985   {
986     if (IsDOSPath(strFolder))
987       strFolder += '\\';
988     else
989       strFolder += '/';
990   }
991 }
992
993 bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
994 {
995   if (strFile.empty()) return false;
996   if (checkURL && IsURL(strFile))
997   {
998     CURL url(strFile);
999     CStdString file = url.GetFileName();
1000     return file.empty() || HasSlashAtEnd(file, false);
1001   }
1002   char kar = strFile.c_str()[strFile.size() - 1];
1003
1004   if (kar == '/' || kar == '\\')
1005     return true;
1006
1007   return false;
1008 }
1009
1010 void URIUtils::RemoveSlashAtEnd(std::string& strFolder)
1011 {
1012   if (IsURL(strFolder))
1013   {
1014     CURL url(strFolder);
1015     std::string file = url.GetFileName();
1016     if (!file.empty() && file != strFolder)
1017     {
1018       RemoveSlashAtEnd(file);
1019       url.SetFileName(file);
1020       strFolder = url.Get();
1021       return;
1022     }
1023     if(url.GetHostName().empty())
1024       return;
1025   }
1026
1027   while (HasSlashAtEnd(strFolder))
1028     strFolder.erase(strFolder.size()-1, 1);
1029 }
1030
1031 bool URIUtils::CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2)
1032 {
1033   CStdString strc1 = strPath1, strc2 = strPath2;
1034   RemoveSlashAtEnd(strc1);
1035   RemoveSlashAtEnd(strc2);
1036   return strc1.Equals(strc2);
1037 }
1038
1039
1040 std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/)
1041 {
1042   const size_t len = path.length();
1043   if (startFrom >= len)
1044     return path;
1045
1046   std::string result(path, 0, startFrom);
1047   result.reserve(len);
1048
1049   const char* const str = path.c_str();
1050   size_t pos = startFrom;
1051   do
1052   {
1053     if (str[pos] == '\\' || str[pos] == '/')
1054     {
1055       result.push_back(slashCharacter);  // append one slash
1056       pos++;
1057       // skip any following slashes
1058       while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
1059         pos++;
1060     }
1061     else
1062       result.push_back(str[pos++]);   // append current char and advance pos to next char
1063
1064   } while (pos < len);
1065
1066   return result;
1067 }
1068
1069
1070 std::string URIUtils::CanonicalizePath(const std::string& path, const char slashCharacter /*= '\\'*/)
1071 {
1072   assert(slashCharacter == '\\' || slashCharacter == '/');
1073
1074   if (path.empty())
1075     return path;
1076
1077   const std::string slashStr(1, slashCharacter);
1078   vector<std::string> pathVec, resultVec;
1079   StringUtils::Tokenize(path, pathVec, slashStr);
1080
1081   for (vector<std::string>::const_iterator it = pathVec.begin(); it != pathVec.end(); ++it)
1082   {
1083     if (*it == ".")
1084     { /* skip - do nothing */ }
1085     else if (*it == ".." && !resultVec.empty() && resultVec.back() != "..")
1086       resultVec.pop_back();
1087     else
1088       resultVec.push_back(*it);
1089   }
1090
1091   std::string result;
1092   if (path[0] == slashCharacter)
1093     result.push_back(slashCharacter); // add slash at the begin
1094
1095   result += StringUtils::Join(resultVec, slashStr);
1096
1097   if (path[path.length() - 1] == slashCharacter  && !result.empty() && result[result.length() - 1] != slashCharacter)
1098     result.push_back(slashCharacter); // add slash at the end if result isn't empty and result isn't "/"
1099
1100   return result;
1101 }
1102
1103 CStdString URIUtils::AddFileToFolder(const CStdString& strFolder, 
1104                                 const CStdString& strFile)
1105 {
1106   if (IsURL(strFolder))
1107   {
1108     CURL url(strFolder);
1109     if (url.GetFileName() != strFolder)
1110     {
1111       url.SetFileName(AddFileToFolder(url.GetFileName(), strFile));
1112       return url.Get();
1113     }
1114   }
1115
1116   CStdString strResult = strFolder;
1117   if (!strResult.empty())
1118     AddSlashAtEnd(strResult);
1119
1120   // Remove any slash at the start of the file
1121   if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
1122     strResult += strFile.substr(1);
1123   else
1124     strResult += strFile;
1125
1126   // correct any slash directions
1127   if (!IsDOSPath(strFolder))
1128     StringUtils::Replace(strResult, '\\', '/');
1129   else
1130     StringUtils::Replace(strResult, '/', '\\');
1131
1132   return strResult;
1133 }
1134
1135 CStdString URIUtils::GetDirectory(const CStdString &strFilePath)
1136 {
1137   // Will from a full filename return the directory the file resides in.
1138   // Keeps the final slash at end and possible |option=foo options.
1139
1140   size_t iPosSlash = strFilePath.find_last_of("/\\");
1141   if (iPosSlash == string::npos)
1142     return ""; // No slash, so no path (ignore any options)
1143
1144   size_t iPosBar = strFilePath.rfind('|');
1145   if (iPosBar == string::npos)
1146     return strFilePath.substr(0, iPosSlash + 1); // Only path
1147
1148   return strFilePath.substr(0, iPosSlash + 1) + strFilePath.substr(iPosBar); // Path + options
1149 }
1150
1151 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
1152                                  const CStdString& strType,
1153                                  const CStdString& strArchivePath,
1154                                  const CStdString& strFilePathInArchive,
1155                                  const CStdString& strPwd)
1156 {
1157   strUrlPath = strType+"://";
1158
1159   if( !strPwd.empty() )
1160   {
1161     strUrlPath += CURL::Encode(strPwd);
1162     strUrlPath += "@";
1163   }
1164
1165   strUrlPath += CURL::Encode(strArchivePath);
1166
1167   CStdString strBuffer(strFilePathInArchive);
1168   StringUtils::Replace(strBuffer, '\\', '/');
1169   StringUtils::TrimLeft(strBuffer, "/");
1170
1171   strUrlPath += "/";
1172   strUrlPath += strBuffer;
1173
1174 #if 0 // options are not used
1175   strBuffer = strCachePath;
1176   strBuffer = CURL::Encode(strBuffer);
1177
1178   strUrlPath += "?cache=";
1179   strUrlPath += strBuffer;
1180
1181   strBuffer = StringUtils::Format("%i", wOptions);
1182   strUrlPath += "&flags=";
1183   strUrlPath += strBuffer;
1184 #endif
1185 }
1186
1187 string URIUtils::GetRealPath(const string &path)
1188 {
1189   if (path.empty())
1190     return path;
1191
1192   CURL url(path);
1193   url.SetHostName(GetRealPath(url.GetHostName()));
1194   url.SetFileName(resolvePath(url.GetFileName()));
1195   
1196   return url.Get();
1197 }
1198
1199 std::string URIUtils::resolvePath(const std::string &path)
1200 {
1201   if (path.empty())
1202     return path;
1203
1204   size_t posSlash = path.find('/');
1205   size_t posBackslash = path.find('\\');
1206   string delim = posSlash < posBackslash ? "/" : "\\";
1207   vector<string> parts = StringUtils::Split(path, delim);
1208   vector<string> realParts;
1209
1210   for (vector<string>::const_iterator part = parts.begin(); part != parts.end(); part++)
1211   {
1212     if (part->empty() || part->compare(".") == 0)
1213       continue;
1214
1215     // go one level back up
1216     if (part->compare("..") == 0)
1217     {
1218       if (!realParts.empty())
1219         realParts.pop_back();
1220       continue;
1221     }
1222
1223     realParts.push_back(*part);
1224   }
1225
1226   CStdString realPath;
1227   // re-add any / or \ at the beginning
1228   for (std::string::const_iterator itPath = path.begin(); itPath != path.end(); ++itPath)
1229   {
1230     if (*itPath != delim.at(0))
1231       break;
1232
1233     realPath += delim;
1234   }
1235   // put together the path
1236   realPath += StringUtils::Join(realParts, delim);
1237   // re-add any / or \ at the end
1238   if (path.at(path.size() - 1) == delim.at(0) && realPath.at(realPath.size() - 1) != delim.at(0))
1239     realPath += delim;
1240
1241   return realPath;
1242 }
1243
1244 bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
1245 {
1246   if (strFilename.empty())
1247     return false;
1248   
1249   CURL url(strFilename);
1250   // if this is a stack:// URL we need to work with its filename
1251   if (URIUtils::IsStack(strFilename))
1252   {
1253     vector<CStdString> files;
1254     if (!CStackDirectory::GetPaths(strFilename, files))
1255       return false;
1256
1257     for (vector<CStdString>::iterator file = files.begin(); file != files.end(); file++)
1258     {
1259       std::string filePath = *file;
1260       UpdateUrlEncoding(filePath);
1261       *file = filePath;
1262     }
1263
1264     CStdString stackPath;
1265     if (!CStackDirectory::ConstructStackPath(files, stackPath))
1266       return false;
1267
1268     url.Parse(stackPath);
1269   }
1270   // if the protocol has an encoded hostname we need to work with its hostname
1271   else if (URIUtils::ProtocolHasEncodedHostname(url.GetProtocol()))
1272   {
1273     std::string hostname = url.GetHostName();
1274     UpdateUrlEncoding(hostname);
1275     url.SetHostName(hostname);
1276   }
1277   else
1278     return false;
1279
1280   std::string newFilename = url.Get();
1281   if (newFilename == strFilename)
1282     return false;
1283   
1284   strFilename = newFilename;
1285   return true;
1286 }