MythTV master
fileserverhandler.cpp
Go to the documentation of this file.
1
2#include <sys/types.h>
3#include <sys/stat.h>
4#include <unistd.h>
5
6#include <QtGlobal>
7#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
8#include <QtSystemDetection>
9#endif
10#include <QReadLocker>
11#include <QString>
12#include <QWriteLocker>
13#include <utility>
14
16#include "libmythbase/mythdb.h"
25
30
32
34{
35 // iterate through transfer list and close if
36 // socket matches connected transfer
37 {
38 QWriteLocker wlock(&m_ftLock);
39 QMap<int, FileTransfer*>::iterator i;
40 for (i = m_ftMap.begin(); i != m_ftMap.end(); ++i)
41 {
42 if ((*i)->GetSocket() == socket)
43 {
44 (*i)->DecrRef();
45 m_ftMap.remove(i.key());
46 return;
47 }
48 }
49 }
50
51 // iterate through file server list and close
52 // if socket matched connected server
53 {
54 QWriteLocker wlock(&m_fsLock);
55 QMap<QString, SocketHandler*>::iterator i;
56 for (i = m_fsMap.begin(); i != m_fsMap.end(); ++i)
57 {
58 if ((*i)->GetSocket() == socket)
59 {
60 (*i)->DecrRef();
61 m_fsMap.remove(i.key());
62 return;
63 }
64 }
65 }
66}
67
68QString FileServerHandler::LocalFilePath(const QString &path,
69 const QString &wantgroup)
70{
71 QString lpath = QString(path);
72
73 if (lpath.section('/', -2, -2) == "channels")
74 {
75 // This must be an icon request. Check channel.icon to be safe.
76 QString file = lpath.section('/', -1);
77 lpath = "";
78
80 query.prepare("SELECT icon FROM channel "
81 "WHERE icon LIKE :FILENAME ;");
82 query.bindValue(":FILENAME", QString("%/") + file);
83
84 if (query.exec() && query.next())
85 {
86 lpath = query.value(0).toString();
87 }
88 else
89 {
90 MythDB::DBError("Icon path", query);
91 }
92 }
93 else
94 {
95 lpath = lpath.section('/', -1);
96
97 QString fpath = lpath;
98 if (fpath.endsWith(".png"))
99 fpath = fpath.left(fpath.length() - 4);
100
101 ProgramInfo pginfo(fpath);
102 if (pginfo.GetChanID())
103 {
104 QString pburl = GetPlaybackURL(&pginfo);
105 if (pburl.startsWith("/"))
106 {
107 lpath = pburl.section('/', 0, -2) + "/" + lpath;
108 LOG(VB_FILE, LOG_INFO,
109 QString("Local file path: %1").arg(lpath));
110 }
111 else
112 {
113 LOG(VB_GENERAL, LOG_ERR,
114 QString("LocalFilePath unable to find local "
115 "path for '%1', found '%2' instead.")
116 .arg(lpath, pburl));
117 lpath = "";
118 }
119 }
120 else if (!lpath.isEmpty())
121 {
122 // For securities sake, make sure filename is really the pathless.
123 QString opath = lpath;
124 StorageGroup sgroup;
125
126 if (!wantgroup.isEmpty())
127 {
128 sgroup.Init(wantgroup);
129 lpath = QString(path);
130 }
131 else
132 {
133 lpath = QFileInfo(lpath).fileName();
134 }
135
136 QString tmpFile = sgroup.FindFile(lpath);
137 if (!tmpFile.isEmpty())
138 {
139 lpath = tmpFile;
140 LOG(VB_FILE, LOG_INFO,
141 QString("LocalFilePath(%1 '%2'), found through "
142 "exhaustive search at '%3'")
143 .arg(path, opath, lpath));
144 }
145 else
146 {
147 LOG(VB_GENERAL, LOG_ERR, QString("LocalFilePath unable to "
148 "find local path for '%1'.")
149 .arg(opath));
150 lpath = "";
151 }
152
153 }
154 else
155 {
156 lpath = "";
157 }
158 }
159
160 return lpath;
161}
162
164{
165 if (deletethread != nullptr)
166 {
167 if (deletethread->isRunning())
168 return;
169
170 delete deletethread;
171 deletethread = nullptr;
172 }
173
176}
177
179 QStringList &commands, QStringList &slist)
180{
181 if (commands[1] == "FileServer")
182 {
183 if (slist.size() >= 3)
184 {
185 auto *handler = new SocketHandler(socket, m_parent, commands[2]);
186
187 handler->BlockShutdown(true);
188 handler->AllowStandardEvents(true);
189 handler->AllowSystemEvents(true);
190
191 handler->WriteStringList(QStringList("OK"));
192
193 QWriteLocker wlock(&m_fsLock);
194 m_fsMap.insert(commands[2], handler);
195 m_parent->AddSocketHandler(handler);
196
197 handler->DecrRef();
198
199 return true;
200 }
201 return false;
202 }
203
204 if (commands[1] != "FileTransfer")
205 return false;
206
207 if (slist.size() < 3)
208 return false;
209
210 if ((commands.size() < 3) || (commands.size() > 6))
211 return false;
212
213 FileTransfer *ft = nullptr;
214 QString hostname = "";
215 QString filename = "";
216 bool writemode = false;
217 bool usereadahead = true;
218 std::chrono::milliseconds timeout = 2s;
219 switch (commands.size())
220 {
221 case 6:
222 timeout = std::chrono::milliseconds(commands[5].toInt());
223 [[fallthrough]];
224 case 5:
225 usereadahead = (commands[4].toInt() != 0);
226 [[fallthrough]];
227 case 4:
228 writemode = (commands[3].toInt() != 0);
229 [[fallthrough]];
230 default:
231 hostname = commands[2];
232 }
233
234 QStringList::const_iterator it = slist.cbegin();
235 QString path = *(++it);
236 QString wantgroup = *(++it);
237
238 QStringList checkfiles;
239 while (++it != slist.cend())
240 checkfiles += *(it);
241
242 slist.clear();
243
244 LOG(VB_GENERAL, LOG_DEBUG, "FileServerHandler::HandleAnnounce");
245 LOG(VB_GENERAL, LOG_INFO, QString("adding: %1 as remote file transfer")
246 .arg(hostname));
247
248 if (writemode)
249 {
250 if (wantgroup.isEmpty())
251 wantgroup = "Default";
252
253 StorageGroup sgroup(wantgroup, gCoreContext->GetHostName(), false);
254 QString dir = sgroup.FindNextDirMostFree();
255 if (dir.isEmpty())
256 {
257 LOG(VB_GENERAL, LOG_ERR, "Unable to determine directory "
258 "to write to in FileTransfer write command");
259
260 slist << "ERROR" << "filetransfer_directory_not_found";
261 socket->WriteStringList(slist);
262 return true;
263 }
264
265 if (path.isEmpty())
266 {
267 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write "
268 "filename is empty in path '%1'.")
269 .arg(path));
270
271 slist << "ERROR" << "filetransfer_filename_empty";
272 socket->WriteStringList(slist);
273 return true;
274 }
275
276 if ((path.contains("/../")) ||
277 (path.startsWith("../")))
278 {
279 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write "
280 "filename '%1' does not pass sanity checks.")
281 .arg(path));
282
283 slist << "ERROR" << "filetransfer_filename_dangerous";
284 socket->WriteStringList(slist);
285 return true;
286 }
287
288 filename = dir + "/" + path;
289 }
290 else
291 {
292 filename = LocalFilePath(path, wantgroup);
293 }
294
295 QFileInfo finfo(filename);
296 if (finfo.isDir())
297 {
298 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer filename "
299 "'%1' is actually a directory, cannot transfer.")
300 .arg(filename));
301
302 slist << "ERROR" << "filetransfer_filename_is_a_directory";
303 socket->WriteStringList(slist);
304 return true;
305 }
306
307 if (writemode)
308 {
309 QString dirPath = finfo.absolutePath();
310 QDir qdir(dirPath);
311 if (!qdir.exists())
312 {
313 if (!qdir.mkpath(dirPath))
314 {
315 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer "
316 "filename '%1' is in a subdirectory which does "
317 "not exist, but can not be created.")
318 .arg(filename));
319
320 slist << "ERROR" << "filetransfer_unable_to_create_subdirectory";
321 socket->WriteStringList(slist);
322 return true;
323 }
324 }
325
326 ft = new FileTransfer(filename, socket, m_parent, writemode);
327 }
328 else
329 {
330 ft = new FileTransfer(filename, socket, m_parent, usereadahead, timeout);
331 }
332
333 ft->BlockShutdown(true);
334
335 {
336 QWriteLocker wlock(&m_ftLock);
337 m_ftMap.insert(socket->GetSocketDescriptor(), ft);
338 }
339
340 slist << "OK"
341 << QString::number(socket->GetSocketDescriptor())
342 << QString::number(ft->GetFileSize());
343
344 if (!checkfiles.empty())
345 {
346 QFileInfo fi(filename);
347 QDir dir = fi.absoluteDir();
348 for (const auto & file : std::as_const(checkfiles))
349 {
350 if (dir.exists(file) &&
351 QFileInfo(dir, file).size() >= kReadTestSize)
352 slist << file;
353 }
354 }
355
356 socket->WriteStringList(slist);
358 ft->DecrRef(); ft = nullptr;
359
360 return true;
361}
362
364 QStringList &commands, QStringList &slist)
365{
366 if (commands[1] == "SlaveBackend")
367 {
368 // were not going to handle these, but we still want to track them
369 // for commands that need access to these sockets
370 if (slist.size() >= 3)
371 {
372 SocketHandler *handler = m_parent->GetConnectionBySocket(socket);
373 if (handler == nullptr)
374 return;
375
376 QWriteLocker wlock(&m_fsLock);
377 m_fsMap.insert(commands[2], handler);
378 }
379 }
380
381}
382
383bool FileServerHandler::HandleQuery(SocketHandler *socket, QStringList &commands,
384 QStringList &slist)
385{
386 bool handled = false;
387 QString command = commands[0];
388
389 if (command == "QUERY_FILETRANSFER")
390 handled = HandleQueryFileTransfer(socket, commands, slist);
391 else if (command == "QUERY_FREE_SPACE")
392 handled = HandleQueryFreeSpace(socket);
393 else if (command == "QUERY_FREE_SPACE_LIST")
394 handled = HandleQueryFreeSpaceList(socket);
395 else if (command == "QUERY_FREE_SPACE_SUMMARY")
396 handled = HandleQueryFreeSpaceSummary(socket);
397 else if (command == "QUERY_CHECKFILE")
398 handled = HandleQueryCheckFile(socket, slist);
399 else if (command == "QUERY_FILE_EXISTS")
400 handled = HandleQueryFileExists(socket, slist);
401 else if (command == "QUERY_FILE_HASH")
402 handled = HandleQueryFileHash(socket, slist);
403 else if (command == "DELETE_FILE")
404 handled = HandleDeleteFile(socket, slist);
405 else if (command == "QUERY_SG_GETFILELIST")
406 handled = HandleGetFileList(socket, slist);
407 else if (command == "QUERY_SG_FILEQUERY")
408 handled = HandleFileQuery(socket, slist);
409 else if (command == "DOWNLOAD_FILE" || command == "DOWNLOAD_FILE_NOW")
410 handled = HandleDownloadFile(socket, slist);
411 return handled;
412}
413
415{
417 return true;
418}
419
421{
422 QStringList hosts;
423
425 for (const auto & disk : std::as_const(disks))
426 if (!hosts.contains(disk.getHostname()))
427 hosts << disk.getHostname();
428
429 // TODO: get max bitrate from encoderlink
430 FileSystemInfoManager::Consolidate(disks, true, 14000, hosts.join(","));
431
433 return true;
434}
435
437{
439 // TODO: get max bitrate from encoderlink
440 FileSystemInfoManager::Consolidate(disks, true, 14000, "FreeSpaceSummary");
441
442 socket->WriteStringList({QString::number(disks.back().getTotalSpace()),
443 QString::number(disks.back().getUsedSpace())});
444 return true;
445}
446
448{
449 const QString localHostName = gCoreContext->GetHostName(); // cache this
450 QStringList groups(StorageGroup::kSpecialGroups);
451 groups.removeAll("LiveTV");
452 QString specialGroups = groups.join("', '");
453
455 query.prepare(QString("SELECT MIN(id),dirname "
456 "FROM storagegroup "
457 "WHERE hostname = :HOSTNAME "
458 "AND groupname NOT IN ( '%1' ) "
459 "GROUP BY dirname;").arg(specialGroups));
460 query.bindValue(":HOSTNAME", localHostName);
461
462 FileSystemInfoList fsInfos;
463 if (query.exec() && query.isActive())
464 {
465 // If we don't have any dirs of our own, fallback to list of Default
466 // dirs since that is what StorageGroup::Init() does.
467 if (!query.size())
468 {
469 query.prepare("SELECT MIN(id),dirname "
470 "FROM storagegroup "
471 "WHERE groupname = :GROUP "
472 "GROUP BY dirname;");
473 query.bindValue(":GROUP", "Default");
474 if (!query.exec())
475 MythDB::DBError("BackendQueryFileSystems", query);
476 }
477
478 QMap<QString, bool> foundDirs;
479
480 while (query.next())
481 {
482 /* The storagegroup.dirname column uses utf8_bin collation, so Qt
483 * uses QString::fromAscii() for toString(). Explicitly convert the
484 * value using QString::fromUtf8() to prevent corruption. */
485 QString currentDir {QString::fromUtf8(query.value(1).toByteArray().constData())};
486 if (currentDir.endsWith("/"))
487 currentDir.remove(currentDir.length() - 1, 1);
488
489 if (!foundDirs.contains(currentDir))
490 {
491 if (QDir(currentDir).exists())
492 {
493 fsInfos.push_back(FileSystemInfo(localHostName, currentDir, query.value(0).toInt()));
494
495 foundDirs[currentDir] = true;
496 }
497 else
498 {
499 foundDirs[currentDir] = false;
500 }
501 }
502 }
503 }
504
505 return fsInfos;
506}
507
509{
511
512 {
513 QReadLocker rlock(&m_fsLock);
514 for (const auto* fs : std::as_const(m_fsMap))
515 {
516 disks << FileSystemInfoManager::GetInfoList(fs->GetSocket());
517 }
518 }
519
520 return disks;
521}
522
529 QStringList &slist)
530{
531 QStringList::const_iterator it = slist.cbegin() + 2;
532 RecordingInfo recinfo(it, slist.cend());
533
534 bool exists = false;
535
536 QString pburl;
537 if (recinfo.HasPathname())
538 {
539 pburl = GetPlaybackURL(&recinfo);
540 exists = QFileInfo::exists(pburl);
541 if (!exists)
542 pburl.clear();
543 }
544
545 QStringList res(QString::number(static_cast<int>(exists)));
546 res << pburl;
547 socket->WriteStringList(res);
548 return true;
549}
550
551
557 QStringList &slist)
558{
559 QString storageGroup = "Default";
560 QStringList res;
561
562 if (slist.size() == 3)
563 {
564 if (!slist[2].isEmpty())
565 storageGroup = slist[2];
566 }
567 else if (slist.size() != 2)
568 {
569 return false;
570 }
571
572 const QString& filename = slist[1];
573 if ((filename.isEmpty()) ||
574 (filename.contains("/../")) ||
575 (filename.startsWith("../")))
576 {
577 LOG(VB_GENERAL, LOG_ERR,
578 QString("ERROR checking for file, filename '%1' "
579 "fails sanity checks").arg(filename));
580 res << "";
581 socket->WriteStringList(res);
582 return true;
583 }
584
585 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
586 QString fullname = sgroup.FindFile(filename);
587
588 if (!fullname.isEmpty())
589 {
590 res << "1"
591 << fullname;
592
593 // TODO: convert me to QFile
594 struct stat fileinfo {};
595 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
596 {
597 res << QString::number(fileinfo.st_dev)
598 << QString::number(fileinfo.st_ino)
599 << QString::number(fileinfo.st_mode)
600 << QString::number(fileinfo.st_nlink)
601 << QString::number(fileinfo.st_uid)
602 << QString::number(fileinfo.st_gid)
603 << QString::number(fileinfo.st_rdev)
604 << QString::number(fileinfo.st_size)
605#ifdef Q_OS_WINDOWS
606 << "0"
607 << "0"
608#else
609 << QString::number(fileinfo.st_blksize)
610 << QString::number(fileinfo.st_blocks)
611#endif
612 << QString::number(fileinfo.st_atime)
613 << QString::number(fileinfo.st_mtime)
614 << QString::number(fileinfo.st_ctime);
615 }
616 }
617 else
618 {
619 res << "0";
620 }
621
622 socket->WriteStringList(res);
623 return true;
624}
625
631 QStringList &slist)
632{
633 QString storageGroup = "Default";
634 QString hostname = gCoreContext->GetHostName();
635 QString filename = "";
636 QStringList res;
637
638 switch (slist.size()) {
639 case 4:
640 if (!slist[3].isEmpty())
641 hostname = slist[3];
642 [[fallthrough]];
643 case 3:
644 if (!slist[2].isEmpty())
645 storageGroup = slist[2];
646 [[fallthrough]];
647 case 2:
648 filename = slist[1];
649 if (filename.isEmpty() ||
650 filename.contains("/../") ||
651 filename.startsWith("../"))
652 {
653 LOG(VB_GENERAL, LOG_ERR,
654 QString("ERROR checking for file, filename '%1' "
655 "fails sanity checks").arg(filename));
656 res << "";
657 socket->WriteStringList(res);
658 return true;
659 }
660 break;
661 default:
662 return false;
663 }
664
665 QString hash = "";
666
668 {
669 // looking for file on me, return directly
670 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
671 QString fullname = sgroup.FindFile(filename);
672 hash = FileHash(fullname);
673 }
674 else
675 {
676 QReadLocker rlock(&m_fsLock);
677 if (m_fsMap.contains(hostname))
678 {
679 // looking for file on connected host, query from it
680 if (m_fsMap.value(hostname)->SendReceiveStringList(slist))
681 hash = slist[0];
682 }
683 // I deleted the incorrect SQL select that was supposed to get
684 // host name from ip address. Since it cannot work and has
685 // been there 6 years I assume it is not important.
686 }
687
688
689 res << hash;
690 socket->WriteStringList(res);
691
692 return true;
693}
694
696 QStringList &slist)
697{
698 if (slist.size() != 3)
699 return false;
700
701 return HandleDeleteFile(socket, slist[1], slist[2]);
702}
703
704bool FileServerHandler::DeleteFile(const QString& filename, const QString& storagegroup)
705{
706 return HandleDeleteFile(nullptr, filename, storagegroup);
707}
708
710 const QString& filename, const QString& storagegroup)
711{
712 StorageGroup sgroup(storagegroup, "", false);
713 QStringList res;
714
715 if ((filename.isEmpty()) ||
716 (filename.contains("/../")) ||
717 (filename.startsWith("../")))
718 {
719 LOG(VB_GENERAL, LOG_ERR,
720 QString("ERROR deleting file, filename '%1' fails sanity checks")
721 .arg(filename));
722 if (socket)
723 {
724 res << "0";
725 socket->WriteStringList(res);
726 return true;
727 }
728 return false;
729 }
730
731 QString fullfile = sgroup.FindFile(filename);
732
733 if (fullfile.isEmpty())
734 {
735 LOG(VB_GENERAL, LOG_ERR,
736 QString("Unable to find %1 in HandleDeleteFile()") .arg(filename));
737 if (socket)
738 {
739 res << "0";
740 socket->WriteStringList(res);
741 return true;
742 }
743 return false;
744 }
745
746 QFile checkFile(fullfile);
747 if (checkFile.exists())
748 {
749 if (socket)
750 {
751 res << "1";
752 socket->WriteStringList(res);
753 }
755 deletethread->AddFile(fullfile);
756 }
757 else
758 {
759 LOG(VB_GENERAL, LOG_ERR, QString("Error deleting file: '%1'")
760 .arg(fullfile));
761 if (socket)
762 {
763 res << "0";
764 socket->WriteStringList(res);
765 }
766 }
767
768 return true;
769}
770
772{
774 return deletethread->AddFile(handler);
775}
776
778 QStringList &slist)
779{
780 QStringList res;
781
782 bool fileNamesOnly = false;
783 if (slist.size() == 5)
784 fileNamesOnly = (slist[4].toInt() != 0);
785 else if (slist.size() != 4)
786 {
787 LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
788 .arg(slist.join("[]:[]")));
789 res << "EMPTY LIST";
790 socket->WriteStringList(res);
791 return true;
792 }
793
794 QString host = gCoreContext->GetHostName();
795 QString wantHost = slist[1];
796 QString groupname = slist[2];
797 QString path = slist[3];
798
799 LOG(VB_FILE, LOG_INFO,
800 QString("HandleSGGetFileList: group = %1 host = %2 "
801 "path = %3 wanthost = %4")
802 .arg(groupname, host, path, wantHost));
803
804 if (gCoreContext->IsThisHost(wantHost))
805 {
806 StorageGroup sg(groupname, host);
807 LOG(VB_FILE, LOG_INFO, "Getting local info");
808 if (fileNamesOnly)
809 res = sg.GetFileList(path);
810 else
811 res = sg.GetFileInfoList(path);
812
813 if (res.count() == 0)
814 res << "EMPTY LIST";
815 }
816 else
817 {
818 // handle request on remote server
819 SocketHandler *remsock = nullptr;
820 {
821 QReadLocker rlock(&m_fsLock);
822 if (m_fsMap.contains(wantHost))
823 {
824 remsock = m_fsMap.value(wantHost);
825 remsock->IncrRef();
826 }
827 }
828
829 if (remsock)
830 {
831 LOG(VB_FILE, LOG_INFO, "Getting remote info");
832 res << "QUERY_SG_GETFILELIST" << wantHost << groupname << path
833 << QString::number(static_cast<int>(fileNamesOnly));
834 remsock->SendReceiveStringList(res);
835 remsock->DecrRef();
836 }
837 else
838 {
839 LOG(VB_FILE, LOG_ERR, QString("Failed to grab slave socket : %1 :")
840 .arg(wantHost));
841 res << "SLAVE UNREACHABLE: " << wantHost;
842 }
843 }
844
845 socket->WriteStringList(res);
846 return true;
847}
848
850 QStringList &slist)
851{
852 QStringList res;
853
854 if (slist.size() != 4)
855 {
856 LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
857 .arg(slist.join("[]:[]")));
858 res << "EMPTY LIST";
859 socket->WriteStringList(res);
860 return true;
861 }
862
863 QString wantHost = slist[1];
864 QString groupname = slist[2];
865 QString filename = slist[3];
866
867 LOG(VB_FILE, LOG_DEBUG, QString("HandleSGFileQuery: myth://%1@%2/%3")
868 .arg(groupname, wantHost, filename));
869
870 if (gCoreContext->IsThisHost(wantHost))
871 {
872 // handle request locally
873 LOG(VB_FILE, LOG_DEBUG, QString("Getting local info"));
874 StorageGroup sg(groupname, gCoreContext->GetHostName());
875 res = sg.GetFileInfo(filename);
876
877 if (res.count() == 0)
878 res << "EMPTY LIST";
879 }
880 else
881 {
882 // handle request on remote server
883 SocketHandler *remsock = nullptr;
884 {
885 QReadLocker rlock(&m_fsLock);
886 if (m_fsMap.contains(wantHost))
887 {
888 remsock = m_fsMap.value(wantHost);
889 remsock->IncrRef();
890 }
891 }
892
893 if (remsock)
894 {
895 res << "QUERY_SG_FILEQUERY" << wantHost << groupname << filename;
896 remsock->SendReceiveStringList(res);
897 remsock->DecrRef();
898 }
899 else
900 {
901 res << "SLAVE UNREACHABLE: " << wantHost;
902 }
903 }
904
905 socket->WriteStringList(res);
906 return true;
907}
908
910 QStringList &commands, QStringList &slist)
911{
912 if (commands.size() != 2)
913 return false;
914
915 if (slist.size() < 2)
916 return false;
917
918 QStringList res;
919 int recnum = commands[1].toInt();
920 FileTransfer *ft = nullptr;
921
922 {
923 QReadLocker rlock(&m_ftLock);
924 if (!m_ftMap.contains(recnum))
925 {
926 if (slist[1] == "DONE")
927 res << "OK";
928 else
929 {
930 LOG(VB_GENERAL, LOG_ERR,
931 QString("Unknown file transfer socket: %1").arg(recnum));
932 res << "ERROR"
933 << "unknown_file_transfer_socket";
934 }
935
936 socket->WriteStringList(res);
937 return true;
938 }
939
940 ft = m_ftMap.value(recnum);
941 ft->IncrRef();
942 }
943
944 if (slist[1] == "REQUEST_BLOCK")
945 {
946 if (slist.size() != 3)
947 {
948 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
949 "REQUEST_BLOCK call");
950 res << "ERROR" << "invalid_call";
951 }
952 else
953 {
954 int size = slist[2].toInt();
955 res << QString::number(ft->RequestBlock(size));
956 }
957 }
958 else if (slist[1] == "WRITE_BLOCK")
959 {
960 if (slist.size() != 3)
961 {
962 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
963 "WRITE_BLOCK call");
964 res << "ERROR" << "invalid_call";
965 }
966 else
967 {
968 int size = slist[2].toInt();
969 res << QString::number(ft->WriteBlock(size));
970 }
971 }
972 else if (slist[1] == "SEEK")
973 {
974 if (slist.size() != 5)
975 {
976 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER SEEK call");
977 res << "ERROR" << "invalid_call";
978 }
979 else
980 {
981 long long pos = slist[2].toLongLong();
982 int whence = slist[3].toInt();
983 long long curpos = slist[4].toLongLong();
984
985 res << QString::number(ft->Seek(curpos, pos, whence));
986 }
987 }
988 else if (slist[1] == "IS_OPEN")
989 {
990 res << QString::number(static_cast<int>(ft->isOpen()));
991 }
992 else if (slist[1] == "DONE")
993 {
994 ft->Stop();
995 res << "OK";
996 }
997 else if (slist[1] == "SET_TIMEOUT")
998 {
999 if (slist.size() != 3)
1000 {
1001 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
1002 "SET_TIMEOUT call");
1003 res << "ERROR" << "invalid_call";
1004 }
1005 else
1006 {
1007 bool fast = slist[2].toInt() != 0;
1008 ft->SetTimeout(fast);
1009 res << "OK";
1010 }
1011 }
1012 else if (slist[1] == "REQUEST_SIZE")
1013 {
1014 // return size and if the file is not opened for writing
1015 res << QString::number(ft->GetFileSize());
1016 res << QString::number(static_cast<int>(!gCoreContext->IsRegisteredFileForWrite(ft->GetFileName())));
1017 }
1018 else
1019 {
1020 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER call");
1021 res << "ERROR" << "invalid_call";
1022 }
1023
1024 ft->DecrRef();
1025 socket->WriteStringList(res);
1026 return true;
1027}
1028
1030 QStringList &slist)
1031{
1032 QStringList res;
1033
1034 if (slist.size() != 4)
1035 {
1036 res << "ERROR" << QString("Bad %1 command").arg(slist[0]);
1037 socket->WriteStringList(res);
1038 return true;
1039 }
1040
1041 bool synchronous = (slist[0] == "DOWNLOAD_FILE_NOW");
1042 QString srcURL = slist[1];
1043 QString storageGroup = slist[2];
1044 QString filename = slist[3];
1045 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName(), false);
1046 QString outDir = sgroup.FindNextDirMostFree();
1047 QString outFile;
1048
1049 if (filename.isEmpty())
1050 {
1051 QFileInfo finfo(srcURL);
1052 filename = finfo.fileName();
1053 }
1054
1055 if (outDir.isEmpty())
1056 {
1057 LOG(VB_GENERAL, LOG_ERR, QString("Unable to determine directory "
1058 "to write to in %1 write command").arg(slist[0]));
1059 res << "ERROR" << "downloadfile_directory_not_found";
1060 socket->WriteStringList(res);
1061 return true;
1062 }
1063
1064 if ((filename.contains("/../")) ||
1065 (filename.startsWith("../")))
1066 {
1067 LOG(VB_GENERAL, LOG_ERR, QString("ERROR: %1 write "
1068 "filename '%2' does not pass sanity checks.")
1069 .arg(slist[0], filename));
1070 res << "ERROR" << "downloadfile_filename_dangerous";
1071 socket->WriteStringList(res);
1072 return true;
1073 }
1074
1075 outFile = outDir + "/" + filename;
1076
1077 if (synchronous)
1078 {
1079 if (GetMythDownloadManager()->download(srcURL, outFile))
1080 {
1081 res << "OK"
1082 << gCoreContext->GetMasterHostPrefix(storageGroup)
1083 + filename;
1084 }
1085 else
1086 {
1087 res << "ERROR";
1088 }
1089 }
1090 else
1091 {
1092 QMutexLocker locker(&m_downloadURLsLock);
1093 m_downloadURLs[outFile] =
1094 gCoreContext->GetMasterHostPrefix(storageGroup) +
1096
1097 GetMythDownloadManager()->queueDownload(srcURL, outFile, this);
1098 res << "OK"
1099 << gCoreContext->GetMasterHostPrefix(storageGroup) + filename;
1100 }
1101
1102 socket->WriteStringList(res);
1103 return true;
1104}
1105
void start(void)
Definition: mainserver.h:85
bool AddFile(const QString &path)
static bool HandleQueryCheckFile(SocketHandler *socket, QStringList &slist)
bool HandleFileQuery(SocketHandler *socket, QStringList &slist)
bool HandleQueryFileTransfer(SocketHandler *socket, QStringList &commands, QStringList &slist)
bool HandleQuery(SocketHandler *socket, QStringList &commands, QStringList &slist) override
static void RunDeleteThread(void)
FileSystemInfoList QueryAllFileSystems(void)
QMap< QString, QString > m_downloadURLs
static FileSystemInfoList QueryFileSystems(void)
bool HandleDownloadFile(SocketHandler *socket, QStringList &slist)
bool HandleQueryFreeSpaceSummary(SocketHandler *socket)
bool HandleQueryFileHash(SocketHandler *socket, QStringList &slist)
static bool HandleQueryFileExists(SocketHandler *socket, QStringList &slist)
bool HandleAnnounce(MythSocket *socket, QStringList &commands, QStringList &slist) override
QReadWriteLock m_ftLock
QReadWriteLock m_fsLock
bool HandleGetFileList(SocketHandler *socket, QStringList &slist)
bool HandleQueryFreeSpaceList(SocketHandler *socket)
static bool HandleQueryFreeSpace(SocketHandler *socket)
void connectionClosed(MythSocket *socket) override
static bool DeleteFile(const QString &filename, const QString &storagegroup)
QMap< int, FileTransfer * > m_ftMap
void connectionAnnounced(MythSocket *socket, QStringList &commands, QStringList &slist) override
QMap< QString, SocketHandler * > m_fsMap
static QString LocalFilePath(const QString &path, const QString &wantgroup)
static bool HandleDeleteFile(SocketHandler *socket, QStringList &slist)
int WriteBlock(int size)
void Stop(void)
long long Seek(long long curpos, long long pos, int whence)
QString GetFileName(void)
bool isOpen(void)
uint64_t GetFileSize(void)
void SetTimeout(bool fast)
int RequestBlock(int size)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:128
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:837
QVariant value(int i) const
Definition: mythdbcon.h:204
int size(void) const
Definition: mythdbcon.h:214
bool isActive(void) const
Definition: mythdbcon.h:215
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:618
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:888
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:812
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:550
bool isRunning(void) const
Definition: mthread.cpp:263
QString GetHostName(void)
bool IsThisHost(const QString &addr)
is this address mapped to this host
QString GetMasterHostPrefix(const QString &storageGroup=QString(), const QString &path=QString())
bool IsRegisteredFileForWrite(const QString &file)
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
void queueDownload(const QString &url, const QString &dest, QObject *caller, bool reload=false)
Adds a url to the download queue.
SocketHandler * GetConnectionBySocket(MythSocket *socket)
void AddSocketHandler(SocketHandler *socket)
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:26
int GetSocketDescriptor(void) const
Definition: mythsocket.cpp:579
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:305
Holds information on recordings and videos.
Definition: programinfo.h:74
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:379
bool HasPathname(void) const
Definition: programinfo.h:365
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:36
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
void BlockShutdown(bool block)
Definition: sockethandler.h:35
bool WriteStringList(const QStringList &strlist)
bool SendReceiveStringList(QStringList &strlist, uint min_reply_length=0)
MythSocketManager * m_parent
void Init(const QString &group="Default", const QString &hostname="", bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
QStringList GetFileInfo(const QString &filename)
QString FindFile(const QString &filename)
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
QStringList GetFileInfoList(const QString &Path)
QString FindNextDirMostFree(void)
QStringList GetFileList(const QString &Path, bool recursive=false)
static QString GetRelativePathname(const QString &filename)
Returns the relative pathname of a file by comparing the filename against all Storage Group directori...
DeleteThread * deletethread
QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
QVector< FileSystemInfo > FileSystemInfoList
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
static constexpr qint64 kReadTestSize
QString FileHash(const QString &filename)
MBASE_PUBLIC FileSystemInfoList GetInfoList(MythSocket *sock=nullptr)
MBASE_PUBLIC QStringList ToStringList(const FileSystemInfoList &fsInfos)
MBASE_PUBLIC void Consolidate(FileSystemInfoList &disks, bool merge, int64_t fuzz, const QString &total_name={})
string hostname
Definition: caa.py:17
bool exists(str path)
Definition: xbmcvfs.py:51