/* grive: an GPL program to sync a local directory with Google Drive Copyright (C) 2012 Wan Wai Ho This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation version 2 of the License. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "CurlAgent.hh" #include "Error.hh" #include "Header.hh" #include "util/log/Log.hh" #include "util/DataStream.hh" #include "util/File.hh" #include // dependent libraries #include #include #include #include #include #include #include #include #include namespace { using namespace gr::http ; using namespace gr ; std::size_t ReadStringCallback( void *ptr, std::size_t size, std::size_t nmemb, std::string *data ) { assert( ptr != 0 ) ; assert( data != 0 ) ; std::size_t count = std::min( size * nmemb, data->size() ) ; if ( count > 0 ) { std::memcpy( ptr, &(*data)[0], count ) ; data->erase( 0, count ) ; } return count ; } std::size_t ReadFileCallback( void *ptr, std::size_t size, std::size_t nmemb, File *file ) { assert( ptr != 0 ) ; assert( file != 0 ) ; std::size_t count = std::min( static_cast(size * nmemb), static_cast(file->Size() - file->Tell()) ) ; assert( count <= std::numeric_limits::max() ) ; if ( count > 0 ) file->Read( static_cast(ptr), static_cast(count) ) ; return count ; } } // end of local namespace namespace gr { namespace http { struct CurlAgent::Impl { CURL *curl ; std::string location ; } ; CurlAgent::CurlAgent() : m_pimpl( new Impl ) { m_pimpl->curl = ::curl_easy_init(); } void CurlAgent::Init() { ::curl_easy_reset( m_pimpl->curl ) ; ::curl_easy_setopt( m_pimpl->curl, CURLOPT_SSL_VERIFYPEER, 0L ) ; ::curl_easy_setopt( m_pimpl->curl, CURLOPT_SSL_VERIFYHOST, 0L ) ; ::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADERFUNCTION, &CurlAgent::HeaderCallback ) ; ::curl_easy_setopt( m_pimpl->curl, CURLOPT_WRITEHEADER , this ) ; ::curl_easy_setopt( m_pimpl->curl, CURLOPT_HEADER, 0L ) ; } CurlAgent::~CurlAgent() { ::curl_easy_cleanup( m_pimpl->curl ); } std::size_t CurlAgent::HeaderCallback( void *ptr, size_t size, size_t nmemb, CurlAgent *pthis ) { char *str = reinterpret_cast(ptr) ; std::string line( str, str + size*nmemb ) ; static const std::string loc = "Location: " ; std::size_t pos = line.find( loc ) ; if ( pos != line.npos ) { std::size_t end_pos = line.find( "\r\n", pos ) ; pthis->m_pimpl->location = line.substr( loc.size(), end_pos - loc.size() ) ; } return size*nmemb ; } std::size_t CurlAgent::Receive( void* ptr, size_t size, size_t nmemb, DataStream *recv ) { assert( recv != 0 ) ; return recv->Write( static_cast(ptr), size * nmemb ) ; } long CurlAgent::ExecCurl( const std::string& url, DataStream *dest, const http::Header& hdr ) { CURL *curl = m_pimpl->curl ; assert( curl != 0 ) ; char error[CURL_ERROR_SIZE] = {} ; ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error ) ; ::curl_easy_setopt(curl, CURLOPT_URL, url.c_str()); ::curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &CurlAgent::Receive ) ; ::curl_easy_setopt(curl, CURLOPT_WRITEDATA, dest ) ; SetHeader( hdr ) ; // dest->Clear() ; CURLcode curl_code = ::curl_easy_perform(curl); // get the HTTP response code long http_code = 0; ::curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); Trace( "HTTP response %1%", http_code ) ; // reset the curl buffer to prevent it from touch our "error" buffer ::curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, 0 ) ; // only throw for libcurl errors if ( curl_code != CURLE_OK ) { BOOST_THROW_EXCEPTION( Error() << CurlCode( curl_code ) << Url( url ) << CurlErrMsg( error ) << HttpHeader( hdr ) ) ; } return http_code ; } long CurlAgent::Put( const std::string& url, const std::string& data, DataStream *dest, const Header& hdr ) { Trace("HTTP PUT \"%1%\"", url ) ; Init() ; CURL *curl = m_pimpl->curl ; std::string put_data = data ; // set common options ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L ) ; ::curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadStringCallback ) ; ::curl_easy_setopt(curl, CURLOPT_READDATA , &put_data ) ; ::curl_easy_setopt(curl, CURLOPT_INFILESIZE, put_data.size() ) ; return ExecCurl( url, dest, hdr ) ; } long CurlAgent::Put( const std::string& url, File *file, DataStream *dest, const Header& hdr ) { assert( file != 0 ) ; Trace("HTTP PUT \"%1%\"", url ) ; Init() ; CURL *curl = m_pimpl->curl ; // set common options ::curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L ) ; ::curl_easy_setopt(curl, CURLOPT_READFUNCTION, &ReadFileCallback ) ; ::curl_easy_setopt(curl, CURLOPT_READDATA , file ) ; ::curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, static_cast(file->Size()) ) ; return ExecCurl( url, dest, hdr ) ; } long CurlAgent::Get( const std::string& url, DataStream *dest, const Header& hdr ) { Trace("HTTP GET \"%1%\"", url ) ; Init() ; // set get specific options ::curl_easy_setopt(m_pimpl->curl, CURLOPT_HTTPGET, 1L); return ExecCurl( url, dest, hdr ) ; } long CurlAgent::Post( const std::string& url, const std::string& post_data, DataStream *dest, const Header& hdr ) { Trace("HTTP POST \"%1%\" with \"%2%\"", url, post_data ) ; Init() ; CURL *curl = m_pimpl->curl ; // set post specific options ::curl_easy_setopt(curl, CURLOPT_POST, 1L); ::curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &post_data[0] ) ; ::curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_data.size() ) ; return ExecCurl( url, dest, hdr ) ; } long CurlAgent::Custom( const std::string& method, const std::string& url, DataStream *dest, const Header& hdr ) { Trace("HTTP %2% \"%1%\"", url, method ) ; CURL *curl = m_pimpl->curl ; ::curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, method.c_str() ); // ::curl_easy_setopt(curl, CURLOPT_VERBOSE, 1 ); return ExecCurl( url, dest, hdr ) ; } void CurlAgent::SetHeader( const Header& hdr ) { // set headers struct curl_slist *curl_hdr = 0 ; for ( Header::iterator i = hdr.begin() ; i != hdr.end() ; ++i ) curl_hdr = curl_slist_append( curl_hdr, i->c_str() ) ; ::curl_easy_setopt( m_pimpl->curl, CURLOPT_HTTPHEADER, curl_hdr ) ; } std::string CurlAgent::RedirLocation() const { return m_pimpl->location ; } std::string CurlAgent::Escape( const std::string& str ) { CURL *curl = m_pimpl->curl ; char *tmp = curl_easy_escape( curl, str.c_str(), str.size() ) ; std::string result = tmp ; curl_free( tmp ) ; return result ; } std::string CurlAgent::Unescape( const std::string& str ) { CURL *curl = m_pimpl->curl ; int r ; char *tmp = curl_easy_unescape( curl, str.c_str(), str.size(), &r ) ; std::string result = tmp ; curl_free( tmp ) ; return result ; } } } // end of namespace