From 5b84d9851cfc6ae57b950643ba7ba0fb8b593f32 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Mon, 7 Nov 2022 11:25:23 +0100 Subject: [PATCH 1/6] Port cmd clinet to commandline parser This also removes the hidden flag concerning hidden files. Fixes: #10147 (cherry picked from commit 34f124878f2178b260df9522c00a54de2504fabb) --- src/cmd/cmd.cpp | 218 ++++++++++++++++++++++-------------------------- 1 file changed, 102 insertions(+), 116 deletions(-) diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index d6ce8fd49a8..a77889aefd0 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -13,22 +13,12 @@ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * for more details. */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include "httpcredentialstext.h" +#include "netrcparser.h" #include "account.h" #include "common/syncjournaldb.h" -#include "config.h" +#include "common/version.h" #include "configfile.h" // ONLY ACCESS THE STATIC FUNCTIONS! #include "csync_exclude.h" #include "libsync/logger.h" @@ -37,8 +27,19 @@ #include "networkjobs/jsonjob.h" #include "syncengine.h" -#include "httpcredentialstext.h" -#include "netrcparser.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include using namespace OCC; @@ -297,119 +298,103 @@ void setupCredentials(SyncCTX &ctx) } } - -[[noreturn]] void help() -{ - const char *binaryName = APPLICATION_EXECUTABLE "cmd"; - - std::cout << binaryName << " - command line " APPLICATION_NAME " client tool" << std::endl; - std::cout << "" << std::endl; - std::cout << "Usage: " << binaryName << " [OPTION] " << std::endl; - std::cout << "" << std::endl; - std::cout << "A proxy can either be set manually using --httpproxy." << std::endl; - std::cout << "Otherwise, the setting from a configured sync client will be used." << std::endl; - std::cout << std::endl; - std::cout << "Options:" << std::endl; - std::cout << " --silent, -s Don't be so verbose" << std::endl; - std::cout << " --httpproxy [proxy] Specify a http proxy to use." << std::endl; - std::cout << " Proxy is http://server:port" << std::endl; - std::cout << " --trust Trust the SSL certification." << std::endl; - std::cout << " --exclude [file] Exclude list file" << std::endl; - std::cout << " --unsyncedfolders [file] File containing the list of unsynced remote folders (selective sync)" << std::endl; - std::cout << " --user, -u [name] Use [name] as the login name" << std::endl; - std::cout << " --password, -p [pass] Use [pass] as password" << std::endl; - std::cout << " -n Use netrc (5) for login" << std::endl; - std::cout << " --non-interactive Do not block execution with interaction" << std::endl; - std::cout << " --max-sync-retries [n] Retries maximum n times (default to 3)" << std::endl; - std::cout << " --uplimit [n] Limit the upload speed of files to n KB/s" << std::endl; - std::cout << " --downlimit [n] Limit the download speed of files to n KB/s" << std::endl; - std::cout << " -h Sync hidden files,do not ignore them" << std::endl; - std::cout << " --version, -v Display version and exit" << std::endl; - std::cout << " --logdebug More verbose logging" << std::endl; - std::cout << "" << std::endl; - exit(0); -} - -[[noreturn]] void showVersion() -{ - std::cout << qPrintable(Theme::instance()->versionSwitchOutput()); - exit(0); -} - CmdOptions parseOptions(const QStringList &app_args) { CmdOptions options; - QStringList args(app_args); + QCommandLineParser parser; + parser.setApplicationDescription(QStringLiteral("%1 version %2 - command line client tool").arg(QCoreApplication::instance()->applicationName(), OCC::Version::displayString())); - int argCount = args.count(); + // this little snippet saves a few lines below + auto addOption = [&parser](const QCommandLineOption &option) { + parser.addOption(option); + return option; + }; - if (argCount < 3) { - if (argCount >= 2) { - const QString option = args.at(1); - if (option == QLatin1String("-v") || option == QLatin1String("--version")) { - showVersion(); - } - } - help(); - } + auto silentOption = addOption({ { QStringLiteral("s"), QStringLiteral("silten") }, QStringLiteral("Don't be so verbose.") }); + auto httpproxyOption = addOption({ { QStringLiteral("httpproxy") }, QStringLiteral("Specify a http proxy to use."), QStringLiteral("http://server:port") }); + auto trustOption = addOption({ { QStringLiteral("trust") }, QStringLiteral("Trust the SSL certification") }); + auto excludeOption = addOption({ { QStringLiteral("exclude") }, QStringLiteral("Path to an exclude list [file]"), QStringLiteral("file") }); + auto unsyncedfoldersOption = addOption({ { QStringLiteral("unsyncedfolders") }, QStringLiteral("File containing the list of unsynced remote folders (selective sync)"), QStringLiteral("file") }); + + auto userOption = addOption({ { QStringLiteral("u"), QStringLiteral("user") }, QStringLiteral("Use [name] as the login name"), QStringLiteral("name") }); + auto passwordOption = addOption({ { QStringLiteral("p"), QStringLiteral("password") }, QStringLiteral("Use [pass] as password"), QStringLiteral("password") }); + auto useNetrcOption = addOption({ { QStringLiteral("n") }, QStringLiteral("Use netrc (5) for login") }); + + auto nonInterActiveOption = addOption({ { QStringLiteral("non-interactive") }, QStringLiteral("Do not block execution with interaction") }); + auto maxRetriesOption = addOption({ { QStringLiteral("max-sync-retries") }, QStringLiteral("Retries maximum n times (default to 3)"), QStringLiteral("n") }); + auto uploadLimitOption = addOption({ { QStringLiteral("uplimit") }, QStringLiteral("Limit the upload speed of files to n KB/s"), QStringLiteral("n") }); + auto downloadLimitption = addOption({ { QStringLiteral("downlimit") }, QStringLiteral("Limit the download speed of files to n KB/s"), QStringLiteral("n") }); - options.target_url = QUrl(args.takeLast()); + auto logdebugOption = addOption({ { QStringLiteral("logdebug") }, QStringLiteral("More verbose logging") }); - options.source_dir = args.takeLast(); - QFileInfo fi(options.source_dir); - if (!fi.exists()) { - std::cerr << "Source dir '" << qPrintable(options.source_dir) << "' does not exist." << std::endl; + parser.addHelpOption(); + parser.addVersionOption(); + + parser.addPositionalArgument(QStringLiteral("source_dir"), QStringLiteral("The source dir")); + parser.addPositionalArgument(QStringLiteral("server_url"), QStringLiteral("The url to the server")); + + parser.process(app_args); + + + const QStringList args = parser.positionalArguments(); + if (args.size() < 2) { + parser.showHelp(); exit(1); } - options.source_dir = fi.absoluteFilePath(); - if (!options.source_dir.endsWith(QLatin1Char('/'))) { - options.source_dir.append(QLatin1Char('/')); - } - QStringListIterator it(args); - // skip file name; - if (it.hasNext()) - it.next(); - - while (it.hasNext()) { - const QString option = it.next(); - - if (option == QLatin1String("--httpproxy") && !it.peekNext().startsWith(QLatin1String("-"))) { - options.proxy = it.next(); - } else if (option == QLatin1String("-s") || option == QLatin1String("--silent")) { - options.silent = true; - } else if (option == QLatin1String("--trust")) { - options.trustSSL = true; - } else if (option == QLatin1String("-n")) { - options.useNetrc = true; - } else if (option == QLatin1String("-h")) { - options.ignoreHiddenFiles = false; - } else if (option == QLatin1String("--non-interactive")) { - options.interactive = false; - } else if ((option == QLatin1String("-u") || option == QLatin1String("--user")) && !it.peekNext().startsWith(QLatin1String("-"))) { - options.user = it.next(); - } else if ((option == QLatin1String("-p") || option == QLatin1String("--password")) && !it.peekNext().startsWith(QLatin1String("-"))) { - options.password = it.next(); - } else if (option == QLatin1String("--exclude") && !it.peekNext().startsWith(QLatin1String("-"))) { - options.exclude = it.next(); - } else if (option == QLatin1String("--unsyncedfolders") && !it.peekNext().startsWith(QLatin1String("-"))) { - options.unsyncedfolders = it.next(); - } else if (option == QLatin1String("--max-sync-retries") && !it.peekNext().startsWith(QLatin1String("-"))) { - options.restartTimes = it.next().toInt(); - } else if (option == QLatin1String("--uplimit") && !it.peekNext().startsWith(QLatin1String("-"))) { - options.uplimit = it.next().toInt() * 1000; - } else if (option == QLatin1String("--downlimit") && !it.peekNext().startsWith(QLatin1String("-"))) { - options.downlimit = it.next().toInt() * 1000; - } else if (option == QLatin1String("--logdebug")) { - Logger::instance()->setLogFile(QStringLiteral("-")); - Logger::instance()->setLogDebug(true); - } else { - help(); + options.target_url = QUrl::fromUserInput(args[1]); + options.source_dir = [arg = args[0]] { + QFileInfo fi(arg); + if (!fi.exists()) { + std::cerr << "Source dir '" << qPrintable(arg) << "' does not exist." << std::endl; + exit(1); } - } + QString sourceDir = fi.absoluteFilePath(); + if (!sourceDir.endsWith(QLatin1Char('/'))) { + sourceDir.append(QLatin1Char('/')); + } + return sourceDir; + }(); - if (options.target_url.isEmpty() || options.source_dir.isEmpty()) { - help(); + if (parser.isSet(httpproxyOption)) { + options.proxy = parser.value(httpproxyOption); + } + if (parser.isSet(silentOption)) { + options.silent = true; + } + if (parser.isSet(trustOption)) { + options.trustSSL = true; + } + if (parser.isSet(useNetrcOption)) { + options.useNetrc = true; + } + if (parser.isSet(nonInterActiveOption)) { + options.interactive = false; + } + if (parser.isSet(userOption)) { + options.user = parser.value(userOption); + } + if (parser.isSet(passwordOption)) { + options.password = parser.value(passwordOption); + } + if (parser.isSet(excludeOption)) { + options.exclude = parser.value(excludeOption); + } + if (parser.isSet(unsyncedfoldersOption)) { + options.unsyncedfolders = parser.value(unsyncedfoldersOption); + } + if (parser.isSet(maxRetriesOption)) { + options.restartTimes = parser.value(maxRetriesOption).toInt(); + } + if (parser.isSet(uploadLimitOption)) { + options.uplimit = parser.value(maxRetriesOption).toInt() * 1000; + } + if (parser.isSet(downloadLimitption)) { + options.downlimit = parser.value(downloadLimitption).toInt() * 1000; + } + if (parser.isSet(logdebugOption)) { + Logger::instance()->setLogFile(QStringLiteral("-")); + Logger::instance()->setLogDebug(true); } return options; } @@ -417,6 +402,7 @@ CmdOptions parseOptions(const QStringList &app_args) int main(int argc, char **argv) { QCoreApplication app(argc, argv); + app.setApplicationVersion(Theme::instance()->versionSwitchOutput()); #ifdef Q_OS_WIN // Ensure OpenSSL config file is only loaded from app directory From 92a264ab6f210728f5fd59f1377460c625e910d9 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Mon, 7 Nov 2022 11:25:51 +0100 Subject: [PATCH 2/6] Remove dead code (cherry picked from commit 859fdfebae7d26a74e63186dc8e023aaec08a251) --- src/cmd/cmd.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index a77889aefd0..d097542c16d 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -64,8 +64,6 @@ struct CmdOptions int restartTimes = 3; int downlimit = 0; int uplimit = 0; - bool deltasync; - qint64 deltasyncminfilesize; }; struct SyncCTX From 1155ccfc104de7c2e53f16c9aebfa85f071d8868 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Mon, 7 Nov 2022 11:43:03 +0100 Subject: [PATCH 3/6] Add a new parameter to set a remote folder Fixes: #10193 (cherry picked from commit 5648f3932af3882acfc495d7a3216bd1b609a676) --- changelog/unreleased/10193 | 5 +++++ src/cmd/cmd.cpp | 18 ++++++++---------- 2 files changed, 13 insertions(+), 10 deletions(-) create mode 100644 changelog/unreleased/10193 diff --git a/changelog/unreleased/10193 b/changelog/unreleased/10193 new file mode 100644 index 00000000000..45d71398230 --- /dev/null +++ b/changelog/unreleased/10193 @@ -0,0 +1,5 @@ +Change: Don't guess remote folder in owncloudcmd + +The commandline client was modified to explicitly accept remote folder, the remote folder must no longer be encoded in the server url. + +https://github.com/owncloud/client/issues/10193 diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index d097542c16d..a6b7d4ed27f 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -50,6 +50,7 @@ struct CmdOptions { QString source_dir; QUrl target_url; + QString remoteFolder; QString config_directory; QString user; QString password; @@ -74,7 +75,6 @@ struct SyncCTX } CmdOptions options; QUrl credentialFreeUrl; - QString folder; AccountPtr account; QString user; }; @@ -133,7 +133,7 @@ void sync(const SyncCTX &ctx) opt.fillFromEnvironmentVariables(); opt.verifyChunkSizes(); auto engine = new SyncEngine( - ctx.account, ctx.account->davUrl(), ctx.options.source_dir, ctx.folder, db); + ctx.account, ctx.account->davUrl(), ctx.options.source_dir, ctx.options.remoteFolder, db); engine->setSyncOptions(opt); engine->setParent(db); @@ -330,17 +330,17 @@ CmdOptions parseOptions(const QStringList &app_args) parser.addPositionalArgument(QStringLiteral("source_dir"), QStringLiteral("The source dir")); parser.addPositionalArgument(QStringLiteral("server_url"), QStringLiteral("The url to the server")); + parser.addPositionalArgument(QStringLiteral("remote_folder"), QStringLiteral("A remote folder")); parser.process(app_args); const QStringList args = parser.positionalArguments(); - if (args.size() < 2) { + if (args.size() < 2 || args.size() > 3) { parser.showHelp(); exit(1); } - options.target_url = QUrl::fromUserInput(args[1]); options.source_dir = [arg = args[0]] { QFileInfo fi(arg); if (!fi.exists()) { @@ -353,6 +353,10 @@ CmdOptions parseOptions(const QStringList &app_args) } return sourceDir; }(); + options.target_url = QUrl::fromUserInput(args[1]); + if (args.size() == 3) { + options.remoteFolder = args[2]; + } if (parser.isSet(httpproxyOption)) { options.proxy = parser.value(httpproxyOption); @@ -433,12 +437,6 @@ int main(int argc, char **argv) QStringList splitted = tmp.path().split(ctx.account->davPath()); tmp.setPath(splitted.value(0)); tmp.setScheme(tmp.scheme().replace(QLatin1String("owncloud"), QLatin1String("http"))); - - // Remote folders typically start with a / and don't end with one - ctx.folder = QLatin1Char('/') + splitted.value(1); - if (ctx.folder.endsWith(QLatin1Char('/')) && ctx.folder != QLatin1Char('/')) { - ctx.folder.chop(1); - } return tmp; }(); ctx.credentialFreeUrl = baseUrl.adjusted(QUrl::RemoveUserInfo); From bc19797c4f73ecd032fe6883816f4fbd35f882c0 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Mon, 7 Nov 2022 12:53:40 +0100 Subject: [PATCH 4/6] Fix unit test (cherry picked from commit 02274cd5241f3f401397511ded63d227d157a15c) --- src/common/utility.cpp | 34 ++++++++++++++++------------------ test/testutility.cpp | 25 +++++++++++-------------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/src/common/utility.cpp b/src/common/utility.cpp index 6f8f44ae83e..d705dc53784 100644 --- a/src/common/utility.cpp +++ b/src/common/utility.cpp @@ -366,24 +366,22 @@ void Utility::crash() // restarting from the installer. QByteArray Utility::versionOfInstalledBinary(const QString &command) { - QByteArray re; - if (isLinux()) { - QString binary(command); - if (binary.isEmpty()) { - binary = qApp->arguments()[0]; - } - QStringList params; - params << QStringLiteral("--version"); - QProcess process; - process.start(binary, params); - process.waitForFinished(); // sets current thread to sleep and waits for pingProcess end - re = process.readAllStandardOutput(); - int newline = re.indexOf('\n'); - if (newline > 0) { - re.truncate(newline); - } + QString binary(command); + if (binary.isEmpty()) { + binary = qApp->arguments()[0]; } - return re; + QStringList params; + params << QStringLiteral("--version"); + QProcess process; + process.start(binary, params); + process.waitForFinished(); // sets current thread to sleep and waits for pingProcess end + QByteArray re = process.readAllStandardOutput(); + qCDebug(lcUtility) << Q_FUNC_INFO << re; + int newline = re.indexOf('\n'); + if (newline > 0) { + re.truncate(newline); + } + return re.trimmed(); } QString Utility::timeAgoInWords(const QDateTime &dt, const QDateTime &from) @@ -645,4 +643,4 @@ QDebug &operator<<(QDebug &debug, nanoseconds in) << min.count() << "min, " << s.count() << "s, " << ms.count() << "ms)"; -} \ No newline at end of file +} diff --git a/test/testutility.cpp b/test/testutility.cpp index 4a2cb41e87e..03dc15be858 100644 --- a/test/testutility.cpp +++ b/test/testutility.cpp @@ -110,20 +110,17 @@ private slots: void testVersionOfInstalledBinary() { - if(isLinux()) { - // pass the cmd client from our build dir - // this is a bit inaccurate as it does not test the "real thing" - // but cmd and gui have the same --version handler by now - // and cmd works without X in CI - QString ver = versionOfInstalledBinary(QStringLiteral(OWNCLOUD_BIN_PATH "/" APPLICATION_EXECUTABLE "cmd")); - qDebug() << "Version of installed ownCloud Binary: " << ver; - QVERIFY(!ver.isEmpty()); - - QRegExp rx(APPLICATION_SHORTNAME " \\d+\\.\\d+\\.\\d+.*"); - QVERIFY(rx.exactMatch(ver)); - } else { - QVERIFY(versionOfInstalledBinary().isEmpty()); - } + // pass the cmd client from our build dir + // this is a bit inaccurate as it does not test the "real thing" + // but cmd and gui have the same --version handler by now + // and cmd works without X in CI + QString ver = versionOfInstalledBinary(QStringLiteral(OWNCLOUD_BIN_PATH "/" APPLICATION_EXECUTABLE "cmd")); + qDebug() << "Version of installed ownCloud Binary: " << ver; + QVERIFY(!ver.isEmpty()); + + QRegExp rx(APPLICATION_SHORTNAME "cmd ownCloud \\d+\\.\\d+\\.\\d+.*", Qt::CaseInsensitive); + qDebug() << rx.pattern(); + QVERIFY(rx.exactMatch(ver)); } void testTimeAgo() From 63f9bbe996b042cd5776c9907e6d77f6090724da Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Tue, 8 Nov 2022 12:52:59 +0100 Subject: [PATCH 5/6] Fix message (cherry picked from commit 772a8e465fbc78fd7a8ec936d9f5a0aa2c08864b) --- src/cmd/cmd.cpp | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index a6b7d4ed27f..498a36e6577 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -159,16 +159,22 @@ void sync(const SyncCTX &ctx) if (!ctx.options.interactive) { abort(false); } else { - std::cout << (dir == SyncFileItem::Down ? "All files in the sync folder '%1' folder were deleted on the server.\n" - "These deletes will be synchronized to your local sync folder, making such files " - "unavailable unless you have a right to restore. \n" - "If you decide to keep the files, they will be re-synced with the server if you have rights to do so.\n" - "If you decide to delete the files, they will be unavailable to you, unless you are the owner." - : "All the files in your local sync folder '%1' were deleted. These deletes will be " - "synchronized with your server, making such files unavailable unless restored.\n" - "Are you sure you want to sync those actions with the server?\n" - "If this was an accident and you decide to keep your files, they will be re-synced from the server.") - << std::endl; + if (dir == SyncFileItem::Down) { + std::cout << "All files in the sync folder '" << qPrintable(ctx.options.remoteFolder) << "' folder were deleted on the server.\n" + << "These deletes will be synchronized to your local sync folder, making such files " + << "unavailable unless you have a right to restore. \n" + << "If you decide to keep the files, they will be re-synced with the server if you have rights to do so.\n" + << "If you decide to delete the files, they will be unavailable to you, unless you are the owner." + << std::endl; + + + } else { + std::cout << "All the files in your local sync folder '" << qPrintable(ctx.options.source_dir) << "' were deleted. These deletes will be " + << "synchronized with your server, making such files unavailable unless restored.\n" + << "Are you sure you want to sync those actions with the server?\n" + << "If this was an accident and you decide to keep your files, they will be re-synced from the server." + << std::endl; + } std::string s; while (true) { std::cout << "Remove all files?[y,n]"; From f9bb5287382b3432042139d250249040549853e4 Mon Sep 17 00:00:00 2001 From: Hannah von Reth Date: Tue, 8 Nov 2022 12:56:52 +0100 Subject: [PATCH 6/6] Allow to specify a server url diferent than the sync url (cherry picked from commit cbb0b37dc341ef257dcc61b679238a40b36cf2c0) --- changelog/unreleased/10239 | 6 ++++++ src/cmd/cmd.cpp | 26 +++++++++++++++++++------- 2 files changed, 25 insertions(+), 7 deletions(-) create mode 100644 changelog/unreleased/10239 diff --git a/changelog/unreleased/10239 b/changelog/unreleased/10239 new file mode 100644 index 00000000000..f58f69b20ff --- /dev/null +++ b/changelog/unreleased/10239 @@ -0,0 +1,6 @@ +Change: owncloudcmd OCIS support + +When using ocis and spaces with the cmd client the additional parameter `--server` is required. +`--server` spcifies the url to the server, while the positional parameter 'server_url' specifies the webdav url. + +https://github.com/owncloud/client/pull/10239 diff --git a/src/cmd/cmd.cpp b/src/cmd/cmd.cpp index 498a36e6577..cdd35e82e15 100644 --- a/src/cmd/cmd.cpp +++ b/src/cmd/cmd.cpp @@ -50,6 +50,8 @@ struct CmdOptions { QString source_dir; QUrl target_url; + QUrl server_url; + QString remoteFolder; QString config_directory; QString user; @@ -74,7 +76,6 @@ struct SyncCTX { } CmdOptions options; - QUrl credentialFreeUrl; AccountPtr account; QString user; }; @@ -133,7 +134,7 @@ void sync(const SyncCTX &ctx) opt.fillFromEnvironmentVariables(); opt.verifyChunkSizes(); auto engine = new SyncEngine( - ctx.account, ctx.account->davUrl(), ctx.options.source_dir, ctx.options.remoteFolder, db); + ctx.account, ctx.options.target_url, ctx.options.source_dir, ctx.options.remoteFolder, db); engine->setSyncOptions(opt); engine->setParent(db); @@ -320,6 +321,7 @@ CmdOptions parseOptions(const QStringList &app_args) auto excludeOption = addOption({ { QStringLiteral("exclude") }, QStringLiteral("Path to an exclude list [file]"), QStringLiteral("file") }); auto unsyncedfoldersOption = addOption({ { QStringLiteral("unsyncedfolders") }, QStringLiteral("File containing the list of unsynced remote folders (selective sync)"), QStringLiteral("file") }); + auto serverOption = addOption({ { QStringLiteral("server") }, QStringLiteral("Use [url] as the location of the server. OCIS only (server location and spaces url can differ)"), QStringLiteral("url") }); auto userOption = addOption({ { QStringLiteral("u"), QStringLiteral("user") }, QStringLiteral("Use [name] as the login name"), QStringLiteral("name") }); auto passwordOption = addOption({ { QStringLiteral("p"), QStringLiteral("password") }, QStringLiteral("Use [pass] as password"), QStringLiteral("password") }); auto useNetrcOption = addOption({ { QStringLiteral("n") }, QStringLiteral("Use netrc (5) for login") }); @@ -379,6 +381,9 @@ CmdOptions parseOptions(const QStringList &app_args) if (parser.isSet(nonInterActiveOption)) { options.interactive = false; } + if (parser.isSet(serverOption)) { + options.server_url = QUrl::fromUserInput(parser.value(serverOption)); + } if (parser.isSet(userOption)) { options.user = parser.value(userOption); } @@ -433,22 +438,29 @@ int main(int argc, char **argv) setupCredentials(ctx); - if (!ctx.options.target_url.path().contains(ctx.account->davPath())) { - ctx.options.target_url = OCC::Utility::concatUrlPath(ctx.options.target_url, ctx.account->davPath()); + if (ctx.options.server_url.isEmpty()) { + ctx.options.server_url = ctx.options.target_url; + // guess dav path + if (!ctx.options.target_url.path().contains(ctx.account->davPath())) { + ctx.options.target_url = OCC::Utility::concatUrlPath(ctx.options.target_url, ctx.account->davPath()); + } } + // don't leak credentials more than needed + ctx.options.server_url = ctx.options.server_url.adjusted(QUrl::RemoveUserInfo); + ctx.options.target_url = ctx.options.target_url.adjusted(QUrl::RemoveUserInfo); + const QUrl baseUrl = [&ctx] { - auto tmp = ctx.options.target_url; + auto tmp = ctx.options.server_url; // Find the folder and the original owncloud url QStringList splitted = tmp.path().split(ctx.account->davPath()); tmp.setPath(splitted.value(0)); tmp.setScheme(tmp.scheme().replace(QLatin1String("owncloud"), QLatin1String("http"))); return tmp; }(); - ctx.credentialFreeUrl = baseUrl.adjusted(QUrl::RemoveUserInfo); - ctx.account->setUrl(ctx.credentialFreeUrl); + ctx.account->setUrl(baseUrl); auto *checkServerJob = CheckServerJobFactory(ctx.account->accessManager()).startJob(ctx.account->url());