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