diff options
author | Daniel Molkentin <danimo@owncloud.com> | 2014-09-17 01:52:28 +0400 |
---|---|---|
committer | Daniel Molkentin <danimo@owncloud.com> | 2014-09-17 01:52:28 +0400 |
commit | fe023e2229dedbc646ab7e701ecd1a457dadef16 (patch) | |
tree | 6d5043bab768c9d09d0afe91cd63fbe48315159c /src/owncloudcmd | |
parent | 97cc05eeea3da08ee01578b1604ed0bc3b0b7d7e (diff) |
Allow passing user/pass explicitly or via netrc
#2211
Diffstat (limited to 'src/owncloudcmd')
-rw-r--r-- | src/owncloudcmd/netrcparser.cpp | 97 | ||||
-rw-r--r-- | src/owncloudcmd/netrcparser.h | 41 | ||||
-rw-r--r-- | src/owncloudcmd/owncloudcmd.cpp | 144 |
3 files changed, 255 insertions, 27 deletions
diff --git a/src/owncloudcmd/netrcparser.cpp b/src/owncloudcmd/netrcparser.cpp new file mode 100644 index 000000000..7db8d2be4 --- /dev/null +++ b/src/owncloudcmd/netrcparser.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) by Daniel Molkentin <danimo@owncloud.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <QDir> +#include <QFile> +#include <QTextStream> + +#include "netrcparser.h" + +namespace Mirall { + +namespace { +QString defaultKeyword = QLatin1String("default"); +QString machineKeyword = QLatin1String("machine"); +QString loginKeyword = QLatin1String("login"); +QString passwordKeyword = QLatin1String("password"); + +} + +NetrcParser::NetrcParser(const QString &fileName) + : _fileName(fileName) +{ + if (_fileName.isEmpty()) { + _fileName = QDir::homePath()+QLatin1String("/.netrc"); + } +} + +void NetrcParser::tryAddEntryAndClear(QString& machine, LoginPair& pair, bool& isDefault) { + if (isDefault) { + _default = pair; + } else if (!machine.isEmpty() && !pair.first.isEmpty()){ + _entries.insert(machine, pair); + } + pair = qMakePair(QString(), QString()); + machine.clear(); + isDefault = false; +} + +bool NetrcParser::parse() +{ + QFile netrc(_fileName); + if (!netrc.open(QIODevice::ReadOnly)) { + return false; + } + + QTextStream ts(&netrc); + LoginPair pair; + QString machine; + bool isDefault = false; + while (!ts.atEnd()) { + QString next; + ts >> next; + if (next == defaultKeyword) { + tryAddEntryAndClear(machine, pair, isDefault); + isDefault = true; + } + if (next == machineKeyword) { + tryAddEntryAndClear(machine, pair, isDefault); + ts >> machine; + } else if (next == loginKeyword) { + ts >> pair.first; + } else if (next == passwordKeyword) { + ts >> pair.second; + } // ignore unsupported tokens + + } + tryAddEntryAndClear(machine, pair, isDefault); + + if (!_entries.isEmpty() || _default != qMakePair(QString(), QString())) { + return true; + } else { + return false; + } +} + +NetrcParser::LoginPair NetrcParser::find(const QString &machine) +{ + QHash<QString, LoginPair>::const_iterator it = _entries.find(machine); + if (it != _entries.end()) { + return *it; + } else { + return _default; + } +} + +} // namespace Mirall diff --git a/src/owncloudcmd/netrcparser.h b/src/owncloudcmd/netrcparser.h new file mode 100644 index 000000000..355333d43 --- /dev/null +++ b/src/owncloudcmd/netrcparser.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) by Daniel Molkentin <danimo@owncloud.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#ifndef NETRCPARSER_H +#define NETRCPARSER_H + +#include <QHash> +#include <QPair> + +namespace Mirall { + +class NetrcParser +{ +public: + typedef QPair<QString, QString> LoginPair; + + NetrcParser(const QString &fileName = QString::null); + bool parse(); + LoginPair find(const QString &machine); + +private: + void tryAddEntryAndClear(QString &machine, LoginPair &pair, bool &isDefault); + QHash<QString, LoginPair> _entries; + LoginPair _default; + QString _fileName; +}; + +} // namespace Mirall + +#endif // NETRCPARSER_H diff --git a/src/owncloudcmd/owncloudcmd.cpp b/src/owncloudcmd/owncloudcmd.cpp index c95755240..bfdcae637 100644 --- a/src/owncloudcmd/owncloudcmd.cpp +++ b/src/owncloudcmd/owncloudcmd.cpp @@ -31,15 +31,26 @@ #include "owncloudcmd.h" #include "simplesslerrorhandler.h" +#include "netrcparser.h" + +#ifdef Q_OS_WIN32 +#include <windows.h> +#else +#include <termios.h> +#endif + using namespace Mirall; struct CmdOptions { QString source_dir; QString target_url; QString config_directory; + QString user; + QString password; QString proxy; bool silent; bool trustSSL; + bool useNetrc; QString exclude; }; @@ -47,21 +58,59 @@ struct CmdOptions { // So we have to use a global variable CmdOptions *opts = 0; -int getauth(const char* prompt, char* buf, size_t len, int a, int b, void *userdata) +class EchoDisabler { - Q_UNUSED(a) Q_UNUSED(b) Q_UNUSED(userdata) +public: + EchoDisabler() + { +#ifdef Q_OS_WIN + HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); + GetConsoleMode(hStdin, &mode); + SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); +#else + tcgetattr(STDIN_FILENO, &tios); + termios tios_new = tios; + tios_new.c_lflag &= ~ECHO; + tcsetattr(STDIN_FILENO, TCSANOW, &tios_new); +#endif + } - std::cout << "** Authentication required: \n" << prompt << std::endl; - std::string s; - if(opts && opts->trustSSL) { - s = "yes"; - } else { - std::getline(std::cin, s); + ~EchoDisabler() + { +#ifdef Q_OS_WIN + SetConsoleMode(hStdin, mode); +#else + tcsetattr(STDIN_FILENO, TCSANOW, &tios); +#endif } - strncpy( buf, s.c_str(), len ); - return 0; +private: +#ifdef Q_OS_WIN + DWORD mode = 0; +#else + termios tios; +#endif +}; + +QString queryPassword(const QString &user) +{ + EchoDisabler disabler; + std::cout << "Password for user " << qPrintable(user) << ": "; + std::string s; + std::getline(std::cin, s); + return QString::fromStdString(s); } +class HttpCredentialsText : public HttpCredentials { +public: + HttpCredentialsText(const QString& user, const QString& password) : HttpCredentials(user, password) {} + QString queryPassword(bool *ok) { + if (ok) { + *ok = true; + } + return ::queryPassword(user()); + } +}; + void help() { std::cout << "owncloudcmd - command line ownCloud client tool." << std::endl; @@ -72,12 +121,15 @@ void help() std::cout << "uses the setting from a configured sync client." << std::endl; std::cout << std::endl; std::cout << "Options:" << std::endl; - std::cout << " --silent Don't be so verbose" << std::endl; + std::cout << " --silent, -s Don't be so verbose" << std::endl; std::cout << " --confdir = configdir: Read config from there." << 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 << " --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 << "" << std::endl; exit(1); @@ -118,10 +170,16 @@ void parseOptions( const QStringList& app_args, CmdOptions *options ) options->config_directory = it.next(); } else if( option == "--httpproxy" && !it.peekNext().startsWith("-")) { options->proxy = it.next(); - } else if( option == "--silent") { + } else if( option == "-s" || option == "--silent") { options->silent = true; } else if( option == "--trust") { options->trustSSL = true; + } else if( option == "-n") { + options->useNetrc = true; + } else if( (option == "-u" || option == "--user") && !it.peekNext().startsWith("-") ) { + options->user = it.next(); + } else if( (option == "-p" || option == "--password") && !it.peekNext().startsWith("-") ) { + options->user = it.next(); } else if( option == "--exclude" && !it.peekNext().startsWith("-") ) { options->exclude = it.next(); } else { @@ -140,27 +198,55 @@ int main(int argc, char **argv) { CmdOptions options; options.silent = false; options.trustSSL = false; + options.useNetrc = false; ClientProxy clientProxy; parseOptions( app.arguments(), &options ); - QUrl url(options.target_url.toUtf8()); + QUrl url = QUrl::fromUserInput(options.target_url); + + // Fetch username and password. If empty, try to retrieve + // from URL and strip URL + QString user; + QString password; + + if (options.useNetrc) { + NetrcParser parser; + if (parser.parse()) { + NetrcParser::LoginPair pair = parser.find(url.host()); + user = pair.first; + password = pair.second; + } + } else { + user = options.user; + if (user.isEmpty()) { + user = url.userName(); + } + password = options.password; + if (password.isEmpty()) { + password = url.password(); + } + + if (user.isEmpty()) { + std::cout << "Please enter user name: "; + std::string s; + std::getline(std::cin, s); + user = QString::fromStdString(s); + } + if (password.isEmpty()) { + password = queryPassword(user); + } + } + + // ### ensure URL is free of credentials if (url.userName().isEmpty()) { - std::cout << "** Please enter the username:" << std::endl; - std::string s; - std::getline(std::cin, s); - url.setUserName(QString::fromStdString(s)); + url.setUserName(user); } if (url.password().isEmpty()) { - std::cout << "** Please enter the password:" << std::endl; - std::string s; - std::getline(std::cin, s); - url.setPassword(QString::fromStdString(s)); + url.setPassword(password); } - QUrl originalUrl = url; - Account account; // Find the folder and the original owncloud url @@ -171,16 +257,19 @@ int main(int argc, char **argv) { SimpleSslErrorHandler *sslErrorHandler = new SimpleSslErrorHandler; + HttpCredentials *cred = new HttpCredentialsText(user, password); + account.setUrl(url); - account.setCredentials(new HttpCredentials(url.userName(), url.password())); + account.setCredentials(cred); account.setSslErrorHandler(sslErrorHandler); + AccountManager::instance()->setAccount(&account); restart_sync: CSYNC *_csync_ctx; if( csync_create( &_csync_ctx, options.source_dir.toUtf8(), - originalUrl.toEncoded().constData()) < 0 ) { + url.toEncoded().constData()) < 0 ) { qFatal("Unable to create csync-context!"); return EXIT_FAILURE; } @@ -192,7 +281,7 @@ restart_sync: csync_set_log_level(options.silent ? 1 : 11); opts = &options; - csync_set_auth_callback( _csync_ctx, getauth ); + cred->syncContextPreInit(_csync_ctx); if( csync_init( _csync_ctx ) < 0 ) { qFatal("Could not initialize csync!"); @@ -239,8 +328,9 @@ restart_sync: csync_add_exclude_list(_csync_ctx, options.exclude.toLocal8Bit()); } - OwncloudCmd owncloudCmd; + cred->syncContextPreStart(_csync_ctx); + OwncloudCmd owncloudCmd; SyncJournalDb db(options.source_dir); SyncEngine engine(_csync_ctx, options.source_dir, QUrl(options.target_url).path(), folder, &db); QObject::connect(&engine, SIGNAL(finished()), &app, SLOT(quit())); |