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