Skip to content

Commit 524bf64

Browse files
committed
Minimal libcloudproviders support #7209
- Add it as an optional dependency for linux builds, controlled by WITH_LIBCLOUDPROVIDERS, which is enabled automatically if the dependencies are available. Note that >=0.3.0 is required. - Add code to export sync folders through the library and to add a minimal "Settings..." menu action. - Set up cloud provider registration by installing a file into a path like /usr/share/cloud-providers/. - DBus name and path are derived from APPLICATION_REV_DOMAIN by default and may be overridden with APPLICATION_CLOUDPROVIDERS_DBUS_NAME and APPLICATION_CLOUDPROVIDERS_DBUS_PATH if necessary.
1 parent d05ac5c commit 524bf64

12 files changed

+462
-2
lines changed

THEME.cmake

+9
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,15 @@ if(NOT DEFINED APPLICATION_VIRTUALFILE_SUFFIX)
1010
set(APPLICATION_VIRTUALFILE_SUFFIX "${APPLICATION_SHORTNAME}_virtual" CACHE STRING "Virtual file suffix (not including the .)")
1111
endif()
1212

13+
# Default dbus name and path
14+
if(NOT DEFINED APPLICATION_CLOUDPROVIDERS_DBUS_NAME)
15+
set(APPLICATION_CLOUDPROVIDERS_DBUS_NAME ${APPLICATION_REV_DOMAIN})
16+
endif()
17+
if(NOT DEFINED APPLICATION_CLOUDPROVIDERS_DBUS_PATH)
18+
set(APPLICATION_CLOUDPROVIDERS_DBUS_PATH "/${APPLICATION_CLOUDPROVIDERS_DBUS_NAME}")
19+
string(REPLACE "." "/" APPLICATION_CLOUDPROVIDERS_DBUS_PATH ${APPLICATION_CLOUDPROVIDERS_DBUS_PATH})
20+
endif()
21+
1322
# need this logic to not mess with re/uninstallations via macosx.pkgproj
1423
if(${APPLICATION_REV_DOMAIN} STREQUAL "com.owncloud.desktopclient")
1524
set(APPLICATION_REV_DOMAIN_INSTALLER "com.ownCloud.client")
+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# (c) 2019 Copyright ownCloud GmbH
2+
# Redistribution and use is allowed according to the terms of the BSD license.
3+
# For details see the accompanying COPYING* file.
4+
5+
find_path(LIBCLOUDPROVIDERS_INCLUDE_DIR
6+
NAMES
7+
cloudprovidersaccountexporter.h
8+
cloudprovidersproviderexporter.h
9+
PATH_SUFFIXES
10+
cloudproviders
11+
)
12+
13+
find_library(LIBCLOUDPROVIDERS_LIBRARY
14+
NAMES
15+
cloudproviders
16+
PATHS
17+
/usr/lib
18+
/usr/lib/${CMAKE_ARCH_TRIPLET}
19+
/usr/local/lib
20+
${CMAKE_LIBRARY_PATH}
21+
${CMAKE_INSTALL_PREFIX}/lib
22+
)
23+
24+
# Using version <0.3.0 would lead to crashes during runtime when accounts are unexported.
25+
get_filename_component(LIBCLOUDPROVIDERS_LIBRARY_REALPATH ${LIBCLOUDPROVIDERS_LIBRARY} REALPATH)
26+
if(${LIBCLOUDPROVIDERS_LIBRARY_REALPATH} MATCHES "\.([0-9]*)\.([0-9]*)\.([0-9]*)$")
27+
if ((${CMAKE_MATCH_1} EQUAL 0) AND (${CMAKE_MATCH_2} LESS 3))
28+
message("libcloudproviders version is older than 0.3.0, not enabling it")
29+
set(LIBCLOUDPROVIDERS_LIBRARY "")
30+
endif()
31+
else()
32+
message("Can't determine libcloudproviders version, not enabling it")
33+
set(LIBCLOUDPROVIDERS_LIBRARY "")
34+
endif()
35+
36+
include(FindPackageHandleStandardArgs)
37+
find_package_handle_standard_args(
38+
LIBCLOUDPROVIDERS
39+
DEFAULT_MSG
40+
LIBCLOUDPROVIDERS_LIBRARY LIBCLOUDPROVIDERS_INCLUDE_DIR)
41+
42+
mark_as_advanced(LIBCLOUDPROVIDERS_INCLUDE_DIR LIBCLOUDPROVIDERS_LIBRARY)

config.h.in

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#cmakedefine APPLICATION_ICON_NAME "@APPLICATION_ICON_NAME@"
2121
#cmakedefine APPLICATION_VIRTUALFILE_SUFFIX "@APPLICATION_VIRTUALFILE_SUFFIX@"
2222
#define APPLICATION_DOTVIRTUALFILE_SUFFIX "." APPLICATION_VIRTUALFILE_SUFFIX
23+
#cmakedefine APPLICATION_CLOUDPROVIDERS_DBUS_NAME "@APPLICATION_CLOUDPROVIDERS_DBUS_NAME@"
24+
#cmakedefine APPLICATION_CLOUDPROVIDERS_DBUS_PATH "@APPLICATION_CLOUDPROVIDERS_DBUS_PATH@"
2325

2426
#cmakedefine ZLIB_FOUND @ZLIB_FOUND@
2527

src/gui/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ endif()
317317
# application.cpp still uses QDesktopServices::storageLocation
318318
target_compile_definitions(${APPLICATION_EXECUTABLE} PRIVATE "QT_DISABLE_DEPRECATED_BEFORE=0")
319319

320+
include(libcloudproviders/libcloudproviders.cmake)
320321

321322
install(TARGETS ${APPLICATION_EXECUTABLE}
322323
RUNTIME DESTINATION bin

src/gui/folderman.cpp

+6-1
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,16 @@ FolderMan::~FolderMan()
9393
_instance = 0;
9494
}
9595

96-
OCC::Folder::Map FolderMan::map()
96+
OCC::Folder::Map FolderMan::map() const
9797
{
9898
return _folderMap;
9999
}
100100

101+
QList<Folder *> FolderMan::list() const
102+
{
103+
return _folderMap.values();
104+
}
105+
101106
void FolderMan::unloadFolder(Folder *f)
102107
{
103108
if (!f) {

src/gui/folderman.h

+2-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,8 @@ class FolderMan : public QObject
8787
*/
8888
static void backwardMigrationSettingsKeys(QStringList *deleteKeys, QStringList *ignoreKeys);
8989

90-
OCC::Folder::Map map();
90+
OCC::Folder::Map map() const;
91+
QList<Folder *> list() const;
9192

9293
/** Adds a folder for an account, ensures the journal is gone and saves it in the settings.
9394
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[Cloud Providers]
2+
BusName=@APPLICATION_CLOUDPROVIDERS_DBUS_NAME@
3+
ObjectPath=@APPLICATION_CLOUDPROVIDERS_DBUS_PATH@
4+
Version=1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
find_package(Libcloudproviders)
2+
find_package(PkgConfig REQUIRED)
3+
pkg_search_module(GIO gio-2.0)
4+
5+
# The cloudproviders feature can only be enabled if the libcloudproviders
6+
# and gio-2.0 libraries are available
7+
set(LIBCLOUDPROVIDERS_POSSIBLE "")
8+
if(LIBCLOUDPROVIDERS_FOUND AND GIO_FOUND)
9+
set(LIBCLOUDPROVIDERS_POSSIBLE "1")
10+
endif()
11+
12+
# User visible config switch
13+
set(WITH_LIBCLOUDPROVIDERS ${LIBCLOUDPROVIDERS_POSSIBLE} CACHE BOOL "Whether to bulid with libcloudproviders")
14+
15+
if(WITH_LIBCLOUDPROVIDERS AND NOT LIBCLOUDPROVIDERS_POSSIBLE)
16+
message(FATAL_ERROR "Trying to enable libcloudproviders but dependencies are missing")
17+
endif()
18+
19+
if(WITH_LIBCLOUDPROVIDERS)
20+
target_sources(${APPLICATION_EXECUTABLE} PRIVATE
21+
libcloudproviders/libcloudproviders.cpp
22+
)
23+
target_include_directories(${APPLICATION_EXECUTABLE} SYSTEM PRIVATE ${GIO_INCLUDE_DIRS})
24+
target_compile_definitions(${APPLICATION_EXECUTABLE} PRIVATE WITH_LIBCLOUDPROVIDERS)
25+
target_link_libraries(${APPLICATION_EXECUTABLE}
26+
cloudproviders
27+
${GIO_LDFLAGS}
28+
)
29+
30+
configure_file(libcloudproviders/cloud-provider.ini.in ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_CLOUDPROVIDERS_DBUS_NAME}.ini)
31+
install(
32+
FILES ${CMAKE_CURRENT_BINARY_DIR}/${APPLICATION_CLOUDPROVIDERS_DBUS_NAME}.ini
33+
DESTINATION "${DATADIR}/cloud-providers")
34+
35+
message("Building with libcloudproviders")
36+
elseif(UNIX AND NOT APPLE)
37+
message("Building without libcloudproviders")
38+
endif()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*
2+
* Copyright (C) by Christian Kamm <[email protected]>
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful, but
10+
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11+
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* for more details.
13+
*/
14+
15+
extern "C" {
16+
#include <cloudproviders/cloudprovidersaccountexporter.h>
17+
#include <cloudproviders/cloudprovidersproviderexporter.h>
18+
#include <gio/gio.h>
19+
#include <glib.h>
20+
}
21+
22+
#include "libcloudproviders.h"
23+
#include "libcloudproviders_p.h"
24+
25+
#include <QMap>
26+
#include "folder.h"
27+
#include "folderman.h"
28+
#include "theme.h"
29+
#include "accountstate.h"
30+
#include "config.h"
31+
#include "account.h"
32+
#include "accountmanager.h"
33+
34+
// These must match the name and path defined in a file in $DATADIR/cloud-providers/
35+
const gchar dbusName[] = APPLICATION_CLOUDPROVIDERS_DBUS_NAME;
36+
const gchar dbusPath[] = APPLICATION_CLOUDPROVIDERS_DBUS_PATH;
37+
38+
namespace OCC {
39+
40+
LibCloudProvidersPrivate::~LibCloudProvidersPrivate()
41+
{
42+
for (const auto folder : _folderExports.keys())
43+
unexportFolder(folder);
44+
g_clear_object(&_exporter);
45+
if (_busOwnerId)
46+
g_bus_unown_name(_busOwnerId);
47+
}
48+
49+
static void onBusAcquired(GDBusConnection *connection, const gchar *, gpointer userData)
50+
{
51+
auto d = static_cast<LibCloudProvidersPrivate *>(userData);
52+
53+
d->_exporter = cloud_providers_provider_exporter_new(connection, dbusName, dbusPath);
54+
cloud_providers_provider_exporter_set_name(d->_exporter, Theme::instance()->appNameGUI().toUtf8().constData());
55+
56+
d->updateExportedFolderList();
57+
}
58+
59+
static void onNameLost(GDBusConnection *, const gchar *, gpointer userData)
60+
{
61+
auto d = static_cast<LibCloudProvidersPrivate *>(userData);
62+
63+
d->_folderExports.clear();
64+
g_clear_object(&d->_exporter);
65+
}
66+
67+
void LibCloudProvidersPrivate::start()
68+
{
69+
_busOwnerId = g_bus_own_name(
70+
G_BUS_TYPE_SESSION, dbusName, G_BUS_NAME_OWNER_FLAGS_NONE,
71+
&onBusAcquired,
72+
nullptr, // onNameAcquired
73+
&onNameLost,
74+
this, // user data
75+
nullptr // user data free func
76+
);
77+
78+
auto folderMan = FolderMan::instance();
79+
connect(folderMan, &FolderMan::folderListChanged,
80+
this, &LibCloudProvidersPrivate::updateExportedFolderList);
81+
connect(folderMan, &FolderMan::folderSyncStateChange,
82+
this, &LibCloudProvidersPrivate::updateFolderExport);
83+
}
84+
85+
void LibCloudProvidersPrivate::updateExportedFolderList()
86+
{
87+
const auto newFolders = FolderMan::instance()->list();
88+
const auto oldFolders = _folderExports.keys();
89+
90+
// Remove folders that are no longer exported
91+
for (const auto old : oldFolders) {
92+
if (!newFolders.contains(old))
93+
unexportFolder(old);
94+
}
95+
96+
// Add new folders
97+
for (const auto n : newFolders) {
98+
if (!oldFolders.contains(n))
99+
exportFolder(n);
100+
}
101+
}
102+
103+
static void actionDispatcher(GSimpleAction *action, GVariant *, gpointer userData)
104+
{
105+
gchar *strval;
106+
g_object_get(action, "name", &strval, NULL);
107+
QByteArray name(strval);
108+
g_free(strval);
109+
110+
auto d = static_cast<LibCloudProvidersPrivate *>(userData);
111+
auto q = d->_q;
112+
if (name == "settings")
113+
q->showSettings();
114+
else
115+
ASSERT(false, "unknown action string");
116+
}
117+
118+
119+
void LibCloudProvidersPrivate::exportFolder(Folder *folder)
120+
{
121+
if (!_exporter)
122+
return;
123+
124+
GError *error = nullptr;
125+
auto icon = g_icon_new_for_string(APPLICATION_ICON_NAME, &error);
126+
if (error) {
127+
qWarning() << "Could not create icon for" << APPLICATION_ICON_NAME << "error" << error->message;
128+
g_error_free(error);
129+
}
130+
131+
auto exporter = cloud_providers_account_exporter_new(_exporter, folder->alias().toUtf8().constData());
132+
cloud_providers_account_exporter_set_path(exporter, folder->path().toUtf8().constData());
133+
cloud_providers_account_exporter_set_icon(exporter, icon);
134+
cloud_providers_account_exporter_set_status(exporter, CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE);
135+
136+
auto menu = g_menu_new();
137+
// The "cloudprovider" scope is hardcoded into the gtk code that uses this data.
138+
// Different scopes will not work.
139+
g_menu_append(menu, tr("Settings").toUtf8().constData(), "cloudprovider.settings");
140+
141+
auto actionGroup = g_simple_action_group_new();
142+
const GActionEntry entries[] = {
143+
{ "settings", actionDispatcher, nullptr, nullptr, nullptr, {}},
144+
};
145+
g_action_map_add_action_entries(G_ACTION_MAP(actionGroup), entries, G_N_ELEMENTS(entries), this);
146+
147+
cloud_providers_account_exporter_set_menu_model(exporter, G_MENU_MODEL(menu));
148+
cloud_providers_account_exporter_set_action_group(exporter, G_ACTION_GROUP(actionGroup));
149+
150+
// Currently there's no reason for us to keep these around: no further modifications are done
151+
g_clear_object(&menu);
152+
g_clear_object(&actionGroup);
153+
154+
_folderExports[folder] = FolderExport{folder, exporter};
155+
updateFolderExport();
156+
}
157+
158+
void LibCloudProvidersPrivate::unexportFolder(Folder *folder)
159+
{
160+
if (!_folderExports.contains(folder))
161+
return;
162+
auto folderExporter = _folderExports[folder]._exporter;
163+
cloud_providers_provider_exporter_remove_account(_exporter, folderExporter);
164+
// the remove_account already calls _unref
165+
_folderExports.remove(folder);
166+
}
167+
168+
void LibCloudProvidersPrivate::updateFolderExport()
169+
{
170+
for (auto folderExport : _folderExports) {
171+
if (!folderExport._folder)
172+
continue;
173+
Folder *folder = folderExport._folder;
174+
175+
// Update the name, may change if accounts are added/removed
176+
QString displayName = folder->shortGuiRemotePathOrAppName();
177+
if (AccountManager::instance()->accounts().size() > 1) {
178+
displayName = QStringLiteral("%1 (%2)").arg(
179+
displayName, folder->accountState()->account()->displayName());
180+
}
181+
cloud_providers_account_exporter_set_name(folderExport._exporter, displayName.toUtf8().constData());
182+
183+
CloudProvidersAccountStatus status = CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID;
184+
auto syncResult = folder->syncResult();
185+
switch (syncResult.status()) {
186+
case SyncResult::Undefined:
187+
status = CLOUD_PROVIDERS_ACCOUNT_STATUS_INVALID;
188+
break;
189+
case SyncResult::NotYetStarted:
190+
case SyncResult::Success:
191+
case SyncResult::Problem:
192+
status = CLOUD_PROVIDERS_ACCOUNT_STATUS_IDLE;
193+
break;
194+
case SyncResult::SyncPrepare:
195+
case SyncResult::SyncRunning:
196+
case SyncResult::SyncAbortRequested:
197+
status = CLOUD_PROVIDERS_ACCOUNT_STATUS_SYNCING;
198+
break;
199+
case SyncResult::Error:
200+
case SyncResult::SetupError:
201+
status = CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR;
202+
break;
203+
case SyncResult::Paused:
204+
// There's no status that fits exactly. If our choice is only
205+
// between IDLE And ERROR, let's go for ERROR to show that no
206+
// syncing is happening.
207+
status = CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR;
208+
break;
209+
}
210+
211+
// Similarly to Paused: If disconnected, show something's wrong!
212+
if (!folder->accountState()->isConnected())
213+
status = CLOUD_PROVIDERS_ACCOUNT_STATUS_ERROR;
214+
215+
auto message = FolderMan::trayTooltipStatusString(
216+
syncResult.status(),
217+
syncResult.hasUnresolvedConflicts(),
218+
folder->syncPaused());
219+
220+
cloud_providers_account_exporter_set_status(folderExport._exporter, status);
221+
cloud_providers_account_exporter_set_status_details(folderExport._exporter, message.toUtf8().constData());
222+
}
223+
}
224+
225+
LibCloudProviders::LibCloudProviders(QObject *parent)
226+
: QObject(parent)
227+
, d_ptr(new LibCloudProvidersPrivate)
228+
{
229+
d_ptr->_q = this;
230+
}
231+
232+
void LibCloudProviders::start()
233+
{
234+
d_ptr->start();
235+
}
236+
237+
} // namespace OCC

0 commit comments

Comments
 (0)