[cstdstring] demise Format, replacing with StringUtils::Format
[vuplus_xbmc] / xbmc / URL.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 "URL.h"
22 #include "utils/RegExp.h"
23 #include "utils/log.h"
24 #include "utils/URIUtils.h"
25 #include "utils/StringUtils.h"
26 #include "Util.h"
27 #include "filesystem/File.h"
28 #include "FileItem.h"
29 #include "filesystem/StackDirectory.h"
30 #include "addons/Addon.h"
31 #include "utils/StringUtils.h"
32 #ifndef TARGET_POSIX
33 #include <sys\types.h>
34 #include <sys\stat.h>
35 #endif
36
37 using namespace std;
38 using namespace ADDON;
39
40 CStdString URLEncodeInline(const CStdString& strData)
41 {
42   CStdString buffer = strData;
43   CURL::Encode(buffer);
44   return buffer;
45 }
46
47 CURL::CURL(const CStdString& strURL1)
48 {
49   Parse(strURL1);
50 }
51
52 CURL::CURL()
53 {
54   m_iPort = 0;
55 }
56
57 CURL::~CURL()
58 {
59 }
60
61 void CURL::Reset()
62 {
63   m_strHostName.clear();
64   m_strDomain.clear();
65   m_strUserName.clear();
66   m_strPassword.clear();
67   m_strShareName.clear();
68   m_strFileName.clear();
69   m_strProtocol.clear();
70   m_strFileType.clear();
71   m_strOptions.clear();
72   m_strProtocolOptions.clear();
73   m_options.Clear();
74   m_protocolOptions.Clear();
75   m_iPort = 0;
76 }
77
78 void CURL::Parse(const CStdString& strURL1)
79 {
80   Reset();
81   // start by validating the path
82   CStdString strURL = CUtil::ValidatePath(strURL1);
83
84   // strURL can be one of the following:
85   // format 1: protocol://[username:password]@hostname[:port]/directoryandfile
86   // format 2: protocol://file
87   // format 3: drive:directoryandfile
88   //
89   // first need 2 check if this is a protocol or just a normal drive & path
90   if (!strURL.size()) return ;
91   if (strURL.Equals("?", true)) return;
92
93   // form is format 1 or 2
94   // format 1: protocol://[domain;][username:password]@hostname[:port]/directoryandfile
95   // format 2: protocol://file
96
97   // decode protocol
98   int iPos = strURL.Find("://");
99   if (iPos < 0)
100   {
101     // This is an ugly hack that needs some work.
102     // example: filename /foo/bar.zip/alice.rar/bob.avi
103     // This should turn into zip://rar:///foo/bar.zip/alice.rar/bob.avi
104     iPos = 0;
105     bool is_apk = (strURL.Find(".apk/", iPos) > 0);
106     while (1)
107     {
108       if (is_apk)
109         iPos = strURL.Find(".apk/", iPos);
110       else
111         iPos = strURL.Find(".zip/", iPos);
112
113       int extLen = 3;
114       if (iPos < 0)
115       {
116         /* set filename and update extension*/
117         SetFileName(strURL);
118         return ;
119       }
120       iPos += extLen + 1;
121       CStdString archiveName = strURL.Left(iPos);
122       struct __stat64 s;
123       if (XFILE::CFile::Stat(archiveName, &s) == 0)
124       {
125 #ifdef TARGET_POSIX
126         if (!S_ISDIR(s.st_mode))
127 #else
128         if (!(s.st_mode & S_IFDIR))
129 #endif
130         {
131           Encode(archiveName);
132           if (is_apk)
133           {
134             CURL c((CStdString)"apk" + "://" + archiveName + '/' + strURL.Right(strURL.size() - iPos - 1));
135             *this = c;
136           }
137           else
138           {
139             CURL c((CStdString)"zip" + "://" + archiveName + '/' + strURL.Right(strURL.size() - iPos - 1));
140             *this = c;
141           }
142           return;
143         }
144       }
145     }
146   }
147   else
148   {
149     SetProtocol(strURL.Left(iPos));
150     iPos += 3;
151   }
152
153   // virtual protocols
154   // why not handle all format 2 (protocol://file) style urls here?
155   // ones that come to mind are iso9660, cdda, musicdb, etc.
156   // they are all local protocols and have no server part, port number, special options, etc.
157   // this removes the need for special handling below.
158   if (
159     m_strProtocol.Equals("stack") ||
160     m_strProtocol.Equals("virtualpath") ||
161     m_strProtocol.Equals("multipath") ||
162     m_strProtocol.Equals("filereader") ||
163     m_strProtocol.Equals("special")
164     )
165   {
166     SetFileName(strURL.Mid(iPos));
167     return;
168   }
169
170   // check for username/password - should occur before first /
171   if (iPos == -1) iPos = 0;
172
173   // for protocols supporting options, chop that part off here
174   // maybe we should invert this list instead?
175   int iEnd = strURL.length();
176   const char* sep = NULL;
177
178   //TODO fix all Addon paths
179   CStdString strProtocol2 = GetTranslatedProtocol();
180   if(m_strProtocol.Equals("rss") ||
181      m_strProtocol.Equals("rar") ||
182      m_strProtocol.Equals("addons") ||
183      m_strProtocol.Equals("image") ||
184      m_strProtocol.Equals("videodb") ||
185      m_strProtocol.Equals("musicdb") ||
186      m_strProtocol.Equals("androidapp"))
187     sep = "?";
188   else
189   if(strProtocol2.Equals("http")
190     || strProtocol2.Equals("https")
191     || strProtocol2.Equals("plugin")
192     || strProtocol2.Equals("addons")
193     || strProtocol2.Equals("hdhomerun")
194     || strProtocol2.Equals("rtsp")
195     || strProtocol2.Equals("apk")
196     || strProtocol2.Equals("zip"))
197     sep = "?;#|";
198   else if(strProtocol2.Equals("ftp")
199        || strProtocol2.Equals("ftps"))
200     sep = "?;|";
201
202   if(sep)
203   {
204     int iOptions = strURL.find_first_of(sep, iPos);
205     if (iOptions >= 0 )
206     {
207       // we keep the initial char as it can be any of the above
208       int iProto = strURL.find_first_of("|",iOptions);
209       if (iProto >= 0)
210       {
211         SetProtocolOptions(strURL.substr(iProto+1));
212         SetOptions(strURL.substr(iOptions,iProto-iOptions));
213       }
214       else
215         SetOptions(strURL.substr(iOptions));
216       iEnd = iOptions;
217     }
218   }
219
220   int iSlash = strURL.Find("/", iPos);
221   if(iSlash >= iEnd)
222     iSlash = -1; // was an invalid slash as it was contained in options
223
224   if( !m_strProtocol.Equals("iso9660") )
225   {
226     int iAlphaSign = strURL.Find("@", iPos);
227     if (iAlphaSign >= 0 && iAlphaSign < iEnd && (iAlphaSign < iSlash || iSlash < 0))
228     {
229       // username/password found
230       CStdString strUserNamePassword = strURL.Mid(iPos, iAlphaSign - iPos);
231
232       // first extract domain, if protocol is smb
233       if (m_strProtocol.Equals("smb"))
234       {
235         int iSemiColon = strUserNamePassword.Find(";");
236
237         if (iSemiColon >= 0)
238         {
239           m_strDomain = strUserNamePassword.Left(iSemiColon);
240           strUserNamePassword.Delete(0, iSemiColon + 1);
241         }
242       }
243
244       // username:password
245       int iColon = strUserNamePassword.Find(":");
246       if (iColon >= 0)
247       {
248         m_strUserName = strUserNamePassword.Left(iColon);
249         iColon++;
250         m_strPassword = strUserNamePassword.Right(strUserNamePassword.size() - iColon);
251       }
252       // username
253       else
254       {
255         m_strUserName = strUserNamePassword;
256       }
257
258       iPos = iAlphaSign + 1;
259       iSlash = strURL.Find("/", iAlphaSign);
260
261       if(iSlash >= iEnd)
262         iSlash = -1;
263     }
264   }
265
266   // detect hostname:port/
267   if (iSlash < 0)
268   {
269     CStdString strHostNameAndPort = strURL.Mid(iPos, iEnd - iPos);
270     int iColon = strHostNameAndPort.Find(":");
271     if (iColon >= 0)
272     {
273       m_strHostName = strHostNameAndPort.Left(iColon);
274       iColon++;
275       CStdString strPort = strHostNameAndPort.Right(strHostNameAndPort.size() - iColon);
276       m_iPort = atoi(strPort.c_str());
277     }
278     else
279     {
280       m_strHostName = strHostNameAndPort;
281     }
282
283   }
284   else
285   {
286     CStdString strHostNameAndPort = strURL.Mid(iPos, iSlash - iPos);
287     int iColon = strHostNameAndPort.Find(":");
288     if (iColon >= 0)
289     {
290       m_strHostName = strHostNameAndPort.Left(iColon);
291       iColon++;
292       CStdString strPort = strHostNameAndPort.Right(strHostNameAndPort.size() - iColon);
293       m_iPort = atoi(strPort.c_str());
294     }
295     else
296     {
297       m_strHostName = strHostNameAndPort;
298     }
299     iPos = iSlash + 1;
300     if (iEnd > iPos)
301     {
302       m_strFileName = strURL.Mid(iPos, iEnd - iPos);
303
304       iSlash = m_strFileName.Find("/");
305       if(iSlash < 0)
306         m_strShareName = m_strFileName;
307       else
308         m_strShareName = m_strFileName.Left(iSlash);
309     }
310   }
311
312   // iso9960 doesnt have an hostname;-)
313   if (m_strProtocol.CompareNoCase("iso9660") == 0
314     || m_strProtocol.CompareNoCase("musicdb") == 0
315     || m_strProtocol.CompareNoCase("videodb") == 0
316     || m_strProtocol.CompareNoCase("sources") == 0
317     || m_strProtocol.CompareNoCase("pvr") == 0
318     || m_strProtocol.Left(3).CompareNoCase("mem") == 0)
319   {
320     if (m_strHostName != "" && m_strFileName != "")
321     {
322       m_strFileName = StringUtils::Format("%s/%s", m_strHostName.c_str(), m_strFileName.c_str());
323       m_strHostName = "";
324     }
325     else
326     {
327       if (!m_strHostName.IsEmpty() && strURL[iEnd-1]=='/')
328         m_strFileName = m_strHostName + "/";
329       else
330         m_strFileName = m_strHostName;
331       m_strHostName = "";
332     }
333   }
334
335   m_strFileName.Replace("\\", "/");
336
337   /* update extension */
338   SetFileName(m_strFileName);
339
340   /* decode urlencoding on this stuff */
341   if(URIUtils::ProtocolHasEncodedHostname(m_strProtocol))
342   {
343     Decode(m_strHostName);
344     // Validate it as it is likely to contain a filename
345     SetHostName(CUtil::ValidatePath(m_strHostName));
346   }
347
348   Decode(m_strUserName);
349   Decode(m_strPassword);
350 }
351
352 void CURL::SetFileName(const CStdString& strFileName)
353 {
354   m_strFileName = strFileName;
355
356   int slash = m_strFileName.find_last_of(GetDirectorySeparator());
357   int period = m_strFileName.find_last_of('.');
358   if(period != -1 && (slash == -1 || period > slash))
359     m_strFileType = m_strFileName.substr(period+1);
360   else
361     m_strFileType = "";
362
363   m_strFileType.Normalize();
364 }
365
366 void CURL::SetHostName(const CStdString& strHostName)
367 {
368   m_strHostName = strHostName;
369 }
370
371 void CURL::SetUserName(const CStdString& strUserName)
372 {
373   m_strUserName = strUserName;
374 }
375
376 void CURL::SetPassword(const CStdString& strPassword)
377 {
378   m_strPassword = strPassword;
379 }
380
381 void CURL::SetProtocol(const CStdString& strProtocol)
382 {
383   m_strProtocol = strProtocol;
384   m_strProtocol.ToLower();
385 }
386
387 void CURL::SetOptions(const CStdString& strOptions)
388 {
389   m_strOptions.Empty();
390   m_options.Clear();
391   if( strOptions.length() > 0)
392   {
393     if( strOptions[0] == '?' || strOptions[0] == '#' || strOptions[0] == ';' || strOptions.Find("xml") >=0 )
394     {
395       m_strOptions = strOptions;
396       m_options.AddOptions(m_strOptions);
397     }
398     else
399       CLog::Log(LOGWARNING, "%s - Invalid options specified for url %s", __FUNCTION__, strOptions.c_str());
400   }
401 }
402
403 void CURL::SetProtocolOptions(const CStdString& strOptions)
404 {
405   m_strProtocolOptions.Empty();
406   m_protocolOptions.Clear();
407   if (strOptions.length() > 0)
408   {
409     if (strOptions[0] == '|')
410       m_strProtocolOptions = strOptions.Mid(1);
411     else
412       m_strProtocolOptions = strOptions;
413     m_protocolOptions.AddOptions(m_strProtocolOptions);
414   }
415 }
416
417 void CURL::SetPort(int port)
418 {
419   m_iPort = port;
420 }
421
422 bool CURL::HasPort() const
423 {
424   return (m_iPort != 0);
425 }
426
427 int CURL::GetPort() const
428 {
429   return m_iPort;
430 }
431
432
433 const CStdString& CURL::GetHostName() const
434 {
435   return m_strHostName;
436 }
437
438 const CStdString&  CURL::GetShareName() const
439 {
440   return m_strShareName;
441 }
442
443 const CStdString& CURL::GetDomain() const
444 {
445   return m_strDomain;
446 }
447
448 const CStdString& CURL::GetUserName() const
449 {
450   return m_strUserName;
451 }
452
453 const CStdString& CURL::GetPassWord() const
454 {
455   return m_strPassword;
456 }
457
458 const CStdString& CURL::GetFileName() const
459 {
460   return m_strFileName;
461 }
462
463 const CStdString& CURL::GetProtocol() const
464 {
465   return m_strProtocol;
466 }
467
468 const CStdString CURL::GetTranslatedProtocol() const
469 {
470   return TranslateProtocol(m_strProtocol);
471 }
472
473 const CStdString& CURL::GetFileType() const
474 {
475   return m_strFileType;
476 }
477
478 const CStdString& CURL::GetOptions() const
479 {
480   return m_strOptions;
481 }
482
483 const CStdString& CURL::GetProtocolOptions() const
484 {
485   return m_strProtocolOptions;
486 }
487
488 const CStdString CURL::GetFileNameWithoutPath() const
489 {
490   // *.zip and *.rar store the actual zip/rar path in the hostname of the url
491   if ((m_strProtocol == "rar"  || 
492        m_strProtocol == "zip"  ||
493        m_strProtocol == "apk") &&
494        m_strFileName.IsEmpty())
495     return URIUtils::GetFileName(m_strHostName);
496
497   // otherwise, we've already got the filepath, so just grab the filename portion
498   CStdString file(m_strFileName);
499   URIUtils::RemoveSlashAtEnd(file);
500   return URIUtils::GetFileName(file);
501 }
502
503 char CURL::GetDirectorySeparator() const
504 {
505 #ifndef TARGET_POSIX
506   if ( IsLocal() )
507     return '\\';
508   else
509 #endif
510     return '/';
511 }
512
513 CStdString CURL::Get() const
514 {
515   unsigned int sizeneed = m_strProtocol.length()
516                         + m_strDomain.length()
517                         + m_strUserName.length()
518                         + m_strPassword.length()
519                         + m_strHostName.length()
520                         + m_strFileName.length()
521                         + m_strOptions.length()
522                         + m_strProtocolOptions.length()
523                         + 10;
524
525   if (m_strProtocol == "")
526     return m_strFileName;
527
528   CStdString strURL;
529   strURL.reserve(sizeneed);
530
531   strURL = GetWithoutFilename();
532   strURL += m_strFileName;
533
534   if( !m_strOptions.empty() )
535     strURL += m_strOptions;
536   if (!m_strProtocolOptions.empty())
537     strURL += "|"+m_strProtocolOptions;
538
539   return strURL;
540 }
541
542 std::string CURL::GetWithoutUserDetails(bool redact) const
543 {
544   std::string strURL;
545
546   if (m_strProtocol.Equals("stack"))
547   {
548     CFileItemList items;
549     std::string strURL2;
550     strURL2 = Get();
551     XFILE::CStackDirectory dir;
552     dir.GetDirectory(strURL2,items);
553     vector<std::string> newItems;
554     for (int i=0;i<items.Size();++i)
555     {
556       CURL url(items[i]->GetPath());
557       items[i]->SetPath(url.GetWithoutUserDetails(redact));
558       newItems.push_back(items[i]->GetPath());
559     }
560     dir.ConstructStackPath(newItems, strURL);
561     return strURL;
562   }
563
564   unsigned int sizeneed = m_strProtocol.length()
565                         + m_strDomain.length()
566                         + m_strHostName.length()
567                         + m_strFileName.length()
568                         + m_strOptions.length()
569                         + m_strProtocolOptions.length()
570                         + 10;
571
572   if (redact)
573     sizeneed += sizeof("USERNAME:PASSWORD@");
574
575   strURL.reserve(sizeneed);
576
577   if (m_strProtocol == "")
578     return m_strFileName;
579
580   strURL = m_strProtocol;
581   strURL += "://";
582
583   if (redact && !m_strUserName.empty())
584   {
585     strURL += "USERNAME";
586     if (!m_strPassword.empty())
587     {
588       strURL += ":PASSWORD";
589     }
590     strURL += "@";
591   }
592
593   if (!m_strHostName.empty())
594   {
595     std::string strHostName;
596
597     if (URIUtils::ProtocolHasParentInHostname(m_strProtocol))
598       strHostName = CURL(m_strHostName).GetWithoutUserDetails();
599     else
600       strHostName = m_strHostName;
601
602     if (URIUtils::ProtocolHasEncodedHostname(m_strProtocol))
603       strURL += URLEncodeInline(strHostName);
604     else
605       strURL += strHostName;
606
607     if ( HasPort() )
608     {
609       strURL += StringUtils::Format(":%i", m_iPort);
610     }
611     strURL += "/";
612   }
613   strURL += m_strFileName;
614
615   if( m_strOptions.length() > 0 )
616     strURL += m_strOptions;
617   if( m_strProtocolOptions.length() > 0 )
618     strURL += "|"+m_strProtocolOptions;
619
620   return strURL;
621 }
622
623 CStdString CURL::GetWithoutFilename() const
624 {
625   if (m_strProtocol == "")
626     return "";
627
628   unsigned int sizeneed = m_strProtocol.length()
629                         + m_strDomain.length()
630                         + m_strUserName.length()
631                         + m_strPassword.length()
632                         + m_strHostName.length()
633                         + 10;
634
635   CStdString strURL;
636   strURL.reserve(sizeneed);
637
638   strURL = m_strProtocol;
639   strURL += "://";
640
641   if (m_strDomain != "")
642   {
643     strURL += m_strDomain;
644     strURL += ";";
645   }
646   else if (m_strUserName != "")
647   {
648     strURL += URLEncodeInline(m_strUserName);
649     if (m_strPassword != "")
650     {
651       strURL += ":";
652       strURL += URLEncodeInline(m_strPassword);
653     }
654     strURL += "@";
655   }
656   else if (m_strDomain != "")
657     strURL += "@";
658
659   if (m_strHostName != "")
660   {
661     if( URIUtils::ProtocolHasEncodedHostname(m_strProtocol) )
662       strURL += URLEncodeInline(m_strHostName);
663     else
664       strURL += m_strHostName;
665     if (HasPort())
666     {
667       CStdString strPort = StringUtils::Format("%i", m_iPort);
668       strURL += ":";
669       strURL += strPort;
670     }
671     strURL += "/";
672   }
673
674   return strURL;
675 }
676
677 std::string CURL::GetRedacted() const
678 {
679   return GetWithoutUserDetails(true);
680 }
681
682 std::string CURL::GetRedacted(const std::string& path)
683 {
684   return CURL(path).GetRedacted();
685 }
686
687 bool CURL::IsLocal() const
688 {
689   return (IsLocalHost() || m_strProtocol.IsEmpty());
690 }
691
692 bool CURL::IsLocalHost() const
693 {
694   return (m_strHostName.Equals("localhost") || m_strHostName.Equals("127.0.0.1"));
695 }
696
697 bool CURL::IsFileOnly(const CStdString &url)
698 {
699   return url.find_first_of("/\\") == CStdString::npos;
700 }
701
702 bool CURL::IsFullPath(const CStdString &url)
703 {
704   if (url.size() && url[0] == '/') return true;     //   /foo/bar.ext
705   if (url.Find("://") >= 0) return true;                 //   foo://bar.ext
706   if (url.size() > 1 && url[1] == ':') return true; //   c:\\foo\\bar\\bar.ext
707   if (StringUtils::StartsWith(url, "\\\\")) return true;    //   \\UNC\path\to\file
708   return false;
709 }
710
711 void CURL::Decode(CStdString& strURLData)
712 //modified to be more accomodating - if a non hex value follows a % take the characters directly and don't raise an error.
713 // However % characters should really be escaped like any other non safe character (www.rfc-editor.org/rfc/rfc1738.txt)
714 {
715   CStdString strResult;
716
717   /* result will always be less than source */
718   strResult.reserve( strURLData.length() );
719
720   for (unsigned int i = 0; i < strURLData.size(); ++i)
721   {
722     int kar = (unsigned char)strURLData[i];
723     if (kar == '+') strResult += ' ';
724     else if (kar == '%')
725     {
726       if (i < strURLData.size() - 2)
727       {
728         CStdString strTmp;
729         strTmp.assign(strURLData.substr(i + 1, 2));
730         int dec_num=-1;
731         sscanf(strTmp,"%x",(unsigned int *)&dec_num);
732         if (dec_num<0 || dec_num>255)
733           strResult += kar;
734         else
735         {
736           strResult += (char)dec_num;
737           i += 2;
738         }
739       }
740       else
741         strResult += kar;
742     }
743     else strResult += kar;
744   }
745   strURLData = strResult;
746 }
747
748 void CURL::Encode(CStdString& strURLData)
749 {
750   CStdString strResult;
751
752   /* wonder what a good value is here is, depends on how often it occurs */
753   strResult.reserve( strURLData.length() * 2 );
754
755   for (int i = 0; i < (int)strURLData.size(); ++i)
756   {
757     int kar = (unsigned char)strURLData[i];
758     //if (kar == ' ') strResult += '+'; // obsolete
759     if (isalnum(kar) || strchr("-_.!()" , kar) ) // Don't URL encode these according to RFC1738
760     {
761       strResult += kar;
762     }
763     else
764     {
765       CStdString strTmp = StringUtils::Format("%%%02.2x", kar);
766       strResult += strTmp;
767     }
768   }
769   strURLData = strResult;
770 }
771
772 std::string CURL::Decode(const std::string& strURLData)
773 {
774   CStdString url = strURLData;
775   Decode(url);
776   return url;
777 }
778
779 std::string CURL::Encode(const std::string& strURLData)
780 {
781   CStdString url = strURLData;
782   Encode(url);
783   return url;
784 }
785
786 CStdString CURL::TranslateProtocol(const CStdString& prot)
787 {
788   if (prot == "shout"
789    || prot == "daap"
790    || prot == "dav"
791    || prot == "tuxbox"
792    || prot == "rss")
793    return "http";
794
795   if (prot == "davs")
796     return "https";
797
798   return prot;
799 }
800
801 void CURL::GetOptions(std::map<CStdString, CStdString> &options) const
802 {
803   CUrlOptions::UrlOptions optionsMap = m_options.GetOptions();
804   for (CUrlOptions::UrlOptions::const_iterator option = optionsMap.begin(); option != optionsMap.end(); option++)
805     options[option->first] = option->second.asString();
806 }
807
808 bool CURL::HasOption(const CStdString &key) const
809 {
810   return m_options.HasOption(key);
811 }
812
813 bool CURL::GetOption(const CStdString &key, CStdString &value) const
814 {
815   CVariant valueObj;
816   if (!m_options.GetOption(key, valueObj))
817     return false;
818
819   value = valueObj.asString();
820   return true;
821 }
822
823 CStdString CURL::GetOption(const CStdString &key) const
824 {
825   CStdString value;
826   if (!GetOption(key, value))
827     return "";
828
829   return value;
830 }
831
832 void CURL::SetOption(const CStdString &key, const CStdString &value)
833 {
834   m_options.AddOption(key, value);
835   SetOptions(m_options.GetOptionsString(true));
836 }
837
838 void CURL::RemoveOption(const CStdString &key)
839 {
840   m_options.RemoveOption(key);
841   SetOptions(m_options.GetOptionsString(true));
842 }
843
844 void CURL::GetProtocolOptions(std::map<CStdString, CStdString> &options) const
845 {
846   CUrlOptions::UrlOptions optionsMap = m_protocolOptions.GetOptions();
847   for (CUrlOptions::UrlOptions::const_iterator option = optionsMap.begin(); option != optionsMap.end(); option++)
848     options[option->first] = option->second.asString();
849 }
850
851 bool CURL::HasProtocolOption(const CStdString &key) const
852 {
853   return m_protocolOptions.HasOption(key);
854 }
855
856 bool CURL::GetProtocolOption(const CStdString &key, CStdString &value) const
857 {
858   CVariant valueObj;
859   if (!m_protocolOptions.GetOption(key, valueObj))
860     return false;
861   
862   value = valueObj.asString();
863   return true;
864 }
865
866 CStdString CURL::GetProtocolOption(const CStdString &key) const
867 {
868   CStdString value;
869   if (!GetProtocolOption(key, value))
870     return "";
871   
872   return value;
873 }
874
875 void CURL::SetProtocolOption(const CStdString &key, const CStdString &value)
876 {
877   m_protocolOptions.AddOption(key, value);
878   m_strProtocolOptions = m_protocolOptions.GetOptionsString(false);
879 }
880
881 void CURL::RemoveProtocolOption(const CStdString &key)
882 {
883   m_protocolOptions.RemoveOption(key);
884   m_strProtocolOptions = m_protocolOptions.GetOptionsString(false);
885 }