mirror of https://github.com/vitalif/grive2
Merge 58a183c876
into 6901fbb169
commit
06ba4eb4fd
41
README.md
41
README.md
|
@ -1,3 +1,28 @@
|
||||||
|
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||||
|
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||||
|
|
||||||
|
- [Grive2 0.5.2-dev](#grive2-052-dev)
|
||||||
|
- [Usage](#usage)
|
||||||
|
- [Exclude specific files and folders from sync: .griveignore](#exclude-specific-files-and-folders-from-sync-griveignore)
|
||||||
|
- [Scheduled syncs and syncs on file change events](#scheduled-syncs-and-syncs-on-file-change-events)
|
||||||
|
- [Shared files](#shared-files)
|
||||||
|
- [Different OAuth2 client to workaround over quota and google approval issues](#different-oauth2-client-to-workaround-over-quota-and-google-approval-issues)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Install dependencies](#install-dependencies)
|
||||||
|
- [Build Debian packages](#build-debian-packages)
|
||||||
|
- [Manual build](#manual-build)
|
||||||
|
- [Version History](#version-history)
|
||||||
|
- [Grive2 v0.5.2-dev](#grive2-v052-dev)
|
||||||
|
- [Grive2 v0.5.1](#grive2-v051)
|
||||||
|
- [Grive2 v0.5](#grive2-v05)
|
||||||
|
- [Grive2 v0.4.2](#grive2-v042)
|
||||||
|
- [Grive2 v0.4.1](#grive2-v041)
|
||||||
|
- [Grive2 v0.4.0](#grive2-v040)
|
||||||
|
- [Grive v0.3](#grive-v03)
|
||||||
|
|
||||||
|
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||||
|
|
||||||
# Grive2 0.5.2-dev
|
# Grive2 0.5.2-dev
|
||||||
|
|
||||||
13 Nov 2019, Vitaliy Filippov
|
13 Nov 2019, Vitaliy Filippov
|
||||||
|
@ -39,12 +64,12 @@ grive -a
|
||||||
|
|
||||||
A URL should be printed. Go to the link. You will need to login to your Google
|
A URL should be printed. Go to the link. You will need to login to your Google
|
||||||
account if you haven't done so. After granting the permission to Grive, the
|
account if you haven't done so. After granting the permission to Grive, the
|
||||||
browser will show you an authenication code. Copy-and-paste that to the
|
authorization code will be forwarded to the Grive application and you will be
|
||||||
standard input of Grive.
|
redirected to a localhost web page confirming the authorization.
|
||||||
|
|
||||||
If everything works fine, Grive will create .grive and .grive_state files in your
|
If everything works fine, Grive will create .grive and .grive\_state files in
|
||||||
current directory. It will also start downloading files from your Google Drive to
|
your current directory. It will also start downloading files from your Google
|
||||||
your current directory.
|
Drive to your current directory.
|
||||||
|
|
||||||
To resync the direcory, run `grive` in the folder.
|
To resync the direcory, run `grive` in the folder.
|
||||||
|
|
||||||
|
@ -154,6 +179,7 @@ You need the following libraries:
|
||||||
- libgcrypt
|
- libgcrypt
|
||||||
- Boost (Boost filesystem, program_options, regex, unit_test_framework and system are required)
|
- Boost (Boost filesystem, program_options, regex, unit_test_framework and system are required)
|
||||||
- expat
|
- expat
|
||||||
|
- [libcpprest-dev](https://github.com/Microsoft/cpprestsdk)
|
||||||
|
|
||||||
There are also some optional dependencies:
|
There are also some optional dependencies:
|
||||||
- CppUnit (for unit tests)
|
- CppUnit (for unit tests)
|
||||||
|
@ -165,16 +191,17 @@ these packages:
|
||||||
|
|
||||||
sudo apt-get install git cmake build-essential libgcrypt20-dev libyajl-dev \
|
sudo apt-get install git cmake build-essential libgcrypt20-dev libyajl-dev \
|
||||||
libboost-all-dev libcurl4-openssl-dev libexpat1-dev libcppunit-dev binutils-dev \
|
libboost-all-dev libcurl4-openssl-dev libexpat1-dev libcppunit-dev binutils-dev \
|
||||||
debhelper zlib1g-dev dpkg-dev pkg-config
|
debhelper zlib1g-dev dpkg-dev pkg-config libcpprest-dev
|
||||||
|
|
||||||
Fedora:
|
Fedora:
|
||||||
|
|
||||||
sudo dnf install git cmake libgcrypt-devel gcc-c++ libstdc++ yajl-devel boost-devel libcurl-devel expat-devel binutils zlib
|
sudo dnf install git cmake libgcrypt-devel gcc-c++ libstdc++ yajl-devel boost-devel libcurl-devel expat-devel binutils zlib cpprest-devel
|
||||||
|
|
||||||
|
|
||||||
FreeBSD:
|
FreeBSD:
|
||||||
|
|
||||||
pkg install git cmake boost-libs yajl libgcrypt pkgconf cppunit libbfd
|
pkg install git cmake boost-libs yajl libgcrypt pkgconf cppunit libbfd
|
||||||
|
cpprestsdk
|
||||||
|
|
||||||
### Build Debian packages
|
### Build Debian packages
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ Source: grive2
|
||||||
Section: net
|
Section: net
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Vitaliy Filippov <vitalif@mail.ru>
|
Maintainer: Vitaliy Filippov <vitalif@mail.ru>
|
||||||
Build-Depends: debhelper, cmake, pkg-config, zlib1g-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libboost-filesystem-dev, libboost-program-options-dev, libboost-test-dev, libboost-regex-dev, libexpat1-dev, libgcrypt-dev, libyajl-dev
|
Build-Depends: debhelper, cmake, pkg-config, zlib1g-dev, libcurl4-openssl-dev | libcurl4-gnutls-dev, libboost-filesystem-dev, libboost-program-options-dev, libboost-test-dev, libboost-regex-dev, libexpat1-dev, libgcrypt-dev, libyajl-dev, libcpprest-dev
|
||||||
Standards-Version: 3.9.6
|
Standards-Version: 3.9.6
|
||||||
Homepage: https://yourcmc.ru/wiki/Grive2
|
Homepage: https://yourcmc.ru/wiki/Grive2
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
project( grive )
|
project( grive )
|
||||||
|
|
||||||
find_package(Boost COMPONENTS program_options REQUIRED)
|
find_package(Boost COMPONENTS program_options REQUIRED)
|
||||||
|
find_package(cpprestsdk REQUIRED)
|
||||||
|
find_package(OpenSSL REQUIRED)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${grive_SOURCE_DIR}/../libgrive/src
|
${grive_SOURCE_DIR}/../libgrive/src
|
||||||
|
@ -18,6 +20,8 @@ add_executable( grive_executable
|
||||||
|
|
||||||
target_link_libraries( grive_executable
|
target_link_libraries( grive_executable
|
||||||
grive
|
grive
|
||||||
|
OpenSSL::Crypto
|
||||||
|
cpprestsdk::cpprest
|
||||||
)
|
)
|
||||||
|
|
||||||
set(DEFAULT_APP_ID "615557989097-i93d4d1ojpen0m0dso18ldr6orjkidgf.apps.googleusercontent.com")
|
set(DEFAULT_APP_ID "615557989097-i93d4d1ojpen0m0dso18ldr6orjkidgf.apps.googleusercontent.com")
|
||||||
|
|
|
@ -45,6 +45,10 @@
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <future>
|
||||||
|
|
||||||
|
#include <cpprest/http_listener.h>
|
||||||
|
#include <cpprest/uri.h>
|
||||||
|
|
||||||
const std::string default_id = APP_ID ;
|
const std::string default_id = APP_ID ;
|
||||||
const std::string default_secret = APP_SECRET ;
|
const std::string default_secret = APP_SECRET ;
|
||||||
|
@ -52,6 +56,9 @@ const std::string default_secret = APP_SECRET ;
|
||||||
using namespace gr ;
|
using namespace gr ;
|
||||||
namespace po = boost::program_options;
|
namespace po = boost::program_options;
|
||||||
|
|
||||||
|
using namespace web::http::experimental::listener;
|
||||||
|
using namespace web::http;
|
||||||
|
|
||||||
// libgcrypt insist this to be done in application, not library
|
// libgcrypt insist this to be done in application, not library
|
||||||
void InitGCrypt()
|
void InitGCrypt()
|
||||||
{
|
{
|
||||||
|
@ -101,6 +108,63 @@ void InitLog( const po::variables_map& vm )
|
||||||
LogBase::Inst( comp_log.release() ) ;
|
LogBase::Inst( comp_log.release() ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthCode reads an authorization code from the "code" query parameter passed
|
||||||
|
// via client-side redirect to the redirect uri specified in uri
|
||||||
|
std::string AuthCode( std::string uri )
|
||||||
|
{
|
||||||
|
// Set up an HTTP listener that is waiting for Google
|
||||||
|
// to hit the specified local URI with the authorization
|
||||||
|
// code response
|
||||||
|
http_listener listener(uri);
|
||||||
|
listener.
|
||||||
|
open().
|
||||||
|
then([uri]() {
|
||||||
|
std::cout
|
||||||
|
<< "\n"
|
||||||
|
<< "Listening on " << uri << " for an authorization code from Google"
|
||||||
|
<< std::endl;
|
||||||
|
}).
|
||||||
|
wait();
|
||||||
|
|
||||||
|
// Set up a promise to read the authorization code from the
|
||||||
|
// redirect
|
||||||
|
std::promise<std::string> result;
|
||||||
|
listener.support(methods::GET, [&result] (http_request req) {
|
||||||
|
// Extract the "code" query parameter
|
||||||
|
auto params = uri::split_query(req.request_uri().query());
|
||||||
|
auto found_code = params.find("code");
|
||||||
|
|
||||||
|
// If auth code is missing, respond with basic web page
|
||||||
|
// containing error message
|
||||||
|
if (found_code == params.end()) {
|
||||||
|
std::cout
|
||||||
|
<< "request received without auth code: " << req.absolute_uri().to_string()
|
||||||
|
<< std::endl;
|
||||||
|
auto msg =
|
||||||
|
"grive2 authorization code redirect missing 'code' query parameter.\n\n"
|
||||||
|
"Try the auth flow again.";
|
||||||
|
req.reply(status_codes::BadRequest, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If found, respond with basic web page telling user to close
|
||||||
|
// the window and pass the actual code back to the rest of the
|
||||||
|
// application
|
||||||
|
auto code = found_code->second;
|
||||||
|
std::cout << "received authorization code" << std::endl;
|
||||||
|
auto msg = "Received grive2 authorization code. You may now close this window.";
|
||||||
|
req.reply(status_codes::OK, msg).wait();
|
||||||
|
result.set_value(code);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Block until we receive an auth code
|
||||||
|
std::string code = result.get_future().get();
|
||||||
|
|
||||||
|
// Having received the code, block until listener is shut down
|
||||||
|
listener.close().wait();
|
||||||
|
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
int Main( int argc, char **argv )
|
int Main( int argc, char **argv )
|
||||||
{
|
{
|
||||||
InitGCrypt() ;
|
InitGCrypt() ;
|
||||||
|
@ -115,6 +179,7 @@ int Main( int argc, char **argv )
|
||||||
( "secret,e", po::value<std::string>(), "Authentication secret")
|
( "secret,e", po::value<std::string>(), "Authentication secret")
|
||||||
( "print-url", "Only print url for request")
|
( "print-url", "Only print url for request")
|
||||||
( "path,p", po::value<std::string>(), "Path to working copy root")
|
( "path,p", po::value<std::string>(), "Path to working copy root")
|
||||||
|
( "redirect-uri", po::value<std::string>(), "local URI on which to listen for auth redirect")
|
||||||
( "dir,s", po::value<std::string>(), "Single subdirectory to sync")
|
( "dir,s", po::value<std::string>(), "Single subdirectory to sync")
|
||||||
( "verbose,V", "Verbose mode. Enable more messages than normal.")
|
( "verbose,V", "Verbose mode. Enable more messages than normal.")
|
||||||
( "log-http", po::value<std::string>(), "Log all HTTP responses in this file for debugging.")
|
( "log-http", po::value<std::string>(), "Log all HTTP responses in this file for debugging.")
|
||||||
|
@ -183,8 +248,9 @@ int Main( int argc, char **argv )
|
||||||
std::string secret = vm.count( "secret" ) > 0
|
std::string secret = vm.count( "secret" ) > 0
|
||||||
? vm["secret"].as<std::string>()
|
? vm["secret"].as<std::string>()
|
||||||
: default_secret ;
|
: default_secret ;
|
||||||
|
std::string redirect_uri = config.Get("redirect-uri").Str();
|
||||||
|
|
||||||
OAuth2 token( http.get(), id, secret ) ;
|
OAuth2 token( http.get(), id, secret, redirect_uri ) ;
|
||||||
|
|
||||||
if ( vm.count("print-url") )
|
if ( vm.count("print-url") )
|
||||||
{
|
{
|
||||||
|
@ -194,33 +260,31 @@ int Main( int argc, char **argv )
|
||||||
|
|
||||||
std::cout
|
std::cout
|
||||||
<< "-----------------------\n"
|
<< "-----------------------\n"
|
||||||
<< "Please go to this URL and get an authentication code:\n\n"
|
<< "Please go to this URL to authorize the app:\n\n"
|
||||||
<< token.MakeAuthURL()
|
<< token.MakeAuthURL()
|
||||||
<< std::endl ;
|
<< std::endl ;
|
||||||
|
|
||||||
std::cout
|
std::string code = AuthCode(redirect_uri);
|
||||||
<< "\n-----------------------\n"
|
|
||||||
<< "Please input the authentication code here: " << std::endl ;
|
|
||||||
std::string code ;
|
|
||||||
std::cin >> code ;
|
|
||||||
|
|
||||||
token.Auth( code ) ;
|
token.Auth( code ) ;
|
||||||
|
|
||||||
// save to config
|
// save to config
|
||||||
config.Set( "id", Val( id ) ) ;
|
config.Set( "id", Val( id ) ) ;
|
||||||
config.Set( "secret", Val( secret ) ) ;
|
config.Set( "secret", Val( secret ) ) ;
|
||||||
config.Set( "refresh_token", Val( token.RefreshToken() ) ) ;
|
config.Set( "refresh_token", Val( token.RefreshToken() ) ) ;
|
||||||
|
config.Set( "redirect-uri", Val( redirect_uri ) ) ;
|
||||||
config.Save() ;
|
config.Save() ;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string refresh_token ;
|
std::string refresh_token ;
|
||||||
std::string id ;
|
std::string id ;
|
||||||
std::string secret ;
|
std::string secret ;
|
||||||
|
std::string redirect_uri ;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
refresh_token = config.Get("refresh_token").Str() ;
|
refresh_token = config.Get("refresh_token").Str() ;
|
||||||
id = config.Get("id").Str() ;
|
id = config.Get("id").Str() ;
|
||||||
secret = config.Get("secret").Str() ;
|
secret = config.Get("secret").Str() ;
|
||||||
|
redirect_uri = config.Get("redirect-uri").Str() ;
|
||||||
}
|
}
|
||||||
catch ( Exception& e )
|
catch ( Exception& e )
|
||||||
{
|
{
|
||||||
|
@ -232,7 +296,7 @@ int Main( int argc, char **argv )
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
OAuth2 token( http.get(), refresh_token, id, secret ) ;
|
OAuth2 token( http.get(), refresh_token, id, secret, redirect_uri ) ;
|
||||||
AuthAgent agent( token, http.get() ) ;
|
AuthAgent agent( token, http.get() ) ;
|
||||||
v2::Syncer2 syncer( &agent );
|
v2::Syncer2 syncer( &agent );
|
||||||
|
|
||||||
|
|
|
@ -35,12 +35,14 @@ const std::string token_url = "https://accounts.google.com/o/oauth2/token" ;
|
||||||
OAuth2::OAuth2(
|
OAuth2::OAuth2(
|
||||||
http::Agent* agent,
|
http::Agent* agent,
|
||||||
const std::string& refresh_code,
|
const std::string& refresh_code,
|
||||||
const std::string& client_id,
|
const std::string& client_id,
|
||||||
const std::string& client_secret ) :
|
const std::string& client_secret,
|
||||||
|
const std::string& redirect_uri ) :
|
||||||
m_refresh( refresh_code ),
|
m_refresh( refresh_code ),
|
||||||
m_agent( agent ),
|
m_agent( agent ),
|
||||||
m_client_id( client_id ),
|
m_client_id( client_id ),
|
||||||
m_client_secret( client_secret )
|
m_client_secret( client_secret ),
|
||||||
|
m_redirect_uri ( redirect_uri )
|
||||||
{
|
{
|
||||||
Refresh( ) ;
|
Refresh( ) ;
|
||||||
}
|
}
|
||||||
|
@ -48,10 +50,12 @@ OAuth2::OAuth2(
|
||||||
OAuth2::OAuth2(
|
OAuth2::OAuth2(
|
||||||
http::Agent* agent,
|
http::Agent* agent,
|
||||||
const std::string& client_id,
|
const std::string& client_id,
|
||||||
const std::string& client_secret ) :
|
const std::string& client_secret,
|
||||||
|
const std::string& redirect_uri ) :
|
||||||
m_agent( agent ),
|
m_agent( agent ),
|
||||||
m_client_id( client_id ),
|
m_client_id( client_id ),
|
||||||
m_client_secret( client_secret )
|
m_client_secret( client_secret ),
|
||||||
|
m_redirect_uri( redirect_uri )
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +65,7 @@ void OAuth2::Auth( const std::string& auth_code )
|
||||||
"code=" + auth_code +
|
"code=" + auth_code +
|
||||||
"&client_id=" + m_client_id +
|
"&client_id=" + m_client_id +
|
||||||
"&client_secret=" + m_client_secret +
|
"&client_secret=" + m_client_secret +
|
||||||
"&redirect_uri=" + "urn:ietf:wg:oauth:2.0:oob" +
|
"&redirect_uri=" + m_redirect_uri +
|
||||||
"&grant_type=authorization_code" ;
|
"&grant_type=authorization_code" ;
|
||||||
|
|
||||||
http::ValResponse resp ;
|
http::ValResponse resp ;
|
||||||
|
@ -85,7 +89,7 @@ std::string OAuth2::MakeAuthURL()
|
||||||
{
|
{
|
||||||
return "https://accounts.google.com/o/oauth2/auth"
|
return "https://accounts.google.com/o/oauth2/auth"
|
||||||
"?scope=" + m_agent->Escape( "https://www.googleapis.com/auth/drive" ) +
|
"?scope=" + m_agent->Escape( "https://www.googleapis.com/auth/drive" ) +
|
||||||
"&redirect_uri=urn:ietf:wg:oauth:2.0:oob"
|
"&redirect_uri=" + m_redirect_uri +
|
||||||
"&response_type=code"
|
"&response_type=code"
|
||||||
"&client_id=" + m_client_id ;
|
"&client_id=" + m_client_id ;
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,12 +35,14 @@ public :
|
||||||
OAuth2(
|
OAuth2(
|
||||||
http::Agent* agent,
|
http::Agent* agent,
|
||||||
const std::string& client_id,
|
const std::string& client_id,
|
||||||
const std::string& client_secret ) ;
|
const std::string& client_secret,
|
||||||
|
const std::string& redirect_uri ) ;
|
||||||
OAuth2(
|
OAuth2(
|
||||||
http::Agent* agent,
|
http::Agent* agent,
|
||||||
const std::string& refresh_code,
|
const std::string& refresh_code,
|
||||||
const std::string& client_id,
|
const std::string& client_id,
|
||||||
const std::string& client_secret ) ;
|
const std::string& client_secret,
|
||||||
|
const std::string& redirect_uri ) ;
|
||||||
|
|
||||||
std::string Str() const ;
|
std::string Str() const ;
|
||||||
|
|
||||||
|
@ -62,6 +64,7 @@ private :
|
||||||
|
|
||||||
const std::string m_client_id ;
|
const std::string m_client_id ;
|
||||||
const std::string m_client_secret ;
|
const std::string m_client_secret ;
|
||||||
|
const std::string m_redirect_uri ;
|
||||||
} ;
|
} ;
|
||||||
|
|
||||||
} // end of namespace
|
} // end of namespace
|
||||||
|
|
|
@ -32,9 +32,10 @@ namespace po = boost::program_options;
|
||||||
|
|
||||||
namespace gr {
|
namespace gr {
|
||||||
|
|
||||||
const std::string default_filename = ".grive";
|
const char *env_name = "GR_CONFIG";
|
||||||
const char *env_name = "GR_CONFIG";
|
const std::string default_filename = ".grive";
|
||||||
const std::string default_root_folder = ".";
|
const std::string default_root_folder = ".";
|
||||||
|
const std::string default_redirect_uri = "http://localhost:9898" ;
|
||||||
|
|
||||||
Config::Config( const po::variables_map& vm )
|
Config::Config( const po::variables_map& vm )
|
||||||
{
|
{
|
||||||
|
@ -58,6 +59,14 @@ Config::Config( const po::variables_map& vm )
|
||||||
|
|
||||||
m_path = GetPath( fs::path(m_cmd["path"].Str()) ) ;
|
m_path = GetPath( fs::path(m_cmd["path"].Str()) ) ;
|
||||||
m_file = Read( ) ;
|
m_file = Read( ) ;
|
||||||
|
|
||||||
|
if ( vm.count( "redirect-uri" ) > 0 ) {
|
||||||
|
m_cmd.Add( "redirect-uri", Val( vm["redirect-uri"].as<std::string>() ) );
|
||||||
|
} else if (m_file.Has( "redirect-uri" )) {
|
||||||
|
;
|
||||||
|
} else {
|
||||||
|
m_cmd.Add( "redirect-uri", Val( default_redirect_uri ) );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fs::path Config::GetPath( const fs::path& root_path )
|
fs::path Config::GetPath( const fs::path& root_path )
|
||||||
|
@ -84,7 +93,7 @@ void Config::Save( )
|
||||||
|
|
||||||
void Config::Set( const std::string& key, const Val& value )
|
void Config::Set( const std::string& key, const Val& value )
|
||||||
{
|
{
|
||||||
m_file.Add( key, value ) ;
|
m_file.Set( key, value ) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
Val Config::Get( const std::string& key ) const
|
Val Config::Get( const std::string& key ) const
|
||||||
|
|
|
@ -36,6 +36,7 @@ BuildRequires: expat-devel
|
||||||
BuildRequires: openssl-devel
|
BuildRequires: openssl-devel
|
||||||
BuildRequires: boost-devel
|
BuildRequires: boost-devel
|
||||||
BuildRequires: binutils-devel
|
BuildRequires: binutils-devel
|
||||||
|
BuildRequires: cpprest-devel
|
||||||
|
|
||||||
%description
|
%description
|
||||||
The purpose of this project is to provide an independent implementation
|
The purpose of this project is to provide an independent implementation
|
||||||
|
|
Loading…
Reference in New Issue