#include "settings/AdvancedSettings.h"
#include "settings/Settings.h"
#include "File.h"
+#include "threads/SystemClock.h"
#include <vector>
#include <climits>
#define XMIN(a,b) ((a)<(b)?(a):(b))
#define FITS_INT(a) (((a) <= INT_MAX) && ((a) >= INT_MIN))
-#define dllselect select
-
-
curl_proxytype proxyType2CUrlProxyType[] = {
CURLPROXY_HTTP,
CURLPROXY_SOCKS4,
m_username = "";
m_password = "";
m_httpauth = "";
+ m_cipherlist = "";
m_proxytype = PROXY_HTTP;
m_state = new CReadState();
m_oldState = NULL;
delete m_oldState;
m_oldState = NULL;
- m_url.Empty();
- m_referer.Empty();
- m_cookie.Empty();
+ m_url.clear();
+ m_referer.clear();
+ m_cookie.clear();
m_opened = false;
m_forWrite = false;
g_curlInterface.easy_setopt(h, CURLOPT_COOKIEJAR, strCookieFile.c_str());
// Set custom cookie if requested
- if (!m_cookie.IsEmpty())
+ if (!m_cookie.empty())
g_curlInterface.easy_setopt(h, CURLOPT_COOKIE, m_cookie.c_str());
g_curlInterface.easy_setopt(h, CURLOPT_COOKIELIST, "FLUSH");
}
// setup Referer header if needed
- if (!m_referer.IsEmpty())
+ if (!m_referer.empty())
g_curlInterface.easy_setopt(h, CURLOPT_REFERER, m_referer.c_str());
else
{
// the 302 response's body length, which cause the next read request failed, so we ignore
// content-length for shoutcast file to workaround this.
g_curlInterface.easy_setopt(h, CURLOPT_IGNORE_CONTENT_LENGTH, 1);
+
+ // Setup allowed TLS/SSL ciphers. New versions of cURL may deprecate things that are still in use.
+ if (!m_cipherlist.empty())
+ g_curlInterface.easy_setopt(h, CURLOPT_SSL_CIPHER_LIST, m_cipherlist.c_str());
}
void CCurlFile::SetRequestHeaders(CReadState* state)
&& !h.GetValue("Content-Disposition").empty() )
{
CStdString strValue = h.GetValue("Content-Disposition");
- if (strValue.Find("filename=") > -1 && strValue.Find(".flv") > -1)
+ if (strValue.find("filename=") != std::string::npos &&
+ strValue.find(".flv") != std::string::npos)
h.AddParam("Content-Type", "video/flv");
}
}
|| strProtocol.Equals("ftps") )
{
// we was using url optons for urls, keep the old code work and warning
- if (!url2.GetOptions().IsEmpty())
+ if (!url2.GetOptions().empty())
{
- CLog::Log(LOGWARNING, "%s: ftp url option is deprecated, please switch to use protocol option (change '?' to '|'), url: [%s]", __FUNCTION__, url2.Get().c_str());
- url2.SetProtocolOptions(url2.GetOptions().Mid(1));
+ CLog::Log(LOGWARNING, "%s: ftp url option is deprecated, please switch to use protocol option (change '?' to '|'), url: [%s]", __FUNCTION__, url2.GetRedacted().c_str());
+ url2.SetProtocolOptions(url2.GetOptions().substr(1));
/* ftp has no options */
url2.SetOptions("");
}
/* url encoded. if handed from ftpdirectory */
/* it won't be so let's handle that case */
- CStdString partial, filename(url2.GetFileName());
+ CStdString filename(url2.GetFileName());
std::vector<std::string> array;
// if server sent us the filename in non-utf8, we need send back with same encoding.
/* TODO: create a tokenizer that doesn't skip empty's */
StringUtils::Tokenize(filename, array, "/");
- filename.Empty();
+ filename.clear();
for(std::vector<std::string>::iterator it = array.begin(); it != array.end(); it++)
{
if(it != array.begin())
filename += "/";
- partial = *it;
- CURL::Encode(partial);
- filename += partial;
+ filename += CURL::Encode(*it);
}
/* make sure we keep slashes */
- if(url2.GetFileName().Right(1) == "/")
+ if(StringUtils::EndsWith(url2.GetFileName(), "/"))
filename += "/";
url2.SetFileName(filename);
if (url2.HasProtocolOption("auth"))
{
m_ftpauth = url2.GetProtocolOption("auth");
- if(m_ftpauth.IsEmpty())
+ if(m_ftpauth.empty())
m_ftpauth = "any";
}
m_ftpport = "";
if (url2.HasProtocolOption("active"))
{
m_ftpport = url2.GetProtocolOption("active");
- if(m_ftpport.IsEmpty())
+ if(m_ftpport.empty())
m_ftpport = "-";
}
m_ftppasvip = url2.HasProtocolOption("pasvip") && url2.GetProtocolOption("pasvip") != "0";
if (CSettings::Get().GetBool("network.usehttpproxy")
&& !CSettings::Get().GetString("network.httpproxyserver").empty()
&& CSettings::Get().GetInt("network.httpproxyport") > 0
- && m_proxy.IsEmpty())
+ && m_proxy.empty())
{
m_proxy = CSettings::Get().GetString("network.httpproxyserver");
- m_proxy.AppendFormat(":%d", CSettings::Get().GetInt("network.httpproxyport"));
- if (CSettings::Get().GetString("network.httpproxyusername").length() > 0 && m_proxyuserpass.IsEmpty())
+ m_proxy += StringUtils::Format(":%d", CSettings::Get().GetInt("network.httpproxyport"));
+ if (CSettings::Get().GetString("network.httpproxyusername").length() > 0 && m_proxyuserpass.empty())
{
m_proxyuserpass = CSettings::Get().GetString("network.httpproxyusername");
m_proxyuserpass += ":" + CSettings::Get().GetString("network.httpproxypassword");
url2.GetProtocolOptions(options);
if (options.size() > 0)
{
- // clear protocol options
- url2.SetProtocolOptions("");
// set xbmc headers
for(std::map<CStdString, CStdString>::const_iterator it = options.begin(); it != options.end(); ++it)
{
if(name.Equals("auth"))
{
m_httpauth = value;
- if(m_httpauth.IsEmpty())
+ if(m_httpauth.empty())
m_httpauth = "any";
}
else if (name.Equals("Referer"))
m_seekable = false;
else if (name.Equals("Accept-Charset"))
SetAcceptCharset(value);
+ else if (name.Equals("HttpProxy"))
+ SetStreamProxy(value, PROXY_HTTP);
+ else if (name.Equals("SSLCipherList"))
+ m_cipherlist = value;
else
SetRequestHeader(name, value);
}
}
}
+
+ // Unset the protocol options to have an url without protocol options
+ url2.SetProtocolOptions("");
if (m_username.length() > 0 && m_password.length() > 0)
m_url = url2.GetWithoutUserDetails();
m_url = url2.Get();
}
+void CCurlFile::SetStreamProxy(const CStdString &proxy, ProxyType type)
+{
+ CURL url(proxy);
+ m_proxy = url.GetWithoutUserDetails();
+ m_proxyuserpass = url.GetUserName();
+ if (!url.GetPassWord().empty())
+ m_proxyuserpass += ":" + url.GetPassWord();
+ m_proxytype = type;
+ CLog::Log(LOGDEBUG, "Overriding proxy from URL parameter: %s, type %d", m_proxy.c_str(), proxyType2CUrlProxyType[m_proxytype]);
+}
+
bool CCurlFile::Post(const CStdString& strURL, const CStdString& strPostData, CStdString& strHTML)
{
m_postdata = strPostData;
}
// Detect whether we are "online" or not! Very simple and dirty!
-bool CCurlFile::IsInternet(bool checkDNS /* = true */)
+bool CCurlFile::IsInternet()
{
CStdString strURL = "http://www.google.com";
- if (!checkDNS)
- strURL = "http://74.125.19.103"; // www.google.com ip
-
bool found = Exists(strURL);
Close();
CURL url2(url);
ParseAndCorrectUrl(url2);
- CLog::Log(LOGDEBUG, "CurlFile::Open(%p) %s", (void*)this, m_url.c_str());
+ std::string redactPath = CURL::GetRedacted(m_url);
+ CLog::Log(LOGDEBUG, "CurlFile::Open(%p) %s", (void*)this, redactPath.c_str());
ASSERT(!(!m_state->m_easyHandle ^ !m_state->m_multiHandle));
if( m_state->m_easyHandle == NULL )
|| !m_state->m_httpheader.GetValue("icy-name").empty()
|| !m_state->m_httpheader.GetValue("icy-br").empty()) && !m_skipshout)
{
- CLog::Log(LOGDEBUG,"CCurlFile::Open - File <%s> is a shoutcast stream. Re-opening", m_url.c_str());
+ CLog::Log(LOGDEBUG,"CCurlFile::Open - File <%s> is a shoutcast stream. Re-opening", redactPath.c_str());
throw new CRedirectException(new CShoutcastFile);
}
CURL url2(url);
ParseAndCorrectUrl(url2);
- CLog::Log(LOGDEBUG, "CCurlFile::OpenForWrite(%p) %s", (void*)this, m_url.c_str());
+ CLog::Log(LOGDEBUG, "CCurlFile::OpenForWrite(%p) %s", (void*)this, CURL::GetRedacted(m_url).c_str());
ASSERT(m_state->m_easyHandle == NULL);
g_curlInterface.easy_aquire(url2.GetProtocol(), url2.GetHostName(), &m_state->m_easyHandle, &m_state->m_multiHandle);
{
long code;
if(g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_RESPONSE_CODE, &code) == CURLE_OK )
- CLog::Log(LOGERROR, "%s - Unable to write curl resource (%s) - %ld", __FUNCTION__, m_url.c_str(), code);
+ CLog::Log(LOGERROR, "%s - Unable to write curl resource (%s) - %ld", __FUNCTION__, CURL::GetRedacted(m_url).c_str(), code);
m_inError = true;
return -1;
}
// if file is already running, get info from it
if( m_opened )
{
- CLog::Log(LOGWARNING, "CCurlFile::Exists - Exist called on open file %s", url.Get().c_str());
+ CLog::Log(LOGWARNING, "CCurlFile::Exists - Exist called on open file %s", url.GetRedacted().c_str());
return true;
}
{
long code;
if(g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_RESPONSE_CODE, &code) == CURLE_OK && code != 404 )
- CLog::Log(LOGERROR, "CCurlFile::Exists - Failed: HTTP returned error %ld for %s", code, url.Get().c_str());
+ CLog::Log(LOGERROR, "CCurlFile::Exists - Failed: HTTP returned error %ld for %s", code, url.GetRedacted().c_str());
}
else if (result != CURLE_REMOTE_FILE_NOT_FOUND && result != CURLE_FTP_COULDNT_RETR_FILE)
{
- CLog::Log(LOGERROR, "CCurlFile::Exists - Failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
+ CLog::Log(LOGERROR, "CCurlFile::Exists - Failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.GetRedacted().c_str());
}
errno = ENOENT;
int64_t CCurlFile::Seek(int64_t iFilePosition, int iWhence)
{
int64_t nextPos = m_state->m_filePos;
+
+ if(!m_seekable)
+ return -1;
+
switch(iWhence)
{
case SEEK_SET:
if(m_state->Seek(nextPos))
return nextPos;
- if (m_oldState && m_oldState->Seek(nextPos))
+ if (m_multisession)
{
- CReadState *tmp = m_state;
- m_state = m_oldState;
- m_oldState = tmp;
- return nextPos;
- }
-
- if(!m_seekable)
- return -1;
-
- CReadState* oldstate = NULL;
- if(m_multisession)
- {
- CURL url(m_url);
- oldstate = m_oldState;
- m_oldState = m_state;
- m_state = new CReadState();
-
- g_curlInterface.easy_aquire(url.GetProtocol(), url.GetHostName(), &m_state->m_easyHandle, &m_state->m_multiHandle );
-
- m_state->m_fileSize = m_oldState->m_fileSize;
+ if (!m_oldState)
+ {
+ CURL url(m_url);
+ m_oldState = m_state;
+ m_state = new CReadState();
+ m_state->m_fileSize = m_oldState->m_fileSize;
+ g_curlInterface.easy_aquire(url.GetProtocol(),
+ url.GetHostName(),
+ &m_state->m_easyHandle,
+ &m_state->m_multiHandle );
+ }
+ else
+ {
+ CReadState *tmp;
+ tmp = m_state;
+ m_state = m_oldState;
+ m_oldState = tmp;
+
+ if (m_state->Seek(nextPos))
+ return nextPos;
+
+ m_state->Disconnect();
+ }
}
else
m_state->Disconnect();
long response = m_state->Connect(m_bufferSize);
if(response < 0 && (m_state->m_fileSize == 0 || m_state->m_fileSize != m_state->m_filePos))
{
- m_seekable = false;
- if(m_multisession && m_oldState)
+ if(m_multisession)
{
- delete m_state;
- m_state = m_oldState;
- m_oldState = oldstate;
+ if (m_oldState)
+ {
+ delete m_state;
+ m_state = m_oldState;
+ m_oldState = NULL;
+ }
+ // Retry without mutlisession
+ m_multisession = false;
+ return Seek(iFilePosition, iWhence);
}
- return -1;
+ else
+ {
+ m_seekable = false;
+ return -1;
+ }
}
SetCorrectHeaders(m_state);
- delete oldstate;
return m_state->m_filePos;
}
// if file is already running, get info from it
if( m_opened )
{
- CLog::Log(LOGWARNING, "CCurlFile::Stat - Stat called on open file %s", url.Get().c_str());
+ CLog::Log(LOGWARNING, "CCurlFile::Stat - Stat called on open file %s", url.GetRedacted().c_str());
if (buffer)
{
memset(buffer, 0, sizeof(struct __stat64));
{
g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
errno = ENOENT;
- CLog::Log(LOGERROR, "CCurlFile::Stat - Failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
+ CLog::Log(LOGERROR, "CCurlFile::Stat - Failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.GetRedacted().c_str());
return -1;
}
if (url.GetProtocol() == "ftp")
{
g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
- CLog::Log(LOGNOTICE, "CCurlFile::Stat - Content length failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
+ CLog::Log(LOGNOTICE, "CCurlFile::Stat - Content length failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.GetRedacted().c_str());
errno = ENOENT;
return -1;
}
result = g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_CONTENT_TYPE, &content);
if (result != CURLE_OK)
{
- CLog::Log(LOGNOTICE, "CCurlFile::Stat - Content type failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
+ CLog::Log(LOGNOTICE, "CCurlFile::Stat - Content type failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.GetRedacted().c_str());
g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
errno = ENOENT;
return -1;
result = g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_FILETIME, &filetime);
if (result != CURLE_OK)
{
- CLog::Log(LOGNOTICE, "CCurlFile::Stat - Filetime failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
+ CLog::Log(LOGNOTICE, "CCurlFile::Stat - Filetime failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.GetRedacted().c_str());
}
else
{
if (CURLM_OK != g_curlInterface.multi_timeout(m_multiHandle, &timeout) || timeout == -1)
timeout = 200;
- struct timeval t = { timeout / 1000, (timeout % 1000) * 1000 };
+ XbmcThreads::EndTime endTime(timeout);
+ int rc;
- /* Wait until data is available or a timeout occurs.
- We call dllselect(maxfd + 1, ...), specially in case of (maxfd == -1),
- we call dllselect(0, ...), which is basically equal to sleep. */
- if (SOCKET_ERROR == dllselect(maxfd + 1, &fdread, &fdwrite, &fdexcep, &t))
+ do
{
- CLog::Log(LOGERROR, "CCurlFile::FillBuffer - Failed with socket error");
+ unsigned int time_left = endTime.MillisLeft();
+ struct timeval t = { time_left / 1000, (time_left % 1000) * 1000 };
+
+ // Wait until data is available or a timeout occurs.
+ rc = select(maxfd + 1, &fdread, &fdwrite, &fdexcep, &t);
+#ifdef TARGET_WINDOWS
+ } while(rc == SOCKET_ERROR && WSAGetLastError() == WSAEINTR);
+#else
+ } while(rc == SOCKET_ERROR && errno == EINTR);
+#endif
+
+ if(rc == SOCKET_ERROR)
+ {
+#ifdef TARGET_WINDOWS
+ char buf[256];
+ strerror_s(buf, 256, WSAGetLastError());
+ CLog::Log(LOGERROR, "CCurlFile::FillBuffer - Failed with socket error:%s", buf);
+#else
+ char const * str = strerror(errno);
+ CLog::Log(LOGERROR, "CCurlFile::FillBuffer - Failed with socket error:%s", str);
+#endif
+
return false;
}
}
void CCurlFile::SetRequestHeader(CStdString header, long value)
{
- CStdString buffer;
- buffer.Format("%ld", value);
- m_requestheaders[header] = buffer;
+ m_requestheaders[header] = StringUtils::Format("%ld", value);
}
std::string CCurlFile::GetServerReportedCharset(void)
}
catch(...)
{
- CLog::Log(LOGERROR, "%s - Exception thrown while trying to retrieve header url: %s", __FUNCTION__, url.Get().c_str());
+ CLog::Log(LOGERROR, "%s - Exception thrown while trying to retrieve header url: %s", __FUNCTION__, url.GetRedacted().c_str());
return false;
}
}
bool CCurlFile::GetMimeType(const CURL &url, CStdString &content, CStdString useragent)
{
CCurlFile file;
- if (!useragent.IsEmpty())
+ if (!useragent.empty())
file.SetUserAgent(useragent);
struct __stat64 buffer;
+ std::string redactUrl = url.GetRedacted();
if( file.Stat(url, &buffer) == 0 )
{
if (buffer.st_mode == _S_IFDIR)
content = "x-directory/normal";
else
content = file.GetMimeType();
- CLog::Log(LOGDEBUG, "CCurlFile::GetMimeType - %s -> %s", url.Get().c_str(), content.c_str());
+ CLog::Log(LOGDEBUG, "CCurlFile::GetMimeType - %s -> %s", redactUrl.c_str(), content.c_str());
return true;
}
- CLog::Log(LOGDEBUG, "CCurlFile::GetMimeType - %s -> failed", url.Get().c_str());
+ CLog::Log(LOGDEBUG, "CCurlFile::GetMimeType - %s -> failed", redactUrl.c_str());
content = "";
return false;
}
if (valuesVec.size() < 7)
{
CLog::Log(LOGERROR, "CCurlFile::GetCookies - invalid cookie: '%s'", curlCookieIter->data);
+ curlCookieIter = curlCookieIter->next;
continue;
}