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