From 7bc9078e0009183e9abdcf504b1eba52c030a759 Mon Sep 17 00:00:00 2001 From: Levi Neely <141506390+lneely@users.noreply.github.com> Date: Sun, 9 Mar 2025 19:16:33 +0100 Subject: [PATCH] support non-interactive command processing - app is now configured in setup_app function - added process_command() to api - support piped input and -c argument - fix memleaks --- control_tools.cpp | 53 ++++++++++++++++++++++++++++++---------------- control_tools.h | 3 +++ main.cpp | 54 +++++++++++++++++++++++++---------------------- pclsync/ppath.c | 3 ++- pclsync/pshm.c | 1 + pclsync/pssl.c | 16 ++++++-------- 6 files changed, 77 insertions(+), 53 deletions(-) diff --git a/control_tools.cpp b/control_tools.cpp index f39de89..0128d60 100644 --- a/control_tools.cpp +++ b/control_tools.cpp @@ -85,13 +85,12 @@ static char** command_completion(const char* text, int start, int end) { return nullptr; } -void process_commands() { - CLI::App app{"pcloudcc-lneely"}; - app.fallthrough(); - app.footer("Type 'help' or '?' for a list of supported commands."); +void setup_app(CLI::App *app) { + app->fallthrough(); + app->footer("Type 'help' or '?' for a list of supported commands."); // top-level commands - app.add_subcommand("help", "Show help")->alias("?")->callback([] { + app->add_subcommand("help", "Show help")->alias("?")->callback([] { std::cout << "Supported commands are:" << std::endl << " help(?): Show this help message" << std::endl << " crypto(c):" << std::endl @@ -104,13 +103,13 @@ void process_commands() { << " finalize(f): Kill daemon and quit" << std::endl << " quit(q): Exit this program" << std::endl; }); - auto crypto_cmd = app.add_subcommand("crypto", "Crypto-related commands")->alias("c"); + auto crypto_cmd = app->add_subcommand("crypto", "Crypto-related commands")->alias("c"); crypto_cmd->require_subcommand(); - auto sync_cmd = app.add_subcommand("sync", "Sync-related commands")->alias("s"); + auto sync_cmd = app->add_subcommand("sync", "Sync-related commands")->alias("s"); sync_cmd->require_subcommand(); // finalize command - app.add_subcommand("finalize", "Finalize and exit")->alias("f")->callback([] { + app->add_subcommand("finalize", "Finalize and exit")->alias("f")->callback([] { char *errm = NULL; size_t errm_size = 0; rpc_call(FINALIZE, "", &errm, &errm_size); @@ -119,15 +118,15 @@ void process_commands() { exit(0); }); - app.add_subcommand("quit", "Quit the program")->alias("q")->callback([] { + app->add_subcommand("quit", "Quit the program")->alias("q")->callback([] { exit(0); }); // crypto start auto start_crypto_cmd = crypto_cmd->add_subcommand("start", "Start crypto"); - std::string start_crypto_pwd; + static std::string start_crypto_pwd; start_crypto_cmd->add_option("password", start_crypto_pwd, "Crypto password")->required(); - start_crypto_cmd->callback([&] { + start_crypto_cmd->callback([] { char *errm = NULL; size_t errm_size = 0; if(int result = rpc_call(STARTCRYPTO, start_crypto_pwd.c_str(), &errm, &errm_size) != 0) { @@ -144,7 +143,7 @@ void process_commands() { crypto_cmd->add_subcommand("stop", "Stop crypto")->callback([] { char *errm = NULL; size_t errm_size = 0; - if(int result = rpc_call(STOPCRYPTO, "", &errm, &errm_size) == 0) { + if(int result = rpc_call(STOPCRYPTO, "", &errm, &errm_size) != 0) { std::cout << "Stop Crypto failed: "<< (errm ? errm : "no message") << std::endl; if (errm) { free(errm); } return result; @@ -187,9 +186,10 @@ void process_commands() { } else { std::cout << "No synchronized folders found." << std::endl; } - free(flist); + if(flist) { free(flist); } } else { std::cout << "failed to read folder list from shm" << std::endl; + if(flist) { free(flist); } return -1; } if(errm) { free(errm); } @@ -198,10 +198,10 @@ void process_commands() { // sync add auto sync_add_cmd = sync_cmd->add_subcommand("add", "Add sync folder"); - std::string localpath, remotepath; + static std::string localpath, remotepath; sync_add_cmd->add_option("localpath", localpath, "Local Path")->required(); sync_add_cmd->add_option("remotepath", remotepath, "Remote Path")->required(); - sync_add_cmd->callback([&] { + sync_add_cmd->callback([] { char *errm = NULL; size_t errmsz = 0; @@ -222,9 +222,9 @@ void process_commands() { // sync remove auto sync_remove_cmd = sync_cmd->add_subcommand("remove", "Remove sync folder (use ls to get folder ID)")->alias("rm"); - std::string syncrm_fid; + static std::string syncrm_fid; sync_remove_cmd->add_option("folderid", syncrm_fid, "Folder ID")->required(); - sync_remove_cmd->callback([&] { + sync_remove_cmd->callback([] { char *errm = NULL; size_t errmsz = 0; const char *folderid = syncrm_fid.c_str(); @@ -239,6 +239,23 @@ void process_commands() { if(errm) { free(errm); } return 0; }); +} + +int process_command(const std::string &command) { + CLI::App app = CLI::App{"pcloudcc-lneely"}; + setup_app(&app); + try { + app.parse(command); + return 0; + } catch (const CLI::ParseError &e) { + std::cerr << "Invalid command: '" << command << "'" << std::endl; + return 1; + } +} + +void process_commands() { + CLI::App app = CLI::App{"pcloudcc-lneely"}; + setup_app(&app); // enable command history and auto-completion using_history(); @@ -266,7 +283,7 @@ void process_commands() { args.push_back(arg); } - invs << "Invalid command: '" << "line" + invs << "Invalid command: '" << line << "'. Type 'help' or '?' to get a list of valid commands."; if (!args.empty()) { try { diff --git a/control_tools.h b/control_tools.h index 69fef4e..62445fb 100644 --- a/control_tools.h +++ b/control_tools.h @@ -29,10 +29,13 @@ #ifndef CONTROL_TOOLS_H #define CONTROL_TOOLS_H +#include + namespace control_tools { void process_commands(); int daemonize(bool do_commands); +int process_command(const std::string &command); } // namespace control_tools diff --git a/main.cpp b/main.cpp index aab0195..e6be23b 100644 --- a/main.cpp +++ b/main.cpp @@ -58,31 +58,21 @@ int main(int argc, char **argv) { try { po::options_description desc("Allowed options"); - desc.add_options()("help,h", "Show this help message.")( - "username,u", po::value(&username), - "pCloud account name.")( - "password,p", po::bool_switch(&passwordsw), - "Ask for pCloud account password.")( - "tfa_code,t", po::value(&tfa_code), - "pCloud tfa code")( - "trusted_device,r", po::bool_switch(&trusted_device), - "Trust this device.")( - "crypto,c", po::bool_switch(&crypto), - "Ask for crypto password.")( - "passascrypto,y", po::value(), - "User password is the same as crypto password.")( - "daemonize,d", po::bool_switch(&daemon), - "Run the process as a background daemon.")( - "commands ,o", po::bool_switch(&commands), - "Keep parent process alive and process commands. ")( - "mountpoint,m", po::value(), - "Specify where pCloud filesystem is mounted.")( - "commands_only,k", po::bool_switch(&commands_only), - "Open command prompt to interact with running daemon.")( - "newuser,n", po::bool_switch(&newuser), - "Register a new pCloud user account.")( - "savepassword,s", po::bool_switch(&save_pass), - "Save user password in the database."); + desc.add_options() + ("help,h", "Show this help message.") + ("username,u", po::value(&username), "pCloud account name.") + ("password,p", po::bool_switch(&passwordsw), "Ask for pCloud account password.") + ("tfa_code,t", po::value(&tfa_code), "pCloud tfa code") + ("trusted_device,r", po::bool_switch(&trusted_device), "Trust this device.") + ("crypto,c", po::bool_switch(&crypto), "Ask for crypto password.") + ("passascrypto,y", po::value(), "User password is the same as crypto password.") + ("daemonize,d", po::bool_switch(&daemon), "Run the process as a background daemon.") + ("commands ,o", po::bool_switch(&commands), "Keep parent process alive and process commands. ") + ("mountpoint,m", po::value(), "Specify where pCloud filesystem is mounted.") + ("commands_only,k", po::bool_switch(&commands_only), "Open command prompt to interact with running daemon.") + ("command,c", po::value(), "Execute a single command and exit.") + ("newuser,n", po::bool_switch(&newuser), "Register a new pCloud user account.") + ("savepassword,s", po::bool_switch(&save_pass), "Save user password in the database."); po::command_line_parser parser{argc, argv}; po::positional_options_description p; @@ -102,6 +92,20 @@ int main(int argc, char **argv) { exit(0); } + bool has_piped_input = !isatty(STDIN_FILENO); + if (has_piped_input && !vm.count("help")) { + std::string line; + if (std::getline(std::cin, line) && !line.empty()) { + return ct::process_command(line); + } + } + + if (vm.count("command")) { + std::string command = vm["command"].as(); + return ct::process_command(command); + } + + if ((!vm.count("username"))) { std::cout << "Username option is required, specify with " << "-u or --username." << std::endl; diff --git a/pclsync/ppath.c b/pclsync/ppath.c index af33754..a67d028 100644 --- a/pclsync/ppath.c +++ b/pclsync/ppath.c @@ -191,11 +191,12 @@ char *ppath_private(char *name) { if (!path) return NULL; rpath = psync_strcat(path, "/", name, NULL); - free(path); if (stat(rpath, &st) && mkdir(path, PSYNC_DEFAULT_POSIX_FOLDER_MODE)) { psync_free(rpath); + free(path); return NULL; } + free(path); return rpath; } diff --git a/pclsync/pshm.c b/pclsync/pshm.c index 20f42d4..6eeb71f 100644 --- a/pclsync/pshm.c +++ b/pclsync/pshm.c @@ -21,6 +21,7 @@ key_t pshm_get_key() { return (key_t)-1; } snprintf(path, sizeof(path), "%s/.pcloud/data.db", home); + free(home); return ftok(path, 'A'); } diff --git a/pclsync/pssl.c b/pclsync/pssl.c index 4b7de7e..c9fa996 100644 --- a/pclsync/pssl.c +++ b/pclsync/pssl.c @@ -610,9 +610,8 @@ psync_rsa_publickey_t prsa_load_public(const unsigned char *keydata, // function gets the actual key length from the ASN.1 header, trims any bytes // that exceed that length, and writes the trimmed key and length to keydata // and keylen. -static unsigned char* trim_der_key(const unsigned char *keydata, size_t *keylen) { +static void trim_der_key(unsigned char *keydata, size_t *keylen) { size_t len, header_size, correct_len; - unsigned char tag; unsigned char *p = (unsigned char *)keydata; const unsigned char *end; int ret; @@ -621,17 +620,16 @@ static unsigned char* trim_der_key(const unsigned char *keydata, size_t *keylen) end = keydata + *keylen; ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret != 0) { - return NULL; + return; } header_size = p - keydata; correct_len = header_size + len; trimmed = malloc(correct_len); - if (!trimmed) return NULL; - + if (!trimmed) return; memcpy(trimmed, keydata, correct_len); + memcpy(keydata, trimmed, correct_len); + free(trimmed); *keylen = correct_len; - - return trimmed; } psync_rsa_privatekey_t prsa_load_private(const unsigned char *keydata, size_t keylen) { @@ -639,8 +637,8 @@ psync_rsa_privatekey_t prsa_load_private(const unsigned char *keydata, size_t ke mbedtls_rsa_context *rsa; int ret; - trim_der_key(keydata, &keylen); - + trim_der_key((unsigned char *)keydata, &keylen); + mbedtls_pk_init(&ctx); ret = mbedtls_pk_parse_key(&ctx, keydata, keylen, NULL, 0, rng_get, &rng); if(unlikely(ret)) {