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