only call IPlayerCallback::OnPlayBackSpeedChanged if the speed has actually changed
[vuplus_xbmc] / xbmc / utils / URIUtils.cpp
1 /*
2  *      Copyright (C) 2005-2010 Team XBMC
3  *      http://www.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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
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/Settings.h"
31 #include "settings/AdvancedSettings.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 CStdString URIUtils::GetParentFolderURI(const CStdString& uri, bool preserveFileNameInPath)
42 {
43   if (preserveFileNameInPath)
44     return AddFileToFolder(GetParentPath(uri), GetFileName(uri));
45   else
46     return GetParentPath(uri);
47 }
48
49 bool URIUtils::IsInPath(const CStdString &uri, const CStdString &baseURI)
50 {
51   CStdString uriPath = CSpecialProtocol::TranslatePath(uri);
52   CStdString basePath = CSpecialProtocol::TranslatePath(baseURI);
53   return (strncmp(uriPath.c_str(), basePath.c_str(), basePath.GetLength()) == 0);
54 }
55
56 /* returns filename extension including period of filename */
57 const CStdString URIUtils::GetExtension(const CStdString& strFileName)
58 {
59   if(IsURL(strFileName))
60   {
61     CURL url(strFileName);
62     return GetExtension(url.GetFileName());
63   }
64
65   int period = strFileName.find_last_of('.');
66   if(period >= 0)
67   {
68     if( strFileName.find_first_of('/', period+1) != string::npos ) return "";
69     if( strFileName.find_first_of('\\', period+1) != string::npos ) return "";
70     return strFileName.substr(period);
71   }
72   else
73     return "";
74 }
75
76 void URIUtils::GetExtension(const CStdString& strFile, CStdString& strExtension)
77 {
78   strExtension = GetExtension(strFile);
79 }
80
81 void URIUtils::RemoveExtension(CStdString& strFileName)
82 {
83   if(IsURL(strFileName))
84   {
85     CURL url(strFileName);
86     strFileName = url.GetFileName();
87     RemoveExtension(strFileName);
88     url.SetFileName(strFileName);
89     strFileName = url.Get();
90     return;
91   }
92
93   int iPos = strFileName.ReverseFind(".");
94   // Extension found
95   if (iPos > 0)
96   {
97     CStdString strExtension;
98     GetExtension(strFileName, strExtension);
99     strExtension.ToLower();
100     strExtension += "|";
101
102     CStdString strFileMask;
103     strFileMask = g_settings.m_pictureExtensions;
104     strFileMask += "|" + g_settings.m_musicExtensions;
105     strFileMask += "|" + g_settings.m_videoExtensions;
106 #if defined(__APPLE__)
107     strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg|.app|.applescript|.workflow";
108 #else
109     strFileMask += "|.py|.xml|.milk|.xpr|.xbt|.cdg";
110 #endif
111     strFileMask += "|";
112
113     if (strFileMask.Find(strExtension) >= 0)
114       strFileName = strFileName.Left(iPos);
115   }
116 }
117
118 CStdString URIUtils::ReplaceExtension(const CStdString& strFile,
119                                       const CStdString& strNewExtension)
120 {
121   if(IsURL(strFile))
122   {
123     CURL url(strFile);
124     url.SetFileName(ReplaceExtension(url.GetFileName(), strNewExtension));
125     return url.Get();
126   }
127
128   CStdString strChangedFile;
129   CStdString strExtension;
130   GetExtension(strFile, strExtension);
131   if ( strExtension.size() )
132   {
133     strChangedFile = strFile.substr(0, strFile.size() - strExtension.size()) ;
134     strChangedFile += strNewExtension;
135   }
136   else
137   {
138     strChangedFile = strFile;
139     strChangedFile += strNewExtension;
140   }
141   return strChangedFile;
142 }
143
144 /* returns a filename given an url */
145 /* handles both / and \, and options in urls*/
146 const CStdString URIUtils::GetFileName(const CStdString& strFileNameAndPath)
147 {
148   if(IsURL(strFileNameAndPath))
149   {
150     CURL url(strFileNameAndPath);
151     return GetFileName(url.GetFileName());
152   }
153
154   /* find any slashes */
155   const int slash1 = strFileNameAndPath.find_last_of('/');
156   const int slash2 = strFileNameAndPath.find_last_of('\\');
157
158   /* select the last one */
159   int slash;
160   if(slash2>slash1)
161     slash = slash2;
162   else
163     slash = slash1;
164
165   return strFileNameAndPath.substr(slash+1);
166 }
167
168 void URIUtils::Split(const CStdString& strFileNameAndPath,
169                      CStdString& strPath, CStdString& strFileName)
170 {
171   //Splits a full filename in path and file.
172   //ex. smb://computer/share/directory/filename.ext -> strPath:smb://computer/share/directory/ and strFileName:filename.ext
173   //Trailing slash will be preserved
174   strFileName = "";
175   strPath = "";
176   int i = strFileNameAndPath.size() - 1;
177   while (i > 0)
178   {
179     char ch = strFileNameAndPath[i];
180     // Only break on ':' if it's a drive separator for DOS (ie d:foo)
181     if (ch == '/' || ch == '\\' || (ch == ':' && i == 1)) break;
182     else i--;
183   }
184   if (i == 0)
185     i--;
186
187   strPath = strFileNameAndPath.Left(i + 1);
188   strFileName = strFileNameAndPath.Right(strFileNameAndPath.size() - i - 1);
189 }
190
191 CStdStringArray URIUtils::SplitPath(const CStdString& strPath)
192 {
193   CURL url(strPath);
194
195   // silly CStdString can't take a char in the constructor
196   CStdString sep(1, url.GetDirectorySeparator());
197
198   // split the filename portion of the URL up into separate dirs
199   CStdStringArray dirs;
200   StringUtils::SplitString(url.GetFileName(), sep, dirs);
201   
202   // we start with the root path
203   CStdString dir = url.GetWithoutFilename();
204   
205   if (!dir.IsEmpty())
206     dirs.insert(dirs.begin(), dir);
207
208   // we don't need empty token on the end
209   if (dirs.size() > 1 && dirs.back().IsEmpty())
210     dirs.erase(dirs.end() - 1);
211
212   return dirs;
213 }
214
215 void URIUtils::GetCommonPath(CStdString& strParent, const CStdString& strPath)
216 {
217   // find the common path of parent and path
218   unsigned int j = 1;
219   while (j <= min(strParent.size(), strPath.size()) && strnicmp(strParent.c_str(), strPath.c_str(), j) == 0)
220     j++;
221   strParent = strParent.Left(j - 1);
222   // they should at least share a / at the end, though for things such as path/cd1 and path/cd2 there won't be
223   if (!HasSlashAtEnd(strParent))
224   {
225     // currently GetDirectory() removes trailing slashes
226     GetDirectory(strParent.Mid(0), strParent);
227     AddSlashAtEnd(strParent);
228   }
229 }
230
231 CStdString URIUtils::GetParentPath(const CStdString& strPath)
232 {
233   CStdString strReturn;
234   GetParentPath(strPath, strReturn);
235   return strReturn;
236 }
237
238 bool URIUtils::GetParentPath(const CStdString& strPath, CStdString& strParent)
239 {
240   strParent = "";
241
242   CURL url(strPath);
243   CStdString strFile = url.GetFileName();
244   if ( ((url.GetProtocol() == "rar") || (url.GetProtocol() == "zip")) && strFile.IsEmpty())
245   {
246     strFile = url.GetHostName();
247     return GetParentPath(strFile, strParent);
248   }
249   else if (url.GetProtocol() == "stack")
250   {
251     CStackDirectory dir;
252     CFileItemList items;
253     dir.GetDirectory(strPath,items);
254     GetDirectory(items[0]->GetPath(),items[0]->m_strDVDLabel);
255     if (items[0]->m_strDVDLabel.Mid(0,6).Equals("rar://") || items[0]->m_strDVDLabel.Mid(0,6).Equals("zip://"))
256       GetParentPath(items[0]->m_strDVDLabel, strParent);
257     else
258       strParent = items[0]->m_strDVDLabel;
259     for( int i=1;i<items.Size();++i)
260     {
261       GetDirectory(items[i]->GetPath(),items[i]->m_strDVDLabel);
262       if (items[0]->m_strDVDLabel.Mid(0,6).Equals("rar://") || items[0]->m_strDVDLabel.Mid(0,6).Equals("zip://"))
263         items[i]->SetPath(GetParentPath(items[i]->m_strDVDLabel));
264       else
265         items[i]->SetPath(items[i]->m_strDVDLabel);
266
267       GetCommonPath(strParent,items[i]->GetPath());
268     }
269     return true;
270   }
271   else if (url.GetProtocol() == "multipath")
272   {
273     // get the parent path of the first item
274     return GetParentPath(CMultiPathDirectory::GetFirstPath(strPath), strParent);
275   }
276   else if (url.GetProtocol() == "plugin")
277   {
278     if (!url.GetOptions().IsEmpty())
279     {
280       url.SetOptions("");
281       strParent = url.Get();
282       return true;
283     }
284     if (!url.GetFileName().IsEmpty())
285     {
286       url.SetFileName("");
287       strParent = url.Get();
288       return true;
289     }
290     if (!url.GetHostName().IsEmpty())
291     {
292       url.SetHostName("");
293       strParent = url.Get();
294       return true;
295     }
296     return true;  // already at root
297   }
298   else if (url.GetProtocol() == "special")
299   {
300     if (HasSlashAtEnd(strFile) )
301       strFile = strFile.Left(strFile.size() - 1);
302     if(strFile.ReverseFind('/') < 0)
303       return false;
304   }
305   else if (strFile.size() == 0)
306   {
307     if (url.GetHostName().size() > 0)
308     {
309       // we have an share with only server or workgroup name
310       // set hostname to "" and return true to get back to root
311       url.SetHostName("");
312       strParent = url.Get();
313       return true;
314     }
315     return false;
316   }
317
318   if (HasSlashAtEnd(strFile) )
319   {
320     strFile = strFile.Left(strFile.size() - 1);
321   }
322
323   int iPos = strFile.ReverseFind('/');
324 #ifndef _LINUX
325   if (iPos < 0)
326   {
327     iPos = strFile.ReverseFind('\\');
328   }
329 #endif
330   if (iPos < 0)
331   {
332     url.SetFileName("");
333     strParent = url.Get();
334     return true;
335   }
336
337   strFile = strFile.Left(iPos);
338
339   AddSlashAtEnd(strFile);
340
341   url.SetFileName(strFile);
342   strParent = url.Get();
343   return true;
344 }
345
346 CStdString URIUtils::SubstitutePath(const CStdString& strPath)
347 {
348   for (CAdvancedSettings::StringMapping::iterator i = g_advancedSettings.m_pathSubstitutions.begin();
349       i != g_advancedSettings.m_pathSubstitutions.end(); i++)
350   {
351     if (strncmp(strPath.c_str(), i->first.c_str(), i->first.size()) == 0)
352       return URIUtils::AddFileToFolder(i->second, strPath.Mid(i->first.size()));
353   }
354   return strPath;
355 }
356
357 bool URIUtils::IsRemote(const CStdString& strFile)
358 {
359   if (IsCDDA(strFile) || IsISO9660(strFile))
360     return false;
361
362   if (IsSpecial(strFile))
363     return IsRemote(CSpecialProtocol::TranslatePath(strFile));
364
365   if(IsStack(strFile))
366     return IsRemote(CStackDirectory::GetFirstStackedFile(strFile));
367
368   if(IsMultiPath(strFile))
369   { // virtual paths need to be checked separately
370     vector<CStdString> paths;
371     if (CMultiPathDirectory::GetPaths(strFile, paths))
372     {
373       for (unsigned int i = 0; i < paths.size(); i++)
374         if (IsRemote(paths[i])) return true;
375     }
376     return false;
377   }
378
379   CURL url(strFile);
380   if(IsInArchive(strFile))
381     return IsRemote(url.GetHostName());
382
383   if (!url.IsLocal())
384     return true;
385
386   return false;
387 }
388
389 bool URIUtils::IsOnDVD(const CStdString& strFile)
390 {
391 #ifdef _WIN32
392   if (strFile.Mid(1,1) == ":")
393     return (GetDriveType(strFile.Left(2)) == DRIVE_CDROM);
394 #else
395   if (strFile.Left(2).CompareNoCase("d:") == 0)
396     return true;
397 #endif
398
399   if (strFile.Left(4).CompareNoCase("dvd:") == 0)
400     return true;
401
402   if (strFile.Left(4).CompareNoCase("udf:") == 0)
403     return true;
404
405   if (strFile.Left(8).CompareNoCase("iso9660:") == 0)
406     return true;
407
408   if (strFile.Left(5).CompareNoCase("cdda:") == 0)
409     return true;
410
411   return false;
412 }
413
414 bool URIUtils::IsOnLAN(const CStdString& strPath)
415 {
416   if(IsMultiPath(strPath))
417     return IsOnLAN(CMultiPathDirectory::GetFirstPath(strPath));
418
419   if(IsStack(strPath))
420     return IsOnLAN(CStackDirectory::GetFirstStackedFile(strPath));
421
422   if(IsSpecial(strPath))
423     return IsOnLAN(CSpecialProtocol::TranslatePath(strPath));
424
425   if(IsDAAP(strPath))
426     return true;
427   
428   if(IsPlugin(strPath))
429     return false;
430
431   if(IsTuxBox(strPath))
432     return true;
433
434   if(IsUPnP(strPath))
435     return true;
436
437   CURL url(strPath);
438   if(IsInArchive(strPath))
439     return IsOnLAN(url.GetHostName());
440
441   if(!IsRemote(strPath))
442     return false;
443
444   CStdString host = url.GetHostName();
445   if(host.length() == 0)
446     return false;
447
448   // assume a hostname without dot's
449   // is local (smb netbios hostnames)
450   if(host.find('.') == string::npos)
451     return true;
452
453   unsigned long address = ntohl(inet_addr(host.c_str()));
454   if(address == INADDR_NONE)
455   {
456     CStdString ip;
457     if(CDNSNameCache::Lookup(host, ip))
458       address = ntohl(inet_addr(ip.c_str()));
459   }
460
461   if(address != INADDR_NONE)
462   {
463     // check if we are on the local subnet
464     if (!g_application.getNetwork().GetFirstConnectedInterface())
465       return false;
466
467     if (g_application.getNetwork().HasInterfaceForIP(address))
468       return true;
469   }
470
471   return false;
472 }
473
474 bool URIUtils::IsMultiPath(const CStdString& strPath)
475 {
476   return strPath.Left(10).Equals("multipath:");
477 }
478
479 bool URIUtils::IsHD(const CStdString& strFileName)
480 {
481   CURL url(strFileName);
482
483   if (IsSpecial(strFileName))
484     return IsHD(CSpecialProtocol::TranslatePath(strFileName));
485
486   if(IsStack(strFileName))
487     return IsHD(CStackDirectory::GetFirstStackedFile(strFileName));
488
489   if (IsInArchive(strFileName))
490     return IsHD(url.GetHostName());
491
492   return url.IsLocal();
493 }
494
495 bool URIUtils::IsDVD(const CStdString& strFile)
496 {
497 #if defined(_WIN32)
498   if (strFile.Left(6).Equals("dvd://"))
499     return true;
500
501   if(strFile.Mid(1) != ":\\"
502   && strFile.Mid(1) != ":")
503     return false;
504
505   if(GetDriveType(strFile.c_str()) == DRIVE_CDROM)
506     return true;
507 #else
508   CStdString strFileLow = strFile;
509   strFileLow.MakeLower();
510   if (strFileLow == "d:/"  || strFileLow == "d:\\"  || strFileLow == "d:" || strFileLow == "iso9660://" || strFileLow == "udf://" || strFileLow == "dvd://1" )
511     return true;
512 #endif
513
514   return false;
515 }
516
517 bool URIUtils::IsStack(const CStdString& strFile)
518 {
519   return strFile.Left(6).Equals("stack:");
520 }
521
522 bool URIUtils::IsRAR(const CStdString& strFile)
523 {
524   CStdString strExtension;
525   GetExtension(strFile,strExtension);
526
527   if (strExtension.Equals(".001") && strFile.Mid(strFile.length()-7,7).CompareNoCase(".ts.001"))
528     return true;
529
530   if (strExtension.CompareNoCase(".cbr") == 0)
531     return true;
532
533   if (strExtension.CompareNoCase(".rar") == 0)
534     return true;
535
536   return false;
537 }
538
539 bool URIUtils::IsInArchive(const CStdString &strFile)
540 {
541   return IsInZIP(strFile) || IsInRAR(strFile);
542 }
543
544 bool URIUtils::IsInZIP(const CStdString& strFile)
545 {
546   CURL url(strFile);
547
548   return url.GetProtocol() == "zip" && url.GetFileName() != "";
549 }
550
551 bool URIUtils::IsInRAR(const CStdString& strFile)
552 {
553   CURL url(strFile);
554
555   return url.GetProtocol() == "rar" && url.GetFileName() != "";
556 }
557
558 bool URIUtils::IsZIP(const CStdString& strFile) // also checks for comic books!
559 {
560   CStdString strExtension;
561   GetExtension(strFile,strExtension);
562
563   if (strExtension.CompareNoCase(".zip") == 0)
564     return true;
565
566   if (strExtension.CompareNoCase(".cbz") == 0)
567     return true;
568
569   return false;
570 }
571
572 bool URIUtils::IsSpecial(const CStdString& strFile)
573 {
574   CStdString strFile2(strFile);
575
576   if (IsStack(strFile))
577     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
578
579   return strFile2.Left(8).Equals("special:");
580 }
581
582 bool URIUtils::IsPlugin(const CStdString& strFile)
583 {
584   CURL url(strFile);
585   return url.GetProtocol().Equals("plugin");
586 }
587
588 bool URIUtils::IsScript(const CStdString& strFile)
589 {
590   CURL url(strFile);
591   return url.GetProtocol().Equals("script");
592 }
593
594 bool URIUtils::IsAddonsPath(const CStdString& strFile)
595 {
596   CURL url(strFile);
597   return url.GetProtocol().Equals("addons");
598 }
599
600 bool URIUtils::IsSourcesPath(const CStdString& strPath)
601 {
602   CURL url(strPath);
603   return url.GetProtocol().Equals("sources");
604 }
605
606 bool URIUtils::IsCDDA(const CStdString& strFile)
607 {
608   return strFile.Left(5).Equals("cdda:");
609 }
610
611 bool URIUtils::IsISO9660(const CStdString& strFile)
612 {
613   return strFile.Left(8).Equals("iso9660:");
614 }
615
616 bool URIUtils::IsSmb(const CStdString& strFile)
617 {
618   CStdString strFile2(strFile);
619
620   if (IsStack(strFile))
621     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
622
623   return strFile2.Left(4).Equals("smb:");
624 }
625
626 bool URIUtils::IsURL(const CStdString& strFile)
627 {
628   return strFile.Find("://") >= 0;
629 }
630
631 bool URIUtils::IsFTP(const CStdString& strFile)
632 {
633   CStdString strFile2(strFile);
634
635   if (IsStack(strFile))
636     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
637
638   CURL url(strFile2);
639
640   return url.GetTranslatedProtocol() == "ftp"  ||
641          url.GetTranslatedProtocol() == "ftps";
642 }
643
644 bool URIUtils::IsInternetStream(const CURL& url, bool bStrictCheck /* = false */)
645 {
646   
647   CStdString strProtocol = url.GetProtocol();
648   
649   if (strProtocol.IsEmpty())
650     return false;
651
652   // there's nothing to stop internet streams from being stacked
653   if (strProtocol == "stack")
654     return IsInternetStream(CStackDirectory::GetFirstStackedFile(url.Get()));
655
656   CStdString strProtocol2 = url.GetTranslatedProtocol();
657
658   // Special case these
659   if (strProtocol2 == "ftp" || strProtocol2 == "ftps" ||
660       strProtocol  == "dav" || strProtocol  == "davs")
661     return bStrictCheck;
662
663   if (strProtocol2 == "http" || strProtocol2 == "https" ||
664       strProtocol  == "rtp"  || strProtocol  == "udp"   ||
665       strProtocol  == "rtmp" || strProtocol  == "rtsp")
666     return true;
667
668   return false;
669 }
670
671 bool URIUtils::IsDAAP(const CStdString& strFile)
672 {
673   return strFile.Left(5).Equals("daap:");
674 }
675
676 bool URIUtils::IsUPnP(const CStdString& strFile)
677 {
678   return strFile.Left(5).Equals("upnp:");
679 }
680
681 bool URIUtils::IsTuxBox(const CStdString& strFile)
682 {
683   return strFile.Left(7).Equals("tuxbox:");
684 }
685
686 bool URIUtils::IsMythTV(const CStdString& strFile)
687 {
688   return strFile.Left(5).Equals("myth:");
689 }
690
691 bool URIUtils::IsHDHomeRun(const CStdString& strFile)
692 {
693   return strFile.Left(10).Equals("hdhomerun:");
694 }
695
696 bool URIUtils::IsSlingbox(const CStdString& strFile)
697 {
698   return strFile.Left(6).Equals("sling:");
699 }
700
701 bool URIUtils::IsVTP(const CStdString& strFile)
702 {
703   return strFile.Left(4).Equals("vtp:");
704 }
705
706 bool URIUtils::IsHTSP(const CStdString& strFile)
707 {
708   return strFile.Left(5).Equals("htsp:");
709 }
710
711 bool URIUtils::IsLiveTV(const CStdString& strFile)
712 {
713   if(IsTuxBox(strFile)
714   || IsVTP(strFile)
715   || IsHDHomeRun(strFile)
716   || IsSlingbox(strFile)
717   || IsHTSP(strFile)
718   || strFile.Left(4).Equals("sap:"))
719     return true;
720
721   if (IsMythTV(strFile) && CMythDirectory::IsLiveTV(strFile))
722     return true;
723
724   return false;
725 }
726
727 bool URIUtils::IsMusicDb(const CStdString& strFile)
728 {
729   return strFile.Left(8).Equals("musicdb:");
730 }
731
732 bool URIUtils::IsNfs(const CStdString& strFile)
733 {
734   CStdString strFile2(strFile);
735   
736   if (IsStack(strFile))
737     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
738   
739   return strFile2.Left(4).Equals("nfs:");
740 }
741
742 bool URIUtils::IsAfp(const CStdString& strFile)
743 {
744   CStdString strFile2(strFile);
745   
746   if (IsStack(strFile))
747     strFile2 = CStackDirectory::GetFirstStackedFile(strFile);
748   
749   return strFile2.Left(4).Equals("afp:");
750 }
751
752
753 bool URIUtils::IsVideoDb(const CStdString& strFile)
754 {
755   return strFile.Left(8).Equals("videodb:");
756 }
757
758 bool URIUtils::IsLastFM(const CStdString& strFile)
759 {
760   return strFile.Left(7).Equals("lastfm:");
761 }
762
763 bool URIUtils::IsDOSPath(const CStdString &path)
764 {
765   if (path.size() > 1 && path[1] == ':' && isalpha(path[0]))
766     return true;
767
768   // windows network drives
769   if (path.size() > 1 && path[0] == '\\' && path[1] == '\\')
770     return true;
771
772   return false;
773 }
774
775 void URIUtils::AddSlashAtEnd(CStdString& strFolder)
776 {
777   if (IsURL(strFolder))
778   {
779     CURL url(strFolder);
780     CStdString file = url.GetFileName();
781     if(!file.IsEmpty() && file != strFolder)
782     {
783       AddSlashAtEnd(file);
784       url.SetFileName(file);
785       strFolder = url.Get();
786     }
787     return;
788   }
789
790   if (!HasSlashAtEnd(strFolder))
791   {
792     if (IsDOSPath(strFolder))
793       strFolder += '\\';
794     else
795       strFolder += '/';
796   }
797 }
798
799 bool URIUtils::HasSlashAtEnd(const CStdString& strFile)
800 {
801   if (strFile.size() == 0) return false;
802   char kar = strFile.c_str()[strFile.size() - 1];
803
804   if (kar == '/' || kar == '\\')
805     return true;
806
807   return false;
808 }
809
810 void URIUtils::RemoveSlashAtEnd(CStdString& strFolder)
811 {
812   if (IsURL(strFolder))
813   {
814     CURL url(strFolder);
815     CStdString file = url.GetFileName();
816     if (!file.IsEmpty() && file != strFolder)
817     {
818       RemoveSlashAtEnd(file);
819       url.SetFileName(file);
820       strFolder = url.Get();
821       return;
822     }
823     if(url.GetHostName().IsEmpty())
824       return;
825   }
826
827   while (HasSlashAtEnd(strFolder))
828     strFolder.Delete(strFolder.size() - 1);
829 }
830
831 void URIUtils::AddFileToFolder(const CStdString& strFolder, 
832                                 const CStdString& strFile,
833                                 CStdString& strResult)
834 {
835   if (IsURL(strFolder))
836   {
837     CURL url(strFolder);
838     if (url.GetFileName() != strFolder)
839     {
840       AddFileToFolder(url.GetFileName(), strFile, strResult);
841       url.SetFileName(strResult);
842       strResult = url.Get();
843       return;
844     }
845   }
846
847   strResult = strFolder;
848   if(!strResult.IsEmpty())
849     AddSlashAtEnd(strResult);
850
851   // Remove any slash at the start of the file
852   if (strFile.size() && (strFile[0] == '/' || strFile[0] == '\\'))
853     strResult += strFile.Mid(1);
854   else
855     strResult += strFile;
856
857   // correct any slash directions
858   if (!IsDOSPath(strFolder))
859     strResult.Replace('\\', '/');
860   else
861     strResult.Replace('/', '\\');
862 }
863
864 void URIUtils::GetDirectory(const CStdString& strFilePath,
865                             CStdString& strDirectoryPath)
866 {
867   // Will from a full filename return the directory the file resides in.
868   // Keeps the final slash at end
869
870   int iPos1 = strFilePath.ReverseFind('/');
871   int iPos2 = strFilePath.ReverseFind('\\');
872
873   if (iPos2 > iPos1)
874   {
875     iPos1 = iPos2;
876   }
877
878   if (iPos1 > 0)
879   {
880     strDirectoryPath = strFilePath.Left(iPos1 + 1); // include the slash
881
882     // Keep possible |option=foo options for certain paths
883     iPos2 = strFilePath.ReverseFind('|');
884     if (iPos2 > 0)
885     {
886       strDirectoryPath += strFilePath.Mid(iPos2);
887     }
888
889   }
890 }
891
892 void URIUtils::CreateArchivePath(CStdString& strUrlPath,
893                                  const CStdString& strType,
894                                  const CStdString& strArchivePath,
895                                  const CStdString& strFilePathInArchive,
896                                  const CStdString& strPwd)
897 {
898   CStdString strBuffer;
899
900   strUrlPath = strType+"://";
901
902   if( !strPwd.IsEmpty() )
903   {
904     strBuffer = strPwd;
905     CURL::Encode(strBuffer);
906     strUrlPath += strBuffer;
907     strUrlPath += "@";
908   }
909
910   strBuffer = strArchivePath;
911   CURL::Encode(strBuffer);
912
913   strUrlPath += strBuffer;
914
915   strBuffer = strFilePathInArchive;
916   strBuffer.Replace('\\', '/');
917   strBuffer.TrimLeft('/');
918
919   strUrlPath += "/";
920   strUrlPath += strBuffer;
921
922 #if 0 // options are not used
923   strBuffer = strCachePath;
924   CURL::Encode(strBuffer);
925
926   strUrlPath += "?cache=";
927   strUrlPath += strBuffer;
928
929   strBuffer.Format("%i", wOptions);
930   strUrlPath += "&flags=";
931   strUrlPath += strBuffer;
932 #endif
933 }