[cstdstring] removes Replace and Remove
[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   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           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.CompareNoCase("iso9660") == 0
309     || m_strProtocol.CompareNoCase("musicdb") == 0
310     || m_strProtocol.CompareNoCase("videodb") == 0
311     || m_strProtocol.CompareNoCase("sources") == 0
312     || m_strProtocol.CompareNoCase("pvr") == 0
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     // Validate it as it is likely to contain a filename
340     SetHostName(CUtil::ValidatePath(m_strHostName));
341   }
342
343   Decode(m_strUserName);
344   Decode(m_strPassword);
345 }
346
347 void CURL::SetFileName(const CStdString& strFileName)
348 {
349   m_strFileName = strFileName;
350
351   int slash = m_strFileName.find_last_of(GetDirectorySeparator());
352   int period = m_strFileName.find_last_of('.');
353   if(period != -1 && (slash == -1 || period > slash))
354     m_strFileType = m_strFileName.substr(period+1);
355   else
356     m_strFileType = "";
357
358   StringUtils::Trim(m_strFileType);
359   StringUtils::ToLower(m_strFileType);
360 }
361
362 void CURL::SetHostName(const CStdString& strHostName)
363 {
364   m_strHostName = strHostName;
365 }
366
367 void CURL::SetUserName(const CStdString& strUserName)
368 {
369   m_strUserName = strUserName;
370 }
371
372 void CURL::SetPassword(const CStdString& strPassword)
373 {
374   m_strPassword = strPassword;
375 }
376
377 void CURL::SetProtocol(const CStdString& strProtocol)
378 {
379   m_strProtocol = strProtocol;
380   StringUtils::ToLower(m_strProtocol);
381 }
382
383 void CURL::SetOptions(const CStdString& strOptions)
384 {
385   m_strOptions.clear();
386   m_options.Clear();
387   if( strOptions.length() > 0)
388   {
389     if(strOptions[0] == '?' ||
390        strOptions[0] == '#' ||
391        strOptions[0] == ';' ||
392        strOptions.find("xml") != std::string::npos)
393     {
394       m_strOptions = strOptions;
395       m_options.AddOptions(m_strOptions);
396     }
397     else
398       CLog::Log(LOGWARNING, "%s - Invalid options specified for url %s", __FUNCTION__, strOptions.c_str());
399   }
400 }
401
402 void CURL::SetProtocolOptions(const CStdString& strOptions)
403 {
404   m_strProtocolOptions.clear();
405   m_protocolOptions.Clear();
406   if (strOptions.length() > 0)
407   {
408     if (strOptions[0] == '|')
409       m_strProtocolOptions = strOptions.substr(1);
410     else
411       m_strProtocolOptions = strOptions;
412     m_protocolOptions.AddOptions(m_strProtocolOptions);
413   }
414 }
415
416 void CURL::SetPort(int port)
417 {
418   m_iPort = port;
419 }
420
421 bool CURL::HasPort() const
422 {
423   return (m_iPort != 0);
424 }
425
426 int CURL::GetPort() const
427 {
428   return m_iPort;
429 }
430
431
432 const CStdString& CURL::GetHostName() const
433 {
434   return m_strHostName;
435 }
436
437 const CStdString&  CURL::GetShareName() const
438 {
439   return m_strShareName;
440 }
441
442 const CStdString& CURL::GetDomain() const
443 {
444   return m_strDomain;
445 }
446
447 const CStdString& CURL::GetUserName() const
448 {
449   return m_strUserName;
450 }
451
452 const CStdString& CURL::GetPassWord() const
453 {
454   return m_strPassword;
455 }
456
457 const CStdString& CURL::GetFileName() const
458 {
459   return m_strFileName;
460 }
461
462 const CStdString& CURL::GetProtocol() const
463 {
464   return m_strProtocol;
465 }
466
467 const CStdString CURL::GetTranslatedProtocol() const
468 {
469   return TranslateProtocol(m_strProtocol);
470 }
471
472 const CStdString& CURL::GetFileType() const
473 {
474   return m_strFileType;
475 }
476
477 const CStdString& CURL::GetOptions() const
478 {
479   return m_strOptions;
480 }
481
482 const CStdString& CURL::GetProtocolOptions() const
483 {
484   return m_strProtocolOptions;
485 }
486
487 const CStdString CURL::GetFileNameWithoutPath() const
488 {
489   // *.zip and *.rar store the actual zip/rar path in the hostname of the url
490   if ((m_strProtocol == "rar"  || 
491        m_strProtocol == "zip"  ||
492        m_strProtocol == "apk") &&
493        m_strFileName.empty())
494     return URIUtils::GetFileName(m_strHostName);
495
496   // otherwise, we've already got the filepath, so just grab the filename portion
497   CStdString file(m_strFileName);
498   URIUtils::RemoveSlashAtEnd(file);
499   return URIUtils::GetFileName(file);
500 }
501
502 char CURL::GetDirectorySeparator() const
503 {
504 #ifndef TARGET_POSIX
505   if ( IsLocal() )
506     return '\\';
507   else
508 #endif
509     return '/';
510 }
511
512 CStdString CURL::Get() const
513 {
514   unsigned int sizeneed = m_strProtocol.length()
515                         + m_strDomain.length()
516                         + m_strUserName.length()
517                         + m_strPassword.length()
518                         + m_strHostName.length()
519                         + m_strFileName.length()
520                         + m_strOptions.length()
521                         + m_strProtocolOptions.length()
522                         + 10;
523
524   if (m_strProtocol == "")
525     return m_strFileName;
526
527   CStdString strURL;
528   strURL.reserve(sizeneed);
529
530   strURL = GetWithoutFilename();
531   strURL += m_strFileName;
532
533   if( !m_strOptions.empty() )
534     strURL += m_strOptions;
535   if (!m_strProtocolOptions.empty())
536     strURL += "|"+m_strProtocolOptions;
537
538   return strURL;
539 }
540
541 std::string CURL::GetWithoutUserDetails(bool redact) const
542 {
543   std::string strURL;
544
545   if (m_strProtocol.Equals("stack"))
546   {
547     CFileItemList items;
548     std::string strURL2;
549     strURL2 = Get();
550     XFILE::CStackDirectory dir;
551     dir.GetDirectory(strURL2,items);
552     vector<std::string> newItems;
553     for (int i=0;i<items.Size();++i)
554     {
555       CURL url(items[i]->GetPath());
556       items[i]->SetPath(url.GetWithoutUserDetails(redact));
557       newItems.push_back(items[i]->GetPath());
558     }
559     dir.ConstructStackPath(newItems, strURL);
560     return strURL;
561   }
562
563   unsigned int sizeneed = m_strProtocol.length()
564                         + m_strDomain.length()
565                         + m_strHostName.length()
566                         + m_strFileName.length()
567                         + m_strOptions.length()
568                         + m_strProtocolOptions.length()
569                         + 10;
570
571   if (redact)
572     sizeneed += sizeof("USERNAME:PASSWORD@");
573
574   strURL.reserve(sizeneed);
575
576   if (m_strProtocol == "")
577     return m_strFileName;
578
579   strURL = m_strProtocol;
580   strURL += "://";
581
582   if (redact && !m_strUserName.empty())
583   {
584     strURL += "USERNAME";
585     if (!m_strPassword.empty())
586     {
587       strURL += ":PASSWORD";
588     }
589     strURL += "@";
590   }
591
592   if (!m_strHostName.empty())
593   {
594     std::string strHostName;
595
596     if (URIUtils::ProtocolHasParentInHostname(m_strProtocol))
597       strHostName = CURL(m_strHostName).GetWithoutUserDetails();
598     else
599       strHostName = m_strHostName;
600
601     if (URIUtils::ProtocolHasEncodedHostname(m_strProtocol))
602       strURL += URLEncodeInline(strHostName);
603     else
604       strURL += strHostName;
605
606     if ( HasPort() )
607     {
608       strURL += StringUtils::Format(":%i", m_iPort);
609     }
610     strURL += "/";
611   }
612   strURL += m_strFileName;
613
614   if( m_strOptions.length() > 0 )
615     strURL += m_strOptions;
616   if( m_strProtocolOptions.length() > 0 )
617     strURL += "|"+m_strProtocolOptions;
618
619   return strURL;
620 }
621
622 CStdString CURL::GetWithoutFilename() const
623 {
624   if (m_strProtocol == "")
625     return "";
626
627   unsigned int sizeneed = m_strProtocol.length()
628                         + m_strDomain.length()
629                         + m_strUserName.length()
630                         + m_strPassword.length()
631                         + m_strHostName.length()
632                         + 10;
633
634   CStdString strURL;
635   strURL.reserve(sizeneed);
636
637   strURL = m_strProtocol;
638   strURL += "://";
639
640   if (m_strDomain != "")
641   {
642     strURL += m_strDomain;
643     strURL += ";";
644   }
645   else if (m_strUserName != "")
646   {
647     strURL += URLEncodeInline(m_strUserName);
648     if (m_strPassword != "")
649     {
650       strURL += ":";
651       strURL += URLEncodeInline(m_strPassword);
652     }
653     strURL += "@";
654   }
655   else if (m_strDomain != "")
656     strURL += "@";
657
658   if (m_strHostName != "")
659   {
660     if( URIUtils::ProtocolHasEncodedHostname(m_strProtocol) )
661       strURL += URLEncodeInline(m_strHostName);
662     else
663       strURL += m_strHostName;
664     if (HasPort())
665     {
666       CStdString strPort = StringUtils::Format("%i", m_iPort);
667       strURL += ":";
668       strURL += strPort;
669     }
670     strURL += "/";
671   }
672
673   return strURL;
674 }
675
676 std::string CURL::GetRedacted() const
677 {
678   return GetWithoutUserDetails(true);
679 }
680
681 std::string CURL::GetRedacted(const std::string& path)
682 {
683   return CURL(path).GetRedacted();
684 }
685
686 bool CURL::IsLocal() const
687 {
688   return (IsLocalHost() || m_strProtocol.empty());
689 }
690
691 bool CURL::IsLocalHost() const
692 {
693   return (m_strHostName.Equals("localhost") || m_strHostName.Equals("127.0.0.1"));
694 }
695
696 bool CURL::IsFileOnly(const CStdString &url)
697 {
698   return url.find_first_of("/\\") == CStdString::npos;
699 }
700
701 bool CURL::IsFullPath(const CStdString &url)
702 {
703   if (url.size() && url[0] == '/') return true;     //   /foo/bar.ext
704   if (url.find("://") != std::string::npos) return true;                 //   foo://bar.ext
705   if (url.size() > 1 && url[1] == ':') return true; //   c:\\foo\\bar\\bar.ext
706   if (StringUtils::StartsWith(url, "\\\\")) return true;    //   \\UNC\path\to\file
707   return false;
708 }
709
710 void CURL::Decode(CStdString& strURLData)
711 //modified to be more accomodating - if a non hex value follows a % take the characters directly and don't raise an error.
712 // However % characters should really be escaped like any other non safe character (www.rfc-editor.org/rfc/rfc1738.txt)
713 {
714   CStdString strResult;
715
716   /* result will always be less than source */
717   strResult.reserve( strURLData.length() );
718
719   for (unsigned int i = 0; i < strURLData.size(); ++i)
720   {
721     int kar = (unsigned char)strURLData[i];
722     if (kar == '+') strResult += ' ';
723     else if (kar == '%')
724     {
725       if (i < strURLData.size() - 2)
726       {
727         CStdString strTmp;
728         strTmp.assign(strURLData.substr(i + 1, 2));
729         int dec_num=-1;
730         sscanf(strTmp,"%x",(unsigned int *)&dec_num);
731         if (dec_num<0 || dec_num>255)
732           strResult += kar;
733         else
734         {
735           strResult += (char)dec_num;
736           i += 2;
737         }
738       }
739       else
740         strResult += kar;
741     }
742     else strResult += kar;
743   }
744   strURLData = strResult;
745 }
746
747 void CURL::Encode(CStdString& strURLData)
748 {
749   CStdString strResult;
750
751   /* wonder what a good value is here is, depends on how often it occurs */
752   strResult.reserve( strURLData.length() * 2 );
753
754   for (int i = 0; i < (int)strURLData.size(); ++i)
755   {
756     int kar = (unsigned char)strURLData[i];
757     //if (kar == ' ') strResult += '+'; // obsolete
758     if (isalnum(kar) || strchr("-_.!()" , kar) ) // Don't URL encode these according to RFC1738
759     {
760       strResult += kar;
761     }
762     else
763     {
764       CStdString strTmp = StringUtils::Format("%%%02.2x", kar);
765       strResult += strTmp;
766     }
767   }
768   strURLData = 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 std::string CURL::Encode(const std::string& strURLData)
779 {
780   CStdString url = strURLData;
781   Encode(url);
782   return url;
783 }
784
785 CStdString CURL::TranslateProtocol(const CStdString& prot)
786 {
787   if (prot == "shout"
788    || prot == "daap"
789    || prot == "dav"
790    || prot == "tuxbox"
791    || prot == "rss")
792    return "http";
793
794   if (prot == "davs")
795     return "https";
796
797   return prot;
798 }
799
800 void CURL::GetOptions(std::map<CStdString, CStdString> &options) const
801 {
802   CUrlOptions::UrlOptions optionsMap = m_options.GetOptions();
803   for (CUrlOptions::UrlOptions::const_iterator option = optionsMap.begin(); option != optionsMap.end(); option++)
804     options[option->first] = option->second.asString();
805 }
806
807 bool CURL::HasOption(const CStdString &key) const
808 {
809   return m_options.HasOption(key);
810 }
811
812 bool CURL::GetOption(const CStdString &key, CStdString &value) const
813 {
814   CVariant valueObj;
815   if (!m_options.GetOption(key, valueObj))
816     return false;
817
818   value = valueObj.asString();
819   return true;
820 }
821
822 CStdString CURL::GetOption(const CStdString &key) const
823 {
824   CStdString value;
825   if (!GetOption(key, value))
826     return "";
827
828   return value;
829 }
830
831 void CURL::SetOption(const CStdString &key, const CStdString &value)
832 {
833   m_options.AddOption(key, value);
834   SetOptions(m_options.GetOptionsString(true));
835 }
836
837 void CURL::RemoveOption(const CStdString &key)
838 {
839   m_options.RemoveOption(key);
840   SetOptions(m_options.GetOptionsString(true));
841 }
842
843 void CURL::GetProtocolOptions(std::map<CStdString, CStdString> &options) const
844 {
845   CUrlOptions::UrlOptions optionsMap = m_protocolOptions.GetOptions();
846   for (CUrlOptions::UrlOptions::const_iterator option = optionsMap.begin(); option != optionsMap.end(); option++)
847     options[option->first] = option->second.asString();
848 }
849
850 bool CURL::HasProtocolOption(const CStdString &key) const
851 {
852   return m_protocolOptions.HasOption(key);
853 }
854
855 bool CURL::GetProtocolOption(const CStdString &key, CStdString &value) const
856 {
857   CVariant valueObj;
858   if (!m_protocolOptions.GetOption(key, valueObj))
859     return false;
860   
861   value = valueObj.asString();
862   return true;
863 }
864
865 CStdString CURL::GetProtocolOption(const CStdString &key) const
866 {
867   CStdString value;
868   if (!GetProtocolOption(key, value))
869     return "";
870   
871   return value;
872 }
873
874 void CURL::SetProtocolOption(const CStdString &key, const CStdString &value)
875 {
876   m_protocolOptions.AddOption(key, value);
877   m_strProtocolOptions = m_protocolOptions.GetOptionsString(false);
878 }
879
880 void CURL::RemoveProtocolOption(const CStdString &key)
881 {
882   m_protocolOptions.RemoveOption(key);
883   m_strProtocolOptions = m_protocolOptions.GetOptionsString(false);
884 }