changed: Add logic to properly handle subtitles for stacked files
[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 CStdString URIUtils::SubstitutePath(const CStdString& strPath, bool reverse /* = false */)
399 {
400   for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
401       i != g_advancedSettings.m_pathSubstitutions.end(); i++)
402   {
403     if (!reverse)
404     {
405       if (strncmp(strPath.c_str(), i->first.c_str(), HasSlashAtEnd(i->first.c_str()) ? i->first.size()-1 : i->first.size()) == 0)
406       {
407         if (strPath.size() > i->first.size())
408           return URIUtils::AddFileToFolder(i->second, strPath.substr(i->first.size()));
409         else
410           return i->second;
411       }
412     }
413     else
414     {
415       if (strncmp(strPath.c_str(), i->second.c_str(), HasSlashAtEnd(i->second.c_str()) ? i->second.size()-1 : i->second.size()) == 0)
416       {
417         if (strPath.size() > i->second.size())
418           return URIUtils::AddFileToFolder(i->first, strPath.substr(i->second.size()));
419         else
420           return i->first;
421       }
422     }
423   }
424   return strPath;
425 }
426
427 bool URIUtils::IsRemote(const CStdString& strFile)
428 {
429   if (IsCDDA(strFile) || IsISO9660(strFile))
430     return false;
431
432   if (IsSpecial(strFile))
433     return IsRemote(CSpecialProtocol::TranslatePath(strFile));
434
435   if(IsStack(strFile))
436     return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
437
438   if(IsMultiPath(strFile))
439   { // virtual paths need to be checked separately
440     vector<CStdString> paths;
441     if (CMultiPathDirectory::GetPaths(strFile, paths))
442     {
443       for (unsigned int i = 0; i < paths.size(); i++)
444         if (IsRemote(paths[i])) return true;
445     }
446     return false;
447   }
448
449   CURL url(strFile);
450   if(ProtocolHasParentInHostname(url.GetProtocol()))
451     return IsRemote(url.GetHostName());
452
453   if (!url.IsLocal())
454     return true;
455
456   return false;
457 }
458
459 bool URIUtils::IsOnDVD(const CStdString& strFile)
460 {
461 #ifdef TARGET_WINDOWS
462   if (strFile.size() >= 2 && strFile.substr(1,1) == ":")
463     return (GetDriveType(strFile.substr(0, 3).c_str()) == DRIVE_CDROM);
464 #endif
465
466   if (StringUtils::StartsWith(strFile, "dvd:"))
467     return true;
468
469   if (StringUtils::StartsWith(strFile, "udf:"))
470     return true;
471
472   if (StringUtils::StartsWith(strFile, "iso9660:"))
473     return true;
474
475   if (StringUtils::StartsWith(strFile, "cdda:"))
476     return true;
477
478   return false;
479 }
480
481 bool URIUtils::IsOnLAN(const CStdString& strPath)
482 {
483   if(IsMultiPath(strPath))
484     return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
485
486   if(IsStack(strPath))
487     return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
488
489   if(IsSpecial(strPath))
490     return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
491
492   if(IsDAAP(strPath))
493     return true;
494   
495   if(IsPlugin(strPath))
496     return false;
497
498   if(IsTuxBox(strPath))
499     return true;
500
501   if(IsUPnP(strPath))
502     return true;
503
504   CURL url(strPath);
505   if (ProtocolHasParentInHostname(url.GetProtocol()))
506     return IsOnLAN(url.GetHostName());
507
508   if(!IsRemote(strPath))
509     return false;
510
511   CStdString host = url.GetHostName();
512
513   return IsHostOnLAN(host);
514 }
515
516 static bool addr_match(uint32_t addr, const char* target, const char* submask)
517 {
518   uint32_t addr2 = ntohl(inet_addr(target));
519   uint32_t mask = ntohl(inet_addr(submask));
520   return (addr & mask) == (addr2 & mask);
521 }
522
523 bool URIUtils::IsHostOnLAN(const CStdString& host, bool offLineCheck)
524 {
525   if(host.length() == 0)
526     return false;
527
528   // assume a hostname without dot's
529   // is local (smb netbios hostnames)
530   if(host.find('.') == string::npos)
531     return true;
532
533   uint32_t address = ntohl(inet_addr(host.c_str()));
534   if(address == INADDR_NONE)
535   {
536     CStdString ip;
537     if(CDNSNameCache::Lookup(host, ip))
538       address = ntohl(inet_addr(ip.c_str()));
539   }
540
541   if(address != INADDR_NONE)
542   {
543     if (offLineCheck) // check if in private range, ref https://en.wikipedia.org/wiki/Private_network
544     {
545       if (
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")
549         )
550         return true;
551     }
552     // check if we are on the local subnet
553     if (!g_application.getNetwork().GetFirstConnectedInterface())
554       return false;
555
556     if (g_application.getNetwork().HasInterfaceForIP(address))
557       return true;
558   }
559
560   return false;
561 }
562
563 bool URIUtils::IsMultiPath(const CStdString& strPath)
564 {
565   return StringUtils::StartsWithNoCase(strPath, "multipath:");
566 }
567
568 bool URIUtils::IsHD(const CStdString& strFileName)
569 {
570   CURL url(strFileName);
571
572   if (IsSpecial(strFileName))
573     return IsHD(CSpecialProtocol::TranslatePath(strFileName));
574
575   if(IsStack(strFileName))
576     return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
577
578   if (ProtocolHasParentInHostname(url.GetProtocol()))
579     return IsHD(url.GetHostName());
580
581   return url.GetProtocol().empty() || url.GetProtocol() == "file";
582 }
583
584 bool URIUtils::IsDVD(const CStdString& strFile)
585 {
586   CStdString strFileLow = strFile;
587   StringUtils::ToLower(strFileLow);
588   if (strFileLow.find("video_ts.ifo") != std::string::npos && IsOnDVD(strFile))
589     return true;
590
591 #if defined(TARGET_WINDOWS)
592   if (StringUtils::StartsWithNoCase(strFile, "dvd://"))
593     return true;
594
595   if(strFile.size() < 2 || (strFile.substr(1) != ":\\" && strFile.substr(1) != ":"))
596     return false;
597
598   if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
599     return true;
600 #else
601   if (strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
602     return true;
603 #endif
604
605   return false;
606 }
607
608 bool URIUtils::IsStack(const CStdString& strFile)
609 {
610   return StringUtils::StartsWithNoCase(strFile, "stack:");
611 }
612
613 bool URIUtils::IsRAR(const CStdString& strFile)
614 {
615   CStdString strExtension = GetExtension(strFile);
616
617   if (strExtension.Equals(".001") && !StringUtils::EndsWithNoCase(strFile, ".ts.001"))
618     return true;
619
620   if (StringUtils::EqualsNoCase(strExtension, ".cbr"))
621     return true;
622
623   if (StringUtils::EqualsNoCase(strExtension, ".rar"))
624     return true;
625
626   return false;
627 }
628
629 bool URIUtils::IsInArchive(const CStdString &strFile)
630 {
631   return IsInZIP(strFile) || IsInRAR(strFile) || IsInAPK(strFile);
632 }
633
634 bool URIUtils::IsInAPK(const CStdString& strFile)
635 {
636   CURL url(strFile);
637
638   return url.GetProtocol() == "apk" && url.GetFileName() != "";
639 }
640
641 bool URIUtils::IsInZIP(const CStdString& strFile)
642 {
643   CURL url(strFile);
644
645   return url.GetProtocol() == "zip" && url.GetFileName() != "";
646 }
647
648 bool URIUtils::IsInRAR(const CStdString& strFile)
649 {
650   CURL url(strFile);
651
652   return url.GetProtocol() == "rar" && url.GetFileName() != "";
653 }
654
655 bool URIUtils::IsAPK(const CStdString& strFile)
656 {
657   return HasExtension(strFile, ".apk");
658 }
659
660 bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
661 {
662   return HasExtension(strFile, ".zip|.cbz");
663 }
664
665 bool URIUtils::IsArchive(const CStdString& strFile)
666 {
667   return HasExtension(strFile, ".zip|.rar|.apk|.cbz|.cbr");
668 }
669
670 bool URIUtils::IsSpecial(const CStdString& strFile)
671 {
672   CStdString strFile2(strFile);
673
674   if (IsStack(strFile))
675     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
676
677   return StringUtils::StartsWithNoCase(strFile2, "special:");
678 }
679
680 bool URIUtils::IsPlugin(const CStdString& strFile)
681 {
682   CURL url(strFile);
683   return url.GetProtocol().Equals("plugin");
684 }
685
686 bool URIUtils::IsScript(const CStdString& strFile)
687 {
688   CURL url(strFile);
689   return url.GetProtocol().Equals("script");
690 }
691
692 bool URIUtils::IsAddonsPath(const CStdString& strFile)
693 {
694   CURL url(strFile);
695   return url.GetProtocol().Equals("addons");
696 }
697
698 bool URIUtils::IsSourcesPath(const CStdString& strPath)
699 {
700   CURL url(strPath);
701   return url.GetProtocol().Equals("sources");
702 }
703
704 bool URIUtils::IsCDDA(const CStdString& strFile)
705 {
706   return StringUtils::StartsWithNoCase(strFile, "cdda:");
707 }
708
709 bool URIUtils::IsISO9660(const CStdString& strFile)
710 {
711   return StringUtils::StartsWithNoCase(strFile, "iso9660:");
712 }
713
714 bool URIUtils::IsSmb(const CStdString& strFile)
715 {
716   CStdString strFile2(strFile);
717
718   if (IsStack(strFile))
719     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
720
721   return StringUtils::StartsWithNoCase(strFile2, "smb:");
722 }
723
724 bool URIUtils::IsURL(const CStdString& strFile)
725 {
726   return strFile.find("://") != std::string::npos;
727 }
728
729 bool URIUtils::IsFTP(const CStdString& strFile)
730 {
731   CStdString strFile2(strFile);
732
733   if (IsStack(strFile))
734     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
735
736   return StringUtils::StartsWithNoCase(strFile2, "ftp:")  ||
737          StringUtils::StartsWithNoCase(strFile2, "ftps:");
738 }
739
740 bool URIUtils::IsDAV(const CStdString& strFile)
741 {
742   CStdString strFile2(strFile);
743
744   if (IsStack(strFile))
745     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
746
747   return StringUtils::StartsWithNoCase(strFile2, "dav:")  ||
748          StringUtils::StartsWithNoCase(strFile2, "davs:");
749 }
750
751 bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
752 {
753   CStdString strProtocol = url.GetProtocol();
754   
755   if (strProtocol.empty())
756     return false;
757
758   // there's nothing to stop internet streams from being stacked
759   if (strProtocol == "stack")
760     return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
761
762   CStdString strProtocol2 = url.GetTranslatedProtocol();
763
764   // Special case these
765   if (strProtocol  == "ftp"   || strProtocol  == "ftps"   ||
766       strProtocol  == "dav"   || strProtocol  == "davs")
767     return bStrictCheck;
768
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")
777     return true;
778
779   return false;
780 }
781
782 bool URIUtils::IsDAAP(const CStdString& strFile)
783 {
784   return StringUtils::StartsWithNoCase(strFile, "daap:");
785 }
786
787 bool URIUtils::IsUPnP(const CStdString& strFile)
788 {
789   return StringUtils::StartsWithNoCase(strFile, "upnp:");
790 }
791
792 bool URIUtils::IsTuxBox(const CStdString& strFile)
793 {
794   return StringUtils::StartsWithNoCase(strFile, "tuxbox:");
795 }
796
797 bool URIUtils::IsMythTV(const CStdString& strFile)
798 {
799   return StringUtils::StartsWithNoCase(strFile, "myth:");
800 }
801
802 bool URIUtils::IsHDHomeRun(const CStdString& strFile)
803 {
804   return StringUtils::StartsWithNoCase(strFile, "hdhomerun:");
805 }
806
807 bool URIUtils::IsSlingbox(const CStdString& strFile)
808 {
809   return StringUtils::StartsWithNoCase(strFile, "sling:");
810 }
811
812 bool URIUtils::IsVTP(const CStdString& strFile)
813 {
814   return StringUtils::StartsWithNoCase(strFile, "vtp:");
815 }
816
817 bool URIUtils::IsHTSP(const CStdString& strFile)
818 {
819   return StringUtils::StartsWithNoCase(strFile, "htsp:");
820 }
821
822 bool URIUtils::IsLiveTV(const CStdString& strFile)
823 {
824   CStdString strFileWithoutSlash(strFile);
825   RemoveSlashAtEnd(strFileWithoutSlash);
826
827   if(IsTuxBox(strFile)
828   || IsVTP(strFile)
829   || IsHDHomeRun(strFile)
830   || IsSlingbox(strFile)
831   || IsHTSP(strFile)
832   || StringUtils::StartsWithNoCase(strFile, "sap:")
833   ||(StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") && !StringUtils::StartsWithNoCase(strFileWithoutSlash, "pvr://recordings")))
834     return true;
835
836   if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
837     return true;
838
839   return false;
840 }
841
842 bool URIUtils::IsPVRRecording(const CStdString& strFile)
843 {
844   CStdString strFileWithoutSlash(strFile);
845   RemoveSlashAtEnd(strFileWithoutSlash);
846
847   return StringUtils::EndsWithNoCase(strFileWithoutSlash, ".pvr") &&
848          StringUtils::StartsWithNoCase(strFile, "pvr://recordings");
849 }
850
851 bool URIUtils::IsMusicDb(const CStdString& strFile)
852 {
853   return StringUtils::StartsWithNoCase(strFile, "musicdb:");
854 }
855
856 bool URIUtils::IsNfs(const CStdString& strFile)
857 {
858   CStdString strFile2(strFile);
859   
860   if (IsStack(strFile))
861     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
862   
863   return StringUtils::StartsWithNoCase(strFile2, "nfs:");
864 }
865
866 bool URIUtils::IsAfp(const CStdString& strFile)
867 {
868   CStdString strFile2(strFile);
869   
870   if (IsStack(strFile))
871     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
872   
873   return StringUtils::StartsWithNoCase(strFile2, "afp:");
874 }
875
876
877 bool URIUtils::IsVideoDb(const CStdString& strFile)
878 {
879   return StringUtils::StartsWithNoCase(strFile, "videodb:");
880 }
881
882 bool URIUtils::IsBluray(const CStdString& strFile)
883 {
884   return StringUtils::StartsWithNoCase(strFile, "bluray:");
885 }
886
887 bool URIUtils::IsAndroidApp(const CStdString &path)
888 {
889   return StringUtils::StartsWithNoCase(path, "androidapp:");
890 }
891
892 bool URIUtils::IsLibraryFolder(const CStdString& strFile)
893 {
894   CURL url(strFile);
895   return url.GetProtocol().Equals("library");
896 }
897
898 bool URIUtils::IsDOSPath(const CStdString &path)
899 {
900   if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
901     return true;
902
903   // windows network drives
904   if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
905     return true;
906
907   return false;
908 }
909
910 void URIUtils::AddSlashAtEnd(std::string& strFolder)
911 {
912   if (IsURL(strFolder))
913   {
914     CURL url(strFolder);
915     std::string file = url.GetFileName();
916     if(!file.empty() && file != strFolder)
917     {
918       AddSlashAtEnd(file);
919       url.SetFileName(file);
920       strFolder = url.Get();
921     }
922     return;
923   }
924
925   if (!HasSlashAtEnd(strFolder))
926   {
927     if (IsDOSPath(strFolder))
928       strFolder += '\\';
929     else
930       strFolder += '/';
931   }
932 }
933
934 bool URIUtils::HasSlashAtEnd(const std::string& strFile, bool checkURL /* = false */)
935 {
936   if (strFile.empty()) return false;
937   if (checkURL && IsURL(strFile))
938   {
939     CURL url(strFile);
940     CStdString file = url.GetFileName();
941     return file.empty() || HasSlashAtEnd(file, false);
942   }
943   char kar = strFile.c_str()[strFile.size() - 1];
944
945   if (kar == '/' || kar == '\\')
946     return true;
947
948   return false;
949 }
950
951 void URIUtils::RemoveSlashAtEnd(std::string& strFolder)
952 {
953   if (IsURL(strFolder))
954   {
955     CURL url(strFolder);
956     std::string file = url.GetFileName();
957     if (!file.empty() && file != strFolder)
958     {
959       RemoveSlashAtEnd(file);
960       url.SetFileName(file);
961       strFolder = url.Get();
962       return;
963     }
964     if(url.GetHostName().empty())
965       return;
966   }
967
968   while (HasSlashAtEnd(strFolder))
969     strFolder.erase(strFolder.size()-1, 1);
970 }
971
972 bool URIUtils::CompareWithoutSlashAtEnd(const CStdString& strPath1, const CStdString& strPath2)
973 {
974   CStdString strc1 = strPath1, strc2 = strPath2;
975   RemoveSlashAtEnd(strc1);
976   RemoveSlashAtEnd(strc2);
977   return strc1.Equals(strc2);
978 }
979
980
981 std::string URIUtils::FixSlashesAndDups(const std::string& path, const char slashCharacter /* = '/' */, const size_t startFrom /*= 0*/)
982 {
983   const size_t len = path.length();
984   if (startFrom >= len)
985     return path;
986
987   std::string result(path, 0, startFrom);
988   result.reserve(len);
989
990   const char* const str = path.c_str();
991   size_t pos = startFrom;
992   do
993   {
994     if (str[pos] == '\\' || str[pos] == '/')
995     {
996       result.push_back(slashCharacter);  // append one slash
997       pos++;
998       // skip any following slashes
999       while (str[pos] == '\\' || str[pos] == '/') // str is null-terminated, no need to check for buffer overrun
1000         pos++;
1001     }
1002     else
1003       result.push_back(str[pos++]);   // append current char and advance pos to next char
1004
1005   } while (pos < len);
1006
1007   return result;
1008 }
1009
1010
1011 CStdString URIUtils::AddFileToFolder(const CStdString& strFolder, 
1012                                 const CStdString& strFile)
1013 {
1014   if (IsURL(strFolder))
1015   {
1016     CURL url(strFolder);
1017     if (url.GetFileName() != strFolder)
1018     {
1019       url.SetFileName(AddFileToFolder(url.GetFileName(), strFile));
1020       return url.Get();
1021     }
1022   }
1023
1024   CStdString strResult = strFolder;
1025   if (!strResult.empty())
1026     AddSlashAtEnd(strResult);
1027
1028   // Remove any slash at the start of the file
1029   if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
1030     strResult += strFile.substr(1);
1031   else
1032     strResult += strFile;
1033
1034   // correct any slash directions
1035   if (!IsDOSPath(strFolder))
1036     StringUtils::Replace(strResult, '\\', '/');
1037   else
1038     StringUtils::Replace(strResult, '/', '\\');
1039
1040   return strResult;
1041 }
1042
1043 CStdString URIUtils::GetDirectory(const CStdString &strFilePath)
1044 {
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.
1047
1048   size_t iPosSlash = strFilePath.find_last_of("/\\");
1049   if (iPosSlash == string::npos)
1050     return ""; // No slash, so no path (ignore any options)
1051
1052   size_t iPosBar = strFilePath.rfind('|');
1053   if (iPosBar == string::npos)
1054     return strFilePath.substr(0, iPosSlash + 1); // Only path
1055
1056   return strFilePath.substr(0, iPosSlash + 1) + strFilePath.substr(iPosBar); // Path + options
1057 }
1058
1059 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
1060                                  const CStdString& strType,
1061                                  const CStdString& strArchivePath,
1062                                  const CStdString& strFilePathInArchive,
1063                                  const CStdString& strPwd)
1064 {
1065   CStdString strBuffer;
1066
1067   strUrlPath = strType+"://";
1068
1069   if( !strPwd.empty() )
1070   {
1071     strBuffer = strPwd;
1072     CURL::Encode(strBuffer);
1073     strUrlPath += strBuffer;
1074     strUrlPath += "@";
1075   }
1076
1077   strBuffer = strArchivePath;
1078   CURL::Encode(strBuffer);
1079
1080   strUrlPath += strBuffer;
1081
1082   strBuffer = strFilePathInArchive;
1083   StringUtils::Replace(strBuffer, '\\', '/');
1084   StringUtils::TrimLeft(strBuffer, "/");
1085
1086   strUrlPath += "/";
1087   strUrlPath += strBuffer;
1088
1089 #if 0 // options are not used
1090   strBuffer = strCachePath;
1091   CURL::Encode(strBuffer);
1092
1093   strUrlPath += "?cache=";
1094   strUrlPath += strBuffer;
1095
1096   strBuffer = StringUtils::Format("%i", wOptions);
1097   strUrlPath += "&flags=";
1098   strUrlPath += strBuffer;
1099 #endif
1100 }
1101
1102 string URIUtils::GetRealPath(const string &path)
1103 {
1104   if (path.empty())
1105     return path;
1106
1107   CURL url(path);
1108   url.SetHostName(GetRealPath(url.GetHostName()));
1109   url.SetFileName(resolvePath(url.GetFileName()));
1110   
1111   return url.Get();
1112 }
1113
1114 std::string URIUtils::resolvePath(const std::string &path)
1115 {
1116   if (path.empty())
1117     return path;
1118
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;
1124
1125   for (vector<string>::const_iterator part = parts.begin(); part != parts.end(); part++)
1126   {
1127     if (part->empty() || part->compare(".") == 0)
1128       continue;
1129
1130     // go one level back up
1131     if (part->compare("..") == 0)
1132     {
1133       if (!realParts.empty())
1134         realParts.pop_back();
1135       continue;
1136     }
1137
1138     realParts.push_back(*part);
1139   }
1140
1141   CStdString realPath;
1142   int i = 0;
1143   // re-add any / or \ at the beginning
1144   while (path.at(i) == delim.at(0))
1145   {
1146     realPath += delim;
1147     i++;
1148   }
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))
1153     realPath += delim;
1154
1155   return realPath;
1156 }
1157
1158 bool URIUtils::UpdateUrlEncoding(std::string &strFilename)
1159 {
1160   if (strFilename.empty())
1161     return false;
1162   
1163   CURL url(strFilename);
1164   // if this is a stack:// URL we need to work with its filename
1165   if (URIUtils::IsStack(strFilename))
1166   {
1167     vector<CStdString> files;
1168     if (!CStackDirectory::GetPaths(strFilename, files))
1169       return false;
1170
1171     for (vector<CStdString>::iterator file = files.begin(); file != files.end(); file++)
1172     {
1173       std::string filePath = *file;
1174       UpdateUrlEncoding(filePath);
1175       *file = filePath;
1176     }
1177
1178     CStdString stackPath;
1179     if (!CStackDirectory::ConstructStackPath(files, stackPath))
1180       return false;
1181
1182     url.Parse(stackPath);
1183   }
1184   // if the protocol has an encoded hostname we need to work with its hostname
1185   else if (URIUtils::ProtocolHasEncodedHostname(url.GetProtocol()))
1186   {
1187     std::string hostname = url.GetHostName();
1188     UpdateUrlEncoding(hostname);
1189     url.SetHostName(hostname);
1190   }
1191   else
1192     return false;
1193
1194   std::string newFilename = url.Get();
1195   if (newFilename == strFilename)
1196     return false;
1197   
1198   strFilename = newFilename;
1199   return true;
1200 }