summaryrefslogtreecommitdiffstats
path: root/xbmc/URL.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-10 18:07:22 +0000
commitc04dcc2e7d834218ef2d4194331e383402495ae1 (patch)
tree7333e38d10d75386e60f336b80c2443c1166031d /xbmc/URL.cpp
parentInitial commit. (diff)
downloadkodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz
kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/URL.cpp')
-rw-r--r--xbmc/URL.cpp804
1 files changed, 804 insertions, 0 deletions
diff --git a/xbmc/URL.cpp b/xbmc/URL.cpp
new file mode 100644
index 0000000..f4a66d0
--- /dev/null
+++ b/xbmc/URL.cpp
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "URL.h"
+#include "utils/log.h"
+#include "utils/URIUtils.h"
+#include "utils/StringUtils.h"
+#include "Util.h"
+#include "filesystem/File.h"
+#include "FileItem.h"
+#include "filesystem/StackDirectory.h"
+#include "network/Network.h"
+#include "ServiceBroker.h"
+#ifndef TARGET_POSIX
+#include <sys\stat.h>
+#endif
+
+#include <string>
+#include <vector>
+
+using namespace ADDON;
+
+CURL::~CURL() = default;
+
+void CURL::Reset()
+{
+ m_strHostName.clear();
+ m_strDomain.clear();
+ m_strUserName.clear();
+ m_strPassword.clear();
+ m_strShareName.clear();
+ m_strFileName.clear();
+ m_strProtocol.clear();
+ m_strFileType.clear();
+ m_strOptions.clear();
+ m_strProtocolOptions.clear();
+ m_options.Clear();
+ m_protocolOptions.Clear();
+ m_iPort = 0;
+}
+
+void CURL::Parse(const std::string& strURL1)
+{
+ Reset();
+ // start by validating the path
+ std::string strURL = CUtil::ValidatePath(strURL1);
+
+ // strURL can be one of the following:
+ // format 1: protocol://[username:password]@hostname[:port]/directoryandfile
+ // format 2: protocol://file
+ // format 3: drive:directoryandfile
+ //
+ // first need 2 check if this is a protocol or just a normal drive & path
+ if (!strURL.size()) return ;
+ if (strURL == "?") return;
+
+ // form is format 1 or 2
+ // format 1: protocol://[domain;][username:password]@hostname[:port]/directoryandfile
+ // format 2: protocol://file
+
+ // decode protocol
+ size_t iPos = strURL.find("://");
+ if (iPos == std::string::npos)
+ {
+ // This is an ugly hack that needs some work.
+ // example: filename /foo/bar.zip/alice.rar/bob.avi
+ // This should turn into zip://rar:///foo/bar.zip/alice.rar/bob.avi
+ iPos = 0;
+ bool is_apk = (strURL.find(".apk/", iPos) != std::string::npos);
+ while (true)
+ {
+ if (is_apk)
+ iPos = strURL.find(".apk/", iPos);
+ else
+ iPos = strURL.find(".zip/", iPos);
+
+ int extLen = 3;
+ if (iPos == std::string::npos)
+ {
+ /* set filename and update extension*/
+ SetFileName(strURL);
+ return ;
+ }
+ iPos += extLen + 1;
+ std::string archiveName = strURL.substr(0, iPos);
+ struct __stat64 s;
+ if (XFILE::CFile::Stat(archiveName, &s) == 0)
+ {
+#ifdef TARGET_POSIX
+ if (!S_ISDIR(s.st_mode))
+#else
+ if (!(s.st_mode & S_IFDIR))
+#endif
+ {
+ archiveName = Encode(archiveName);
+ if (is_apk)
+ {
+ CURL c("apk://" + archiveName + "/" + strURL.substr(iPos + 1));
+ *this = c;
+ }
+ else
+ {
+ CURL c("zip://" + archiveName + "/" + strURL.substr(iPos + 1));
+ *this = c;
+ }
+ return;
+ }
+ }
+ }
+ }
+ else
+ {
+ SetProtocol(strURL.substr(0, iPos));
+ iPos += 3;
+ }
+
+ // virtual protocols
+ // why not handle all format 2 (protocol://file) style urls here?
+ // ones that come to mind are iso9660, cdda, musicdb, etc.
+ // they are all local protocols and have no server part, port number, special options, etc.
+ // this removes the need for special handling below.
+ if (
+ IsProtocol("stack") ||
+ IsProtocol("virtualpath") ||
+ IsProtocol("multipath") ||
+ IsProtocol("special") ||
+ IsProtocol("resource")
+ )
+ {
+ SetFileName(strURL.substr(iPos));
+ return;
+ }
+
+ if (IsProtocol("udf") || IsProtocol("iso9660"))
+ {
+ std::string lower(strURL);
+ StringUtils::ToLower(lower);
+ size_t isoPos = lower.find(".iso\\", iPos);
+ if (isoPos == std::string::npos)
+ isoPos = lower.find(".udf\\", iPos);
+ if (isoPos != std::string::npos)
+ {
+ strURL = strURL.replace(isoPos + 4, 1, "/");
+ }
+ }
+
+ // check for username/password - should occur before first /
+ if (iPos == std::string::npos) iPos = 0;
+
+ // for protocols supporting options, chop that part off here
+ // maybe we should invert this list instead?
+ size_t iEnd = strURL.length();
+ const char* sep = NULL;
+
+ //! @todo fix all Addon paths
+ std::string strProtocol2 = GetTranslatedProtocol();
+ if(IsProtocol("rss") ||
+ IsProtocol("rsss") ||
+ IsProtocol("rar") ||
+ IsProtocol("apk") ||
+ IsProtocol("xbt") ||
+ IsProtocol("zip") ||
+ IsProtocol("addons") ||
+ IsProtocol("image") ||
+ IsProtocol("videodb") ||
+ IsProtocol("musicdb") ||
+ IsProtocol("androidapp") ||
+ IsProtocol("pvr"))
+ sep = "?";
+ else
+ if( IsProtocolEqual(strProtocol2, "http")
+ || IsProtocolEqual(strProtocol2, "https")
+ || IsProtocolEqual(strProtocol2, "plugin")
+ || IsProtocolEqual(strProtocol2, "addons")
+ || IsProtocolEqual(strProtocol2, "rtsp"))
+ sep = "?;#|";
+ else if(IsProtocolEqual(strProtocol2, "ftp")
+ || IsProtocolEqual(strProtocol2, "ftps"))
+ sep = "?;|";
+
+ if(sep)
+ {
+ size_t iOptions = strURL.find_first_of(sep, iPos);
+ if (iOptions != std::string::npos)
+ {
+ // we keep the initial char as it can be any of the above
+ size_t iProto = strURL.find_first_of('|', iOptions);
+ if (iProto != std::string::npos)
+ {
+ SetProtocolOptions(strURL.substr(iProto+1));
+ SetOptions(strURL.substr(iOptions,iProto-iOptions));
+ }
+ else
+ SetOptions(strURL.substr(iOptions));
+ iEnd = iOptions;
+ }
+ }
+
+ size_t iSlash = strURL.find('/', iPos);
+ if(iSlash >= iEnd)
+ iSlash = std::string::npos; // was an invalid slash as it was contained in options
+
+ // also skip parsing username:password@ for udp/rtp as it not valid
+ // and conflicts with the following example: rtp://sourceip@multicastip
+ size_t iAlphaSign = strURL.find('@', iPos);
+ if (iAlphaSign != std::string::npos && iAlphaSign < iEnd &&
+ (iAlphaSign < iSlash || iSlash == std::string::npos) &&
+ !IsProtocol("udp") && !IsProtocol("rtp"))
+ {
+ // username/password found
+ std::string strUserNamePassword = strURL.substr(iPos, iAlphaSign - iPos);
+
+ // first extract domain, if protocol is smb
+ if (IsProtocol("smb"))
+ {
+ size_t iSemiColon = strUserNamePassword.find(';');
+
+ if (iSemiColon != std::string::npos)
+ {
+ m_strDomain = strUserNamePassword.substr(0, iSemiColon);
+ strUserNamePassword.erase(0, iSemiColon + 1);
+ }
+ }
+
+ // username:password
+ size_t iColon = strUserNamePassword.find(':');
+ if (iColon != std::string::npos)
+ {
+ m_strUserName = strUserNamePassword.substr(0, iColon);
+ m_strPassword = strUserNamePassword.substr(iColon + 1);
+ }
+ // username
+ else
+ {
+ m_strUserName = strUserNamePassword;
+ }
+
+ iPos = iAlphaSign + 1;
+ iSlash = strURL.find('/', iAlphaSign);
+
+ if (iSlash >= iEnd)
+ iSlash = std::string::npos;
+ }
+
+ std::string strHostNameAndPort = strURL.substr(iPos, (iSlash == std::string::npos) ? iEnd - iPos : iSlash - iPos);
+ // check for IPv6 numerical representation inside [].
+ // if [] found, let's store string inside as hostname
+ // and remove that parsed part from strHostNameAndPort
+ size_t iBrk = strHostNameAndPort.rfind(']');
+ if (iBrk != std::string::npos && strHostNameAndPort.find('[') == 0)
+ {
+ m_strHostName = strHostNameAndPort.substr(1, iBrk-1);
+ strHostNameAndPort.erase(0, iBrk+1);
+ }
+
+ // detect hostname:port/ or just :port/ if previous step found [IPv6] format
+ size_t iColon = strHostNameAndPort.rfind(':');
+ if (iColon != std::string::npos && iColon == strHostNameAndPort.find(':'))
+ {
+ if (m_strHostName.empty())
+ m_strHostName = strHostNameAndPort.substr(0, iColon);
+ m_iPort = atoi(strHostNameAndPort.substr(iColon + 1).c_str());
+ }
+
+ // if we still don't have hostname, the strHostNameAndPort substring
+ // is 'just' hostname without :port specification - so use it as is.
+ if (m_strHostName.empty())
+ m_strHostName = strHostNameAndPort;
+
+ if (iSlash != std::string::npos)
+ {
+ iPos = iSlash + 1;
+ if (iEnd > iPos)
+ m_strFileName = strURL.substr(iPos, iEnd - iPos);
+ }
+
+ if (IsProtocol("musicdb") || IsProtocol("videodb") || IsProtocol("sources") || IsProtocol("pvr"))
+ {
+ if (m_strHostName != "" && m_strFileName != "")
+ {
+ m_strFileName = StringUtils::Format("{}/{}", m_strHostName, m_strFileName);
+ m_strHostName = "";
+ }
+ else
+ {
+ if (!m_strHostName.empty() && strURL[iEnd-1]=='/')
+ m_strFileName = m_strHostName + "/";
+ else
+ m_strFileName = m_strHostName;
+ m_strHostName = "";
+ }
+ }
+
+ StringUtils::Replace(m_strFileName, '\\', '/');
+
+ /* update extension + sharename */
+ SetFileName(m_strFileName);
+
+ /* decode urlencoding on this stuff */
+ if(URIUtils::HasEncodedHostname(*this))
+ {
+ m_strHostName = Decode(m_strHostName);
+ SetHostName(m_strHostName);
+ }
+
+ m_strUserName = Decode(m_strUserName);
+ m_strPassword = Decode(m_strPassword);
+}
+
+void CURL::SetFileName(const std::string& strFileName)
+{
+ m_strFileName = strFileName;
+
+ size_t slash = m_strFileName.find_last_of(GetDirectorySeparator());
+ size_t period = m_strFileName.find_last_of('.');
+ if(period != std::string::npos && (slash == std::string::npos || period > slash))
+ m_strFileType = m_strFileName.substr(period+1);
+ else
+ m_strFileType = "";
+
+ slash = m_strFileName.find_first_of(GetDirectorySeparator());
+ if(slash == std::string::npos)
+ m_strShareName = m_strFileName;
+ else
+ m_strShareName = m_strFileName.substr(0, slash);
+
+ StringUtils::Trim(m_strFileType);
+ StringUtils::ToLower(m_strFileType);
+}
+
+void CURL::SetProtocol(const std::string& strProtocol)
+{
+ m_strProtocol = strProtocol;
+ StringUtils::ToLower(m_strProtocol);
+}
+
+void CURL::SetOptions(const std::string& strOptions)
+{
+ m_strOptions.clear();
+ m_options.Clear();
+ if( strOptions.length() > 0)
+ {
+ if(strOptions[0] == '?' ||
+ strOptions[0] == '#' ||
+ strOptions[0] == ';' ||
+ strOptions.find("xml") != std::string::npos)
+ {
+ m_strOptions = strOptions;
+ m_options.AddOptions(m_strOptions);
+ }
+ else
+ CLog::Log(LOGWARNING, "{} - Invalid options specified for url {}", __FUNCTION__, strOptions);
+ }
+}
+
+void CURL::SetProtocolOptions(const std::string& strOptions)
+{
+ m_strProtocolOptions.clear();
+ m_protocolOptions.Clear();
+ if (strOptions.length() > 0)
+ {
+ if (strOptions[0] == '|')
+ m_strProtocolOptions = strOptions.substr(1);
+ else
+ m_strProtocolOptions = strOptions;
+ m_protocolOptions.AddOptions(m_strProtocolOptions);
+ }
+}
+
+const std::string CURL::GetTranslatedProtocol() const
+{
+ if (IsProtocol("shout")
+ || IsProtocol("dav")
+ || IsProtocol("rss"))
+ return "http";
+
+ if (IsProtocol("davs")
+ || IsProtocol("rsss"))
+ return "https";
+
+ return GetProtocol();
+}
+
+const std::string CURL::GetFileNameWithoutPath() const
+{
+ // *.zip and *.rar store the actual zip/rar path in the hostname of the url
+ if ((IsProtocol("rar") ||
+ IsProtocol("zip") ||
+ IsProtocol("xbt") ||
+ IsProtocol("apk")) &&
+ m_strFileName.empty())
+ return URIUtils::GetFileName(m_strHostName);
+
+ // otherwise, we've already got the filepath, so just grab the filename portion
+ std::string file(m_strFileName);
+ URIUtils::RemoveSlashAtEnd(file);
+ return URIUtils::GetFileName(file);
+}
+
+inline
+void protectIPv6(std::string &hn)
+{
+ if (!hn.empty() && hn.find(':') != hn.rfind(':') && hn.find(':') != std::string::npos)
+ {
+ hn = '[' + hn + ']';
+ }
+}
+
+char CURL::GetDirectorySeparator() const
+{
+#ifndef TARGET_POSIX
+ //We don't want to use IsLocal here, it can return true
+ //for network protocols that matches localhost or hostname
+ //we only ever want to use \ for win32 local filesystem
+ if ( m_strProtocol.empty() )
+ return '\\';
+ else
+#endif
+ return '/';
+}
+
+std::string CURL::Get() const
+{
+ if (m_strProtocol.empty())
+ return m_strFileName;
+
+ unsigned int sizeneed = m_strProtocol.length()
+ + m_strDomain.length()
+ + m_strUserName.length()
+ + m_strPassword.length()
+ + m_strHostName.length()
+ + m_strFileName.length()
+ + m_strOptions.length()
+ + m_strProtocolOptions.length()
+ + 10;
+
+ std::string strURL;
+ strURL.reserve(sizeneed);
+
+ strURL = GetWithoutOptions();
+
+ if( !m_strOptions.empty() )
+ strURL += m_strOptions;
+
+ if (!m_strProtocolOptions.empty())
+ strURL += "|"+m_strProtocolOptions;
+
+ return strURL;
+}
+
+std::string CURL::GetWithoutOptions() const
+{
+ if (m_strProtocol.empty())
+ return m_strFileName;
+
+ std::string strGet = GetWithoutFilename();
+
+ // Prevent double slash when concatenating host part and filename part
+ if (m_strFileName.size() && (m_strFileName[0] == '/' || m_strFileName[0] == '\\') && URIUtils::HasSlashAtEnd(strGet))
+ URIUtils::RemoveSlashAtEnd(strGet);
+
+ return strGet + m_strFileName;
+}
+
+std::string CURL::GetWithoutUserDetails(bool redact) const
+{
+ std::string strURL;
+
+ if (IsProtocol("stack"))
+ {
+ CFileItemList items;
+ XFILE::CStackDirectory dir;
+ dir.GetDirectory(*this,items);
+ std::vector<std::string> newItems;
+ for (int i=0;i<items.Size();++i)
+ {
+ CURL url(items[i]->GetPath());
+ items[i]->SetPath(url.GetWithoutUserDetails(redact));
+ newItems.push_back(items[i]->GetPath());
+ }
+ dir.ConstructStackPath(newItems, strURL);
+ return strURL;
+ }
+
+ unsigned int sizeneed = m_strProtocol.length()
+ + m_strHostName.length()
+ + m_strFileName.length()
+ + m_strOptions.length()
+ + m_strProtocolOptions.length()
+ + 10;
+
+ if (redact && !m_strUserName.empty())
+ {
+ sizeneed += sizeof("USERNAME");
+ if (!m_strPassword.empty())
+ sizeneed += sizeof(":PASSWORD@");
+ if (!m_strDomain.empty())
+ sizeneed += sizeof("DOMAIN;");
+ }
+
+ strURL.reserve(sizeneed);
+
+ if (m_strProtocol.empty())
+ return m_strFileName;
+
+ strURL = m_strProtocol;
+ strURL += "://";
+
+ if (redact && !m_strUserName.empty())
+ {
+ if (!m_strDomain.empty())
+ strURL += "DOMAIN;";
+ strURL += "USERNAME";
+ if (!m_strPassword.empty())
+ strURL += ":PASSWORD";
+ strURL += "@";
+ }
+
+ if (!m_strHostName.empty())
+ {
+ std::string strHostName;
+
+ if (URIUtils::HasParentInHostname(*this))
+ strHostName = CURL(m_strHostName).GetWithoutUserDetails();
+ else
+ strHostName = m_strHostName;
+
+ if (URIUtils::HasEncodedHostname(*this))
+ strHostName = Encode(strHostName);
+
+ if ( HasPort() )
+ {
+ protectIPv6(strHostName);
+ strURL += strHostName + StringUtils::Format(":{}", m_iPort);
+ }
+ else
+ strURL += strHostName;
+
+ strURL += "/";
+ }
+ strURL += m_strFileName;
+
+ if( m_strOptions.length() > 0 )
+ strURL += m_strOptions;
+ if( m_strProtocolOptions.length() > 0 )
+ strURL += "|"+m_strProtocolOptions;
+
+ return strURL;
+}
+
+std::string CURL::GetWithoutFilename() const
+{
+ if (m_strProtocol.empty())
+ return "";
+
+ unsigned int sizeneed = m_strProtocol.length()
+ + m_strDomain.length()
+ + m_strUserName.length()
+ + m_strPassword.length()
+ + m_strHostName.length()
+ + 10;
+
+ std::string strURL;
+ strURL.reserve(sizeneed);
+
+ strURL = m_strProtocol;
+ strURL += "://";
+
+ if (!m_strUserName.empty())
+ {
+ if (!m_strDomain.empty())
+ {
+ strURL += Encode(m_strDomain);
+ strURL += ";";
+ }
+ strURL += Encode(m_strUserName);
+ if (!m_strPassword.empty())
+ {
+ strURL += ":";
+ strURL += Encode(m_strPassword);
+ }
+ strURL += "@";
+ }
+
+ if (!m_strHostName.empty())
+ {
+ std::string hostname;
+
+ if( URIUtils::HasEncodedHostname(*this) )
+ hostname = Encode(m_strHostName);
+ else
+ hostname = m_strHostName;
+
+ if (HasPort())
+ {
+ protectIPv6(hostname);
+ strURL += hostname + StringUtils::Format(":{}", m_iPort);
+ }
+ else
+ strURL += hostname;
+
+ strURL += "/";
+ }
+
+ return strURL;
+}
+
+std::string CURL::GetRedacted() const
+{
+ return GetWithoutUserDetails(true);
+}
+
+std::string CURL::GetRedacted(const std::string& path)
+{
+ return CURL(path).GetRedacted();
+}
+
+bool CURL::IsLocal() const
+{
+ return (m_strProtocol.empty() || IsLocalHost() || IsProtocol("win-lib"));
+}
+
+bool CURL::IsLocalHost() const
+{
+ return CServiceBroker::GetNetwork().IsLocalHost(m_strHostName);
+}
+
+bool CURL::IsFileOnly(const std::string &url)
+{
+ return url.find_first_of("/\\") == std::string::npos;
+}
+
+bool CURL::IsFullPath(const std::string &url)
+{
+ if (url.size() && url[0] == '/') return true; // /foo/bar.ext
+ if (url.find("://") != std::string::npos) return true; // foo://bar.ext
+ if (url.size() > 1 && url[1] == ':') return true; // c:\\foo\\bar\\bar.ext
+ if (StringUtils::StartsWith(url, "\\\\")) return true; // \\UNC\path\to\file
+ return false;
+}
+
+std::string CURL::Decode(const std::string& strURLData)
+//modified to be more accommodating - if a non hex value follows a % take the characters directly and don't raise an error.
+// However % characters should really be escaped like any other non safe character (www.rfc-editor.org/rfc/rfc1738.txt)
+{
+ std::string strResult;
+
+ /* result will always be less than source */
+ strResult.reserve( strURLData.length() );
+
+ for (unsigned int i = 0; i < strURLData.size(); ++i)
+ {
+ int kar = (unsigned char)strURLData[i];
+ if (kar == '+') strResult += ' ';
+ else if (kar == '%')
+ {
+ if (i < strURLData.size() - 2)
+ {
+ std::string strTmp;
+ strTmp.assign(strURLData.substr(i + 1, 2));
+ int dec_num=-1;
+ sscanf(strTmp.c_str(), "%x", (unsigned int *)&dec_num);
+ if (dec_num<0 || dec_num>255)
+ strResult += kar;
+ else
+ {
+ strResult += (char)dec_num;
+ i += 2;
+ }
+ }
+ else
+ strResult += kar;
+ }
+ else strResult += kar;
+ }
+
+ return strResult;
+}
+
+std::string CURL::Encode(const std::string& strURLData)
+{
+ std::string strResult;
+
+ /* wonder what a good value is here is, depends on how often it occurs */
+ strResult.reserve( strURLData.length() * 2 );
+
+ for (size_t i = 0; i < strURLData.size(); ++i)
+ {
+ const char kar = strURLData[i];
+
+ // Don't URL encode "-_.!()" according to RFC1738
+ //! @todo Update it to "-_.~" after Gotham according to RFC3986
+ if (StringUtils::isasciialphanum(kar) || kar == '-' || kar == '.' || kar == '_' || kar == '!' || kar == '(' || kar == ')')
+ strResult.push_back(kar);
+ else
+ strResult += StringUtils::Format("%{:02x}", (unsigned int)((unsigned char)kar));
+ }
+
+ return strResult;
+}
+
+bool CURL::IsProtocolEqual(const std::string &protocol, const char *type)
+{
+ /*
+ NOTE: We're currently using == here as m_strProtocol is assigned as lower-case in SetProtocol(),
+ and we've assumed all other callers are calling with protocol lower-case otherwise.
+ We possibly shouldn't do this (as CURL(foo).Get() != foo, though there are other reasons for this as well)
+ but it handles the requirements of RFC-1738 which allows the scheme to be case-insensitive.
+ */
+ if (type)
+ return protocol == type;
+ return false;
+}
+
+void CURL::GetOptions(std::map<std::string, std::string> &options) const
+{
+ CUrlOptions::UrlOptions optionsMap = m_options.GetOptions();
+ for (CUrlOptions::UrlOptions::const_iterator option = optionsMap.begin(); option != optionsMap.end(); option++)
+ options[option->first] = option->second.asString();
+}
+
+bool CURL::HasOption(const std::string &key) const
+{
+ return m_options.HasOption(key);
+}
+
+bool CURL::GetOption(const std::string &key, std::string &value) const
+{
+ CVariant valueObj;
+ if (!m_options.GetOption(key, valueObj))
+ return false;
+
+ value = valueObj.asString();
+ return true;
+}
+
+std::string CURL::GetOption(const std::string &key) const
+{
+ std::string value;
+ if (!GetOption(key, value))
+ return "";
+
+ return value;
+}
+
+void CURL::SetOption(const std::string &key, const std::string &value)
+{
+ m_options.AddOption(key, value);
+ SetOptions(m_options.GetOptionsString(true));
+}
+
+void CURL::RemoveOption(const std::string &key)
+{
+ m_options.RemoveOption(key);
+ SetOptions(m_options.GetOptionsString(true));
+}
+
+void CURL::GetProtocolOptions(std::map<std::string, std::string> &options) const
+{
+ CUrlOptions::UrlOptions optionsMap = m_protocolOptions.GetOptions();
+ for (CUrlOptions::UrlOptions::const_iterator option = optionsMap.begin(); option != optionsMap.end(); option++)
+ options[option->first] = option->second.asString();
+}
+
+bool CURL::HasProtocolOption(const std::string &key) const
+{
+ return m_protocolOptions.HasOption(key);
+}
+
+bool CURL::GetProtocolOption(const std::string &key, std::string &value) const
+{
+ CVariant valueObj;
+ if (!m_protocolOptions.GetOption(key, valueObj))
+ return false;
+
+ value = valueObj.asString();
+ return true;
+}
+
+std::string CURL::GetProtocolOption(const std::string &key) const
+{
+ std::string value;
+ if (!GetProtocolOption(key, value))
+ return "";
+
+ return value;
+}
+
+void CURL::SetProtocolOption(const std::string &key, const std::string &value)
+{
+ m_protocolOptions.AddOption(key, value);
+ m_strProtocolOptions = m_protocolOptions.GetOptionsString(false);
+}
+
+void CURL::RemoveProtocolOption(const std::string &key)
+{
+ m_protocolOptions.RemoveOption(key);
+ m_strProtocolOptions = m_protocolOptions.GetOptionsString(false);
+}