[Pkg-owncloud-commits] [owncloud-client] 121/333: Improve libowncloudsync

Sandro Knauß hefee-guest at moszumanska.debian.org
Thu Apr 17 23:16:42 UTC 2014


This is an automated email from the git hooks/post-receive script.

hefee-guest pushed a commit to branch master
in repository owncloud-client.

commit 4e7e25c5698c88637764d63a47c886bbf59a3a11
Author: Markus Goetz <markus at woboq.com>
Date:   Tue Mar 11 16:55:53 2014 +0100

    Improve libowncloudsync
    
    * Introduce TokenCredentials
    * Introduce static compiling
    * Have compile flags for smaller compile
---
 csync/src/CMakeLists.txt         |   1 +
 src/CMakeLists.txt               | 128 ++++++++++++++-------
 src/creds/credentialsfactory.cpp |   9 ++
 src/creds/tokencredentials.cpp   | 240 +++++++++++++++++++++++++++++++++++++++
 src/creds/tokencredentials.h     |  67 +++++++++++
 5 files changed, 405 insertions(+), 40 deletions(-)

diff --git a/csync/src/CMakeLists.txt b/csync/src/CMakeLists.txt
index 6d47425..899d52d 100644
--- a/csync/src/CMakeLists.txt
+++ b/csync/src/CMakeLists.txt
@@ -100,6 +100,7 @@ include_directories(
 )
 
 add_library(${CSYNC_LIBRARY} SHARED ${csync_SRCS})
+add_library(${CSYNC_LIBRARY}_static STATIC ${csync_SRCS})
 
 target_link_libraries(${CSYNC_LINK_LIBRARIES})
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6213e2c..c2467ea 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,5 +1,16 @@
 include_directories(${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
 
+# this option removes Http authentication, keychain, shibboleth etc and is intended for
+# external authentication mechanisms
+option(TOKEN_AUTH_ONLY "TOKEN_AUTH_ONLY" OFF)
+if(TOKEN_AUTH_ONLY)
+   message("Compiling with token authentication")
+   add_definitions(-DTOKEN_AUTH_ONLY=1)
+endif()
+
+# this option creates only libocsync and libowncloudsync
+option(BUILD_LIBRARIES_ONLY "BUILD_LIBRARIES_ONLY" OFF)
+
 qt_add_resources(MIRALL_RC_SRC ../mirall.qrc)
 if ( IS_DIRECTORY ${OEM_THEME_DIR} )
     qt_add_resources(MIRALL_RC_SRC ${OEM_THEME_DIR}/theme.qrc)
@@ -85,19 +96,30 @@ set(libsync_SRCS
     mirall/clientproxy.cpp
     creds/dummycredentials.cpp
     creds/abstractcredentials.cpp
-    creds/httpcredentials.cpp
     creds/credentialsfactory.cpp
     creds/http/httpconfigfile.cpp
-    creds/shibbolethcredentials.cpp
-    creds/shibboleth/shibbolethaccessmanager.cpp
-    creds/shibboleth/shibbolethcookiejar.cpp
-    creds/shibboleth/shibbolethwebview.cpp
-    creds/shibboleth/shibbolethrefresher.cpp
-    creds/shibboleth/shibbolethconfigfile.cpp
-    creds/shibboleth/authenticationdialog.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/shibbolethaccessmanager.cpp
+	    	creds/shibboleth/shibbolethcookiejar.cpp
+	    	creds/shibboleth/shibbolethwebview.cpp
+	    	creds/shibboleth/shibbolethrefresher.cpp
+	    	creds/shibboleth/shibbolethconfigfile.cpp
+	    	creds/shibboleth/authenticationdialog.cpp		
+	)	
+endif()
+
 
 set(libsync_HEADERS
     mirall/folderman.h
@@ -119,19 +141,30 @@ set(libsync_HEADERS
     mirall/clientproxy.h
     creds/abstractcredentials.h
     creds/dummycredentials.h
-    creds/httpcredentials.h
     creds/credentialsfactory.h
     creds/http/httpconfigfile.h
-    creds/shibbolethcredentials.h
-    creds/shibboleth/shibbolethaccessmanager.h
-    creds/shibboleth/shibbolethcookiejar.h
-    creds/shibboleth/shibbolethwebview.h
-    creds/shibboleth/shibbolethrefresher.h
-    creds/shibboleth/shibbolethconfigfile.h
-    creds/shibboleth/authenticationdialog.h
     creds/credentialscommon.h
     3rdparty/qjson/json.h
 )
+if(TOKEN_AUTH_ONLY)
+	set (libsync_HEADERS
+	    ${libsync_HEADERS}
+	    creds/tokencredentials.h
+	)
+else()
+	set (libsync_HEADERS
+	    ${libsync_HEADERS}
+	    creds/httpcredentials.h	
+		 creds/shibbolethcredentials.h
+       creds/shibboleth/shibbolethaccessmanager.h
+       creds/shibboleth/shibbolethcookiejar.h
+       creds/shibboleth/shibbolethwebview.h
+       creds/shibboleth/shibbolethrefresher.h
+       creds/shibboleth/shibbolethconfigfile.h
+       creds/shibboleth/authenticationdialog.h
+    )
+endif()
+
 
 IF( NOT WIN32 AND NOT APPLE )
     set(libsync_SRCS ${libsync_SRCS} mirall/folderwatcher_linux.cpp)
@@ -201,15 +234,22 @@ if(NEON_FOUND)
 endif()
 
 add_library(${synclib_NAME} SHARED ${libsync_SRCS} ${syncMoc})
+add_library(${synclib_NAME}_static STATIC ${libsync_SRCS} ${syncMoc})
 
 qt5_use_modules(${synclib_NAME} Widgets Network Xml WebKitWidgets Sql)
+qt5_use_modules(${synclib_NAME}_static Widgets Network Xml WebKitWidgets Sql)
 
 set_target_properties( ${synclib_NAME}  PROPERTIES
 	VERSION ${MIRALL_VERSION}
 	SOVERSION ${MIRALL_SOVERSION}
 )
+set_target_properties( ${synclib_NAME}_static  PROPERTIES
+	VERSION ${MIRALL_VERSION}
+	SOVERSION ${MIRALL_SOVERSION}
+)
 
 target_link_libraries(${synclib_NAME} ${libsync_LINK_TARGETS} )
+target_link_libraries(${synclib_NAME}_static ${libsync_LINK_TARGETS} )
 
 if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
     install(TARGETS ${synclib_NAME}
@@ -276,6 +316,8 @@ set(mirall_SRCS
     mirall/sslbutton.cpp
 )
 
+
+
 set(mirall_HEADERS
     mirall/application.h
     mirall/systray.h
@@ -370,7 +412,7 @@ endif(WITH_DBUS)
 if(NOT BUILD_OWNCLOUD_OSX_BUNDLE)
     set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
 
-if(NOT WIN32)
+if(NOT WIN32 AND NOT BUILD_LIBRARIES_ONLY)
 	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} )
@@ -386,7 +428,7 @@ endif(NOT WIN32)
     # 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})
-else()
+elif(NOT BUILD_LIBRARIES_ONLY)
     set(BIN_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}")
     include(DeployQt4)
 
@@ -409,25 +451,28 @@ 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}
-)
-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 "."
-)
+if(NOT BUILD_LIBRARIES_ONLY)
+  set_target_properties( ${APPLICATION_EXECUTABLE} PROPERTIES
+          RUNTIME_OUTPUT_DIRECTORY  ${BIN_OUTPUT_DIRECTORY}
+  )
+  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)
+if(BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY)
     install_qt4_executable(${OWNCLOUD_OSX_BUNDLE} "qtaccessiblewidgets;qsqlite")
 endif()
 
@@ -443,13 +488,16 @@ endif()
 
 set(owncloudcmd_NAME ${APPLICATION_EXECUTABLE}cmd)
 set(OWNCLOUDCMD_SRC owncloudcmd/owncloudcmd.cpp)
-add_executable(${owncloudcmd_NAME}  ${OWNCLOUDCMD_SRC})
-qt5_use_modules(${owncloudcmd_NAME} Network Sql)
-set_target_properties(${owncloudcmd_NAME} PROPERTIES
-	        RUNTIME_OUTPUT_DIRECTORY  ${BIN_OUTPUT_DIRECTORY} )
-target_link_libraries(${owncloudcmd_NAME} ${synclib_NAME})
+if(NOT BUILD_LIBRARIES_ONLY)
+   add_executable(${owncloudcmd_NAME}  ${OWNCLOUDCMD_SRC})
+	qt5_use_modules(${owncloudcmd_NAME} Network Sql)
+	set_target_properties(${owncloudcmd_NAME} PROPERTIES
+		        RUNTIME_OUTPUT_DIRECTORY  ${BIN_OUTPUT_DIRECTORY} )
+	target_link_libraries(${owncloudcmd_NAME} ${synclib_NAME})
+endif()
+
 include_directories(${CMAKE_CURRENT_SOURCE_DIR}/mirall)
-if(BUILD_OWNCLOUD_OSX_BUNDLE)
+if(BUILD_OWNCLOUD_OSX_BUNDLE AND NOT BUILD_LIBRARIES_ONLY)
     install(TARGETS ${owncloudcmd_NAME} DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/MacOS)
     if (SPARKLE_FOUND)
         install(FILES ${CMAKE_SOURCE_DIR}/admin/osx/deny_autoupdate_com.owncloud.desktopclient.plist
@@ -457,7 +505,7 @@ if(BUILD_OWNCLOUD_OSX_BUNDLE)
         install(FILES ${CMAKE_SOURCE_DIR}/admin/osx/sparkle/dsa_pub.pem
                 DESTINATION ${OWNCLOUD_OSX_BUNDLE}/Contents/Resources)
     endif()
-else()
+elsif(NOT BUILD_LIBRARIES_ONLY)
     install(TARGETS ${owncloudcmd_NAME}
 	    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
 	    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
diff --git a/src/creds/credentialsfactory.cpp b/src/creds/credentialsfactory.cpp
index 72cc252..41eccae 100644
--- a/src/creds/credentialsfactory.cpp
+++ b/src/creds/credentialsfactory.cpp
@@ -13,9 +13,13 @@
 
 #include <QString>
 
+#ifdef TOKEN_AUTH_ONLY
+#include "creds/tokencredentials.h"
+#else
 #include "creds/httpcredentials.h"
 #include "creds/dummycredentials.h"
 #include "creds/shibbolethcredentials.h"
+#endif
 
 namespace Mirall
 {
@@ -25,6 +29,10 @@ namespace CredentialsFactory
 
 AbstractCredentials* create(const QString& type)
 {
+#ifdef TOKEN_AUTH_ONLY
+	return new TokenCredentials;
+#else
+	
     // empty string might happen for old version of configuration
     if (type == "http" || type == "") {
         return new HttpCredentials;
@@ -36,6 +44,7 @@ AbstractCredentials* create(const QString& type)
         qWarning("Unknown credentials type: %s", qPrintable(type));
         return new DummyCredentials;
     }
+#endif
 }
 
 } // ns CredentialsFactory
diff --git a/src/creds/tokencredentials.cpp b/src/creds/tokencredentials.cpp
new file mode 100644
index 0000000..52a891e
--- /dev/null
+++ b/src/creds/tokencredentials.cpp
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) by Klaas Freitag <freitag at kde.org>
+ * Copyright (c) by Markus Goetz <guruz at 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 <QMutex>
+#include <QDebug>
+#include <QNetworkReply>
+#include <QSettings>
+#include <QInputDialog>
+
+
+#include "mirall/account.h"
+#include "mirall/mirallaccessmanager.h"
+#include "mirall/utility.h"
+#include "mirall/theme.h"
+#include "creds/credentialscommon.h"
+#include "creds/tokencredentials.h"
+
+
+namespace Mirall
+{
+
+namespace
+{
+
+int getauth(const char *prompt,
+            char *buf,
+            size_t len,
+            int echo,
+            int verify,
+            void *userdata)
+{
+    int re = 0;
+    QMutex mutex;
+    // ### safe?
+    TokenCredentials* http_credentials = qobject_cast<TokenCredentials*>(AccountManager::instance()->account()->credentials());
+
+    if (!http_credentials) {
+      qDebug() << "Not a HTTP creds instance!";
+      return -1;
+    }
+
+    QString qPrompt = QString::fromLatin1( prompt ).trimmed();
+    QString user = http_credentials->user();
+    QString pwd  = http_credentials->password();
+
+    if( qPrompt == QLatin1String("Enter your username:") ) {
+        // qDebug() << "OOO Username requested!";
+        QMutexLocker locker( &mutex );
+        qstrncpy( buf, user.toUtf8().constData(), len );
+    } else if( qPrompt == QLatin1String("Enter your password:") ) {
+        QMutexLocker locker( &mutex );
+        // qDebug() << "OOO Password requested!";
+        qstrncpy( buf, pwd.toUtf8().constData(), len );
+    } else {
+        re = handleNeonSSLProblems(prompt, buf, len, echo, verify, userdata);
+    }
+    return re;
+}
+
+const char userC[] = "user";
+
+} // ns
+
+class TokenCredentialsAccessManager : public MirallAccessManager {
+public:
+    TokenCredentialsAccessManager(const TokenCredentials *cred, QObject* parent = 0)
+        : MirallAccessManager(parent), _cred(cred) {}
+protected:
+    QNetworkReply *createRequest(Operation op, const QNetworkRequest &request, QIODevice *outgoingData) {
+        QByteArray credHash = QByteArray(_cred->user().toUtf8()+":"+_cred->password().toUtf8()).toBase64();
+        QNetworkRequest req(request);
+        req.setRawHeader(QByteArray("Authorization"), QByteArray("Basic ") + credHash);
+        //qDebug() << "Request for " << req.url() << "with authorization" << QByteArray::fromBase64(credHash);
+        return MirallAccessManager::createRequest(op, req, outgoingData);
+    }
+private:
+    const TokenCredentials *_cred;
+};
+
+TokenCredentials::TokenCredentials()
+    : _user(),
+      _password(),
+      _ready(false)
+{
+}
+
+TokenCredentials::TokenCredentials(const QString& user, const QString& password)
+    : _user(user),
+      _password(password),
+      _ready(true)
+{
+}
+
+void TokenCredentials::syncContextPreInit (CSYNC* ctx)
+{
+    csync_set_auth_callback (ctx, getauth);
+}
+
+void TokenCredentials::syncContextPreStart (CSYNC* ctx)
+{
+    // TODO: This should not be a part of this method, but we don't have
+    // any way to get "session_key" module property from csync. Had we
+    // have it, then we could remove this code and keep it in
+    // csyncthread code (or folder code, git remembers).
+    QList<QNetworkCookie> cookies(AccountManager::instance()->account()->lastAuthCookies());
+    QString cookiesAsString;
+
+    // Stuff cookies inside csync, then we can avoid the intermediate HTTP 401 reply
+    // when https://github.com/owncloud/core/pull/4042 is merged.
+    foreach(QNetworkCookie c, cookies) {
+        cookiesAsString += c.name();
+        cookiesAsString += '=';
+        cookiesAsString += c.value();
+        cookiesAsString += "; ";
+    }
+
+    csync_set_module_property(ctx, "session_key", cookiesAsString.toLatin1().data());
+}
+
+bool TokenCredentials::changed(AbstractCredentials* credentials) const
+{
+    TokenCredentials* other(dynamic_cast< TokenCredentials* >(credentials));
+
+    if (!other || (other->user() != this->user())) {
+        return true;
+    }
+
+    return false;
+}
+
+QString TokenCredentials::authType() const
+{
+    return QString::fromLatin1("http");
+}
+
+QString TokenCredentials::user() const
+{
+    return _user;
+}
+
+QString TokenCredentials::password() const
+{
+    return _password;
+}
+
+QNetworkAccessManager* TokenCredentials::getQNAM() const
+{
+    MirallAccessManager* qnam = new TokenCredentialsAccessManager(this);
+
+    connect( qnam, SIGNAL(authenticationRequired(QNetworkReply*, QAuthenticator*)),
+             this, SLOT(slotAuthentication(QNetworkReply*,QAuthenticator*)));
+
+    return qnam;
+}
+
+bool TokenCredentials::ready() const
+{
+    return _ready;
+}
+
+QString TokenCredentials::fetchUser(Account* account)
+{
+    _user = account->credentialSetting(QLatin1String(userC)).toString();
+    return _user;
+}
+
+void TokenCredentials::fetch(Account *account)
+{
+    if( !account ) {
+        return;
+    }
+    Q_EMIT fetched();
+}
+bool TokenCredentials::stillValid(QNetworkReply *reply)
+{
+    return ((reply->error() != QNetworkReply::AuthenticationRequiredError)
+            // returned if user or password is incorrect
+            && (reply->error() != QNetworkReply::OperationCanceledError));
+}
+
+QString TokenCredentials::queryPassword(bool *ok)
+{
+    qDebug() << AccountManager::instance()->account()->state();
+    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);
+        qDebug() << AccountManager::instance()->account()->state();
+        return str;
+    } else {
+        return QString();
+    }
+}
+
+void TokenCredentials::invalidateToken(Account *account)
+{
+    _password = QString();
+    _ready = false;
+
+    // User must be fetched from config file to generate a valid key
+    fetchUser(account);
+
+    const QString kck = keychainKey(account->url().toString(), _user);
+    if( kck.isEmpty() ) {
+        qDebug() << "InvalidateToken: User is empty, bailing out!";
+        return;
+    }
+
+    account->clearCookieJar();
+}
+
+void TokenCredentials::persist(Account *account)
+{
+}
+
+
+void TokenCredentials::slotAuthentication(QNetworkReply* reply, QAuthenticator* authenticator)
+{
+    Q_UNUSED(authenticator)
+    // we cannot use QAuthenticator, because it sends username and passwords with latin1
+    // instead of utf8 encoding. Instead, we send it manually. Thus, if we reach this signal,
+    // those credentials were invalid and we terminate.
+    qDebug() << "Stop request: Authentication failed for " << reply->url().toString();
+    reply->close();
+}
+
+} // ns Mirall
diff --git a/src/creds/tokencredentials.h b/src/creds/tokencredentials.h
new file mode 100644
index 0000000..c2a272f
--- /dev/null
+++ b/src/creds/tokencredentials.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) by Klaas Freitag <freitag at kde.org>
+ * Copyright (c) by Markus Goetz <guruz at 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 MIRALL_CREDS_HTTP_CREDENTIALS_H
+#define MIRALL_CREDS_HTTP_CREDENTIALS_H
+
+#include <QMap>
+
+#include "creds/abstractcredentials.h"
+
+class QNetworkReply;
+class QAuthenticator;
+
+namespace QKeychain {
+class Job;
+}
+
+namespace Mirall
+{
+
+class TokenCredentials : public AbstractCredentials
+{
+    Q_OBJECT
+
+public:
+    TokenCredentials();
+    TokenCredentials(const QString& user, const QString& password);
+
+    void syncContextPreInit(CSYNC* ctx);
+    void syncContextPreStart(CSYNC* ctx);
+    bool changed(AbstractCredentials* credentials) const;
+    QString authType() const;
+    QNetworkAccessManager* getQNAM() const;
+    bool ready() const;
+    void fetch(Account *account);
+    bool stillValid(QNetworkReply *reply);
+    void persist(Account *account);
+    QString user() const;
+    QString password() const;
+    QString queryPassword(bool *ok);
+    void invalidateToken(Account *account);
+    QString fetchUser(Account *account);
+
+private Q_SLOTS:
+    void slotAuthentication(QNetworkReply*, QAuthenticator*);
+
+private:
+    QString _user;
+    QString _password;
+    bool _ready;
+};
+
+} // ns Mirall
+
+#endif

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-owncloud/owncloud-client.git



More information about the Pkg-owncloud-commits mailing list