2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
22 #include "utils/URIUtils.h"
25 #include "settings/AdvancedSettings.h"
26 #include "settings/Settings.h"
35 #include "../linux/XFileUtils.h"
36 #include "../linux/XTimeUtils.h"
37 #include "../linux/ConvUtils.h"
40 #include "DllLibCurl.h"
41 #include "ShoutcastFile.h"
42 #include "SpecialProtocol.h"
43 #include "utils/CharsetConverter.h"
44 #include "utils/log.h"
46 using namespace XFILE;
47 using namespace XCURL;
49 #define XMIN(a,b) ((a)<(b)?(a):(b))
50 #define FITS_INT(a) (((a) <= INT_MAX) && ((a) >= INT_MIN))
52 #define dllselect select
55 curl_proxytype proxyType2CUrlProxyType[] = {
60 CURLPROXY_SOCKS5_HOSTNAME,
63 // curl calls this routine to debug
64 extern "C" int debug_callback(CURL_HANDLE *handle, curl_infotype info, char *output, size_t size, void *data)
66 if (info == CURLINFO_DATA_IN || info == CURLINFO_DATA_OUT)
69 if ((g_advancedSettings.m_extraLogLevels & LOGCURL) == 0)
73 strLine.append(output, size);
74 std::vector<CStdString> vecLines;
75 CUtil::Tokenize(strLine, vecLines, "\r\n");
76 std::vector<CStdString>::const_iterator it = vecLines.begin();
81 case CURLINFO_TEXT : infotype = (char *) "TEXT: "; break;
82 case CURLINFO_HEADER_IN : infotype = (char *) "HEADER_IN: "; break;
83 case CURLINFO_HEADER_OUT : infotype = (char *) "HEADER_OUT: "; break;
84 case CURLINFO_SSL_DATA_IN : infotype = (char *) "SSL_DATA_IN: "; break;
85 case CURLINFO_SSL_DATA_OUT : infotype = (char *) "SSL_DATA_OUT: "; break;
86 case CURLINFO_END : infotype = (char *) "END: "; break;
87 default : infotype = (char *) ""; break;
90 while (it != vecLines.end())
92 CLog::Log(LOGDEBUG, "Curl::Debug - %s%s", infotype, (*it).c_str());
98 /* curl calls this routine to get more data */
99 extern "C" size_t write_callback(char *buffer,
104 if(userp == NULL) return 0;
106 CCurlFile::CReadState *state = (CCurlFile::CReadState *)userp;
107 return state->WriteCallback(buffer, size, nitems);
110 extern "C" size_t read_callback(char *buffer,
115 if(userp == NULL) return 0;
117 CCurlFile::CReadState *state = (CCurlFile::CReadState *)userp;
118 return state->ReadCallback(buffer, size, nitems);
121 extern "C" size_t header_callback(void *ptr, size_t size, size_t nmemb, void *stream)
123 CCurlFile::CReadState *state = (CCurlFile::CReadState *)stream;
124 return state->HeaderCallback(ptr, size, nmemb);
127 /* fix for silly behavior of realloc */
128 static inline void* realloc_simple(void *ptr, size_t size)
130 void *ptr2 = realloc(ptr, size);
131 if(ptr && !ptr2 && size > 0)
140 size_t CCurlFile::CReadState::HeaderCallback(void *ptr, size_t size, size_t nmemb)
142 // clear any previous header
145 m_httpheader.Clear();
146 m_headerdone = false;
149 // libcurl doc says that this info is not always \0 terminated
150 char* strData = (char*)ptr;
151 int iSize = size * nmemb;
153 if (strData[iSize] != 0)
155 strData = (char*)malloc(iSize + 1);
156 strncpy(strData, (char*)ptr, iSize);
159 else strData = strdup((char*)ptr);
161 if(strcmp(strData, "\r\n") == 0)
164 m_httpheader.Parse(strData);
171 size_t CCurlFile::CReadState::ReadCallback(char *buffer, size_t size, size_t nitems)
176 if (m_filePos >= m_fileSize)
179 return CURL_READFUNC_PAUSE;
182 int64_t retSize = XMIN(m_fileSize - m_filePos, int64_t(nitems * size));
183 memcpy(buffer, m_readBuffer + m_filePos, retSize);
184 m_filePos += retSize;
189 size_t CCurlFile::CReadState::WriteCallback(char *buffer, size_t size, size_t nitems)
191 unsigned int amount = size * nitems;
192 // CLog::Log(LOGDEBUG, "CCurlFile::WriteCallback (%p) with %i bytes, readsize = %i, writesize = %i", this, amount, m_buffer.getMaxReadSize(), m_buffer.getMaxWriteSize() - m_overflowSize);
195 // we have our overflow buffer - first get rid of as much as we can
196 unsigned int maxWriteable = XMIN((unsigned int)m_buffer.getMaxWriteSize(), m_overflowSize);
199 if (!m_buffer.WriteData(m_overflowBuffer, maxWriteable))
200 CLog::Log(LOGERROR, "CCurlFile::WriteCallback - Unable to write to buffer - what's up?");
201 if (m_overflowSize > maxWriteable)
202 { // still have some more - copy it down
203 memmove(m_overflowBuffer, m_overflowBuffer + maxWriteable, m_overflowSize - maxWriteable);
205 m_overflowSize -= maxWriteable;
208 // ok, now copy the data into our ring buffer
209 unsigned int maxWriteable = XMIN((unsigned int)m_buffer.getMaxWriteSize(), amount);
212 if (!m_buffer.WriteData(buffer, maxWriteable))
214 CLog::Log(LOGERROR, "CCurlFile::WriteCallback - Unable to write to buffer with %i bytes - what's up?", maxWriteable);
218 amount -= maxWriteable;
219 buffer += maxWriteable;
224 // CLog::Log(LOGDEBUG, "CCurlFile::WriteCallback(%p) not enough free space for %i bytes", (void*)this, amount);
226 m_overflowBuffer = (char*)realloc_simple(m_overflowBuffer, amount + m_overflowSize);
227 if(m_overflowBuffer == NULL)
229 CLog::Log(LOGWARNING, "CCurlFile::WriteCallback - Failed to grow overflow buffer from %i bytes to %i bytes", m_overflowSize, amount + m_overflowSize);
232 memcpy(m_overflowBuffer + m_overflowSize, buffer, amount);
233 m_overflowSize += amount;
235 return size * nitems;
238 CCurlFile::CReadState::CReadState()
241 m_multiHandle = NULL;
242 m_overflowBuffer = NULL;
250 m_headerdone = false;
253 m_curlHeaderList = NULL;
254 m_curlAliasList = NULL;
257 CCurlFile::CReadState::~CReadState()
262 g_curlInterface.easy_release(&m_easyHandle, &m_multiHandle);
265 bool CCurlFile::CReadState::Seek(int64_t pos)
270 if(FITS_INT(pos - m_filePos) && m_buffer.SkipBytes((int)(pos - m_filePos)))
276 if(pos > m_filePos && pos < m_filePos + m_bufferSize)
278 int len = m_buffer.getMaxReadSize();
280 m_buffer.SkipBytes(len);
281 if(!FillBuffer(m_bufferSize))
283 if(!m_buffer.SkipBytes(-len))
284 CLog::Log(LOGERROR, "%s - Failed to restore position after failed fill", __FUNCTION__);
290 if(!FITS_INT(pos - m_filePos) || !m_buffer.SkipBytes((int)(pos - m_filePos)))
292 CLog::Log(LOGERROR, "%s - Failed to skip to position after having filled buffer", __FUNCTION__);
293 if(!m_buffer.SkipBytes(-len))
294 CLog::Log(LOGERROR, "%s - Failed to restore position after failed seek", __FUNCTION__);
305 void CCurlFile::CReadState::SetResume(void)
308 * Explicitly set RANGE header when filepos=0 as some http servers require us to always send the range
309 * request header. If we don't the server may provide different content causing seeking to fail.
310 * This only affects HTTP-like items, for FTP it's a null operation.
312 if (m_sendRange && m_filePos == 0)
313 g_curlInterface.easy_setopt(m_easyHandle, CURLOPT_RANGE, "0-");
316 g_curlInterface.easy_setopt(m_easyHandle, CURLOPT_RANGE, NULL);
320 g_curlInterface.easy_setopt(m_easyHandle, CURLOPT_RESUME_FROM_LARGE, m_filePos);
323 long CCurlFile::CReadState::Connect(unsigned int size)
326 CLog::Log(LOGDEBUG,"CurlFile::CReadState::Connect - Resume from position %"PRId64, m_filePos);
329 g_curlInterface.multi_add_handle(m_multiHandle, m_easyHandle);
333 m_buffer.Create(size * 3);
334 m_headerdone = false;
336 // read some data in to try and obtain the length
337 // maybe there's a better way to get this info??
341 CLog::Log(LOGERROR, "CCurlFile::CReadState::Connect, didn't get any data from stream.");
346 if (CURLE_OK == g_curlInterface.easy_getinfo(m_easyHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length))
350 m_fileSize = m_filePos + (int64_t)length;
354 if (CURLE_OK == g_curlInterface.easy_getinfo(m_easyHandle, CURLINFO_RESPONSE_CODE, &response))
360 void CCurlFile::CReadState::Disconnect()
362 if(m_multiHandle && m_easyHandle)
363 g_curlInterface.multi_remove_handle(m_multiHandle, m_easyHandle);
366 free(m_overflowBuffer);
367 m_overflowBuffer = NULL;
375 if( m_curlHeaderList )
376 g_curlInterface.slist_free_all(m_curlHeaderList);
377 m_curlHeaderList = NULL;
379 if( m_curlAliasList )
380 g_curlInterface.slist_free_all(m_curlAliasList);
381 m_curlAliasList = NULL;
385 CCurlFile::~CCurlFile()
390 g_curlInterface.Unload();
393 CCurlFile::CCurlFile()
395 g_curlInterface.Load(); // loads the curl dll and resolves exports etc.
399 m_multisession = true;
401 m_useOldHttpVersion = false;
402 m_connecttimeout = 0;
407 m_bufferSize = 32768;
410 m_postdataset = false;
414 m_proxytype = PROXY_HTTP;
415 m_state = new CReadState();
421 //Has to be called before Open()
422 void CCurlFile::SetBufferSize(unsigned int size)
427 void CCurlFile::Close()
429 if (m_opened && m_forWrite && !m_inError)
432 m_state->Disconnect();
445 void CCurlFile::SetCommonOptions(CReadState* state)
447 CURL_HANDLE* h = state->m_easyHandle;
449 g_curlInterface.easy_reset(h);
451 g_curlInterface.easy_setopt(h, CURLOPT_DEBUGFUNCTION, debug_callback);
453 if( g_advancedSettings.m_logLevel >= LOG_LEVEL_DEBUG )
454 g_curlInterface.easy_setopt(h, CURLOPT_VERBOSE, TRUE);
456 g_curlInterface.easy_setopt(h, CURLOPT_VERBOSE, FALSE);
458 g_curlInterface.easy_setopt(h, CURLOPT_WRITEDATA, state);
459 g_curlInterface.easy_setopt(h, CURLOPT_WRITEFUNCTION, write_callback);
461 g_curlInterface.easy_setopt(h, CURLOPT_READDATA, state);
462 g_curlInterface.easy_setopt(h, CURLOPT_READFUNCTION, read_callback);
464 // set username and password for current handle
465 if (m_username.length() > 0 && m_password.length() > 0)
467 CStdString userpwd = m_username + ":" + m_password;
468 g_curlInterface.easy_setopt(h, CURLOPT_USERPWD, userpwd.c_str());
471 // make sure headers are seperated from the data stream
472 g_curlInterface.easy_setopt(h, CURLOPT_WRITEHEADER, state);
473 g_curlInterface.easy_setopt(h, CURLOPT_HEADERFUNCTION, header_callback);
474 g_curlInterface.easy_setopt(h, CURLOPT_HEADER, FALSE);
476 g_curlInterface.easy_setopt(h, CURLOPT_FTP_USE_EPSV, 0); // turn off epsv
478 // Allow us to follow two redirects
479 g_curlInterface.easy_setopt(h, CURLOPT_FOLLOWLOCATION, TRUE);
480 g_curlInterface.easy_setopt(h, CURLOPT_MAXREDIRS, 5);
482 // Enable cookie engine for current handle to re-use them in future requests
483 CStdString strCookieFile;
484 CStdString strTempPath = CSpecialProtocol::TranslatePath(g_advancedSettings.m_cachePath);
485 strCookieFile = URIUtils::AddFileToFolder(strTempPath, "cookies.dat");
487 g_curlInterface.easy_setopt(h, CURLOPT_COOKIEFILE, strCookieFile.c_str());
488 g_curlInterface.easy_setopt(h, CURLOPT_COOKIEJAR, strCookieFile.c_str());
490 // Set custom cookie if requested
491 if (!m_cookie.IsEmpty())
492 g_curlInterface.easy_setopt(h, CURLOPT_COOKIE, m_cookie.c_str());
494 g_curlInterface.easy_setopt(h, CURLOPT_COOKIELIST, "FLUSH");
496 // When using multiple threads you should set the CURLOPT_NOSIGNAL option to
497 // TRUE for all handles. Everything will work fine except that timeouts are not
498 // honored during the DNS lookup - which you can work around by building libcurl
499 // with c-ares support. c-ares is a library that provides asynchronous name
500 // resolves. Unfortunately, c-ares does not yet support IPv6.
501 g_curlInterface.easy_setopt(h, CURLOPT_NOSIGNAL, TRUE);
503 // not interested in failed requests
504 g_curlInterface.easy_setopt(h, CURLOPT_FAILONERROR, 1);
506 // enable support for icecast / shoutcast streams
507 if ( NULL == state->m_curlAliasList )
508 // m_curlAliasList is used only by this one place, but SetCommonOptions can
509 // be called multiple times, only append to list if it's empty.
510 state->m_curlAliasList = g_curlInterface.slist_append(state->m_curlAliasList, "ICY 200 OK");
511 g_curlInterface.easy_setopt(h, CURLOPT_HTTP200ALIASES, state->m_curlAliasList);
513 // never verify peer, we don't have any certificates to do this
514 g_curlInterface.easy_setopt(h, CURLOPT_SSL_VERIFYPEER, 0);
515 g_curlInterface.easy_setopt(h, CURLOPT_SSL_VERIFYHOST, 0);
517 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_URL, m_url.c_str());
518 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_TRANSFERTEXT, FALSE);
520 // setup POST data if it is set (and it may be empty)
523 g_curlInterface.easy_setopt(h, CURLOPT_POST, 1 );
524 g_curlInterface.easy_setopt(h, CURLOPT_POSTFIELDSIZE, m_postdata.length());
525 g_curlInterface.easy_setopt(h, CURLOPT_POSTFIELDS, m_postdata.c_str());
528 // setup Referer header if needed
529 if (!m_referer.IsEmpty())
530 g_curlInterface.easy_setopt(h, CURLOPT_REFERER, m_referer.c_str());
533 g_curlInterface.easy_setopt(h, CURLOPT_REFERER, NULL);
534 g_curlInterface.easy_setopt(h, CURLOPT_AUTOREFERER, TRUE);
537 // setup any requested authentication
538 if( m_ftpauth.length() > 0 )
540 g_curlInterface.easy_setopt(h, CURLOPT_FTP_SSL, CURLFTPSSL_TRY);
541 if( m_ftpauth.Equals("any") )
542 g_curlInterface.easy_setopt(h, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_DEFAULT);
543 else if( m_ftpauth.Equals("ssl") )
544 g_curlInterface.easy_setopt(h, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_SSL);
545 else if( m_ftpauth.Equals("tls") )
546 g_curlInterface.easy_setopt(h, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS);
549 // setup requested http authentication method
550 if(m_httpauth.length() > 0)
552 if( m_httpauth.Equals("any") )
553 g_curlInterface.easy_setopt(h, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
554 else if( m_httpauth.Equals("anysafe") )
555 g_curlInterface.easy_setopt(h, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
556 else if( m_httpauth.Equals("digest") )
557 g_curlInterface.easy_setopt(h, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST);
558 else if( m_httpauth.Equals("ntlm") )
559 g_curlInterface.easy_setopt(h, CURLOPT_HTTPAUTH, CURLAUTH_NTLM);
562 // allow passive mode for ftp
563 if( m_ftpport.length() > 0 )
564 g_curlInterface.easy_setopt(h, CURLOPT_FTPPORT, m_ftpport.c_str());
566 g_curlInterface.easy_setopt(h, CURLOPT_FTPPORT, NULL);
568 // allow curl to not use the ip address in the returned pasv response
570 g_curlInterface.easy_setopt(h, CURLOPT_FTP_SKIP_PASV_IP, 0);
572 g_curlInterface.easy_setopt(h, CURLOPT_FTP_SKIP_PASV_IP, 1);
574 // setup Content-Encoding if requested
575 if( m_contentencoding.length() > 0 )
576 g_curlInterface.easy_setopt(h, CURLOPT_ENCODING, m_contentencoding.c_str());
578 if (m_userAgent.length() > 0)
579 g_curlInterface.easy_setopt(h, CURLOPT_USERAGENT, m_userAgent.c_str());
580 else /* set some default agent as shoutcast doesn't return proper stuff otherwise */
581 g_curlInterface.easy_setopt(h, CURLOPT_USERAGENT, g_advancedSettings.m_userAgent.c_str());
583 if (m_useOldHttpVersion)
584 g_curlInterface.easy_setopt(h, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
586 SetRequestHeader("Connection", "keep-alive");
588 if (g_advancedSettings.m_curlDisableIPV6)
589 g_curlInterface.easy_setopt(h, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
591 if (m_proxy.length() > 0)
593 g_curlInterface.easy_setopt(h, CURLOPT_PROXY, m_proxy.c_str());
594 g_curlInterface.easy_setopt(h, CURLOPT_PROXYTYPE, proxyType2CUrlProxyType[m_proxytype]);
595 if (m_proxyuserpass.length() > 0)
596 g_curlInterface.easy_setopt(h, CURLOPT_PROXYUSERPWD, m_proxyuserpass.c_str());
599 if (m_customrequest.length() > 0)
600 g_curlInterface.easy_setopt(h, CURLOPT_CUSTOMREQUEST, m_customrequest.c_str());
602 if (m_connecttimeout == 0)
603 m_connecttimeout = g_advancedSettings.m_curlconnecttimeout;
605 // set our timeouts, we abort connection after m_timeout, and reads after no data for m_timeout seconds
606 g_curlInterface.easy_setopt(h, CURLOPT_CONNECTTIMEOUT, m_connecttimeout);
608 // We abort in case we transfer less than 1byte/second
609 g_curlInterface.easy_setopt(h, CURLOPT_LOW_SPEED_LIMIT, 1);
611 if (m_lowspeedtime == 0)
612 m_lowspeedtime = g_advancedSettings.m_curllowspeedtime;
614 // Set the lowspeed time very low as it seems Curl takes much longer to detect a lowspeed condition
615 g_curlInterface.easy_setopt(h, CURLOPT_LOW_SPEED_TIME, m_lowspeedtime);
618 // For shoutcast file, content-length should not be set, and in libcurl there is a bug, if the
619 // cast file was 302 redirected then getinfo of CURLINFO_CONTENT_LENGTH_DOWNLOAD will return
620 // the 302 response's body length, which cause the next read request failed, so we ignore
621 // content-length for shoutcast file to workaround this.
622 g_curlInterface.easy_setopt(h, CURLOPT_IGNORE_CONTENT_LENGTH, 1);
625 void CCurlFile::SetRequestHeaders(CReadState* state)
627 if(state->m_curlHeaderList)
629 g_curlInterface.slist_free_all(state->m_curlHeaderList);
630 state->m_curlHeaderList = NULL;
633 MAPHTTPHEADERS::iterator it;
634 for(it = m_requestheaders.begin(); it != m_requestheaders.end(); it++)
636 CStdString buffer = it->first + ": " + it->second;
637 state->m_curlHeaderList = g_curlInterface.slist_append(state->m_curlHeaderList, buffer.c_str());
640 // add user defined headers
641 if (state->m_easyHandle)
642 g_curlInterface.easy_setopt(state->m_easyHandle, CURLOPT_HTTPHEADER, state->m_curlHeaderList);
645 void CCurlFile::SetCorrectHeaders(CReadState* state)
647 CHttpHeader& h = state->m_httpheader;
648 /* workaround for shoutcast server wich doesn't set content type on standard mp3 */
649 if( h.GetMimeType().IsEmpty() )
651 if( !h.GetValue("icy-notice1").IsEmpty()
652 || !h.GetValue("icy-name").IsEmpty()
653 || !h.GetValue("icy-br").IsEmpty() )
654 h.Parse("Content-Type: audio/mpeg\r\n");
657 /* hack for google video */
658 if ( h.GetMimeType().Equals("text/html")
659 && !h.GetValue("Content-Disposition").IsEmpty() )
661 CStdString strValue = h.GetValue("Content-Disposition");
662 if (strValue.Find("filename=") > -1 && strValue.Find(".flv") > -1)
663 h.Parse("Content-Type: video/flv\r\n");
667 void CCurlFile::ParseAndCorrectUrl(CURL &url2)
669 CStdString strProtocol = url2.GetTranslatedProtocol();
670 url2.SetProtocol(strProtocol);
672 if( strProtocol.Equals("ftp")
673 || strProtocol.Equals("ftps") )
675 // we was using url optons for urls, keep the old code work and warning
676 if (!url2.GetOptions().IsEmpty())
678 CLog::Log(LOGWARNING, "%s: ftp url option is deprecated, please switch to use protocol option (change '?' to '|'), url: [%s]", __FUNCTION__, url2.Get().c_str());
679 url2.SetProtocolOptions(url2.GetOptions().Mid(1));
680 /* ftp has no options */
684 /* this is uggly, depending on from where */
685 /* we get the link it may or may not be */
686 /* url encoded. if handed from ftpdirectory */
687 /* it won't be so let's handle that case */
689 CStdString partial, filename(url2.GetFileName());
690 CStdStringArray array;
692 // if server sent us the filename in non-utf8, we need send back with same encoding.
693 if (url2.GetProtocolOption("utf8") == "0")
694 g_charsetConverter.utf8ToStringCharset(filename);
696 /* TODO: create a tokenizer that doesn't skip empty's */
697 CUtil::Tokenize(filename, array, "/");
699 for(CStdStringArray::iterator it = array.begin(); it != array.end(); it++)
701 if(it != array.begin())
705 CURL::Encode(partial);
709 /* make sure we keep slashes */
710 if(url2.GetFileName().Right(1) == "/")
713 url2.SetFileName(filename);
716 if (url2.HasProtocolOption("auth"))
718 m_ftpauth = url2.GetProtocolOption("auth");
719 if(m_ftpauth.IsEmpty())
723 if (url2.HasProtocolOption("active"))
725 m_ftpport = url2.GetProtocolOption("active");
726 if(m_ftpport.IsEmpty())
729 m_ftppasvip = url2.HasProtocolOption("pasvip") && url2.GetProtocolOption("pasvip") != "0";
731 else if( strProtocol.Equals("http")
732 || strProtocol.Equals("https"))
734 if (CSettings::Get().GetBool("network.usehttpproxy")
735 && !CSettings::Get().GetString("network.httpproxyserver").empty()
736 && CSettings::Get().GetInt("network.httpproxyport") > 0
737 && m_proxy.IsEmpty())
739 m_proxy = CSettings::Get().GetString("network.httpproxyserver");
740 m_proxy.AppendFormat(":%d", CSettings::Get().GetInt("network.httpproxyport"));
741 if (CSettings::Get().GetString("network.httpproxyusername").length() > 0 && m_proxyuserpass.IsEmpty())
743 m_proxyuserpass = CSettings::Get().GetString("network.httpproxyusername");
744 m_proxyuserpass += ":" + CSettings::Get().GetString("network.httpproxypassword");
746 m_proxytype = (ProxyType)CSettings::Get().GetInt("network.httpproxytype");
747 CLog::Log(LOGDEBUG, "Using proxy %s, type %d", m_proxy.c_str(), proxyType2CUrlProxyType[m_proxytype]);
750 // get username and password
751 m_username = url2.GetUserName();
752 m_password = url2.GetPassWord();
754 // handle any protocol options
755 std::map<CStdString, CStdString> options;
756 url2.GetProtocolOptions(options);
757 if (options.size() > 0)
759 // clear protocol options
760 url2.SetProtocolOptions("");
762 for(std::map<CStdString, CStdString>::const_iterator it = options.begin(); it != options.end(); ++it)
764 const CStdString &name = it->first;
765 const CStdString &value = it->second;
767 if(name.Equals("auth"))
770 if(m_httpauth.IsEmpty())
773 else if (name.Equals("Referer"))
775 else if (name.Equals("User-Agent"))
777 else if (name.Equals("Cookie"))
779 else if (name.Equals("Encoding"))
780 SetContentEncoding(value);
781 else if (name.Equals("noshout") && value.Equals("true"))
783 else if (name.Equals("seekable") && value.Equals("0"))
786 SetRequestHeader(name, value);
791 if (m_username.length() > 0 && m_password.length() > 0)
792 m_url = url2.GetWithoutUserDetails();
797 bool CCurlFile::Post(const CStdString& strURL, const CStdString& strPostData, CStdString& strHTML)
799 m_postdata = strPostData;
800 m_postdataset = true;
801 return Service(strURL, strHTML);
804 bool CCurlFile::Get(const CStdString& strURL, CStdString& strHTML)
807 m_postdataset = false;
808 return Service(strURL, strHTML);
811 bool CCurlFile::Service(const CStdString& strURL, CStdString& strHTML)
815 if (ReadData(strHTML))
825 bool CCurlFile::ReadData(CStdString& strHTML)
831 while( (size_read = Read(buffer, sizeof(buffer)-1) ) > 0 )
833 buffer[size_read] = 0;
834 strHTML.append(buffer, size_read);
835 data_size += size_read;
837 if (m_state->m_cancelled)
842 bool CCurlFile::Download(const CStdString& strURL, const CStdString& strFileName, LPDWORD pdwSize)
844 CLog::Log(LOGINFO, "CCurlFile::Download - %s->%s", strURL.c_str(), strFileName.c_str());
847 if (!Get(strURL, strData))
851 if (!file.OpenForWrite(strFileName, true))
853 CLog::Log(LOGERROR, "CCurlFile::Download - Unable to open file %s: %u",
854 strFileName.c_str(), GetLastError());
858 file.Write(strData.c_str(), strData.size());
863 *pdwSize = strData.size();
869 // Detect whether we are "online" or not! Very simple and dirty!
870 bool CCurlFile::IsInternet(bool checkDNS /* = true */)
872 CStdString strURL = "http://www.google.com";
874 strURL = "http://74.125.19.103"; // www.google.com ip
876 bool found = Exists(strURL);
882 void CCurlFile::Cancel()
884 m_state->m_cancelled = true;
889 void CCurlFile::Reset()
891 m_state->m_cancelled = false;
894 bool CCurlFile::Open(const CURL& url)
900 ParseAndCorrectUrl(url2);
902 CLog::Log(LOGDEBUG, "CurlFile::Open(%p) %s", (void*)this, m_url.c_str());
904 ASSERT(!(!m_state->m_easyHandle ^ !m_state->m_multiHandle));
905 if( m_state->m_easyHandle == NULL )
906 g_curlInterface.easy_aquire(url2.GetProtocol(), url2.GetHostName(), &m_state->m_easyHandle, &m_state->m_multiHandle );
908 // setup common curl options
909 SetCommonOptions(m_state);
910 SetRequestHeaders(m_state);
911 m_state->m_sendRange = m_seekable;
913 m_httpresponse = m_state->Connect(m_bufferSize);
914 if( m_httpresponse < 0 || m_httpresponse >= 400)
917 SetCorrectHeaders(m_state);
919 // since we can't know the stream size up front if we're gzipped/deflated
920 // flag the stream with an unknown file size rather than the compressed
922 if (m_contentencoding.size() > 0)
923 m_state->m_fileSize = 0;
925 // check if this stream is a shoutcast stream. sometimes checking the protocol line is not enough so examine other headers as well.
926 // shoutcast streams should be handled by FileShoutcast.
927 if ((m_state->m_httpheader.GetProtoLine().Left(3) == "ICY" || !m_state->m_httpheader.GetValue("icy-notice1").IsEmpty()
928 || !m_state->m_httpheader.GetValue("icy-name").IsEmpty()
929 || !m_state->m_httpheader.GetValue("icy-br").IsEmpty()) && !m_skipshout)
931 CLog::Log(LOGDEBUG,"CCurlFile::Open - File <%s> is a shoutcast stream. Re-opening", m_url.c_str());
932 throw new CRedirectException(new CShoutcastFile);
935 m_multisession = false;
936 if(url2.GetProtocol().Equals("http") || url2.GetProtocol().Equals("https"))
938 m_multisession = true;
939 if(m_state->m_httpheader.GetValue("Server").Find("Portable SDK for UPnP devices") >= 0)
941 CLog::Log(LOGWARNING, "CCurlFile::Open - Disabling multi session due to broken libupnp server");
942 m_multisession = false;
946 if(m_state->m_httpheader.GetValue("Transfer-Encoding").Equals("chunked"))
947 m_state->m_fileSize = 0;
949 if(m_state->m_fileSize <= 0)
953 if(url2.GetProtocol().Equals("http")
954 || url2.GetProtocol().Equals("https"))
956 // if server says explicitly it can't seek, respect that
957 if(m_state->m_httpheader.GetValue("Accept-Ranges").Equals("none"))
963 if (CURLE_OK == g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_EFFECTIVE_URL,&efurl) && efurl)
969 bool CCurlFile::OpenForWrite(const CURL& url, bool bOverWrite)
974 if (Exists(url) && !bOverWrite)
978 ParseAndCorrectUrl(url2);
980 CLog::Log(LOGDEBUG, "CCurlFile::OpenForWrite(%p) %s", (void*)this, m_url.c_str());
982 ASSERT(m_state->m_easyHandle == NULL);
983 g_curlInterface.easy_aquire(url2.GetProtocol(), url2.GetHostName(), &m_state->m_easyHandle, &m_state->m_multiHandle);
985 // setup common curl options
986 SetCommonOptions(m_state);
987 SetRequestHeaders(m_state);
990 if (CURLE_OK == g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_EFFECTIVE_URL,&efurl) && efurl)
998 ASSERT(m_state->m_multiHandle);
1000 SetCommonOptions(m_state);
1001 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_UPLOAD, 1);
1003 g_curlInterface.multi_add_handle(m_state->m_multiHandle, m_state->m_easyHandle);
1005 m_state->SetReadBuffer(NULL, 0);
1010 int CCurlFile::Write(const void* lpBuf, int64_t uiBufSize)
1012 if (!(m_opened && m_forWrite) || m_inError)
1015 ASSERT(m_state->m_multiHandle);
1017 m_state->SetReadBuffer(lpBuf, uiBufSize);
1018 m_state->m_isPaused = false;
1019 g_curlInterface.easy_pause(m_state->m_easyHandle, CURLPAUSE_CONT);
1021 CURLMcode result = CURLM_OK;
1024 while (m_stillRunning && !m_state->m_isPaused)
1026 while ((result = g_curlInterface.multi_perform(m_state->m_multiHandle, &m_stillRunning)) == CURLM_CALL_MULTI_PERFORM);
1028 if (!m_stillRunning)
1031 if (result != CURLM_OK)
1034 if(g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_RESPONSE_CODE, &code) == CURLE_OK )
1035 CLog::Log(LOGERROR, "%s - Unable to write curl resource (%s) - %ld", __FUNCTION__, m_url.c_str(), code);
1041 m_writeOffset += m_state->m_filePos;
1042 return m_state->m_filePos;
1045 bool CCurlFile::CReadState::ReadString(char *szLine, int iLineLength)
1047 unsigned int want = (unsigned int)iLineLength;
1049 if((m_fileSize == 0 || m_filePos < m_fileSize) && !FillBuffer(want))
1052 // ensure only available data is considered
1053 want = XMIN((unsigned int)m_buffer.getMaxReadSize(), want);
1055 /* check if we finished prematurely */
1056 if (!m_stillRunning && (m_fileSize == 0 || m_filePos != m_fileSize) && !want)
1058 if (m_fileSize != 0)
1059 CLog::Log(LOGWARNING, "%s - Transfer ended before entire file was retrieved pos %"PRId64", size %"PRId64, __FUNCTION__, m_filePos, m_fileSize);
1064 char* pLine = szLine;
1067 if (!m_buffer.ReadData(pLine, 1))
1071 } while (((pLine - 1)[0] != '\n') && ((unsigned int)(pLine - szLine) < want));
1073 m_filePos += (pLine - szLine);
1074 return (bool)((pLine - szLine) > 0);
1077 bool CCurlFile::Exists(const CURL& url)
1079 // if file is already running, get info from it
1082 CLog::Log(LOGWARNING, "CCurlFile::Exists - Exist called on open file %s", url.Get().c_str());
1087 ParseAndCorrectUrl(url2);
1089 ASSERT(m_state->m_easyHandle == NULL);
1090 g_curlInterface.easy_aquire(url2.GetProtocol(), url2.GetHostName(), &m_state->m_easyHandle, NULL);
1092 SetCommonOptions(m_state);
1093 SetRequestHeaders(m_state);
1094 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_TIMEOUT, 5);
1095 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_NOBODY, 1);
1096 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_WRITEDATA, NULL); /* will cause write failure*/
1098 if(url2.GetProtocol() == "ftp")
1100 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_FILETIME, 1);
1101 // nocwd is less standard, will return empty list for non-existed remote dir on some ftp server, avoid it.
1102 if (url2.GetFileName().Right(1).Equals("/"))
1103 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD);
1105 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD);
1108 CURLcode result = g_curlInterface.easy_perform(m_state->m_easyHandle);
1109 g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
1111 if (result == CURLE_WRITE_ERROR || result == CURLE_OK)
1114 if (result == CURLE_HTTP_RETURNED_ERROR)
1117 if(g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_RESPONSE_CODE, &code) == CURLE_OK && code != 404 )
1118 CLog::Log(LOGERROR, "CCurlFile::Exists - Failed: HTTP returned error %ld for %s", code, url.Get().c_str());
1120 else if (result != CURLE_REMOTE_FILE_NOT_FOUND && result != CURLE_FTP_COULDNT_RETR_FILE)
1122 CLog::Log(LOGERROR, "CCurlFile::Exists - Failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
1129 int64_t CCurlFile::Seek(int64_t iFilePosition, int iWhence)
1131 int64_t nextPos = m_state->m_filePos;
1135 nextPos = iFilePosition;
1138 nextPos += iFilePosition;
1141 if (m_state->m_fileSize)
1142 nextPos = m_state->m_fileSize + iFilePosition;
1150 // We can't seek beyond EOF
1151 if (m_state->m_fileSize && nextPos > m_state->m_fileSize) return -1;
1153 if(m_state->Seek(nextPos))
1156 if (m_oldState && m_oldState->Seek(nextPos))
1158 CReadState *tmp = m_state;
1159 m_state = m_oldState;
1167 CReadState* oldstate = NULL;
1171 oldstate = m_oldState;
1172 m_oldState = m_state;
1173 m_state = new CReadState();
1175 g_curlInterface.easy_aquire(url.GetProtocol(), url.GetHostName(), &m_state->m_easyHandle, &m_state->m_multiHandle );
1177 m_state->m_fileSize = m_oldState->m_fileSize;
1180 m_state->Disconnect();
1182 // re-setup common curl options
1183 SetCommonOptions(m_state);
1185 /* caller might have changed some headers (needed for daap)*/
1186 SetRequestHeaders(m_state);
1188 m_state->m_filePos = nextPos;
1189 m_state->m_sendRange = true;
1191 long response = m_state->Connect(m_bufferSize);
1192 if(response < 0 && (m_state->m_fileSize == 0 || m_state->m_fileSize != m_state->m_filePos))
1195 if(m_multisession && m_oldState)
1198 m_state = m_oldState;
1199 m_oldState = oldstate;
1204 SetCorrectHeaders(m_state);
1207 return m_state->m_filePos;
1210 int64_t CCurlFile::GetLength()
1212 if (!m_opened) return 0;
1213 return m_state->m_fileSize;
1216 int64_t CCurlFile::GetPosition()
1218 if (!m_opened) return 0;
1219 return m_state->m_filePos;
1222 int CCurlFile::Stat(const CURL& url, struct __stat64* buffer)
1224 // if file is already running, get info from it
1227 CLog::Log(LOGWARNING, "CCurlFile::Stat - Stat called on open file %s", url.Get().c_str());
1230 memset(buffer, 0, sizeof(struct __stat64));
1231 buffer->st_size = GetLength();
1232 buffer->st_mode = _S_IFREG;
1238 ParseAndCorrectUrl(url2);
1240 ASSERT(m_state->m_easyHandle == NULL);
1241 g_curlInterface.easy_aquire(url2.GetProtocol(), url2.GetHostName(), &m_state->m_easyHandle, NULL);
1243 SetCommonOptions(m_state);
1244 SetRequestHeaders(m_state);
1245 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_TIMEOUT, g_advancedSettings.m_curlconnecttimeout);
1246 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_NOBODY, 1);
1247 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_WRITEDATA, NULL); /* will cause write failure*/
1248 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_FILETIME , 1);
1250 if(url2.GetProtocol() == "ftp")
1252 // nocwd is less standard, will return empty list for non-existed remote dir on some ftp server, avoid it.
1253 if (url2.GetFileName().Right(1).Equals("/"))
1254 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_SINGLECWD);
1256 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_FTP_FILEMETHOD, CURLFTPMETHOD_NOCWD);
1259 CURLcode result = g_curlInterface.easy_perform(m_state->m_easyHandle);
1261 if(result == CURLE_HTTP_RETURNED_ERROR)
1264 if(g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_RESPONSE_CODE, &code) == CURLE_OK && code == 404 )
1268 if(result == CURLE_GOT_NOTHING
1269 || result == CURLE_HTTP_RETURNED_ERROR
1270 || result == CURLE_RECV_ERROR /* some silly shoutcast servers */ )
1272 /* some http servers and shoutcast servers don't give us any data on a head request */
1273 /* request normal and just fail out, it's their loss */
1274 /* somehow curl doesn't reset CURLOPT_NOBODY properly so reset everything */
1275 SetCommonOptions(m_state);
1276 SetRequestHeaders(m_state);
1277 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_TIMEOUT, g_advancedSettings.m_curlconnecttimeout);
1278 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_RANGE, "0-0");
1279 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_WRITEDATA, NULL); /* will cause write failure*/
1280 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_FILETIME, 1);
1281 result = g_curlInterface.easy_perform(m_state->m_easyHandle);
1284 if( result == CURLE_HTTP_RANGE_ERROR )
1286 /* crap can't use the range option, disable it and try again */
1287 g_curlInterface.easy_setopt(m_state->m_easyHandle, CURLOPT_RANGE, NULL);
1288 result = g_curlInterface.easy_perform(m_state->m_easyHandle);
1291 if( result != CURLE_WRITE_ERROR && result != CURLE_OK )
1293 g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
1295 CLog::Log(LOGERROR, "CCurlFile::Stat - Failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
1300 result = g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &length);
1301 if (result != CURLE_OK || length < 0.0)
1303 if (url.GetProtocol() == "ftp")
1305 g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
1306 CLog::Log(LOGNOTICE, "CCurlFile::Stat - Content length failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
1314 SetCorrectHeaders(m_state);
1319 result = g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_CONTENT_TYPE, &content);
1320 if (result != CURLE_OK)
1322 CLog::Log(LOGNOTICE, "CCurlFile::Stat - Content type failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
1323 g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
1329 memset(buffer, 0, sizeof(struct __stat64));
1330 buffer->st_size = (int64_t)length;
1331 if(content && strstr(content, "text/html")) //consider html files directories
1332 buffer->st_mode = _S_IFDIR;
1334 buffer->st_mode = _S_IFREG;
1337 result = g_curlInterface.easy_getinfo(m_state->m_easyHandle, CURLINFO_FILETIME, &filetime);
1338 if (result != CURLE_OK)
1340 CLog::Log(LOGNOTICE, "CCurlFile::Stat - Filetime failed: %s(%d) for %s", g_curlInterface.easy_strerror(result), result, url.Get().c_str());
1345 buffer->st_mtime = filetime;
1348 g_curlInterface.easy_release(&m_state->m_easyHandle, NULL);
1352 unsigned int CCurlFile::CReadState::Read(void* lpBuf, int64_t uiBufSize)
1354 /* only request 1 byte, for truncated reads (only if not eof) */
1355 if((m_fileSize == 0 || m_filePos < m_fileSize) && !FillBuffer(1))
1358 /* ensure only available data is considered */
1359 unsigned int want = (unsigned int)XMIN(m_buffer.getMaxReadSize(), uiBufSize);
1361 /* xfer data to caller */
1362 if (m_buffer.ReadData((char *)lpBuf, want))
1368 /* check if we finished prematurely */
1369 if (!m_stillRunning && (m_fileSize == 0 || m_filePos != m_fileSize))
1371 CLog::Log(LOGWARNING, "%s - Transfer ended before entire file was retrieved pos %"PRId64", size %"PRId64, __FUNCTION__, m_filePos, m_fileSize);
1378 /* use to attempt to fill the read buffer up to requested number of bytes */
1379 bool CCurlFile::CReadState::FillBuffer(unsigned int want)
1386 // only attempt to fill buffer if transactions still running and buffer
1387 // doesnt exceed required size already
1388 while ((unsigned int)m_buffer.getMaxReadSize() < want && m_buffer.getMaxWriteSize() > 0 )
1393 /* if there is data in overflow buffer, try to use that first */
1396 unsigned amount = XMIN((unsigned int)m_buffer.getMaxWriteSize(), m_overflowSize);
1397 m_buffer.WriteData(m_overflowBuffer, amount);
1399 if (amount < m_overflowSize)
1400 memcpy(m_overflowBuffer, m_overflowBuffer+amount,m_overflowSize-amount);
1402 m_overflowSize -= amount;
1403 m_overflowBuffer = (char*)realloc_simple(m_overflowBuffer, m_overflowSize);
1407 CURLMcode result = g_curlInterface.multi_perform(m_multiHandle, &m_stillRunning);
1408 if (!m_stillRunning)
1410 if (result == CURLM_OK)
1412 /* if we still have stuff in buffer, we are fine */
1413 if (m_buffer.getMaxReadSize())
1416 /* verify that we are actually okey */
1418 CURLcode CURLresult = CURLE_OK;
1420 while ((msg = g_curlInterface.multi_info_read(m_multiHandle, &msgs)))
1422 if (msg->msg == CURLMSG_DONE)
1424 if (msg->data.result == CURLE_OK)
1427 CLog::Log(LOGERROR, "CCurlFile::FillBuffer - Failed: %s(%d)", g_curlInterface.easy_strerror(msg->data.result), msg->data.result);
1429 // We need to check the result here as we don't want to retry on every error
1430 if ( (msg->data.result == CURLE_OPERATION_TIMEDOUT ||
1431 msg->data.result == CURLE_PARTIAL_FILE ||
1432 msg->data.result == CURLE_COULDNT_CONNECT ||
1433 msg->data.result == CURLE_RECV_ERROR) &&
1435 CURLresult = msg->data.result;
1436 else if ( (msg->data.result == CURLE_HTTP_RANGE_ERROR ||
1437 msg->data.result == CURLE_HTTP_RETURNED_ERROR) &&
1442 // If server returns a range or http error, retry with range disabled
1443 CURLresult = msg->data.result;
1444 m_sendRange = false;
1451 // Don't retry when we didn't "see" any error
1452 if (CURLresult == CURLE_OK)
1456 if (m_multiHandle && m_easyHandle)
1457 g_curlInterface.multi_remove_handle(m_multiHandle, m_easyHandle);
1459 // Reset all the stuff like we would in Disconnect()
1461 free(m_overflowBuffer);
1462 m_overflowBuffer = NULL;
1465 // If we got here something is wrong
1466 if (++retry > g_advancedSettings.m_curlretries)
1468 CLog::Log(LOGERROR, "CCurlFile::FillBuffer - Reconnect failed!");
1469 // Reset the rest of the variables like we would in Disconnect()
1477 CLog::Log(LOGNOTICE, "CCurlFile::FillBuffer - Reconnect, (re)try %i", retry);
1479 // Connect + seek to current position (again)
1481 g_curlInterface.multi_add_handle(m_multiHandle, m_easyHandle);
1483 // Return to the beginning of the loop:
1489 // We've finished out first loop
1490 if(m_bFirstLoop && m_buffer.getMaxReadSize() > 0)
1491 m_bFirstLoop = false;
1502 // get file descriptors from the transfers
1503 g_curlInterface.multi_fdset(m_multiHandle, &fdread, &fdwrite, &fdexcep, &maxfd);
1506 if (CURLM_OK != g_curlInterface.multi_timeout(m_multiHandle, &timeout) || timeout == -1)
1509 struct timeval t = { timeout / 1000, (timeout % 1000) * 1000 };
1511 /* Wait until data is available or a timeout occurs.
1512 We call dllselect(maxfd + 1, ...), specially in case of (maxfd == -1),
1513 we call dllselect(0, ...), which is basically equal to sleep. */
1514 if (SOCKET_ERROR == dllselect(maxfd + 1, &fdread, &fdwrite, &fdexcep, &t))
1516 CLog::Log(LOGERROR, "CCurlFile::FillBuffer - Failed with socket error");
1521 case CURLM_CALL_MULTI_PERFORM:
1523 // we don't keep calling here as that can easily overwrite our buffer which we want to avoid
1524 // docs says we should call it soon after, but aslong as we are reading data somewhere
1525 // this aught to be soon enough. should stay in socket otherwise
1531 CLog::Log(LOGERROR, "CCurlFile::FillBuffer - Multi perform failed with code %d, aborting", result);
1540 void CCurlFile::CReadState::SetReadBuffer(const void* lpBuf, int64_t uiBufSize)
1542 m_readBuffer = (char*)lpBuf;
1543 m_fileSize = uiBufSize;
1547 void CCurlFile::ClearRequestHeaders()
1549 m_requestheaders.clear();
1552 void CCurlFile::SetRequestHeader(CStdString header, CStdString value)
1554 m_requestheaders[header] = value;
1557 void CCurlFile::SetRequestHeader(CStdString header, long value)
1560 buffer.Format("%ld", value);
1561 m_requestheaders[header] = buffer;
1564 /* STATIC FUNCTIONS */
1565 bool CCurlFile::GetHttpHeader(const CURL &url, CHttpHeader &headers)
1570 if(file.Stat(url, NULL) == 0)
1572 headers = file.GetHttpHeader();
1579 CLog::Log(LOGERROR, "%s - Exception thrown while trying to retrieve header url: %s", __FUNCTION__, url.Get().c_str());
1584 bool CCurlFile::GetMimeType(const CURL &url, CStdString &content, CStdString useragent)
1587 if (!useragent.IsEmpty())
1588 file.SetUserAgent(useragent);
1590 struct __stat64 buffer;
1591 if( file.Stat(url, &buffer) == 0 )
1593 if (buffer.st_mode == _S_IFDIR)
1594 content = "x-directory/normal";
1596 content = file.GetMimeType();
1597 CLog::Log(LOGDEBUG, "CCurlFile::GetMimeType - %s -> %s", url.Get().c_str(), content.c_str());
1600 CLog::Log(LOGDEBUG, "CCurlFile::GetMimeType - %s -> failed", url.Get().c_str());
1605 int CCurlFile::IoControl(EIoControl request, void* param)
1607 if(request == IOCTRL_SEEK_POSSIBLE)
1608 return m_seekable ? 1 : 0;