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,5,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 {
785 fileNamesOnly = (slist[4].toInt() != 0);
786 }
787 else if (slist.size() != 4)
788 {
789 LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
790 .arg(slist.join("[]:[]")));
791 res << "EMPTY LIST";
792 socket->WriteStringList(res);
793 return true;
794 }
795
796 QString host = gCoreContext->GetHostName();
797 QString wantHost = slist[1];
798 QString groupname = slist[2];
799 QString path = slist[3];
800
801 LOG(VB_FILE, LOG_INFO,
802 QString("HandleSGGetFileList: group = %1 host = %2 "
803 "path = %3 wanthost = %4")
804 .arg(groupname, host, path, wantHost));
805
806 if (gCoreContext->IsThisHost(wantHost))
807 {
808 StorageGroup sg(groupname, host);
809 LOG(VB_FILE, LOG_INFO, "Getting local info");
810 if (fileNamesOnly)
811 res = sg.GetFileList(path);
812 else
813 res = sg.GetFileInfoList(path);
814
815 if (res.count() == 0)
816 res << "EMPTY LIST";
817 }
818 else
819 {
820 // handle request on remote server
821 SocketHandler *remsock = nullptr;
822 {
823 QReadLocker rlock(&m_fsLock);
824 if (m_fsMap.contains(wantHost))
825 {
826 remsock = m_fsMap.value(wantHost);
827 remsock->IncrRef();
828 }
829 }
830
831 if (remsock)
832 {
833 LOG(VB_FILE, LOG_INFO, "Getting remote info");
834 res << "QUERY_SG_GETFILELIST" << wantHost << groupname << path
835 << QString::number(static_cast<int>(fileNamesOnly));
836 remsock->SendReceiveStringList(res);
837 remsock->DecrRef();
838 }
839 else
840 {
841 LOG(VB_FILE, LOG_ERR, QString("Failed to grab slave socket : %1 :")
842 .arg(wantHost));
843 res << "SLAVE UNREACHABLE: " << wantHost;
844 }
845 }
846
847 socket->WriteStringList(res);
848 return true;
849}
850
852 QStringList &slist)
853{
854 QStringList res;
855
856 if (slist.size() != 4)
857 {
858 LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
859 .arg(slist.join("[]:[]")));
860 res << "EMPTY LIST";
861 socket->WriteStringList(res);
862 return true;
863 }
864
865 QString wantHost = slist[1];
866 QString groupname = slist[2];
867 QString filename = slist[3];
868
869 LOG(VB_FILE, LOG_DEBUG, QString("HandleSGFileQuery: myth://%1@%2/%3")
870 .arg(groupname, wantHost, filename));
871
872 if (gCoreContext->IsThisHost(wantHost))
873 {
874 // handle request locally
875 LOG(VB_FILE, LOG_DEBUG, QString("Getting local info"));
876 StorageGroup sg(groupname, gCoreContext->GetHostName());
877 res = sg.GetFileInfo(filename);
878
879 if (res.count() == 0)
880 res << "EMPTY LIST";
881 }
882 else
883 {
884 // handle request on remote server
885 SocketHandler *remsock = nullptr;
886 {
887 QReadLocker rlock(&m_fsLock);
888 if (m_fsMap.contains(wantHost))
889 {
890 remsock = m_fsMap.value(wantHost);
891 remsock->IncrRef();
892 }
893 }
894
895 if (remsock)
896 {
897 res << "QUERY_SG_FILEQUERY" << wantHost << groupname << filename;
898 remsock->SendReceiveStringList(res);
899 remsock->DecrRef();
900 }
901 else
902 {
903 res << "SLAVE UNREACHABLE: " << wantHost;
904 }
905 }
906
907 socket->WriteStringList(res);
908 return true;
909}
910
912 QStringList &commands, QStringList &slist)
913{
914 if (commands.size() != 2)
915 return false;
916
917 if (slist.size() < 2)
918 return false;
919
920 QStringList res;
921 int recnum = commands[1].toInt();
922 FileTransfer *ft = nullptr;
923
924 {
925 QReadLocker rlock(&m_ftLock);
926 if (!m_ftMap.contains(recnum))
927 {
928 if (slist[1] == "DONE")
929 {
930 res << "OK";
931 }
932 else
933 {
934 LOG(VB_GENERAL, LOG_ERR,
935 QString("Unknown file transfer socket: %1").arg(recnum));
936 res << "ERROR"
937 << "unknown_file_transfer_socket";
938 }
939
940 socket->WriteStringList(res);
941 return true;
942 }
943
944 ft = m_ftMap.value(recnum);
945 ft->IncrRef();
946 }
947
948 if (slist[1] == "REQUEST_BLOCK")
949 {
950 if (slist.size() != 3)
951 {
952 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
953 "REQUEST_BLOCK call");
954 res << "ERROR" << "invalid_call";
955 }
956 else
957 {
958 int size = slist[2].toInt();
959 res << QString::number(ft->RequestBlock(size));
960 }
961 }
962 else if (slist[1] == "WRITE_BLOCK")
963 {
964 if (slist.size() != 3)
965 {
966 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
967 "WRITE_BLOCK call");
968 res << "ERROR" << "invalid_call";
969 }
970 else
971 {
972 int size = slist[2].toInt();
973 res << QString::number(ft->WriteBlock(size));
974 }
975 }
976 else if (slist[1] == "SEEK")
977 {
978 if (slist.size() != 5)
979 {
980 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER SEEK call");
981 res << "ERROR" << "invalid_call";
982 }
983 else
984 {
985 long long pos = slist[2].toLongLong();
986 int whence = slist[3].toInt();
987 long long curpos = slist[4].toLongLong();
988
989 res << QString::number(ft->Seek(curpos, pos, whence));
990 }
991 }
992 else if (slist[1] == "IS_OPEN")
993 {
994 res << QString::number(static_cast<int>(ft->isOpen()));
995 }
996 else if (slist[1] == "DONE")
997 {
998 ft->Stop();
999 res << "OK";
1000 }
1001 else if (slist[1] == "SET_TIMEOUT")
1002 {
1003 if (slist.size() != 3)
1004 {
1005 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
1006 "SET_TIMEOUT call");
1007 res << "ERROR" << "invalid_call";
1008 }
1009 else
1010 {
1011 bool fast = slist[2].toInt() != 0;
1012 ft->SetTimeout(fast);
1013 res << "OK";
1014 }
1015 }
1016 else if (slist[1] == "REQUEST_SIZE")
1017 {
1018 // return size and if the file is not opened for writing
1019 res << QString::number(ft->GetFileSize());
1020 res << QString::number(static_cast<int>(!gCoreContext->IsRegisteredFileForWrite(ft->GetFileName())));
1021 }
1022 else
1023 {
1024 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER call");
1025 res << "ERROR" << "invalid_call";
1026 }
1027
1028 ft->DecrRef();
1029 socket->WriteStringList(res);
1030 return true;
1031}
1032
1034 QStringList &slist)
1035{
1036 QStringList res;
1037
1038 if (slist.size() != 4)
1039 {
1040 res << "ERROR" << QString("Bad %1 command").arg(slist[0]);
1041 socket->WriteStringList(res);
1042 return true;
1043 }
1044
1045 bool synchronous = (slist[0] == "DOWNLOAD_FILE_NOW");
1046 QString srcURL = slist[1];
1047 QString storageGroup = slist[2];
1048 QString filename = slist[3];
1049 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName(), false);
1050 QString outDir = sgroup.FindNextDirMostFree();
1051 QString outFile;
1052
1053 if (filename.isEmpty())
1054 {
1055 QFileInfo finfo(srcURL);
1056 filename = finfo.fileName();
1057 }
1058
1059 if (outDir.isEmpty())
1060 {
1061 LOG(VB_GENERAL, LOG_ERR, QString("Unable to determine directory "
1062 "to write to in %1 write command").arg(slist[0]));
1063 res << "ERROR" << "downloadfile_directory_not_found";
1064 socket->WriteStringList(res);
1065 return true;
1066 }
1067
1068 if ((filename.contains("/../")) ||
1069 (filename.startsWith("../")))
1070 {
1071 LOG(VB_GENERAL, LOG_ERR, QString("ERROR: %1 write "
1072 "filename '%2' does not pass sanity checks.")
1073 .arg(slist[0], filename));
1074 res << "ERROR" << "downloadfile_filename_dangerous";
1075 socket->WriteStringList(res);
1076 return true;
1077 }
1078
1079 outFile = outDir + "/" + filename;
1080
1081 if (synchronous)
1082 {
1083 if (GetMythDownloadManager()->download(srcURL, outFile))
1084 {
1085 res << "OK"
1086 << gCoreContext->GetMasterHostPrefix(storageGroup)
1087 + filename;
1088 }
1089 else
1090 {
1091 res << "ERROR";
1092 }
1093 }
1094 else
1095 {
1096 QMutexLocker locker(&m_downloadURLsLock);
1097 m_downloadURLs[outFile] =
1098 gCoreContext->GetMasterHostPrefix(storageGroup) +
1100
1101 GetMythDownloadManager()->queueDownload(srcURL, outFile, this);
1102 res << "OK"
1103 << gCoreContext->GetMasterHostPrefix(storageGroup) + filename;
1104 }
1105
1106 socket->WriteStringList(res);
1107 return true;
1108}
1109
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:838
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:619
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
bool isRunning(void) const
Definition: mthread.cpp:247
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:225
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:380
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