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 | |
54 | K_PLUGIN_FACTORY(SolidUiServerFactory, |
55 | registerPlugin<SolidUiServer>(); |
56 | ) |
57 | K_EXPORT_PLUGIN(SolidUiServerFactory("soliduiserver" )) |
58 | |
59 | |
60 | SolidUiServer::SolidUiServer(QObject* parent, const QList<QVariant>&) |
61 | : KDEDModule(parent) |
62 | { |
63 | } |
64 | |
65 | SolidUiServer::~SolidUiServer() |
66 | { |
67 | } |
68 | |
69 | void 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 | |
123 | void 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 | |
134 | void 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 | |
192 | void 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 | |
228 | void SolidUiServer::onPassphraseDialogRejected() |
229 | { |
230 | onPassphraseDialogCompleted(QString(), false); |
231 | } |
232 | |
233 | void 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 | |