Procházet zdrojové kódy

Allow passing user/pass explicitly or via netrc

#2211
Daniel Molkentin před 11 roky
rodič
revize
fe023e2229

+ 1 - 1
src/CMakeLists.txt

@@ -427,7 +427,7 @@ if(KRAZY2_EXECUTABLE)
 endif()
 
 set(owncloudcmd_NAME ${APPLICATION_EXECUTABLE}cmd)
-set(OWNCLOUDCMD_SRC owncloudcmd/simplesslerrorhandler.cpp owncloudcmd/owncloudcmd.cpp)
+set(OWNCLOUDCMD_SRC owncloudcmd/simplesslerrorhandler.cpp owncloudcmd/owncloudcmd.cpp owncloudcmd/netrcparser.cpp)
 if(NOT BUILD_LIBRARIES_ONLY)
 
    add_executable(${owncloudcmd_NAME}  ${OWNCLOUDCMD_SRC})

+ 428 - 0
src/CMakeLists.txt.orig

@@ -0,0 +1,428 @@
+# TODO: OSX and LIB_ONLY seem to require this to go to binary dir only
+set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
+
+set(synclib_NAME ${APPLICATION_EXECUTABLE}sync)
+
+<<<<<<< HEAD
+add_subdirectory(libsync)
+if (NOT BUILD_LIBRARIES_ONLY)
+    add_subdirectory(gui)
+    add_subdirectory(cmd)
+endif(NOT BUILD_LIBRARIES_ONLY)
+=======
+if ( APPLE )
+    list(APPEND OS_SPECIFIC_LINK_LIBRARIES
+         /System/Library/Frameworks/CoreServices.framework
+         /System/Library/Frameworks/Foundation.framework
+         /System/Library/Frameworks/AppKit.framework
+    )
+endif()
+
+IF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+    list(APPEND OS_SPECIFIC_LINK_LIBRARIES
+	inotify
+    )
+ENDIF(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
+
+if(SPARKLE_FOUND AND NOT BUILD_LIBRARIES_ONLY)
+    list (APPEND OS_SPECIFIC_LINK_LIBRARIES ${SPARKLE_LIBRARY})
+endif()
+
+set(3rdparty_SRC
+3rdparty/qtsingleapplication/qtsingleapplication.cpp
+3rdparty/qtsingleapplication/qtlocalpeer.cpp
+3rdparty/qtsingleapplication/qtsinglecoreapplication.cpp
+3rdparty/qtlockedfile/qtlockedfile.cpp
+3rdparty/fancylineedit/fancylineedit.cpp
+3rdparty/QProgressIndicator/QProgressIndicator.cpp
+)
+
+if (APPLE)
+        list(APPEND 3rdparty_SRC
+        3rdparty/qtmacgoodies/src/macpreferenceswindow.mm
+        3rdparty/qtmacgoodies/src/macstandardicon.mm
+        3rdparty/qtmacgoodies/src/macwindow.mm
+        )
+endif()
+
+if(NOT WIN32)
+	list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_unix.cpp)
+else()
+	list(APPEND 3rdparty_SRC 3rdparty/qtlockedfile/qtlockedfile_win.cpp )
+endif()
+
+set(3rdparty_INC
+    ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtlockedfile
+    ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtsingleapplication
+    ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/QProgressIndicator
+    ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/fancylineedit
+    ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qjson
+    ${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/qtmacgoodies/src
+    )
+
+set(libsync_SRCS
+    mirall/authenticationdialog.cpp
+    mirall/syncresult.cpp
+    mirall/mirallconfigfile.cpp
+    mirall/syncengine.cpp
+    mirall/owncloudpropagator.cpp
+    mirall/propagatorjobs.cpp
+    mirall/propagator_qnam.cpp
+    mirall/propagator_legacy.cpp
+    mirall/syncjournalfilerecord.cpp
+    mirall/syncjournaldb.cpp
+    mirall/theme.cpp
+    mirall/owncloudtheme.cpp
+    mirall/logger.cpp
+    mirall/utility.cpp
+    mirall/filesystem.cpp
+    mirall/connectionvalidator.cpp
+    mirall/progressdispatcher.cpp
+    mirall/mirallaccessmanager.cpp
+    mirall/networkjobs.cpp
+    mirall/account.cpp
+    mirall/quotainfo.cpp
+    mirall/clientproxy.cpp
+    mirall/cookiejar.cpp
+    mirall/syncfilestatus.cpp
+    mirall/discoveryphase.cpp
+    creds/dummycredentials.cpp
+    creds/abstractcredentials.cpp
+    creds/credentialsfactory.cpp
+    creds/http/httpconfigfile.cpp
+    creds/credentialscommon.cpp
+    3rdparty/qjson/json.cpp
+)
+if(TOKEN_AUTH_ONLY)
+	set (libsync_SRCS
+		${libsync_SRCS}
+		creds/tokencredentials.cpp
+	)
+else()
+    set (libsync_SRCS
+        ${libsync_SRCS}
+        creds/httpcredentials.cpp
+        creds/shibbolethcredentials.cpp
+        creds/shibboleth/shibbolethwebview.cpp
+        creds/shibboleth/shibbolethrefresher.cpp
+        creds/shibboleth/shibbolethuserjob.cpp
+    )
+endif()
+
+# These headers are installed for libowncloudsync to be used by 3rd party apps
+set(owncloudsync_HEADERS
+    mirall/account.h
+    mirall/syncengine.h
+    mirall/mirallconfigfile.h
+    mirall/networkjobs.h
+    mirall/progressdispatcher.h
+    mirall/syncfileitem.h
+    mirall/syncjournaldb.h
+    mirall/syncresult.h
+)
+
+set(creds_HEADERS
+    creds/abstractcredentials.h
+    creds/httpcredentials.h
+)
+
+IF (NOT APPLE)
+    INSTALL(
+        FILES ${owncloudsync_HEADERS}
+	DESTINATION ${INCLUDE_INSTALL_DIR}/${synclib_NAME}/mirall
+    )
+    INSTALL(
+        FILES ${creds_HEADERS}
+	DESTINATION ${INCLUDE_INSTALL_DIR}/${synclib_NAME}/creds
+    )
+ENDIF(NOT APPLE)
+
+list(APPEND libsync_LINK_TARGETS
+    ${QT_LIBRARIES}
+    ocsync
+    httpbf
+    ${OS_SPECIFIC_LINK_LIBRARIES}
+)
+
+if(QTKEYCHAIN_FOUND OR QT5KEYCHAIN_FOUND)
+    list(APPEND libsync_LINK_TARGETS ${QTKEYCHAIN_LIBRARY})
+    include_directories(${QTKEYCHAIN_INCLUDE_DIR})
+endif()
+
+if(NEON_FOUND)
+    list(APPEND libsync_LINK_TARGETS ${NEON_LIBRARIES})
+    include_directories(${NEON_INCLUDE_DIRS})
+
+    if(NEON_WITH_LFS)
+        add_definitions(-DNE_LFS)
+    endif()
+
+endif()
+
+add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
+GENERATE_EXPORT_HEADER( ${synclib_NAME}
+	BASE_NAME ${synclib_NAME}
+	EXPORT_MACRO_NAME OWNCLOUDSYNC_EXPORT
+        EXPORT_FILE_NAME owncloudlib.h
+	STATIC_DEFINE OWNCLOUD_BUILT_AS_STATIC
+)
+
+
+if(TOKEN_AUTH_ONLY)
+    qt5_use_modules(${synclib_NAME} Network Xml Sql)
+else()
+    qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets Sql)
+endif()
+
+set_target_properties( ${synclib_NAME}  PROPERTIES
+	VERSION ${MIRALL_VERSION}
+	SOVERSION ${MIRALL_SOVERSION}
+)
+set_target_properties( ${synclib_NAME} PROPERTIES
+	INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE}" )
+
+target_link_libraries(${synclib_NAME} ${libsync_LINK_TARGETS} )
+
+if(BUILD_LIBRARIES_ONLY)
+    #add_library(${synclib_NAME}_static STATIC ${libsync_SRCS} ${syncMoc})
+    #qt5_use_modules(${synclib_NAME}_static Widgets Network Xml Sql)
+
+    #set_target_properties( ${synclib_NAME}_static  PROPERTIES
+    #    VERSION ${MIRALL_VERSION}
+    #    SOVERSION ${MIRALL_SOVERSION}
+    #)
+
+    #target_link_libraries(${synclib_NAME}_static ${libsync_LINK_TARGETS} )
+endif()
+
+
+
+if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
+    install(TARGETS ${synclib_NAME}
+        RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
+        LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
+        ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
+    )
+    if(NOT WIN32)
+        configure_file(${CMAKE_SOURCE_DIR}/mirall.desktop.in
+		${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop)
+        install(FILES  ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_EXECUTABLE}.desktop DESTINATION share/applications )
+    endif()
+else()
+    install(TARGETS ${synclib_NAME} DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
+    if (SPARKLE_FOUND)
+        install(DIRECTORY "${SPARKLE_LIBRARY}"
+	        DESTINATION "${OWNCLOUD_OSX_BUNDLE}/Contents/Frameworks")
+    endif (SPARKLE_FOUND)
+
+endif()
+
+set(mirall_UI
+mirall/folderwizardsourcepage.ui
+mirall/folderwizardtargetpage.ui
+mirall/sslerrordialog.ui
+mirall/settingsdialog.ui
+mirall/generalsettings.ui
+mirall/networksettings.ui
+mirall/accountsettings.ui
+mirall/ignorelisteditor.ui
+mirall/protocolwidget.ui
+wizard/owncloudsetupnocredspage.ui
+wizard/owncloudhttpcredspage.ui
+wizard/owncloudwizardresultpage.ui
+wizard/owncloudadvancedsetuppage.ui
+)
+
+qt_wrap_ui(mirall_UI_SRCS ${mirall_UI})
+
+set(mirall_SRCS
+    mirall/application.cpp
+    mirall/systray.cpp
+    mirall/folderman.cpp
+    mirall/folder.cpp
+    mirall/folderwatcher.cpp
+    mirall/folderwizard.cpp
+    mirall/folderstatusmodel.cpp
+    mirall/protocolwidget.cpp
+    mirall/openfilemanager.cpp
+    wizard/owncloudwizard.cpp
+    wizard/owncloudsetuppage.cpp
+    wizard/owncloudhttpcredspage.cpp
+    wizard/abstractcredswizardpage.cpp
+    wizard/owncloudwizardresultpage.cpp
+    wizard/owncloudwizardcommon.cpp
+    wizard/owncloudshibbolethcredspage.cpp
+    wizard/owncloudadvancedsetuppage.cpp
+    mirall/owncloudsetupwizard.cpp
+    mirall/sslerrordialog.cpp
+    mirall/logbrowser.cpp
+    mirall/settingsdialog.cpp
+    mirall/generalsettings.cpp
+    mirall/networksettings.cpp
+    mirall/accountsettings.cpp
+    mirall/ignorelisteditor.cpp
+    mirall/owncloudgui.cpp
+    mirall/socketapi.cpp
+    mirall/sslbutton.cpp
+    mirall/syncrunfilelog.cpp
+    mirall/selectivesyncdialog.cpp
+)
+
+
+set(updater_SRCS
+    updater/updateinfo.cpp
+    updater/updater.cpp
+    updater/ocupdater.cpp
+)
+
+IF( APPLE )
+    list(APPEND mirall_SRCS mirall/cocoainitializer_mac.mm)
+
+    list(APPEND mirall_SRCS mirall/settingsdialogmac.cpp)
+
+    if(SPARKLE_FOUND)
+       # Define this, we need to check in updater.cpp
+       add_definitions( -DHAVE_SPARKLE )
+       list(APPEND updater_SRCS updater/sparkleupdater_mac.mm)
+   endif()
+ENDIF()
+
+IF( NOT WIN32 AND NOT APPLE )
+set(mirall_SRCS ${mirall_SRCS} mirall/folderwatcher_linux.cpp)
+ENDIF()
+IF( WIN32 )
+set(mirall_SRCS ${mirall_SRCS} mirall/folderwatcher_win.cpp)
+ENDIF()
+IF( APPLE )
+list(APPEND mirall_SRCS mirall/folderwatcher_mac.cpp)
+ENDIF()
+
+# csync is required.
+include_directories(../csync/src ../csync/src/httpbf/src ${CMAKE_CURRENT_BINARY_DIR}/../csync ${CMAKE_CURRENT_BINARY_DIR}/../csync/src )
+include_directories(${3rdparty_INC})
+
+qt_add_translation(mirall_I18N ${TRANSLATIONS})
+
+set( final_src
+    ${MIRALL_RC_SRC}
+    ${mirall_SRCS}
+    ${mirall_UI_SRCS}
+    ${mirallMoc}
+    ${mirall_I18N}
+    ${3rdparty_SRC}
+    ${3rdparty_MOC}
+)
+
+# add executable icon on windows and osx
+include( AddAppIconMacro )
+set(ownCloud_old ${ownCloud})
+
+# set an icon_app_name. For historical reasons we can not use the 
+# application_shortname for ownCloud but must rather set it manually.
+if ( EXISTS ${OEM_THEME_DIR}/OEM.cmake )
+    set(ICON_APP_NAME ${APPLICATION_SHORTNAME})
+else()
+    set(ICON_APP_NAME "owncloud")
+endif()
+
+kde4_add_app_icon( ownCloud "${theme_dir}/colored/${ICON_APP_NAME}-icon*.png")
+list(APPEND final_src ${ownCloud})
+set(ownCloud ${ownCloud_old})
+
+if (WITH_DBUS)
+    set(ADDITIONAL_APP_MODULES DBus)
+endif(WITH_DBUS)
+
+if(NOT BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY)
+    set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
+
+    if(NOT WIN32)
+        file( GLOB _icons "${theme_dir}/colored/${ICON_APP_NAME}-icon-*.png" )
+        foreach( _file ${_icons} )
+            string( REPLACE "${theme_dir}/colored/${ICON_APP_NAME}-icon-" "" _res ${_file} )
+            string( REPLACE ".png" "" _res ${_res} )
+        install( FILES ${_file} RENAME ${ICON_APP_NAME}.png DESTINATION ${DATADIR}/icons/hicolor/${_res}x${_res}/apps )
+        endforeach( _file )
+    endif(NOT WIN32)
+
+    install(FILES ${mirall_I18N} DESTINATION share/${APPLICATION_EXECUTABLE}/i18n)
+
+    # we may not add MACOSX_BUNDLE here, if not building one
+
+    # add_executable( ${APPLICATION_EXECUTABLE} main.cpp ${final_src})
+    add_executable( ${APPLICATION_EXECUTABLE} WIN32 main.cpp ${final_src})
+    qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql ${ADDITIONAL_APP_MODULES})
+elseif(NOT BUILD_LIBRARIES_ONLY)
+    set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
+    if (Qt5Core_FOUND)
+        include(DeployQt5)
+    else(Qt5Core_FOUND)
+        include(DeployQt4)
+    endif(Qt5Core_FOUND)
+
+    set(CMAKE_INSTALL_PREFIX ".") # Examples use /Applications. hurmpf.
+    set(MACOSX_BUNDLE_ICON_FILE "ownCloud.icns")
+
+    # we must add MACOSX_BUNDLE only if building a bundle
+    add_executable( ${APPLICATION_EXECUTABLE} WIN32 MACOSX_BUNDLE main.cpp ${final_src})
+    qt5_use_modules(${APPLICATION_EXECUTABLE} Widgets Network Xml WebKitWidgets Sql  ${ADDITIONAL_APP_MODULES})
+
+    set (QM_DIR ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources/Translations)
+    install(FILES ${mirall_I18N} DESTINATION ${QM_DIR})
+    file(GLOB qt_I18N ${QT_TRANSLATIONS_DIR}/qt_??.qm ${QT_TRANSLATIONS_DIR}/qt_??_??.qm)
+    install(FILES ${qt_I18N} DESTINATION ${QM_DIR})
+    file(GLOB qtkeychain_I18N ${QT_TRANSLATIONS_DIR}/qtkeychain*.qm)
+    install(FILES ${qtkeychain_I18N} DESTINATION ${QM_DIR})
+endif()
+
+if(NOT BUILD_LIBRARIES_ONLY)
+  add_library(updater STATIC ${updater_SRCS} ${updaterMoc})
+  target_link_libraries(updater ${synclib_NAME})
+  qt5_use_modules(updater Widgets Network Xml)
+
+  set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
+          RUNTIME_OUTPUT_DIRECTORY  ${BIN_OUTPUT_DIRECTORY}
+  )
+  set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
+	  INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${LIB_INSTALL_DIR}/${APPLICATION_EXECUTABLE}" )
+
+  target_link_libraries( ${APPLICATION_EXECUTABLE} ${QT_LIBRARIES} )
+  target_link_libraries( ${APPLICATION_EXECUTABLE} ${synclib_NAME} )
+  target_link_libraries( ${APPLICATION_EXECUTABLE} updater )
+  target_link_libraries( ${APPLICATION_EXECUTABLE} ${OS_SPECIFIC_LINK_LIBRARIES} )
+
+  install(TARGETS ${APPLICATION_EXECUTABLE}
+          RUNTIME DESTINATION bin
+          LIBRARY DESTINATION lib
+          ARCHIVE DESTINATION lib
+          BUNDLE  DESTINATION "."
+ )
+endif()
+
+
+#FIXME: find a nice solution to make the second if(BUILD_OWNCLOUD_OSX_BUNDLE) unnecessary
+# currently it needs to be done because the code right above needs to be executed no matter
+# if building a bundle or not and the install_qt4_executable needs to be called afterwards
+if(BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY)
+    if(Qt5Core_FOUND)
+        install_qt5_executable(${OWNCLOUD_OSX_BUNDLE} "qtaccessiblewidgets;qsqlite;qcocoa")
+    else(Qt5Core_FOUND)
+        install_qt4_executable(${OWNCLOUD_OSX_BUNDLE} "qtaccessiblewidgets;qsqlite")
+    endif(Qt5Core_FOUND)
+endif()
+>>>>>>> origin/master
+
+find_program(KRAZY2_EXECUTABLE krazy2)
+if(KRAZY2_EXECUTABLE)
+    # s/y k/y ALL k/ for building this target always
+    add_custom_target( krazy krazy2 --check-sets c++,qt4,foss
+                       ${PROJECT_SOURCE_DIR}/src/libsync/*.ui
+                       ${PROJECT_SOURCE_DIR}/src/libsync/*.h
+                       ${PROJECT_SOURCE_DIR}/src/libsync/*.cpp
+                       ${PROJECT_SOURCE_DIR}/src/gui/*.ui
+                       ${PROJECT_SOURCE_DIR}/src/gui/*.h
+                       ${PROJECT_SOURCE_DIR}/src/gui/*.cpp
+                       ${PROJECT_SOURCE_DIR}/src/cmd/*.h
+                       ${PROJECT_SOURCE_DIR}/src/cmd/*.cpp
+)
+endif()

+ 1 - 1
src/creds/credentialsfactory.cpp

@@ -36,7 +36,7 @@ AbstractCredentials* create(const QString& type)
 	
     // empty string might happen for old version of configuration
     if (type == "http" || type == "") {
-        return new HttpCredentials;
+        return new HttpCredentialsGui;
     } else if (type == "dummy") {
         return new DummyCredentials;
     } else if (type == "shibboleth") {

+ 15 - 18
src/creds/httpcredentials.cpp

@@ -37,9 +37,6 @@ using namespace QKeychain;
 namespace Mirall
 {
 
-namespace
-{
-
 int getauth(const char *prompt,
             char *buf,
             size_t len,
@@ -74,10 +71,10 @@ int getauth(const char *prompt,
     return re;
 }
 
+namespace
+{
 const char userC[] = "user";
 const char authenticationFailedC[] = "owncloud-authentication-failed";
-
-
 } // ns
 
 class HttpCredentialsAccessManager : public MirallAccessManager {
@@ -299,19 +296,6 @@ void HttpCredentials::slotReadJobDone(QKeychain::Job *job)
     }
 }
 
-QString HttpCredentials::queryPassword(bool *ok)
-{
-    if (ok) {
-        QString str = QInputDialog::getText(0, tr("Enter Password"),
-                                     tr("Please enter %1 password for user '%2':")
-                                     .arg(Theme::instance()->appNameGUI(), _user),
-                                     QLineEdit::Password, QString(), ok);
-        return str;
-    } else {
-        return QString();
-    }
-}
-
 void HttpCredentials::invalidateToken(Account *account)
 {
     _password = QString();
@@ -380,4 +364,17 @@ void HttpCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* a
     reply->close();
 }
 
+QString HttpCredentialsGui::queryPassword(bool *ok)
+{
+    if (ok) {
+        QString str = QInputDialog::getText(0, tr("Enter Password"),
+                                     tr("Please enter %1 password for user '%2':")
+                                     .arg(Theme::instance()->appNameGUI(), _user),
+                                     QLineEdit::Password, QString(), ok);
+        return str;
+    } else {
+        return QString();
+    }
+}
+
 } // ns Mirall

+ 11 - 2
src/creds/httpcredentials.h

@@ -49,7 +49,7 @@ public:
     void persist(Account *account) Q_DECL_OVERRIDE;
     QString user() const Q_DECL_OVERRIDE;
     QString password() const;
-    QString queryPassword(bool *ok);
+    virtual QString queryPassword(bool *ok) = 0;
     void invalidateToken(Account *account) Q_DECL_OVERRIDE;
     QString fetchUser(Account *account);
 
@@ -58,14 +58,23 @@ private Q_SLOTS:
     void slotReadJobDone(QKeychain::Job*);
     void slotWriteJobDone(QKeychain::Job*);
 
-private:
+protected:
     QString _user;
     QString _password;
+
+private:
     bool _ready;
     bool _fetchJobInProgress; //True if the keychain job is in progress or the input dialog visible
     bool _readPwdFromDeprecatedPlace;
 };
 
+class OWNCLOUDSYNC_EXPORT HttpCredentialsGui : public HttpCredentials {
+public:
+    HttpCredentialsGui() : HttpCredentials() {}
+    HttpCredentialsGui(const QString& user, const QString& password) : HttpCredentials(user, password) {}
+    QString queryPassword(bool *ok) Q_DECL_OVERRIDE;
+};
+
 } // ns Mirall
 
 #endif

+ 97 - 0
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

+ 41 - 0
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

+ 117 - 27
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()));

+ 1 - 1
src/wizard/owncloudhttpcredspage.cpp

@@ -148,7 +148,7 @@ void OwncloudHttpCredsPage::setErrorString(const QString& err)
 
 AbstractCredentials* OwncloudHttpCredsPage::getCredentials() const
 {
-    return new HttpCredentials(_ui.leUsername->text(), _ui.lePassword->text());
+    return new HttpCredentialsGui(_ui.leUsername->text(), _ui.lePassword->text());
 }
 
 void OwncloudHttpCredsPage::setConfigExists(bool config)

+ 1 - 0
test/CMakeLists.txt

@@ -26,5 +26,6 @@ if( UNIX AND NOT APPLE )
 endif(UNIX AND NOT APPLE)
 
 owncloud_add_test(CSyncSqlite "")
+owncloud_add_test(NetrcParser ../src/owncloudcmd/netrcparser.cpp)
 
 

+ 76 - 0
test/testnetrcparser.h

@@ -0,0 +1,76 @@
+/*
+ *    This software is in the public domain, furnished "as is", without technical
+ *       support, and with no warranty, express or implied, as to its usefulness for
+ *          any purpose.
+ *          */
+
+#ifndef MIRALL_INOTIFYWATCHER_H
+#define MIRALL_INOTIFYWATCHER_H
+
+#include <QtTest>
+
+#include "owncloudcmd/netrcparser.h"
+
+using namespace Mirall;
+
+namespace {
+
+const char testfileC[] = "netrctest";
+const char testfileWithDefaultC[] = "netrctestDefault";
+const char testfileEmptyC[] = "netrctestEmpty";
+
+}
+
+class TestNetrcParser : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void initTestCase() {
+       QFile netrc(testfileC);
+       QVERIFY(netrc.open(QIODevice::WriteOnly));
+       netrc.write("machine foo login bar password baz\n");
+       netrc.write("machine broken login bar2 dontbelonghere password baz2 extratokens dontcare andanother\n");
+       netrc.write("machine\nfunnysplit\tlogin bar3 password baz3\n");
+       QFile netrcWithDefault(testfileWithDefaultC);
+       QVERIFY(netrcWithDefault.open(QIODevice::WriteOnly));
+       netrcWithDefault.write("machine foo login bar password baz\n");
+       netrcWithDefault.write("default login user password pass\n");
+       QFile netrcEmpty(testfileEmptyC);
+       QVERIFY(netrcEmpty.open(QIODevice::WriteOnly));
+    }
+
+    void cleanupTestCase() {
+       QVERIFY(QFile::remove(testfileC));
+       QVERIFY(QFile::remove(testfileWithDefaultC));
+       QVERIFY(QFile::remove(testfileEmptyC));
+    }
+
+    void testValidNetrc() {
+       NetrcParser parser(testfileC);
+       QVERIFY(parser.parse());
+       QCOMPARE(parser.find("foo"), qMakePair(QString("bar"), QString("baz")));
+       QCOMPARE(parser.find("broken"), qMakePair(QString("bar2"), QString("baz2")));
+       QCOMPARE(parser.find("funnysplit"), qMakePair(QString("bar3"), QString("baz3")));
+    }
+
+    void testEmptyNetrc() {
+       NetrcParser parser(testfileEmptyC);
+       QVERIFY(!parser.parse());
+       QCOMPARE(parser.find("foo"), qMakePair(QString(), QString()));
+    }
+
+    void testValidNetrcWithDefault() {
+       NetrcParser parser(testfileWithDefaultC);
+       QVERIFY(parser.parse());
+       QCOMPARE(parser.find("foo"), qMakePair(QString("bar"), QString("baz")));
+       QCOMPARE(parser.find("dontknow"), qMakePair(QString("user"), QString("pass")));
+    }
+
+    void testInvalidNetrc() {
+       NetrcParser parser("/invalid");
+       QVERIFY(!parser.parse());
+    }
+};
+
+#endif