From 4f9487bb0681fcdf8447283ae160130a62b4ca10 Mon Sep 17 00:00:00 2001 From: lqwer Date: Thu, 14 May 2026 07:56:14 +0000 Subject: [PATCH] Prompt for TFA code in CLI on PSTATUS_TFA_REQUIRED. The library already exposes psync_tfa_send_nofification / psync_tfa_send_sms / psync_tfa_set_code, but the console client never wired them up, so a login on a TFA-protected (or new-device-verified) account looped forever on LOGIN_REQUIRED. Handle PSTATUS_TFA_REQUIRED by triggering a code via notification (falling back to SMS), reading it from stdin, and submitting it with trust=1; re-prompt on PSTATUS_BAD_TFA_CODE. A 'r:' prefix routes to psync_tfa_set_code with is_recovery=1 for recovery codes. --- pCloudCC/pclsync_lib.cpp | 54 ++++++++++++++++++++++++++++++++++++++++ pCloudCC/pclsync_lib.h | 1 + 2 files changed, 55 insertions(+) diff --git a/pCloudCC/pclsync_lib.cpp b/pCloudCC/pclsync_lib.cpp index 4f49a59e..e553459a 100644 --- a/pCloudCC/pclsync_lib.cpp +++ b/pCloudCC/pclsync_lib.cpp @@ -163,10 +163,49 @@ static char const * status2string (uint32_t status){ case PSTATUS_SCANNING: return "SCANNING"; case PSTATUS_USER_MISMATCH: return "USER_MISMATCH"; case PSTATUS_ACCOUT_EXPIRED: return "ACCOUT_EXPIRED"; + case PSTATUS_TFA_REQUIRED: return "TFA_REQUIRED"; + case PSTATUS_BAD_TFA_CODE: return "BAD_TFA_CODE"; default :return "Unrecognized status"; } } +static void prompt_and_submit_tfa(bool request_code){ + if (request_code){ + plogged_device_list_t *devs=NULL; + int rc=psync_tfa_send_nofification(&devs); + if (rc==0 && devs && devs->entrycnt>0){ + std::cout << "A login code was sent via notification to:" << std::endl; + for (uint32_t i=0; ientrycnt; ++i) + std::cout << " - " << devs->devices[i].name << std::endl; + psync_free(devs); + } else { + if (devs) psync_free(devs); + char *country_code=NULL, *phone=NULL; + rc=psync_tfa_send_sms(&country_code, &phone); + if (rc==0){ + std::cout << "A login code was sent via SMS"; + if (country_code && phone) + std::cout << " to +" << country_code << " " << phone; + std::cout << "." << std::endl; + } else { + std::cout << "Could not auto-send a code (notification rc=" << rc + << "). If you have a recovery code, enter it prefixed with 'r:'." << std::endl; + } + if (country_code) psync_free(country_code); + if (phone) psync_free(phone); + } + } + std::cout << "Enter login code (prefix with 'r:' for recovery code): " << std::flush; + std::string code; + std::getline(std::cin, code); + int is_recovery=0; + if (code.rfind("r:", 0)==0){ + is_recovery=1; + code.erase(0, 2); + } + psync_tfa_set_code(code.c_str(), 1 /*trust this device*/, is_recovery); +} + static void status_change(pstatus_t* status) { static int cryptocheck=0; static int mount_set=0; @@ -207,6 +246,21 @@ static void status_change(pstatus_t* status) { } } + else if (status->status==PSTATUS_TFA_REQUIRED){ + if (clib::pclsync_lib::get_lib().is_daemon()){ + std::cout << "TFA required but running as daemon; cannot prompt for code." << std::endl; + exit(1); + } + prompt_and_submit_tfa(true); + } + else if (status->status==PSTATUS_BAD_TFA_CODE){ + if (clib::pclsync_lib::get_lib().is_daemon()){ + std::cout << "Bad TFA code and running as daemon; cannot re-prompt." << std::endl; + exit(1); + } + std::cout << "Code rejected, try again." << std::endl; + prompt_and_submit_tfa(false); + } if (status->status==PSTATUS_READY || status->status==PSTATUS_UPLOADING || status->status==PSTATUS_DOWNLOADING || status->status==PSTATUS_DOWNLOADINGANDUPLOADING){ if (!cryptocheck){ cryptocheck=1; diff --git a/pCloudCC/pclsync_lib.h b/pCloudCC/pclsync_lib.h index 17d8b417..a57856c7 100644 --- a/pCloudCC/pclsync_lib.h +++ b/pCloudCC/pclsync_lib.h @@ -57,6 +57,7 @@ namespace console_client { void setupsetup_crypto(bool p) {setup_crypto_ = p;} void set_newuser(bool p) {newuser_ = p;} void set_daemon(bool p) {daemon_ = p;} + bool is_daemon() const { return daemon_; } void set_status_callback(status_callback_t p) {status_callback_ = p;} //Console void get_pass_from_console();