1 /***************************************************************************
2 This class handles the homes shares
3 -------------------
4 begin : Do Aug 10 2006
5 copyright : (C) 2006-2019 by Alexander Reinholdt
6 email : alexander.reinholdt@kdemail.net
7 ***************************************************************************/
8
9 /***************************************************************************
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, but *
16 * WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
18 * General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston,*
23 * MA 02110-1335 USA *
24 ***************************************************************************/
25
26 // application specific includes
27 #include "smb4khomesshareshandler.h"
28 #include "smb4khomesshareshandler_p.h"
29 #include "smb4kshare.h"
30 #include "smb4ksettings.h"
31 #include "smb4kauthinfo.h"
32 #include "smb4knotification.h"
33 #include "smb4kprofilemanager.h"
34
35 // Qt includes
36 #include <QFile>
37 #include <QTextCodec>
38 #include <QXmlStreamWriter>
39 #include <QXmlStreamReader>
40 #include <QApplication>
41 #include <QPointer>
42
43 // KDE includes
44 #define TRANSLATION_DOMAIN "smb4k-core"
45 #include <KI18n/KLocalizedString>
46
47 Q_GLOBAL_STATIC(Smb4KHomesSharesHandlerStatic, p);
48
49
Smb4KHomesSharesHandler(QObject * parent)50 Smb4KHomesSharesHandler::Smb4KHomesSharesHandler(QObject *parent)
51 : QObject(parent), d(new Smb4KHomesSharesHandlerPrivate)
52 {
53 // First we need the directory.
54 QString path = dataLocation();
55
56 QDir dir;
57
58 if (!dir.exists(path))
59 {
60 dir.mkpath(path);
61 }
62
63 d->homesUsers = readUserNames(false);
64
65 //
66 // Connections
67 //
68 connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(slotAboutToQuit()));
69 connect(Smb4KProfileManager::self(), SIGNAL(activeProfileChanged(QString)), this, SLOT(slotActiveProfileChanged(QString)));
70 }
71
72
~Smb4KHomesSharesHandler()73 Smb4KHomesSharesHandler::~Smb4KHomesSharesHandler()
74 {
75 while (!d->homesUsers.isEmpty())
76 {
77 delete d->homesUsers.takeFirst();
78 }
79 }
80
81
self()82 Smb4KHomesSharesHandler *Smb4KHomesSharesHandler::self()
83 {
84 return &p->instance;
85 }
86
87
specifyUser(const SharePtr & share,bool overwrite)88 bool Smb4KHomesSharesHandler::specifyUser(const SharePtr &share, bool overwrite)
89 {
90 Q_ASSERT(share);
91 bool success = false;
92
93 // Avoid that the dialog is opened although the homes
94 // user name has already been defined.
95 if (share->isHomesShare() && (share->homeUrl().isEmpty() || overwrite))
96 {
97 QStringList users = findHomesUsers(share);
98
99 QPointer<Smb4KHomesUserDialog> dlg = new Smb4KHomesUserDialog(share, QApplication::activeWindow());
100 dlg->setUserNames(users);
101
102 if (dlg->exec() == QDialog::Accepted)
103 {
104 QString login = dlg->login();
105 users = dlg->userNames();
106 addHomesUsers(share, users);
107
108 if (!login.isEmpty())
109 {
110 // If the login names do not match, clear the password.
111 if (!share->login().isEmpty() && QString::compare(share->login(), login) != 0)
112 {
113 share->setPassword(QString());
114 }
115
116 // Set the login name.
117 share->setLogin(login);
118 success = true;
119 }
120
121 writeUserNames(d->homesUsers);
122 }
123
124 delete dlg;
125 }
126 else
127 {
128 // The user name has already been set.
129 success = true;
130 }
131
132 return success;
133 }
134
135
homesUsers(const SharePtr & share)136 QStringList Smb4KHomesSharesHandler::homesUsers(const SharePtr &share)
137 {
138 Q_ASSERT(share);
139 QStringList users = findHomesUsers(share);
140 return users;
141 }
142
143
readUserNames(bool allUsers)144 const QList<Smb4KHomesUsers *> Smb4KHomesSharesHandler::readUserNames(bool allUsers)
145 {
146 QList<Smb4KHomesUsers *> list;
147
148 // Locate the XML file.
149 QFile xmlFile(dataLocation()+QDir::separator()+"homes_shares.xml");
150
151 if (xmlFile.open(QIODevice::ReadOnly | QIODevice::Text))
152 {
153 QXmlStreamReader xmlReader(&xmlFile);
154
155 while (!xmlReader.atEnd())
156 {
157 xmlReader.readNext();
158
159 if (xmlReader.isStartElement())
160 {
161 if (xmlReader.name() == "homes_shares" && xmlReader.attributes().value("version") != "1.0")
162 {
163 xmlReader.raiseError(i18n("%1 is not a version 1.0 file.", xmlFile.fileName()));
164 break;
165 }
166 else
167 {
168 if (xmlReader.name() == "homes")
169 {
170 QString profile = xmlReader.attributes().value("profile").toString();
171
172 // FIXME: Remove the last check in the if statement with Smb4K > 2.0.
173 // It was introduced for migration, because the default profile
174 // (e.g. use of no profiles) was not empty but named "Default"...
175 if (allUsers || QString::compare(profile, Smb4KProfileManager::self()->activeProfile(), Qt::CaseSensitive) == 0 ||
176 (!Smb4KProfileManager::self()->useProfiles() && QString::compare(profile, "Default", Qt::CaseSensitive) == 0))
177 {
178 Smb4KHomesUsers *users = new Smb4KHomesUsers();
179 users->setProfile(profile);
180 users->setShareName(xmlReader.name().toString());
181
182 while (!(xmlReader.isEndElement() && xmlReader.name() == "homes"))
183 {
184 xmlReader.readNext();
185
186 if (xmlReader.isStartElement())
187 {
188 if (xmlReader.name() == "host")
189 {
190 users->setHostName(xmlReader.readElementText());
191 }
192 else if (xmlReader.name() == "workgroup")
193 {
194 users->setWorkgroupName(xmlReader.readElementText());
195 }
196 else if (xmlReader.name() == "ip")
197 {
198 users->setHostIP(xmlReader.readElementText());
199 }
200 else if (xmlReader.name() == "users")
201 {
202 QStringList u;
203
204 while (!(xmlReader.isEndElement() && xmlReader.name() == "users"))
205 {
206 xmlReader.readNext();
207
208 if (xmlReader.isStartElement() && xmlReader.name() == "user")
209 {
210 u << xmlReader.readElementText();
211 }
212 }
213
214 users->setUsers(u);
215 }
216
217 continue;
218 }
219 else
220 {
221 continue;
222 }
223 }
224
225 list << users;
226 }
227 else
228 {
229 continue;
230 }
231 }
232 else
233 {
234 continue;
235 }
236 }
237 }
238 else
239 {
240 continue;
241 }
242 }
243
244 xmlFile.close();
245
246 if (xmlReader.hasError())
247 {
248 Smb4KNotification::readingFileFailed(xmlFile, xmlReader.errorString());
249 }
250 }
251 else
252 {
253 if (xmlFile.exists())
254 {
255 Smb4KNotification::openingFileFailed(xmlFile);
256 }
257 }
258
259 return list;
260 }
261
262
writeUserNames(const QList<Smb4KHomesUsers * > & list,bool listOnly)263 void Smb4KHomesSharesHandler::writeUserNames(const QList<Smb4KHomesUsers *> &list, bool listOnly)
264 {
265 QList<Smb4KHomesUsers *> allUsers;
266
267 if (!listOnly)
268 {
269 // First read all entries. Then remove all, that belong to
270 // the currently active profile.
271 allUsers = readUserNames(true);
272
273 QMutableListIterator<Smb4KHomesUsers *> it(allUsers);
274
275 while (it.hasNext())
276 {
277 Smb4KHomesUsers *users = it.next();
278
279 if (QString::compare(users->profile(), Smb4KProfileManager::self()->activeProfile()) == 0)
280 {
281 it.remove();
282 }
283 }
284 }
285
286 for (Smb4KHomesUsers *users : list)
287 {
288 allUsers << new Smb4KHomesUsers(*users);
289 }
290
291 QFile xmlFile(dataLocation()+QDir::separator()+"homes_shares.xml");
292
293 if (!allUsers.isEmpty())
294 {
295 if (xmlFile.open(QIODevice::WriteOnly|QIODevice::Text))
296 {
297 QXmlStreamWriter xmlWriter(&xmlFile);
298 xmlWriter.setAutoFormatting(true);
299 xmlWriter.writeStartDocument();
300 xmlWriter.writeStartElement("homes_shares");
301 xmlWriter.writeAttribute("version", "1.0");
302
303 for (Smb4KHomesUsers *users : allUsers)
304 {
305 xmlWriter.writeStartElement("homes");
306
307 // FIXME: Remove this block with Smb4K > 2.0 and use the commented line below.
308 // This block was introduced for migration, because the default profile
309 // (i.e. use of no profiles) was not empty but named "Default"...
310 if (!Smb4KProfileManager::self()->useProfiles())
311 {
312 xmlWriter.writeAttribute("profile", Smb4KSettings::self()->activeProfile());
313 }
314 else
315 {
316 xmlWriter.writeAttribute("profile", users->profile());
317 }
318 // xmlWriter.writeAttribute("profile", users->profile());
319 xmlWriter.writeTextElement("host", users->hostName());
320 xmlWriter.writeTextElement("workgroup", users->workgroupName());
321 xmlWriter.writeTextElement("ip", users->hostIP());
322 xmlWriter.writeStartElement("users");
323
324 for (const QString &user : users->users())
325 {
326 xmlWriter.writeTextElement("user", user);
327 }
328
329 xmlWriter.writeEndElement();
330 xmlWriter.writeEndElement();
331 }
332
333 xmlWriter.writeEndDocument();
334 xmlFile.close();
335 }
336 else
337 {
338 Smb4KNotification::openingFileFailed(xmlFile);
339 }
340 }
341 else
342 {
343 xmlFile.remove();
344 }
345
346 while (!allUsers.isEmpty())
347 {
348 delete allUsers.takeFirst();
349 }
350 }
351
352
findHomesUsers(const SharePtr & share)353 const QStringList Smb4KHomesSharesHandler::findHomesUsers(const SharePtr &share)
354 {
355 Q_ASSERT(share);
356
357 QStringList users;
358
359 if (!d->homesUsers.isEmpty())
360 {
361 for (int i = 0; i < d->homesUsers.size(); ++i)
362 {
363 if (QString::compare(share->hostName(), d->homesUsers.at(i)->hostName(), Qt::CaseInsensitive) == 0 &&
364 QString::compare(share->shareName(), d->homesUsers.at(i)->shareName(), Qt::CaseInsensitive) == 0 &&
365 ((d->homesUsers.at(i)->workgroupName().isEmpty() || share->workgroupName().isEmpty()) ||
366 QString::compare(share->workgroupName(), d->homesUsers.at(i)->workgroupName(), Qt::CaseInsensitive) == 0))
367 {
368 users = d->homesUsers.at(i)->users();
369 break;
370 }
371 else
372 {
373 continue;
374 }
375 }
376 }
377
378 return users;
379 }
380
381
addHomesUsers(const SharePtr & share,const QStringList & users)382 void Smb4KHomesSharesHandler::addHomesUsers(const SharePtr &share, const QStringList &users)
383 {
384 Q_ASSERT(share);
385
386 bool found = false;
387
388 if (!d->homesUsers.isEmpty())
389 {
390 for (int i = 0; i < d->homesUsers.size(); ++i)
391 {
392 if (QString::compare(share->hostName(), d->homesUsers.at(i)->hostName(), Qt::CaseInsensitive) == 0 &&
393 QString::compare(share->shareName(), d->homesUsers.at(i)->shareName(), Qt::CaseInsensitive) == 0 &&
394 ((d->homesUsers.at(i)->workgroupName().isEmpty() || share->workgroupName().isEmpty()) ||
395 QString::compare(share->workgroupName(), d->homesUsers.at(i)->workgroupName(), Qt::CaseInsensitive) == 0))
396 {
397 d->homesUsers[i]->setUsers(users);
398 found = true;
399 break;
400 }
401 else
402 {
403 continue;
404 }
405 }
406 }
407
408 if (!found)
409 {
410 Smb4KHomesUsers *u = new Smb4KHomesUsers(share, users);
411 u->setProfile(Smb4KProfileManager::self()->activeProfile());
412 d->homesUsers << u;
413 }
414 }
415
416
migrateProfile(const QString & from,const QString & to)417 void Smb4KHomesSharesHandler::migrateProfile(const QString& from, const QString& to)
418 {
419 // Read all entries for later conversion.
420 QList<Smb4KHomesUsers *> allUsers = readUserNames(true);
421
422 // Replace the old profile name with the new one.
423 for (int i = 0; i < allUsers.size(); ++i)
424 {
425 if (QString::compare(allUsers.at(i)->profile(), from, Qt::CaseSensitive) == 0)
426 {
427 allUsers[i]->setProfile(to);
428 }
429 }
430
431 // Write the new list to the file.
432 writeUserNames(allUsers, true);
433
434 // Profile settings changed, so invoke the slot.
435 slotActiveProfileChanged(Smb4KProfileManager::self()->activeProfile());
436
437 // Clear the temporary lists of bookmarks and groups.
438 while (!allUsers.isEmpty())
439 {
440 delete allUsers.takeFirst();
441 }
442 }
443
444
removeProfile(const QString & name)445 void Smb4KHomesSharesHandler::removeProfile(const QString& name)
446 {
447 // Read all entries for later removal.
448 QList<Smb4KHomesUsers *> allUsers = readUserNames(true);
449 QMutableListIterator<Smb4KHomesUsers *> it(allUsers);
450
451 while (it.hasNext())
452 {
453 Smb4KHomesUsers *user = it.next();
454
455 if (QString::compare(user->profile(), name, Qt::CaseSensitive) == 0)
456 {
457 it.remove();
458 }
459 }
460
461 // Write the new list to the file.
462 writeUserNames(allUsers, true);
463
464 // Profile settings changed, so invoke the slot.
465 slotActiveProfileChanged(Smb4KProfileManager::self()->activeProfile());
466
467 // Clear the temporary list of homes users.
468 while (!allUsers.isEmpty())
469 {
470 delete allUsers.takeFirst();
471 }
472 }
473
474
475 /////////////////////////////////////////////////////////////////////////////
476 // SLOT IMPLEMENTATIONS
477 /////////////////////////////////////////////////////////////////////////////
478
slotAboutToQuit()479 void Smb4KHomesSharesHandler::slotAboutToQuit()
480 {
481 writeUserNames(d->homesUsers);
482 }
483
484
slotActiveProfileChanged(const QString &)485 void Smb4KHomesSharesHandler::slotActiveProfileChanged(const QString& /*activeProfile*/)
486 {
487 // Clear the list of homes users.
488 while (!d->homesUsers.isEmpty())
489 {
490 delete d->homesUsers.takeFirst();
491 }
492
493 // Reload the list of homes users.
494 d->homesUsers = readUserNames(false);
495 }
496
497