1 /***************************************************************************
2     This is the global namespace for Smb4K.
3                              -------------------
4     begin                : Sa Apr 2 2005
5     copyright            : (C) 2005-2020 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 "smb4kglobal.h"
28 #include "smb4kglobal_p.h"
29 #include "smb4knotification.h"
30 #include "smb4ksynchronizer.h"
31 #include "smb4kclient.h"
32 #include "smb4kmounter.h"
33 
34 // Qt includes
35 #include <QMutex>
36 #include <QUrl>
37 #include <QStandardPaths>
38 #include <QDebug>
39 #include <QDirIterator>
40 
41 // KDE includes
42 #include <KCoreAddons/KShell>
43 #include <kio_version.h>
44 #if KIO_VERSION < QT_VERSION_CHECK(5, 71, 0)
45 #include <KIOWidgets/KRun>
46 #else
47 #include <KIO/OpenUrlJob>
48 #include <KIO/CommandLauncherJob>
49 #endif
50 
51 Q_GLOBAL_STATIC(Smb4KGlobalPrivate, p);
52 QMutex mutex(QMutex::Recursive /* needed to avoid dead-locks */);
53 
54 
initCore(bool modifyCursor,bool initClasses)55 void Smb4KGlobal::initCore(bool modifyCursor, bool initClasses)
56 {
57   if (!p->coreInitialized)
58   {
59     //
60     // Busy cursor
61     //
62     p->modifyCursor = modifyCursor;
63 
64     //
65     // Initialize the necessary core classes
66     //
67     if (initClasses)
68     {
69       Smb4KClient::self()->start();
70       Smb4KMounter::self()->start();
71     }
72 
73     p->coreInitialized = true;
74   }
75 }
76 
77 
abortCore()78 void Smb4KGlobal::abortCore()
79 {
80   Smb4KClient::self()->abort();
81   Smb4KMounter::self()->abort();
82   Smb4KSynchronizer::self()->abort();
83 }
84 
85 
coreIsRunning()86 bool Smb4KGlobal::coreIsRunning()
87 {
88   return (Smb4KClient::self()->isRunning() ||
89           Smb4KMounter::self()->isRunning() ||
90           Smb4KSynchronizer::self()->isRunning());
91 }
92 
93 
coreIsInitialized()94 bool Smb4KGlobal::coreIsInitialized()
95 {
96   return p->coreInitialized;
97 }
98 
99 
workgroupsList()100 const QList<WorkgroupPtr> &Smb4KGlobal::workgroupsList()
101 {
102   return p->workgroupsList;
103 }
104 
105 
findWorkgroup(const QString & name)106 WorkgroupPtr Smb4KGlobal::findWorkgroup(const QString &name)
107 {
108   WorkgroupPtr workgroup;
109 
110   mutex.lock();
111 
112   for (const WorkgroupPtr &w : p->workgroupsList)
113   {
114     if (QString::compare(w->workgroupName(), name, Qt::CaseInsensitive) == 0)
115     {
116       workgroup = w;
117       break;
118     }
119   }
120 
121   mutex.unlock();
122 
123   return workgroup;
124 }
125 
126 
addWorkgroup(WorkgroupPtr workgroup)127 bool Smb4KGlobal::addWorkgroup(WorkgroupPtr workgroup)
128 {
129   Q_ASSERT(workgroup);
130 
131   bool added = false;
132 
133   if (workgroup)
134   {
135     mutex.lock();
136 
137     if (!findWorkgroup(workgroup->workgroupName()))
138     {
139       p->workgroupsList.append(workgroup);
140       added = true;
141     }
142 
143     mutex.unlock();
144   }
145 
146   return added;
147 }
148 
149 
updateWorkgroup(WorkgroupPtr workgroup)150 bool Smb4KGlobal::updateWorkgroup(WorkgroupPtr workgroup)
151 {
152   Q_ASSERT(workgroup);
153 
154   bool updated = false;
155 
156   if (workgroup)
157   {
158     mutex.lock();
159 
160     WorkgroupPtr existingWorkgroup = findWorkgroup(workgroup->workgroupName());
161 
162     if (existingWorkgroup)
163     {
164       existingWorkgroup->update(workgroup.data());
165       updated = true;
166     }
167 
168     mutex.unlock();
169   }
170 
171   return updated;
172 }
173 
174 
175 
removeWorkgroup(WorkgroupPtr workgroup)176 bool Smb4KGlobal::removeWorkgroup(WorkgroupPtr workgroup)
177 {
178   Q_ASSERT(workgroup);
179 
180   bool removed = false;
181 
182   if (workgroup)
183   {
184     mutex.lock();
185 
186     int index = p->workgroupsList.indexOf(workgroup);
187 
188     if (index != -1)
189     {
190       // The workgroup was found. Remove it.
191       p->workgroupsList.takeAt(index).clear();
192       removed = true;
193     }
194     else
195     {
196       // Try harder to find the workgroup.
197       WorkgroupPtr wg = findWorkgroup(workgroup->workgroupName());
198 
199       if (wg)
200       {
201         index = p->workgroupsList.indexOf(wg);
202 
203         if (index != -1)
204         {
205           p->workgroupsList.takeAt(index).clear();
206           removed = true;
207         }
208       }
209 
210       workgroup.clear();
211     }
212 
213     mutex.unlock();
214   }
215 
216   return removed;
217 }
218 
219 
clearWorkgroupsList()220 void Smb4KGlobal::clearWorkgroupsList()
221 {
222   mutex.lock();
223 
224   while (!p->workgroupsList.isEmpty())
225   {
226     p->workgroupsList.takeFirst().clear();
227   }
228 
229   mutex.unlock();
230 }
231 
232 
hostsList()233 const QList<HostPtr> &Smb4KGlobal::hostsList()
234 {
235   return p->hostsList;
236 }
237 
238 
findHost(const QString & name,const QString & workgroup)239 HostPtr Smb4KGlobal::findHost(const QString &name, const QString &workgroup)
240 {
241   HostPtr host;
242 
243   mutex.lock();
244 
245   for (const HostPtr &h : p->hostsList)
246   {
247     if ((workgroup.isEmpty() || QString::compare(h->workgroupName(), workgroup, Qt::CaseInsensitive) == 0) &&
248         QString::compare(h->hostName(), name, Qt::CaseInsensitive) == 0)
249     {
250       host = h;
251       break;
252     }
253   }
254 
255   mutex.unlock();
256 
257   return host;
258 }
259 
260 
addHost(HostPtr host)261 bool Smb4KGlobal::addHost(HostPtr host)
262 {
263   Q_ASSERT(host);
264 
265   bool added = false;
266 
267   if (host)
268   {
269     mutex.lock();
270 
271     if (!findHost(host->hostName(), host->workgroupName()))
272     {
273       p->hostsList.append(host);
274       added = true;
275     }
276 
277     mutex.unlock();
278   }
279 
280   return added;
281 }
282 
283 
updateHost(HostPtr host)284 bool Smb4KGlobal::updateHost(HostPtr host)
285 {
286   Q_ASSERT(host);
287 
288   bool updated = false;
289 
290   if (host)
291   {
292     mutex.lock();
293 
294     HostPtr existingHost = findHost(host->hostName(), host->workgroupName());
295 
296     if (existingHost)
297     {
298       existingHost->update(host.data());
299       updated = true;
300     }
301 
302     mutex.unlock();
303   }
304 
305   return updated;
306 }
307 
308 
309 
removeHost(HostPtr host)310 bool Smb4KGlobal::removeHost(HostPtr host)
311 {
312   Q_ASSERT(host);
313 
314   bool removed = false;
315 
316   if (host)
317   {
318     mutex.lock();
319 
320     int index = p->hostsList.indexOf(host);
321 
322     if (index != -1)
323     {
324       // The host was found. Remove it.
325       p->hostsList.takeAt(index).clear();
326       removed = true;
327     }
328     else
329     {
330       // Try harder to find the host.
331       HostPtr h = findHost(host->hostName(), host->workgroupName());
332 
333       if (h)
334       {
335         index = p->hostsList.indexOf(h);
336 
337         if (index != -1)
338         {
339           p->hostsList.takeAt(index).clear();
340           removed = true;
341         }
342       }
343 
344       host.clear();
345     }
346 
347     mutex.unlock();
348   }
349 
350   return removed;
351 }
352 
353 
clearHostsList()354 void Smb4KGlobal::clearHostsList()
355 {
356   mutex.lock();
357 
358   while (!p->hostsList.isEmpty())
359   {
360     p->hostsList.takeFirst().clear();
361   }
362 
363   mutex.unlock();
364 }
365 
366 
workgroupMembers(WorkgroupPtr workgroup)367 QList<HostPtr> Smb4KGlobal::workgroupMembers(WorkgroupPtr workgroup)
368 {
369   QList<HostPtr> hosts;
370 
371   mutex.lock();
372 
373   for (const HostPtr &h : p->hostsList)
374   {
375     if (QString::compare(h->workgroupName(), workgroup->workgroupName(), Qt::CaseInsensitive) == 0)
376     {
377       hosts << h;
378     }
379   }
380 
381   mutex.unlock();
382 
383   return hosts;
384 }
385 
386 
sharesList()387 const QList<SharePtr> &Smb4KGlobal::sharesList()
388 {
389   return p->sharesList;
390 }
391 
392 
findShare(const QUrl & url,const QString & workgroup)393 SharePtr Smb4KGlobal::findShare(const QUrl& url, const QString& workgroup)
394 {
395   SharePtr share;
396 
397   mutex.lock();
398 
399   for (const SharePtr &s : p->sharesList)
400   {
401     if (QString::compare(s->url().toString(QUrl::RemoveUserInfo|QUrl::RemovePort),
402                          url.toString(QUrl::RemoveUserInfo|QUrl::RemovePort), Qt::CaseInsensitive) == 0 &&
403         (workgroup.isEmpty() || QString::compare(s->workgroupName(), workgroup, Qt::CaseInsensitive) == 0))
404     {
405       share = s;
406       break;
407     }
408   }
409 
410   mutex.unlock();
411 
412   return share;
413 }
414 
415 
416 
addShare(SharePtr share)417 bool Smb4KGlobal::addShare(SharePtr share)
418 {
419   Q_ASSERT(share);
420 
421   bool added = false;
422 
423   if (share)
424   {
425     mutex.lock();
426 
427     //
428     // Add the share
429     //
430     if (!findShare(share->url(), share->workgroupName()))
431     {
432       //
433       // Set the share mounted
434       // Only honor shares that are owned by the user
435       //
436       QList<SharePtr> mountedShares = findShareByUrl(share->url());
437 
438       if (!mountedShares.isEmpty())
439       {
440         for (const SharePtr &s : mountedShares)
441         {
442           if (!s->isForeign())
443           {
444             share->setMountData(s.data());
445             break;
446           }
447           else
448           {
449             continue;
450           }
451         }
452       }
453 
454       //
455       // Add it
456       //
457       p->sharesList.append(share);
458       added = true;
459     }
460   }
461 
462   mutex.unlock();
463 
464   return added;
465 }
466 
467 
updateShare(SharePtr share)468 bool Smb4KGlobal::updateShare(SharePtr share)
469 {
470   Q_ASSERT(share);
471 
472   bool updated = false;
473 
474   if (share)
475   {
476     mutex.lock();
477 
478     //
479     // Updated the share
480     //
481     SharePtr existingShare = findShare(share->url(), share->workgroupName());
482 
483     if (existingShare)
484     {
485       //
486       // Set the share mounted
487       // Only honor shares that are owned by the user
488       //
489       QList<SharePtr> mountedShares = findShareByUrl(share->url());
490 
491       if (!mountedShares.isEmpty())
492       {
493         for (const SharePtr &s : mountedShares)
494         {
495           if (!s->isForeign())
496           {
497             share->setMountData(s.data());
498             break;
499           }
500           else
501           {
502             continue;
503           }
504         }
505       }
506 
507       //
508       // Update it
509       //
510       existingShare->update(share.data());
511       updated = true;
512     }
513 
514     mutex.unlock();
515   }
516 
517   return updated;
518 }
519 
520 
521 
removeShare(SharePtr share)522 bool Smb4KGlobal::removeShare(SharePtr share)
523 {
524   Q_ASSERT(share);
525 
526   bool removed = false;
527 
528   if (share)
529   {
530     mutex.lock();
531 
532     int index = p->sharesList.indexOf(share);
533 
534     if (index != -1)
535     {
536       // The share was found. Remove it.
537       p->sharesList.takeAt(index).clear();
538       removed = true;
539     }
540     else
541     {
542       // Try harder to find the share.
543       SharePtr s = findShare(share->url(), share->workgroupName());
544 
545       if (s)
546       {
547         index = p->sharesList.indexOf(s);
548 
549         if (index != -1)
550         {
551           p->sharesList.takeAt(index).clear();
552           removed = true;
553         }
554       }
555 
556       share.clear();
557     }
558 
559     mutex.unlock();
560   }
561 
562   return removed;
563 }
564 
565 
clearSharesList()566 void Smb4KGlobal::clearSharesList()
567 {
568   mutex.lock();
569 
570   while (!p->sharesList.isEmpty())
571   {
572     p->sharesList.takeFirst().clear();
573   }
574 
575   mutex.unlock();
576 }
577 
578 
sharedResources(HostPtr host)579 QList<SharePtr> Smb4KGlobal::sharedResources(HostPtr host)
580 {
581   QList<SharePtr> shares;
582 
583   mutex.lock();
584 
585   for (const SharePtr &s : p->sharesList)
586   {
587     if (QString::compare(s->hostName(), host->hostName(), Qt::CaseInsensitive) == 0 &&
588         QString::compare(s->workgroupName(), host->workgroupName(), Qt::CaseInsensitive) == 0)
589     {
590       shares += s;
591     }
592   }
593 
594   mutex.unlock();
595 
596   return shares;
597 }
598 
599 
mountedSharesList()600 const QList<SharePtr> &Smb4KGlobal::mountedSharesList()
601 {
602   return p->mountedSharesList;
603 }
604 
605 
findShareByPath(const QString & path)606 SharePtr Smb4KGlobal::findShareByPath(const QString &path)
607 {
608   SharePtr share;
609 
610   mutex.lock();
611 
612   if (!path.isEmpty() && !p->mountedSharesList.isEmpty())
613   {
614     for (const SharePtr &s : p->mountedSharesList)
615     {
616       if (QString::compare(s->path(), path, Qt::CaseInsensitive) == 0 || QString::compare(s->canonicalPath(), path, Qt::CaseInsensitive) == 0)
617       {
618         share = s;
619         break;
620       }
621     }
622   }
623 
624   mutex.unlock();
625 
626   return share;
627 }
628 
629 
findShareByUrl(const QUrl & url)630 QList<SharePtr> Smb4KGlobal::findShareByUrl(const QUrl &url)
631 {
632   QList<SharePtr> shares;
633 
634   mutex.lock();
635 
636   if (!url.isEmpty() && url.isValid() && !p->mountedSharesList.isEmpty())
637   {
638     for (const SharePtr &s : p->mountedSharesList)
639     {
640       if (QString::compare(s->url().toString(QUrl::RemoveUserInfo|QUrl::RemovePort), url.toString(QUrl::RemoveUserInfo|QUrl::RemovePort), Qt::CaseInsensitive) == 0)
641       {
642         shares << s;
643         break;
644       }
645     }
646   }
647 
648   mutex.unlock();
649 
650   return shares;
651 }
652 
653 
findInaccessibleShares()654 QList<SharePtr> Smb4KGlobal::findInaccessibleShares()
655 {
656   QList<SharePtr> inaccessibleShares;
657 
658   mutex.lock();
659 
660   for (const SharePtr &s : p->mountedSharesList)
661   {
662     if (s->isInaccessible())
663     {
664       inaccessibleShares += s;
665     }
666   }
667 
668   mutex.unlock();
669 
670   return inaccessibleShares;
671 }
672 
673 
addMountedShare(SharePtr share)674 bool Smb4KGlobal::addMountedShare(SharePtr share)
675 {
676   Q_ASSERT(share);
677 
678   bool added = false;
679 
680   if (share)
681   {
682     mutex.lock();
683 
684     //
685     // Copy the mount data to the network share if available.
686     // Only honor shares that were mounted by the user.
687     //
688     if (!share->isForeign())
689     {
690       // Network share
691       SharePtr networkShare = findShare(share->url(), share->workgroupName());
692 
693       if (networkShare)
694       {
695         networkShare->setMountData(share.data());
696       }
697     }
698 
699     if (!findShareByPath(share->path()))
700     {
701       //
702       // Check if we have to add a workgroup name and/or IP address
703       //
704       HostPtr networkHost = findHost(share->hostName(), share->workgroupName());
705 
706       if (networkHost)
707       {
708         // Set the IP address
709         if (!share->hasHostIpAddress() || networkHost->ipAddress() != share->hostIpAddress())
710         {
711           share->setHostIpAddress(networkHost->ipAddress());
712         }
713 
714         // Set the workgroup name
715         if (share->workgroupName().isEmpty())
716         {
717           share->setWorkgroupName(networkHost->workgroupName());
718         }
719       }
720 
721       p->mountedSharesList.append(share);
722       added = true;
723 
724       p->onlyForeignShares = true;
725 
726       for (const SharePtr &s : p->mountedSharesList)
727       {
728         if (!s->isForeign())
729         {
730           p->onlyForeignShares = false;
731           break;
732         }
733       }
734     }
735 
736     mutex.unlock();
737   }
738 
739   return added;
740 }
741 
742 
updateMountedShare(SharePtr share)743 bool Smb4KGlobal::updateMountedShare(SharePtr share)
744 {
745   Q_ASSERT(share);
746 
747   bool updated = false;
748 
749   if (share)
750   {
751     mutex.lock();
752 
753     //
754     // Copy the mount data to the network share (needed for unmounting from the network browser)
755     // Only honor shares that were mounted by the user.
756     //
757     if (!share->isForeign())
758     {
759       SharePtr networkShare = findShare(share->url(), share->workgroupName());
760 
761       if (networkShare)
762       {
763         networkShare->setMountData(share.data());
764       }
765     }
766 
767     SharePtr mountedShare = findShareByPath(share->path());
768 
769     if (mountedShare)
770     {
771       //
772       // Check if we have to add a workgroup name and/or IP address
773       //
774       HostPtr networkHost = findHost(share->hostName(), share->workgroupName());
775 
776       if (networkHost)
777       {
778         // Set the IP address
779         if (!share->hasHostIpAddress() || networkHost->ipAddress() != share->hostIpAddress())
780         {
781           share->setHostIpAddress(networkHost->ipAddress());
782         }
783 
784         // Set the workgroup name
785         if (share->workgroupName().isEmpty())
786         {
787           share->setWorkgroupName(networkHost->workgroupName());
788         }
789       }
790 
791       //
792       // Update share
793       //
794       mountedShare->setMountData(share.data());
795       updated = true;
796     }
797 
798     mutex.unlock();
799   }
800 
801   return updated;
802 }
803 
804 
removeMountedShare(SharePtr share)805 bool Smb4KGlobal::removeMountedShare(SharePtr share)
806 {
807   Q_ASSERT(share);
808 
809   bool removed = false;
810 
811   if (share)
812   {
813     mutex.lock();
814 
815     //
816     // Reset the mount data for the network share and the
817     // search result
818     //
819     if (!share->isForeign())
820     {
821       // Network share
822       SharePtr networkShare = findShare(share->url(), share->workgroupName());
823 
824       if (networkShare)
825       {
826         networkShare->resetMountData();
827       }
828     }
829 
830     //
831     // Remove the mounted share
832     //
833     int index = p->mountedSharesList.indexOf(share);
834 
835     if (index != -1)
836     {
837       // The share was found. Remove it.
838       p->mountedSharesList.takeAt(index).clear();
839       removed = true;
840     }
841     else
842     {
843       // Try harder to find the share.
844       SharePtr s = findShareByPath(share->isInaccessible() ? share->path() : share->canonicalPath());
845 
846       if (s)
847       {
848         index = p->mountedSharesList.indexOf(s);
849 
850         if (index != -1)
851         {
852           p->mountedSharesList.takeAt(index).clear();
853           removed = true;
854         }
855       }
856 
857       share.clear();
858     }
859 
860     for (const SharePtr &s : p->mountedSharesList)
861     {
862       if (!s->isForeign())
863       {
864         p->onlyForeignShares = false;
865         break;
866       }
867     }
868 
869     mutex.unlock();
870   }
871 
872   return removed;
873 }
874 
875 
onlyForeignMountedShares()876 bool Smb4KGlobal::onlyForeignMountedShares()
877 {
878   return p->onlyForeignShares;
879 }
880 
881 
openShare(SharePtr share,OpenWith openWith)882 void Smb4KGlobal::openShare(SharePtr share, OpenWith openWith)
883 {
884   if (!share || share->isInaccessible())
885   {
886     return;
887   }
888 
889   switch (openWith)
890   {
891     case FileManager:
892     {
893       QUrl url = QUrl::fromLocalFile(share->canonicalPath());
894 
895 #if KIO_VERSION < QT_VERSION_CHECK(5,71,0)
896       (void) new KRun(url, 0);
897 #else
898       KIO::OpenUrlJob *job = new KIO::OpenUrlJob(url);
899       job->setFollowRedirections(false);
900       job->setAutoDelete(true);
901       job->start();
902 #endif
903 
904       break;
905     }
906     case Konsole:
907     {
908       QString konsole = QStandardPaths::findExecutable("konsole");
909 
910       if (!konsole.isEmpty())
911       {
912 #if KIO_VERSION < QT_VERSION_CHECK(5,71,0)
913         KRun::runCommand(konsole+" --workdir "+KShell::quoteArg(share->canonicalPath()), 0);
914 #else
915         KIO::CommandLauncherJob *job = new KIO::CommandLauncherJob(konsole);
916         job->setWorkingDirectory(share->canonicalPath());
917         job->setAutoDelete(true);
918         job->start();
919 #endif
920       }
921       else
922       {
923         Smb4KNotification::commandNotFound("konsole");
924       }
925 
926       break;
927     }
928     default:
929     {
930       break;
931     }
932   }
933 }
934 
935 
machineNetbiosName()936 const QString Smb4KGlobal::machineNetbiosName()
937 {
938   return p->machineNetbiosName;
939 }
940 
941 
machineWorkgroupName()942 const QString Smb4KGlobal::machineWorkgroupName()
943 {
944   return p->machineWorkgroupName;
945 }
946 
947 
948 
modifyCursor()949 bool Smb4KGlobal::modifyCursor()
950 {
951   return p->modifyCursor;
952 }
953 
954 
955 #if defined(Q_OS_LINUX)
allowedMountArguments()956 QStringList Smb4KGlobal::allowedMountArguments()
957 {
958   return p->allowedMountArguments;
959 }
960 #endif
961 
962 
findMountExecutable()963 const QString Smb4KGlobal::findMountExecutable()
964 {
965   QStringList paths;
966   paths << "/bin";
967   paths << "/sbin";
968   paths << "/usr/bin";
969   paths << "/usr/sbin";
970   paths << "/usr/local/bin";
971   paths << "/usr/local/sbin";
972 
973 #if defined(Q_OS_LINUX)
974   return QStandardPaths::findExecutable("mount.cifs", paths);
975 #elif defined(Q_OS_FREEBSD) || defined(Q_OS_NETBSD)
976   return QStandardPaths::findExecutable("mount_smbfs", paths);
977 #else
978   return QString();
979 #endif
980 }
981 
982 
findUmountExecutable()983 const QString Smb4KGlobal::findUmountExecutable()
984 {
985   QStringList paths;
986   paths << "/bin";
987   paths << "/sbin";
988   paths << "/usr/bin";
989   paths << "/usr/sbin";
990   paths << "/usr/local/bin";
991   paths << "/usr/local/sbin";
992 
993   return QStandardPaths::findExecutable("umount", paths);
994 }
995 
996 
dataLocation()997 const QString Smb4KGlobal::dataLocation()
998 {
999   return QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation)+QDir::separator()+"smb4k";
1000 }
1001 
1002