first version. can download files

pull/40/head
Matchman Green 2012-04-26 00:13:17 +08:00
commit 6fe956d22c
11 changed files with 816 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
.kdev4
grive.kdev4

12
CMakeLists.txt Normal file
View File

@ -0,0 +1,12 @@
project(grive)
add_executable( grive
src/main.cc
src/OAuth2.cc
src/HTTP.cc
src/Json.cc
src/Drive.cc )
target_link_libraries( grive
curl
json )

73
src/Drive.cc Normal file
View File

@ -0,0 +1,73 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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 "Drive.hh"
#include "HTTP.hh"
#include "Json.hh"
#include "OAuth2.hh"
// for debugging only
#include <iostream>
namespace gr {
Drive::Drive( OAuth2& auth ) :
m_auth( auth )
{
m_http_hdr.push_back( "Authorization: Bearer " + m_auth.AccessToken() ) ;
m_http_hdr.push_back( "GData-Version: 3.0" ) ;
Json resp( HttpGet( "https://docs.google.com/feeds/default/private/full?alt=json", m_http_hdr ) ) ;
/* Json::Object map = resp["feed"]["id"].As<Json::Object>() ;
for ( Json::Object::iterator i = map.begin() ; i != map.end() ; ++i )
{
std::cout << i->first << "\t" << i->second.DataType() << std::endl ;
}*/
Json::Array a = resp["feed"]["entry"].As<Json::Array>() ;
for ( Json::Array::iterator i = a.begin() ; i != a.end() ; ++i )
{
DownloadEntry( *i ) ;
}
}
void Drive::DownloadEntry( const Json& entry )
{
Json::Object map = entry.As<Json::Object>() ;
for ( Json::Object::iterator i = map.begin() ; i != map.end() ; ++i )
{
// std::cout << i->first << "\t" << i->second.DataType() << std::endl ;
}
// only handle uploaded files
if ( entry.Has( "docs$filename" ) )
{
// use title as the filename
std::string filename = entry["docs$filename"]["$t"].As<std::string>() ;
std::string url = entry["content"]["src"].As<std::string>() ;
std::cout << "downloading " << filename << " from " << url << std::endl ;
HttpGetFile( url, filename, m_http_hdr ) ;
}
}
} // end of namespace

43
src/Drive.hh Normal file
View File

@ -0,0 +1,43 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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.
*/
#pragma once
#include <string>
#include <vector>
namespace gr {
class OAuth2 ;
class Json ;
class Drive
{
public :
Drive( OAuth2& auth ) ;
private :
void DownloadEntry( const Json& entry ) ;
private :
OAuth2& m_auth ;
std::vector<std::string> m_http_hdr ;
} ;
} // end of namespace

184
src/HTTP.cc Normal file
View File

@ -0,0 +1,184 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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 "HTTP.hh"
#include <curl/curl.h>
#include <cassert>
#include <fstream>
#include <iostream>
#include <sstream>
#include <streambuf>
namespace gr {
HttpException::HttpException( int curl_code, int http_code, const char *err_buf )
: runtime_error( Format( curl_code, http_code, err_buf ) )
{
}
std::string HttpException::Format( int curl_code, int http_code, const char *err_buf )
{
std::ostringstream ss ;
ss << "CURL code = " << curl_code << " HTTP code = " << http_code << " (" << err_buf << ")" ;
return ss.str() ;
}
// libcurl callback to append to a string
std::size_t WriteCallback( char *data, size_t size, size_t nmemb, std::string *resp )
{
assert( resp != 0 ) ;
assert( data != 0 ) ;
std::size_t count = size * nmemb ;
resp->append( data, count ) ;
return count ;
}
// libcurl callback to write to a file
std::size_t DownloadCallback( char *data, size_t size, size_t nmemb, std::streambuf *file )
{
assert( file != 0 ) ;
assert( data != 0 ) ;
return file->sputn( data, size * nmemb ) ;
}
CURL* InitCurl( const std::string& url, std::string *resp, const Headers& hdr )
{
CURL *curl = curl_easy_init();
if ( curl == 0 )
throw std::bad_alloc() ;
// set common options
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl, CURLOPT_HEADER, 0);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, resp ) ;
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
// set headers
struct curl_slist *curl_hdr = 0 ;
for ( Headers::const_iterator i = hdr.begin() ; i != hdr.end() ; ++i )
curl_hdr = curl_slist_append( curl_hdr, i->c_str() ) ;
curl_easy_setopt( curl, CURLOPT_HTTPHEADER, curl_hdr ) ;
return curl ;
}
void DoCurl( CURL *curl )
{
char error_buf[CURL_ERROR_SIZE] = {} ;
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buf ) ;
CURLcode curl_code = curl_easy_perform(curl);
int http_code = 0;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
// clean up
curl_easy_cleanup(curl);
if ( curl_code != CURLE_OK )
{
throw HttpException( curl_code, http_code, error_buf ) ;
}
else if (http_code >= 400 )
{
std::cout << "http error " << http_code << std::endl ;
throw HttpException( curl_code, http_code, error_buf ) ;
}
}
std::string HttpGet( const std::string& url, const Headers& hdr )
{
std::string resp ;
CURL *curl = InitCurl( url, &resp, hdr ) ;
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
DoCurl( curl ) ;
return resp;
}
void HttpGetFile(
const std::string& url,
const std::string& filename,
const Headers& hdr )
{
std::ofstream file( filename.c_str(), std::ios::binary | std::ios::out ) ;
if ( !file )
throw std::runtime_error( "cannot open file " + filename + " for writing" ) ;
CURL *curl = InitCurl( url, 0, hdr ) ;
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DownloadCallback);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, file.rdbuf() ) ;
curl_easy_setopt(curl, CURLOPT_HTTPGET, 1L);
DoCurl( curl ) ;
}
std::string HttpPostData( const std::string& url, const std::string& data, const Headers& hdr )
{
std::string resp ;
CURL *curl = InitCurl( url, &resp, hdr ) ;
std::string post_data = data ;
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, &post_data[0] ) ;
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, post_data.size() ) ;
DoCurl( curl ) ;
return resp;
}
std::string HttpPostFile( const std::string& url, const std::string& filename, const Headers& hdr )
{
std::string resp ;
return resp;
}
std::string Escape( const std::string& str )
{
CURL *curl = curl_easy_init();
char *tmp = curl_easy_escape( curl, str.c_str(), str.size() ) ;
std::string result = tmp ;
curl_free( tmp ) ;
curl_easy_cleanup(curl);
return result ;
}
std::string Unescape( const std::string& str )
{
CURL *curl = curl_easy_init();
int r ;
char *tmp = curl_easy_unescape( curl, str.c_str(), str.size(), &r ) ;
std::string result = tmp ;
curl_free( tmp ) ;
curl_easy_cleanup(curl);
return result ;
}
}

56
src/HTTP.hh Normal file
View File

@ -0,0 +1,56 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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.
*/
#pragma once
#include <string>
#include <stdexcept>
#include <vector>
namespace gr
{
typedef std::vector<std::string> Headers ;
std::string HttpGet( const std::string& url, const Headers& hdr = Headers() ) ;
void HttpGetFile(
const std::string& url,
const std::string& filename,
const Headers& hdr = Headers() ) ;
std::string HttpPostData(
const std::string& url,
const std::string& data,
const Headers& hdr = Headers() ) ;
std::string HttpPostFile(
const std::string& url,
const std::string& filename,
const Headers& hdr = Headers() ) ;
std::string Escape( const std::string& str ) ;
std::string Unescape( const std::string& str ) ;
class HttpException : public std::runtime_error
{
public :
HttpException( int curl_code, int http_code, const char *err_buf ) ;
private :
static std::string Format( int curl_code, int http_code, const char *err_buf ) ;
} ;
}

168
src/Json.cc Normal file
View File

@ -0,0 +1,168 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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 "Json.hh"
#include <json/json_tokener.h>
#include <json/linkhash.h>
#include <cassert>
#include <ostream>
#include <stdexcept>
namespace gr {
Json::Json( const std::string& str ) :
m_json( ::json_tokener_parse( str.c_str() ) )
{
if ( m_json == 0 )
throw std::runtime_error( "json parse error" ) ;
}
Json::Json( struct json_object *json ) :
m_json( json )
{
assert( json != 0 ) ;
::json_object_get( m_json ) ;
}
Json::Json( const Json& rhs ) :
m_json( rhs.m_json )
{
assert( m_json != 0 ) ;
::json_object_get( m_json ) ;
}
Json::~Json( )
{
assert( m_json != 0 ) ;
::json_object_put( m_json ) ;
}
Json& Json::operator=( const Json& rhs )
{
Json tmp( rhs ) ;
Swap( tmp ) ;
return *this ;
}
void Json::Swap( Json& other )
{
assert( m_json != 0 ) ;
assert( other.m_json != 0 ) ;
std::swap( m_json, other.m_json ) ;
}
Json Json::operator[]( const std::string& key ) const
{
assert( m_json != 0 ) ;
struct json_object *j = ::json_object_object_get( m_json, key.c_str() ) ;
if ( j == 0 )
throw std::runtime_error( "key: " + key + " is not found in object" ) ;
return Json( j ) ;
}
bool Json::Has( const std::string& key ) const
{
assert( m_json != 0 ) ;
return ::json_object_object_get( m_json, key.c_str() ) != 0 ;
}
template <>
std::string Json::As<std::string>() const
{
assert( m_json != 0 ) ;
return ::json_object_get_string( m_json ) ;
}
template <>
bool Json::Is<std::string>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_string ) ;
}
template <>
int Json::As<int>() const
{
assert( m_json != 0 ) ;
return ::json_object_get_int( m_json ) ;
}
template <>
bool Json::Is<int>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_int ) ;
}
std::ostream& operator<<( std::ostream& os, const Json& json )
{
assert( json.m_json != 0 ) ;
return os << ::json_object_to_json_string( json.m_json ) ;
}
Json::Type Json::DataType() const
{
assert( m_json != 0 ) ;
return static_cast<Type>( ::json_object_get_type( m_json ) ) ;
}
template <>
Json::Object Json::As<Json::Object>() const
{
Object result ;
json_object_object_foreach( m_json, key, val )
{
result.insert( Object::value_type( key, Json( val ) ) ) ;
}
return result ;
}
template <>
bool Json::Is<Json::Object>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_object ) ;
}
template <>
Json::Array Json::As<Json::Array>() const
{
std::size_t count = ::json_object_array_length( m_json ) ;
Array result ;
for ( std::size_t i = 0 ; i < count ; ++i )
result.push_back( Json( ::json_object_array_get_idx( m_json, i ) ) ) ;
return result ;
}
template <>
bool Json::Is<Json::Array>() const
{
assert( m_json != 0 ) ;
return ::json_object_is_type( m_json, json_type_array ) ;
}
}

67
src/Json.hh Normal file
View File

@ -0,0 +1,67 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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.
*/
#pragma once
#include <string>
#include <map>
#include <vector>
struct json_object ;
namespace gr {
class Json
{
public :
typedef std::map<std::string, Json> Object ;
typedef std::vector<Json> Array ;
public :
Json( const std::string& str ) ;
Json( const Json& rhs ) ;
~Json( ) ;
Json operator[]( const std::string& key ) const ;
Json& operator=( const Json& rhs ) ;
void Swap( Json& other ) ;
template <typename T>
T As() const ;
template <typename T>
bool Is() const ;
bool Has( const std::string& key ) const ;
friend std::ostream& operator<<( std::ostream& os, const Json& json ) ;
enum Type { null_type, bool_type, double_type, int_type, object_type, array_type, string_type } ;
Type DataType() const ;
private :
Json( struct json_object *json ) ;
private :
struct json_object *m_json ;
} ;
}

100
src/OAuth2.cc Normal file
View File

@ -0,0 +1,100 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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 "OAuth2.hh"
#include "HTTP.hh"
#include "Json.hh"
// for debugging
#include <iostream>
namespace gr {
const std::string token_url = "https://accounts.google.com/o/oauth2/token" ;
const std::string client_id = "472848141496.apps.googleusercontent.com" ;
const std::string client_secret = "hmBLwnBSU24Wombgjbffs6DS" ;
OAuth2::OAuth2( const std::string& refresh_code ) :
m_refresh( refresh_code )
{
Refresh( ) ;
}
OAuth2::OAuth2( )
{
}
void OAuth2::Auth( const std::string& auth_code )
{
std::string post =
"code=" + auth_code +
"&client_id=" + client_id +
"&client_secret=" + client_secret +
"&redirect_uri=" + "urn:ietf:wg:oauth:2.0:oob" +
"&grant_type=authorization_code" ;
Json resp( HttpPostData( token_url, post ) ) ;
m_access = resp["access_token"].As<std::string>() ;
m_refresh = resp["refresh_token"].As<std::string>() ;
}
std::string OAuth2::MakeAuthURL(
const std::string& client_id,
const std::string& state )
{
return "https://accounts.google.com/o/oauth2/auth"
"?scope=" +
Escape( "https://www.googleapis.com/auth/userinfo.email" ) + "+" +
Escape( "https://www.googleapis.com/auth/userinfo.profile" ) + "+" +
Escape( "https://docs.google.com/feeds/" ) + "+" +
Escape( "https://docs.googleusercontent.com/" ) + "+" +
Escape( "https://spreadsheets.google.com/feeds/" ) +
"&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
"&response_type=code"
"&client_id=" + client_id ;
}
void OAuth2::Refresh( )
{
std::string post =
"refresh_token=" + m_refresh +
"&client_id=" + client_id +
"&client_secret=" + client_secret +
"&grant_type=refresh_token" ;
Json resp( HttpPostData( token_url, post ) ) ;
m_access = resp["access_token"].As<std::string>() ;
}
std::string OAuth2::RefreshToken( ) const
{
return m_refresh ;
}
std::string OAuth2::AccessToken( ) const
{
return m_access ;
}
std::string OAuth2::HttpHeader( ) const
{
return "Authorization: Bearer " + m_access ;
}
} // end of namespace

52
src/OAuth2.hh Normal file
View File

@ -0,0 +1,52 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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.
*/
#pragma once
#include <string>
namespace gr {
class OAuth2
{
public :
OAuth2( ) ;
explicit OAuth2( const std::string& refresh_code ) ;
std::string Str() const ;
static std::string MakeAuthURL(
const std::string& client_id,
const std::string& state = std::string() ) ;
void Auth( const std::string& auth_code ) ;
void Refresh( ) ;
std::string RefreshToken( ) const ;
std::string AccessToken( ) const ;
// adding HTTP auth header
std::string HttpHeader( ) const ;
private :
std::string m_access ;
std::string m_refresh ;
} ;
} // end of namespace

59
src/main.cc Normal file
View File

@ -0,0 +1,59 @@
/*
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; either version 2
of the License, or (at your option) any later version.
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 "OAuth2.hh"
#include "Drive.hh"
#include <cstdlib>
#include <iostream>
int main( int argc, char **argv )
{
using namespace gr ;
int c ;
while ((c = getopt (argc, argv, "ac:")) != -1)
{
switch ( c )
{
case 'a' :
{
std::cout <<
OAuth2::MakeAuthURL( "472848141496.apps.googleusercontent.com" ) << std::endl ;
return 0 ;
}
case 'c' :
{
OAuth2 token ;
token.Auth( optarg ) ;
// print the refresh token an exist
std::cout << token.RefreshToken() << std::endl ;
return 0 ;
}
}
}
OAuth2 token( getenv( "GR_REFRESH_CODE" ) ) ;
Drive drive( token ) ;
return 0 ;
}