diff --git a/control_tools.cpp b/control_tools.cpp index 59fd253..6186391 100644 --- a/control_tools.cpp +++ b/control_tools.cpp @@ -41,6 +41,7 @@ #include "pclsync_lib.h" #include "pclsync/pshm.h" +#include "pclsync/putil.h" #include "pclsync/pfoldersync.h" #include "pclsync/pcommands.h" @@ -60,6 +61,9 @@ static char* command_generator(const char* text, int state) { "sync", "sync ls", "sync add", "sync remove", "sync rm", "sync pause", "sync resume", "s", "s ls", "s add", "s remove", "s rm", "s pause", "s resume", "pending", "p", + "status", "st", + "tfa", + "auth", "finalize", "f", "quit", "q", nullptr @@ -104,6 +108,9 @@ void setup_app(CLI::App *app) { << " pause: Pause syncing (filesystem remains mounted)" << std::endl << " resume: Resume syncing" << std::endl << " pending(p): Check for pending transfers" << std::endl + << " status(st): Show current sync state" << std::endl + << " tfa : Supply 2FA code to a running daemon" << std::endl + << " auth : Supply password to a running daemon" << std::endl << " finalize(f): Kill daemon and quit" << std::endl << " quit(q): Exit this program" << std::endl; }); @@ -151,6 +158,75 @@ void setup_app(CLI::App *app) { exit(0); }); + // tfa command + auto tfa_cmd = app->add_subcommand("tfa", "Supply 2FA code to a running daemon"); + static std::string tfa_code_input; + tfa_cmd->add_option("code", tfa_code_input, "2FA code")->required(); + tfa_cmd->callback([] { + char *errm = NULL; + size_t errm_size = 0; + RpcClient *rpc = new RpcClient(); + if (int result = rpc->Call(SENDTFA, tfa_code_input.c_str(), &errm, &errm_size) != 0) { + std::cerr << "Failed to send 2FA code: " << (errm ? errm : "no message") << std::endl; + if (errm) { free(errm); } + delete rpc; + return result; + } + delete rpc; + std::cout << "2FA code sent." << std::endl; + if (errm) { free(errm); } + return 0; + }); + + // auth command + auto auth_cmd = app->add_subcommand("auth", "Supply password to a running daemon"); + static std::string auth_pass_input; + auth_cmd->add_option("password", auth_pass_input, "Password")->required(); + auth_cmd->callback([] { + char *errm = NULL; + size_t errm_size = 0; + RpcClient *rpc = new RpcClient(); + int result = rpc->Call(SENDAUTH, auth_pass_input.c_str(), &errm, &errm_size); + putil_wipe(auth_pass_input.data(), auth_pass_input.size()); + auth_pass_input.clear(); + if (result != 0) { + std::cerr << "Failed to send auth: " << (errm ? errm : "no message") << std::endl; + if (errm) { free(errm); } + delete rpc; + return result; + } + delete rpc; + std::cout << "Auth sent." << std::endl; + if (errm) { free(errm); } + return 0; + }); + + // status command + app->add_subcommand("status", "Show current sync state")->alias("st")->callback([] { + char *errm = NULL; + size_t errm_size = 0; + RpcClient *rpc = new RpcClient(); + if (int result = rpc->Call(GETSTATUS, "", &errm, &errm_size) != 0) { + std::cerr << "Status failed: " << (errm ? errm : "no message") << std::endl; + if (errm) { free(errm); } + delete rpc; + return result; + } + delete rpc; + + char *status_str = nullptr; + if (pshm_read((void**)&status_str, nullptr) && status_str) { + std::cout << status_str << std::endl; + free(status_str); + } else { + std::cerr << "Failed to read status from daemon." << std::endl; + if (errm) { free(errm); } + return -1; + } + if (errm) { free(errm); } + return 0; + }); + // pending command app->add_subcommand("pending", "Check for pending transfers")->alias("p")->callback([] { char *errm = NULL; diff --git a/pclsync/pcommands.h b/pclsync/pcommands.h index 35f07ee..2dd95e3 100644 --- a/pclsync/pcommands.h +++ b/pclsync/pcommands.h @@ -7,5 +7,8 @@ enum command_ids_ { STOPSYNC, CHECKPENDING, SYNCPAUSE, - SYNCRESUME + SYNCRESUME, + SENDTFA, + SENDAUTH, + GETSTATUS }; diff --git a/pclsync_lib.cpp b/pclsync_lib.cpp index df2dd1e..464b8db 100644 --- a/pclsync_lib.cpp +++ b/pclsync_lib.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,10 @@ namespace cc = console_client; namespace clib = cc::clibrary; static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t tfa_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t tfa_cond = PTHREAD_COND_INITIALIZER; +static pthread_mutex_t auth_mtx = PTHREAD_MUTEX_INITIALIZER; +static pthread_cond_t auth_cond = PTHREAD_COND_INITIALIZER; static const std::string client_name = "pCloud CC v3.0.0"; @@ -161,25 +166,79 @@ clib::pclsync_lib &clib::pclsync_lib::get_lib() { } void clib::pclsync_lib::read_password() { + if (daemon_) { + syslog(LOG_NOTICE, + "pcloudcc: password required. " + "Provide with: echo 'auth PASSWORD' | pcloudcc -k"); + pthread_mutex_lock(&auth_mtx); + while (password_.empty()) { + pthread_cond_wait(&auth_cond, &auth_mtx); + } + pthread_mutex_unlock(&auth_mtx); + return; + } read_from_stdin(password_); } -void clib::pclsync_lib::read_tfa_code() +int clib::pclsync_lib::receive_auth(const char *pass) { + pthread_mutex_lock(&auth_mtx); + get_lib().password_ = std::string(pass); + pthread_cond_signal(&auth_cond); + pthread_mutex_unlock(&auth_mtx); + std::cout << "Auth received." << std::endl; + return 0; +} + +void clib::pclsync_lib::read_tfa_code(bool auto_sms) { if (daemon_) { - std::cout << "Not able to read 2fa code when started as daemon." << std::endl; - exit(1); + if (auto_sms) { + char *country_code = nullptr; + char *phone_number = nullptr; + int result = psync_tfa_send_sms(&country_code, &phone_number); + if (result == 0) { + if (country_code && phone_number) { + syslog(LOG_NOTICE, + "pcloudcc: 2FA required. SMS sent to +%s %s. " + "Provide code with: echo 'tfa CODE' | pcloudcc -k", + country_code, phone_number); + free(country_code); + free(phone_number); + } else { + syslog(LOG_NOTICE, + "pcloudcc: 2FA required. SMS sent. " + "Provide code with: echo 'tfa CODE' | pcloudcc -k"); + } + } else { + syslog(LOG_WARNING, + "pcloudcc: 2FA required but SMS failed (error %d). " + "Provide code with: echo 'tfa CODE' | pcloudcc -k", result); + } + } else { + syslog(LOG_WARNING, + "pcloudcc: 2FA code was rejected. " + "Provide correct code with: echo 'tfa CODE' | pcloudcc -k"); + } + + // Block until code arrives via RPC (pcloudcc -k) + pthread_mutex_lock(&tfa_mtx); + while (tfa_code_.empty()) { + pthread_cond_wait(&tfa_cond, &tfa_mtx); + } + pthread_mutex_unlock(&tfa_mtx); + return; } - + + // Interactive mode std::cout << "2FA required. Enter code from authenticator app, or type 'sms' to receive code via SMS: "; std::string input; std::getline(std::cin, input); - + if (input == "sms" || input == "SMS") { char *country_code = nullptr; char *phone_number = nullptr; int result = psync_tfa_send_sms(&country_code, &phone_number); - + if (result == 0) { if (country_code && phone_number) { std::cout << "SMS sent to +" << country_code << " " << phone_number << std::endl; @@ -199,6 +258,15 @@ void clib::pclsync_lib::read_tfa_code() } } +int clib::pclsync_lib::receive_tfa_code(const char *code) { + pthread_mutex_lock(&tfa_mtx); + get_lib().tfa_code_ = std::string(code); + pthread_cond_signal(&tfa_cond); + pthread_mutex_unlock(&tfa_mtx); + std::cout << "2FA code received." << std::endl; + return 0; +} + void clib::pclsync_lib::read_cryptopass() { read_from_stdin(crypto_pass_); } @@ -379,10 +447,15 @@ static void status_change(pstatus_t *status) { if (clib::pclsync_lib::get_lib().get_tfa_code().empty()) { clib::pclsync_lib::get_lib().read_tfa_code(); } - psync_tfa_set_code(clib::pclsync_lib::get_lib().get_tfa_code().c_str(), - clib::pclsync_lib::get_lib().get_trusted_device(), - 0); + 1 /* trusted */, 0); + clib::pclsync_lib::get_lib().wipe_tfa_code(); + } else if (status->status == PSTATUS_BAD_TFA_CODE) { + clib::pclsync_lib::get_lib().wipe_tfa_code(); + clib::pclsync_lib::get_lib().read_tfa_code(false /* auto_sms */); + psync_tfa_set_code(clib::pclsync_lib::get_lib().get_tfa_code().c_str(), + 1 /* trusted */, 0); + clib::pclsync_lib::get_lib().wipe_tfa_code(); } else if (status->status == PSTATUS_BAD_LOGIN_DATA) { if (!clib::pclsync_lib::get_lib().newuser_) { clib::pclsync_lib::get_lib().read_password(); @@ -467,6 +540,25 @@ int clib::pclsync_lib::check_pending(const char *unused) { return 0; } +int clib::pclsync_lib::get_status(const char *unused) { + (void)unused; + + pstatus_t st; + psync_get_status(&st); + + char buf[512]; + snprintf(buf, sizeof(buf), + "Status: %s\n" + "Download: %s\n" + "Upload: %s", + status2string(st.status), + st.downloadstr ? st.downloadstr : "", + st.uploadstr ? st.uploadstr : ""); + + pshm_write(buf, strlen(buf) + 1); + return 0; +} + int clib::pclsync_lib::finalize(const char *unused) { (void)unused; psync_destroy(); @@ -602,6 +694,9 @@ int clib::pclsync_lib::init() { prpc_register(STOPSYNC, &remove_sync_folder); prpc_register(SYNCPAUSE, &pause_sync); prpc_register(SYNCRESUME, &resume_sync); + prpc_register(SENDTFA, &receive_tfa_code); + prpc_register(SENDAUTH, &receive_auth); + prpc_register(GETSTATUS, &get_status); return 0; } diff --git a/pclsync_lib.h b/pclsync_lib.h index 59a05ac..babe72b 100644 --- a/pclsync_lib.h +++ b/pclsync_lib.h @@ -47,7 +47,7 @@ public: ~pclsync_lib(); pclsync_lib(); - bool trusted_device_; + bool trusted_device_ = true; bool crypto_on_; bool save_pass_; bool setup_crypto_; @@ -87,7 +87,7 @@ public: static pclsync_lib &get_lib(); // Console - void read_tfa_code(); + void read_tfa_code(bool auto_sms = true); void read_password(); void read_cryptopass(); @@ -104,6 +104,9 @@ public: static int remove_sync_folder(const char *path); static int pause_sync(const char *unused); static int resume_sync(const char *unused); + static int receive_tfa_code(const char *code); + static int receive_auth(const char *pass); + static int get_status(const char *unused); int logout(); int unlink();