File indexing completed on 2024-04-21 15:42:48

0001 /***************************************************************************
0002     This class handles the homes shares
0003                              -------------------
0004     begin                : Do Aug 10 2006
0005     copyright            : (C) 2006-2019 by Alexander Reinholdt
0006     email                : alexander.reinholdt@kdemail.net
0007  ***************************************************************************/
0008 
0009 /***************************************************************************
0010  *   This program is free software; you can redistribute it and/or modify  *
0011  *   it under the terms of the GNU General Public License as published by  *
0012  *   the Free Software Foundation; either version 2 of the License, or     *
0013  *   (at your option) any later version.                                   *
0014  *                                                                         *
0015  *   This program is distributed in the hope that it will be useful, but   *
0016  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
0017  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
0018  *   General Public License for more details.                              *
0019  *                                                                         *
0020  *   You should have received a copy of the GNU General Public License     *
0021  *   along with this program; if not, write to the                         *
0022  *   Free Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston,*
0023  *   MA 02110-1335 USA                                                     *
0024  ***************************************************************************/
0025 
0026 #ifdef HAVE_CONFIG_H
0027 #include <config.h>
0028 #endif
0029 
0030 // application specific includes
0031 #include "smb4khomesshareshandler.h"
0032 #include "smb4khomesshareshandler_p.h"
0033 #include "smb4kshare.h"
0034 #include "smb4ksettings.h"
0035 #include "smb4kauthinfo.h"
0036 #include "smb4knotification.h"
0037 #include "smb4kprofilemanager.h"
0038 
0039 // Qt includes
0040 #include <QFile>
0041 #include <QTextCodec>
0042 #include <QXmlStreamWriter>
0043 #include <QXmlStreamReader>
0044 #include <QApplication>
0045 #include <QPointer>
0046 
0047 // KDE includes
0048 #define TRANSLATION_DOMAIN "smb4k-core"
0049 #include <KI18n/KLocalizedString>
0050 
0051 Q_GLOBAL_STATIC(Smb4KHomesSharesHandlerStatic, p);
0052 
0053 
0054 Smb4KHomesSharesHandler::Smb4KHomesSharesHandler(QObject *parent)
0055 : QObject(parent), d(new Smb4KHomesSharesHandlerPrivate)
0056 {
0057   // First we need the directory.
0058   QString path = dataLocation();
0059   
0060   QDir dir;
0061   
0062   if (!dir.exists(path))
0063   {
0064     dir.mkpath(path);
0065   }
0066   
0067   d->homesUsers = readUserNames(false);
0068   
0069   // 
0070   // Connections
0071   // 
0072   connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(slotAboutToQuit()));
0073   connect(Smb4KProfileManager::self(), SIGNAL(activeProfileChanged(QString)), this, SLOT(slotActiveProfileChanged(QString)));
0074 }
0075 
0076 
0077 Smb4KHomesSharesHandler::~Smb4KHomesSharesHandler()
0078 {
0079   while (!d->homesUsers.isEmpty())
0080   {
0081     delete d->homesUsers.takeFirst();
0082   }
0083 }
0084 
0085 
0086 Smb4KHomesSharesHandler *Smb4KHomesSharesHandler::self()
0087 {
0088   return &p->instance;
0089 }
0090 
0091 
0092 bool Smb4KHomesSharesHandler::specifyUser(const SharePtr &share, bool overwrite)
0093 {
0094   Q_ASSERT(share);
0095   bool success = false;
0096   
0097   // Avoid that the dialog is opened although the homes
0098   // user name has already been defined.
0099   if (share->isHomesShare() && (share->homeUrl().isEmpty() || overwrite))
0100   {
0101     QStringList users = findHomesUsers(share);
0102     
0103     QPointer<Smb4KHomesUserDialog> dlg = new Smb4KHomesUserDialog(share, QApplication::activeWindow());
0104     dlg->setUserNames(users);
0105     
0106     if (dlg->exec() == QDialog::Accepted)
0107     {
0108       QString login = dlg->login();
0109       users = dlg->userNames();
0110       addHomesUsers(share, users);
0111       
0112       if (!login.isEmpty())
0113       {
0114         // If the login names do not match, clear the password.
0115         if (!share->login().isEmpty() && QString::compare(share->login(), login) != 0)
0116         {
0117           share->setPassword(QString());
0118         }
0119         
0120         // Set the login name.
0121         share->setLogin(login);        
0122         success = true;
0123       }
0124       
0125       writeUserNames(d->homesUsers);
0126     }
0127     
0128     delete dlg;
0129   }
0130   else
0131   {
0132     // The user name has already been set.
0133     success = true;
0134   }
0135   
0136   return success;
0137 }
0138 
0139 
0140 QStringList Smb4KHomesSharesHandler::homesUsers(const SharePtr &share)
0141 {
0142   Q_ASSERT(share);
0143   QStringList users = findHomesUsers(share);
0144   return users;
0145 }
0146 
0147 
0148 const QList<Smb4KHomesUsers *> Smb4KHomesSharesHandler::readUserNames(bool allUsers)
0149 {
0150   QList<Smb4KHomesUsers *> list;
0151   
0152   // Locate the XML file.
0153   QFile xmlFile(dataLocation()+QDir::separator()+"homes_shares.xml");
0154 
0155   if (xmlFile.open(QIODevice::ReadOnly | QIODevice::Text))
0156   {
0157     QXmlStreamReader xmlReader(&xmlFile);
0158 
0159     while (!xmlReader.atEnd())
0160     {
0161       xmlReader.readNext();
0162 
0163       if (xmlReader.isStartElement())
0164       {
0165         if (xmlReader.name() == "homes_shares" && xmlReader.attributes().value("version") != "1.0")
0166         {
0167           xmlReader.raiseError(i18n("%1 is not a version 1.0 file.", xmlFile.fileName()));
0168           break;
0169         }
0170         else
0171         {
0172           if (xmlReader.name() == "homes")
0173           {
0174             QString profile = xmlReader.attributes().value("profile").toString();
0175             
0176             // FIXME: Remove the last check in the if statement with Smb4K > 2.0.
0177             // It was introduced for migration, because the default profile 
0178             // (e.g. use of no profiles) was not empty but named "Default"...
0179             if (allUsers || QString::compare(profile, Smb4KProfileManager::self()->activeProfile(), Qt::CaseSensitive) == 0 ||
0180                 (!Smb4KProfileManager::self()->useProfiles() && QString::compare(profile, "Default", Qt::CaseSensitive) == 0))
0181             {
0182               Smb4KHomesUsers *users = new Smb4KHomesUsers();
0183               users->setProfile(profile);
0184               users->setShareName(xmlReader.name().toString());
0185               
0186               while (!(xmlReader.isEndElement() && xmlReader.name() == "homes"))
0187               {
0188                 xmlReader.readNext();
0189 
0190                 if (xmlReader.isStartElement())
0191                 {
0192                   if (xmlReader.name() == "host")
0193                   {
0194                     users->setHostName(xmlReader.readElementText());
0195                   }
0196                   else if (xmlReader.name() == "workgroup")
0197                   {
0198                     users->setWorkgroupName(xmlReader.readElementText());
0199                   }
0200                   else if (xmlReader.name() == "ip")
0201                   {
0202                     users->setHostIP(xmlReader.readElementText());
0203                   }
0204                   else if (xmlReader.name() == "users")
0205                   {
0206                     QStringList u;
0207                     
0208                     while (!(xmlReader.isEndElement() && xmlReader.name() == "users"))
0209                     {
0210                       xmlReader.readNext();
0211 
0212                       if (xmlReader.isStartElement() && xmlReader.name() == "user")
0213                       {
0214                         u << xmlReader.readElementText();
0215                       }
0216                     }
0217                     
0218                     users->setUsers(u);
0219                   }
0220 
0221                   continue;
0222                 }
0223                 else
0224                 {
0225                   continue;
0226                 }
0227               }
0228               
0229               list << users;   
0230             }
0231             else
0232             {
0233               continue;
0234             }
0235           }
0236           else
0237           {
0238             continue;
0239           }
0240         }
0241       }
0242       else
0243       {
0244         continue;
0245       }
0246     }
0247 
0248     xmlFile.close();
0249 
0250     if (xmlReader.hasError())
0251     {
0252       Smb4KNotification::readingFileFailed(xmlFile, xmlReader.errorString());
0253     }
0254   }
0255   else
0256   {
0257     if (xmlFile.exists())
0258     {
0259       Smb4KNotification::openingFileFailed(xmlFile);
0260     }
0261   }
0262   
0263   return list;
0264 }
0265 
0266 
0267 void Smb4KHomesSharesHandler::writeUserNames(const QList<Smb4KHomesUsers *> &list, bool listOnly)
0268 {
0269   QList<Smb4KHomesUsers *> allUsers;
0270   
0271   if (!listOnly)
0272   {  
0273     // First read all entries. Then remove all, that belong to
0274     // the currently active profile.
0275     allUsers = readUserNames(true);
0276     
0277     QMutableListIterator<Smb4KHomesUsers *> it(allUsers);
0278     
0279     while (it.hasNext())
0280     {
0281       Smb4KHomesUsers *users = it.next();
0282       
0283       if (QString::compare(users->profile(), Smb4KProfileManager::self()->activeProfile()) == 0)
0284       {
0285         it.remove();
0286       }
0287     }
0288   }
0289   
0290   for (Smb4KHomesUsers *users : list)
0291   {
0292     allUsers << new Smb4KHomesUsers(*users);
0293   }
0294   
0295   QFile xmlFile(dataLocation()+QDir::separator()+"homes_shares.xml");
0296 
0297   if (!allUsers.isEmpty())
0298   {
0299     if (xmlFile.open(QIODevice::WriteOnly|QIODevice::Text))
0300     {
0301       QXmlStreamWriter xmlWriter(&xmlFile);
0302       xmlWriter.setAutoFormatting(true);
0303       xmlWriter.writeStartDocument();
0304       xmlWriter.writeStartElement("homes_shares");
0305       xmlWriter.writeAttribute("version", "1.0");
0306 
0307       for (Smb4KHomesUsers *users : allUsers)
0308       {
0309         xmlWriter.writeStartElement("homes");
0310         
0311         // FIXME: Remove this block with Smb4K > 2.0 and use the commented line below. 
0312         // This block was introduced for migration, because the default profile 
0313         // (i.e. use of no profiles) was not empty but named "Default"...
0314         if (!Smb4KProfileManager::self()->useProfiles())
0315         {
0316           xmlWriter.writeAttribute("profile", Smb4KSettings::self()->activeProfile());
0317         }
0318         else
0319         {
0320           xmlWriter.writeAttribute("profile", users->profile());
0321         }
0322         // xmlWriter.writeAttribute("profile", users->profile());
0323         xmlWriter.writeTextElement("host", users->hostName());
0324         xmlWriter.writeTextElement("workgroup", users->workgroupName());
0325         xmlWriter.writeTextElement("ip", users->hostIP());
0326         xmlWriter.writeStartElement("users");
0327 
0328         for (const QString &user : users->users())
0329         {
0330           xmlWriter.writeTextElement("user", user);
0331         }
0332 
0333         xmlWriter.writeEndElement();
0334         xmlWriter.writeEndElement();
0335       }
0336 
0337       xmlWriter.writeEndDocument();
0338       xmlFile.close();
0339     }
0340     else
0341     {
0342       Smb4KNotification::openingFileFailed(xmlFile);
0343     }
0344   }
0345   else
0346   {
0347     xmlFile.remove();
0348   }
0349   
0350   while (!allUsers.isEmpty())
0351   {
0352     delete allUsers.takeFirst();
0353   }
0354 }
0355 
0356 
0357 const QStringList Smb4KHomesSharesHandler::findHomesUsers(const SharePtr &share)
0358 {
0359   Q_ASSERT(share);
0360   
0361   QStringList users;
0362  
0363   if (!d->homesUsers.isEmpty())
0364   {
0365     for (int i = 0; i < d->homesUsers.size(); ++i)
0366     {
0367       if (QString::compare(share->hostName(), d->homesUsers.at(i)->hostName(), Qt::CaseInsensitive) == 0 &&
0368            QString::compare(share->shareName(), d->homesUsers.at(i)->shareName(), Qt::CaseInsensitive) == 0 &&
0369            ((d->homesUsers.at(i)->workgroupName().isEmpty() || share->workgroupName().isEmpty()) ||
0370            QString::compare(share->workgroupName(), d->homesUsers.at(i)->workgroupName(), Qt::CaseInsensitive) == 0))
0371       {
0372         users = d->homesUsers.at(i)->users();
0373         break;
0374       }
0375       else
0376       {
0377         continue;
0378       }
0379     }
0380   }
0381   
0382   return users;
0383 }
0384 
0385 
0386 void Smb4KHomesSharesHandler::addHomesUsers(const SharePtr &share, const QStringList &users)
0387 {
0388   Q_ASSERT(share);
0389   
0390   bool found = false;
0391   
0392   if (!d->homesUsers.isEmpty())
0393   {
0394     for (int i = 0; i < d->homesUsers.size(); ++i)
0395     {
0396       if (QString::compare(share->hostName(), d->homesUsers.at(i)->hostName(), Qt::CaseInsensitive) == 0 &&
0397           QString::compare(share->shareName(), d->homesUsers.at(i)->shareName(), Qt::CaseInsensitive) == 0 &&
0398           ((d->homesUsers.at(i)->workgroupName().isEmpty() || share->workgroupName().isEmpty()) ||
0399           QString::compare(share->workgroupName(), d->homesUsers.at(i)->workgroupName(), Qt::CaseInsensitive) == 0))
0400       {
0401         d->homesUsers[i]->setUsers(users);
0402         found = true;
0403         break;
0404       }
0405       else
0406       {
0407         continue;
0408       }
0409     }
0410   }
0411   
0412   if (!found)
0413   {
0414     Smb4KHomesUsers *u = new Smb4KHomesUsers(share, users);
0415     u->setProfile(Smb4KProfileManager::self()->activeProfile());
0416     d->homesUsers << u;
0417   }
0418 }
0419 
0420 
0421 void Smb4KHomesSharesHandler::migrateProfile(const QString& from, const QString& to)
0422 {
0423   // Read all entries for later conversion.
0424   QList<Smb4KHomesUsers *> allUsers = readUserNames(true);
0425   
0426   // Replace the old profile name with the new one.
0427   for (int i = 0; i < allUsers.size(); ++i)
0428   {
0429     if (QString::compare(allUsers.at(i)->profile(), from, Qt::CaseSensitive) == 0)
0430     {
0431       allUsers[i]->setProfile(to);
0432     }
0433   }
0434   
0435   // Write the new list to the file.
0436   writeUserNames(allUsers, true);
0437   
0438   // Profile settings changed, so invoke the slot.
0439   slotActiveProfileChanged(Smb4KProfileManager::self()->activeProfile());
0440   
0441   // Clear the temporary lists of bookmarks and groups.
0442   while (!allUsers.isEmpty())
0443   {
0444     delete allUsers.takeFirst();
0445   }
0446 }
0447 
0448 
0449 void Smb4KHomesSharesHandler::removeProfile(const QString& name)
0450 {
0451   // Read all entries for later removal.
0452   QList<Smb4KHomesUsers *> allUsers = readUserNames(true);
0453   QMutableListIterator<Smb4KHomesUsers *> it(allUsers);
0454   
0455   while (it.hasNext())
0456   {
0457     Smb4KHomesUsers *user = it.next();
0458     
0459     if (QString::compare(user->profile(), name, Qt::CaseSensitive) == 0)
0460     {
0461       it.remove();
0462     }
0463   }
0464   
0465   // Write the new list to the file.
0466   writeUserNames(allUsers, true);
0467   
0468   // Profile settings changed, so invoke the slot.
0469   slotActiveProfileChanged(Smb4KProfileManager::self()->activeProfile());
0470   
0471   // Clear the temporary list of homes users.
0472   while (!allUsers.isEmpty())
0473   {
0474     delete allUsers.takeFirst();
0475   }
0476 }
0477 
0478 
0479 /////////////////////////////////////////////////////////////////////////////
0480 // SLOT IMPLEMENTATIONS
0481 /////////////////////////////////////////////////////////////////////////////
0482 
0483 void Smb4KHomesSharesHandler::slotAboutToQuit()
0484 {
0485   writeUserNames(d->homesUsers);
0486 }
0487 
0488 
0489 void Smb4KHomesSharesHandler::slotActiveProfileChanged(const QString& /*activeProfile*/)
0490 {
0491   // Clear the list of homes users.
0492   while (!d->homesUsers.isEmpty())
0493   {
0494     delete d->homesUsers.takeFirst();
0495   }
0496   
0497   // Reload the list of homes users.
0498   d->homesUsers = readUserNames(false);
0499 }
0500