1/* This file is part of the KDE Project
2 Copyright (c) 2005 Jean-Remy Falleri <jr.falleri@laposte.net>
3 Copyright (c) 2005-2007 Kevin Ottens <ervin@kde.org>
4 Copyright (c) 2007 Alexis Ménard <darktears31@gmail.com>
5 Copyright (c) 2011 Lukas Tinkl <ltinkl@redhat.com>
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Library General Public
9 License version 2 as published by the Free Software Foundation.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
15
16 You should have received a copy of the GNU Library General Public License
17 along with this library; see the file COPYING.LIB. If not, write to
18 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 Boston, MA 02110-1301, USA.
20*/
21
22#include "soliduiserver.h"
23
24#include <QFile>
25#include <QtDBus/QDBusInterface>
26#include <QtDBus/QDBusReply>
27#include <QtDBus/QDBusError>
28#include <kjobuidelegate.h>
29
30#include <kapplication.h>
31#include <kdebug.h>
32#include <klocale.h>
33#include <kprocess.h>
34#include <krun.h>
35#include <kmessagebox.h>
36#include <kstandardguiitem.h>
37#include <kstandarddirs.h>
38#include <kdesktopfileactions.h>
39#include <kwindowsystem.h>
40#include <kpassworddialog.h>
41#include <kwallet.h>
42#include <kicon.h>
43#include <solid/storagevolume.h>
44
45#include "deviceactionsdialog.h"
46#include "deviceaction.h"
47#include "deviceserviceaction.h"
48#include "devicenothingaction.h"
49
50
51#include <kpluginfactory.h>
52#include <kpluginloader.h>
53
54K_PLUGIN_FACTORY(SolidUiServerFactory,
55 registerPlugin<SolidUiServer>();
56 )
57K_EXPORT_PLUGIN(SolidUiServerFactory("soliduiserver"))
58
59
60SolidUiServer::SolidUiServer(QObject* parent, const QList<QVariant>&)
61 : KDEDModule(parent)
62{
63}
64
65SolidUiServer::~SolidUiServer()
66{
67}
68
69void SolidUiServer::showActionsDialog(const QString &udi,
70 const QStringList &desktopFiles)
71{
72 if (m_udiToActionsDialog.contains(udi)) {
73 DeviceActionsDialog *dialog = m_udiToActionsDialog[udi];
74 dialog->activateWindow();
75 return;
76 }
77
78
79 QList<DeviceAction*> actions;
80
81 foreach (const QString &desktop, desktopFiles) {
82 QString filePath = KStandardDirs::locate("data", "solid/actions/"+desktop);
83
84 QList<KServiceAction> services
85 = KDesktopFileActions::userDefinedServices(filePath, true);
86
87 foreach (const KServiceAction &service, services) {
88 DeviceServiceAction *action = new DeviceServiceAction();
89 action->setService(service);
90 actions << action;
91 }
92 }
93
94 // Only one action, execute directly
95 if (actions.size()==1) {
96 DeviceAction *action = actions.takeFirst();
97 Solid::Device device(udi);
98 action->execute(device);
99 delete action;
100 return;
101 }
102
103 actions << new DeviceNothingAction();
104
105 DeviceActionsDialog *dialog = new DeviceActionsDialog();
106 dialog->setDevice(Solid::Device(udi));
107 dialog->setActions(actions);
108
109 connect(dialog, SIGNAL(finished()),
110 this, SLOT(onActionDialogFinished()));
111
112 m_udiToActionsDialog[udi] = dialog;
113
114 // Update user activity timestamp, otherwise the notification dialog will be shown
115 // in the background due to focus stealing prevention. Entering a new media can
116 // be seen as a kind of user activity after all. It'd be better to update the timestamp
117 // as soon as the media is entered, but it apparently takes some time to get here.
118 kapp->updateUserTimestamp();
119
120 dialog->show();
121}
122
123void SolidUiServer::onActionDialogFinished()
124{
125 DeviceActionsDialog *dialog = qobject_cast<DeviceActionsDialog*>(sender());
126
127 if (dialog) {
128 QString udi = dialog->device().udi();
129 m_udiToActionsDialog.remove(udi);
130 }
131}
132
133
134void SolidUiServer::showPassphraseDialog(const QString &udi,
135 const QString &returnService, const QString &returnObject,
136 uint wId, const QString &appId)
137{
138 if (m_idToPassphraseDialog.contains(returnService+':'+udi)) {
139 KPasswordDialog *dialog = m_idToPassphraseDialog[returnService+':'+udi];
140 dialog->activateWindow();
141 return;
142 }
143
144 Solid::Device device(udi);
145
146 KPasswordDialog *dialog = new KPasswordDialog(0, KPasswordDialog::ShowKeepPassword);
147
148 QString label = device.vendor();
149 if (!label.isEmpty()) label+=' ';
150 label+= device.product();
151
152 dialog->setPrompt(i18n("'%1' needs a password to be accessed. Please enter a password.", label));
153 dialog->setPixmap(KIcon(device.icon()).pixmap(64, 64));
154 dialog->setProperty("soliduiserver.udi", udi);
155 dialog->setProperty("soliduiserver.returnService", returnService);
156 dialog->setProperty("soliduiserver.returnObject", returnObject);
157
158 QString uuid;
159 if (device.is<Solid::StorageVolume>())
160 uuid = device.as<Solid::StorageVolume>()->uuid();
161
162 // read the password from wallet and prefill it to the dialog
163 if (!uuid.isEmpty()) {
164 dialog->setProperty("soliduiserver.uuid", uuid);
165
166 KWallet::Wallet * wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), (WId) wId);
167 const QString folderName = QString::fromLatin1("SolidLuks");
168 if (wallet && wallet->hasFolder(folderName)) {
169 wallet->setFolder(folderName);
170 QString savedPassword;
171 if (wallet->readPassword(uuid, savedPassword) == 0) {
172 dialog->setKeepPassword(true);
173 dialog->setPassword(savedPassword);
174 }
175 wallet->closeWallet(wallet->walletName(), false);
176 }
177 delete wallet;
178 }
179
180
181 connect(dialog, SIGNAL(gotPassword(const QString&, bool)),
182 this, SLOT(onPassphraseDialogCompleted(const QString&, bool)));
183 connect(dialog, SIGNAL(rejected()),
184 this, SLOT(onPassphraseDialogRejected()));
185
186 m_idToPassphraseDialog[returnService+':'+udi] = dialog;
187
188 reparentDialog(dialog, (WId)wId, appId, true);
189 dialog->show();
190}
191
192void SolidUiServer::onPassphraseDialogCompleted(const QString &pass, bool keep)
193{
194 KPasswordDialog *dialog = qobject_cast<KPasswordDialog*>(sender());
195
196 if (dialog) {
197 QString returnService = dialog->property("soliduiserver.returnService").toString();
198 QString returnObject = dialog->property("soliduiserver.returnObject").toString();
199 QDBusInterface returnIface(returnService, returnObject);
200
201 QDBusReply<void> reply = returnIface.call("passphraseReply", pass);
202
203 QString udi = dialog->property("soliduiserver.udi").toString();
204 m_idToPassphraseDialog.remove(returnService+':'+udi);
205
206 if (!reply.isValid()) {
207 kWarning() << "Impossible to send the passphrase to the application, D-Bus said: "
208 << reply.error().name() << ", " << reply.error().message() << endl;
209 return; // don't save into wallet if an error occurs
210 }
211
212 if (keep) { // save the password into the wallet
213 KWallet::Wallet * wallet = KWallet::Wallet::openWallet(KWallet::Wallet::LocalWallet(), 0);
214 if (wallet) {
215 const QString folderName = QString::fromLatin1("SolidLuks");
216 const QString uuid = dialog->property("soliduiserver.uuid").toString();
217 if (!wallet->hasFolder(folderName))
218 wallet->createFolder(folderName);
219 if (wallet->setFolder(folderName))
220 wallet->writePassword(uuid, pass);
221 wallet->closeWallet(wallet->walletName(), false);
222 delete wallet;
223 }
224 }
225 }
226}
227
228void SolidUiServer::onPassphraseDialogRejected()
229{
230 onPassphraseDialogCompleted(QString(), false);
231}
232
233void SolidUiServer::reparentDialog(QWidget *dialog, WId wId, const QString &appId, bool modal)
234{
235 Q_UNUSED(appId);
236 // Code borrowed from kwalletd
237
238 KWindowSystem::setMainWindow(dialog, wId); // correct, set dialog parent
239
240#ifdef Q_WS_X11
241 if (modal) {
242 KWindowSystem::setState(dialog->winId(), NET::Modal);
243 } else {
244 KWindowSystem::clearState(dialog->winId(), NET::Modal);
245 }
246#endif
247
248 // allow dialog activation even if it interrupts, better than trying hacks
249 // with keeping the dialog on top or on all desktops
250 kapp->updateUserTimestamp();
251}
252
253#include "soliduiserver.moc"
254