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