7 #include <QWriteLocker> 12 #include "ringbuffer.h" 34 QMap<int, FileTransfer*>::iterator i;
37 if ((*i)->GetSocket() == socket)
50 QMap<QString, SocketHandler*>::iterator i;
53 if ((*i)->GetSocket() == socket)
64 const QString &wantgroup)
66 QString lpath = QString(path);
68 if (lpath.section(
'/', -2, -2) ==
"channels")
73 QString file = lpath.section(
'/', -1);
77 query.
prepare(
"SELECT icon FROM channel WHERE icon LIKE :FILENAME ;");
78 query.
bindValue(
":FILENAME", QString(
"%/") + file);
82 lpath = query.
value(0).toString();
91 lpath = lpath.section(
'/', -1);
93 QString fpath = lpath;
94 if (fpath.endsWith(
".png"))
95 fpath = fpath.left(fpath.length() - 4);
101 if (pburl.startsWith(
"/"))
103 lpath = pburl.section(
'/', 0, -2) +
"/" + lpath;
104 LOG(VB_FILE, LOG_INFO,
105 QString(
"Local file path: %1").arg(lpath));
109 LOG(VB_GENERAL, LOG_ERR,
110 QString(
"LocalFilePath unable to find local " 111 "path for '%1', found '%2' instead.")
112 .arg(lpath).arg(pburl));
116 else if (!lpath.isEmpty())
119 QString opath = lpath;
122 if (!wantgroup.isEmpty())
124 sgroup.
Init(wantgroup);
125 lpath = QString(path);
129 lpath = QFileInfo(lpath).fileName();
132 QString tmpFile = sgroup.
FindFile(lpath);
133 if (!tmpFile.isEmpty())
136 LOG(VB_FILE, LOG_INFO,
137 QString(
"LocalFilePath(%1 '%2'), found through " 138 "exhaustive search at '%3'")
139 .arg(path).arg(opath).arg(lpath));
143 LOG(VB_GENERAL, LOG_ERR, QString(
"LocalFilePath unable to " 144 "find local path for '%1'.")
175 QStringList &commands, QStringList &slist)
177 if (commands[1] ==
"FileServer")
179 if (slist.size() >= 3)
191 m_fsMap.insert(commands[2], handler);
201 if (commands[1] !=
"FileTransfer")
204 if (slist.size() < 3)
207 if ((commands.size() < 3) || (commands.size() > 6))
212 QString filename =
"";
213 bool writemode =
false;
214 bool usereadahead =
true;
215 int timeout_ms = 2000;
216 switch (commands.size())
219 timeout_ms = commands[5].toInt();
220 [[clang::fallthrough]];
222 usereadahead = commands[4].toInt();
223 [[clang::fallthrough]];
225 writemode = commands[3].toInt();
226 [[clang::fallthrough]];
231 QStringList::const_iterator it = slist.begin();
232 QString path = *(++it);
233 QString wantgroup = *(++it);
235 QStringList checkfiles;
236 while (++it != slist.end())
241 LOG(VB_GENERAL, LOG_DEBUG,
"FileServerHandler::HandleAnnounce");
242 LOG(VB_GENERAL, LOG_INFO, QString(
"adding: %1 as remote file transfer")
247 if (wantgroup.isEmpty())
248 wantgroup =
"Default";
254 LOG(VB_GENERAL, LOG_ERR,
"Unable to determine directory " 255 "to write to in FileTransfer write command");
257 slist <<
"ERROR" <<
"filetransfer_directory_not_found";
264 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer write " 265 "filename is empty in path '%1'.")
268 slist <<
"ERROR" <<
"filetransfer_filename_empty";
273 if ((path.contains(
"/../")) ||
274 (path.startsWith(
"../")))
276 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer write " 277 "filename '%1' does not pass sanity checks.")
280 slist <<
"ERROR" <<
"filetransfer_filename_dangerous";
285 filename = dir +
"/" + path;
290 QFileInfo finfo(filename);
293 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer filename " 294 "'%1' is actually a directory, cannot transfer.")
297 slist <<
"ERROR" <<
"filetransfer_filename_is_a_directory";
304 QString dirPath = finfo.absolutePath();
308 if (!qdir.mkpath(dirPath))
310 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer " 311 "filename '%1' is in a subdirectory which does " 312 "not exist, but can not be created.")
315 slist <<
"ERROR" <<
"filetransfer_unable_to_create_subdirectory";
337 if (checkfiles.size())
339 QFileInfo fi(filename);
340 QDir dir = fi.absoluteDir();
341 for (it = checkfiles.begin(); it != checkfiles.end(); ++it)
343 if (dir.exists(*it) &&
357 QStringList &commands, QStringList &slist)
359 if (commands[1] ==
"SlaveBackend")
363 if (slist.size() >= 3)
366 if (handler ==
nullptr)
370 m_fsMap.insert(commands[2], handler);
379 bool handled =
false;
380 QString command = commands[0];
382 if (command ==
"QUERY_FILETRANSFER")
384 else if (command ==
"QUERY_FREE_SPACE")
386 else if (command ==
"QUERY_FREE_SPACE_LIST")
388 else if (command ==
"QUERY_FREE_SPACE_SUMMARY")
390 else if (command ==
"QUERY_CHECKFILE")
392 else if (command ==
"QUERY_FILE_EXISTS")
394 else if (command ==
"QUERY_FILE_HASH")
396 else if (command ==
"DELETE_FILE")
398 else if (command ==
"QUERY_SG_GETFILELIST")
400 else if (command ==
"QUERY_SG_FILEQUERY")
402 else if (command ==
"DOWNLOAD_FILE" || command ==
"DOWNLOAD_FILE_NOW")
412 QList<FileSystemInfo>::const_iterator i;
413 for (i = disks.begin(); i != disks.end(); ++i)
414 i->ToStringList(res);
426 QList<FileSystemInfo>::const_iterator i;
427 for (i = disks.begin(); i != disks.end(); ++i)
428 if (!hosts.contains(i->getHostname()))
429 hosts << i->getHostname();
436 for (i = disks.begin(); i != disks.end(); ++i)
438 i->ToStringList(res);
439 total += i->getTotalSpace();
440 used += i->getUsedSpace();
443 res << hosts.join(
",")
449 << QString::number(total)
450 << QString::number(used);
463 QList<FileSystemInfo>::const_iterator i;
466 for (i = disks.begin(); i != disks.end(); ++i)
468 total += i->getTotalSpace();
469 used += i->getUsedSpace();
472 res << QString::number(total) << QString::number(used);
480 groups.removeAll(
"LiveTV");
481 QString specialGroups = groups.join(
"', '");
484 query.
prepare(QString(
"SELECT MIN(id),dirname " 486 "WHERE hostname = :HOSTNAME " 487 "AND groupname NOT IN ( '%1' ) " 488 "GROUP BY dirname;").arg(specialGroups));
491 QList<FileSystemInfo> disks;
496 query.
prepare(
"SELECT MIN(id),dirname " 498 "WHERE groupname = :GROUP " 499 "GROUP BY dirname;");
508 QMap <QString, bool>foundDirs;
521 currentDir = QString::fromUtf8(query.
value(1)
522 .toByteArray().constData());
525 if (currentDir.endsWith(
"/"))
526 currentDir.remove(currentDir.length() - 1, 1);
528 checkDir.setPath(currentDir);
529 if (!foundDirs.contains(currentDir))
531 if (checkDir.exists())
537 foundDirs[currentDir] =
true;
540 foundDirs[currentDir] =
false;
555 QMap<QString, SocketHandler*>::iterator i;
571 QStringList::const_iterator it = slist.begin() + 2;
577 if (recinfo.HasPathname())
580 exists = QFileInfo(pburl).exists();
585 QStringList res(QString::number(exists));
599 QString storageGroup =
"Default";
602 if (slist.size() == 3)
604 if (!slist[2].isEmpty())
605 storageGroup = slist[2];
607 else if (slist.size() != 2)
610 QString filename = slist[1];
611 if ((filename.isEmpty()) ||
612 (filename.contains(
"/../")) ||
613 (filename.startsWith(
"../")))
615 LOG(VB_GENERAL, LOG_ERR,
616 QString(
"ERROR checking for file, filename '%1' " 617 "fails sanity checks").arg(filename));
624 QString fullname = sgroup.
FindFile(filename);
626 if (!fullname.isEmpty())
632 struct stat fileinfo;
633 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
635 res << QString::number(fileinfo.st_dev)
636 << QString::number(fileinfo.st_ino)
637 << QString::number(fileinfo.st_mode)
638 << QString::number(fileinfo.st_nlink)
639 << QString::number(fileinfo.st_uid)
640 << QString::number(fileinfo.st_gid)
641 << QString::number(fileinfo.st_rdev)
642 << QString::number(fileinfo.st_size)
647 << QString::number(fileinfo.st_blksize)
648 << QString::number(fileinfo.st_blocks)
650 << QString::number(fileinfo.st_atime)
651 << QString::number(fileinfo.st_mtime)
652 << QString::number(fileinfo.st_ctime);
669 QString storageGroup =
"Default";
671 QString filename =
"";
674 switch (slist.size()) {
676 if (!slist[3].isEmpty())
678 [[clang::fallthrough]];
680 if (!slist[2].isEmpty())
681 storageGroup = slist[2];
682 [[clang::fallthrough]];
685 if (filename.isEmpty() ||
686 filename.contains(
"/../") ||
687 filename.startsWith(
"../"))
689 LOG(VB_GENERAL, LOG_ERR,
690 QString(
"ERROR checking for file, filename '%1' " 691 "fails sanity checks").arg(filename));
707 QString fullname = sgroup.
FindFile(filename);
734 if (slist.size() != 3)
746 QString filename, QString storagegroup)
751 if ((filename.isEmpty()) ||
752 (filename.contains(
"/../")) ||
753 (filename.startsWith(
"../")))
755 LOG(VB_GENERAL, LOG_ERR,
756 QString(
"ERROR deleting file, filename '%1' fails sanity checks")
767 QString fullfile = sgroup.
FindFile(filename);
769 if (fullfile.isEmpty())
771 LOG(VB_GENERAL, LOG_ERR,
772 QString(
"Unable to find %1 in HandleDeleteFile()") .arg(filename));
782 QFile checkFile(fullfile);
783 if (checkFile.exists())
795 LOG(VB_GENERAL, LOG_ERR, QString(
"Error deleting file: '%1'")
818 bool fileNamesOnly =
false;
819 if (slist.size() == 5)
820 fileNamesOnly = slist[4].toInt();
821 else if (slist.size() != 4)
823 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid Request. %1")
824 .arg(slist.join(
"[]:[]")));
831 QString wantHost = slist[1];
832 QString groupname = slist[2];
833 QString path = slist[3];
835 LOG(VB_FILE, LOG_INFO,
836 QString(
"HandleSGGetFileList: group = %1 host = %2 " 837 "path = %3 wanthost = %4")
838 .arg(groupname).arg(host).arg(path).arg(wantHost));
843 LOG(VB_FILE, LOG_INFO,
"Getting local info");
849 if (res.count() == 0)
858 if (
m_fsMap.contains(wantHost))
867 LOG(VB_FILE, LOG_INFO,
"Getting remote info");
868 res <<
"QUERY_SG_GETFILELIST" << wantHost << groupname << path
869 << QString::number(fileNamesOnly);
875 LOG(VB_FILE, LOG_ERR, QString(
"Failed to grab slave socket : %1 :")
877 res <<
"SLAVE UNREACHABLE: " << wantHost;
890 if (slist.size() != 4)
892 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid Request. %1")
893 .arg(slist.join(
"[]:[]")));
899 QString wantHost = slist[1];
900 QString groupname = slist[2];
901 QString filename = slist[3];
903 LOG(VB_FILE, LOG_DEBUG, QString(
"HandleSGFileQuery: myth://%1@%2/%3")
904 .arg(groupname).arg(wantHost).arg(filename));
909 LOG(VB_FILE, LOG_DEBUG, QString(
"Getting local info"));
913 if (res.count() == 0)
922 if (
m_fsMap.contains(wantHost))
931 res <<
"QUERY_SG_FILEQUERY" << wantHost << groupname << filename;
937 res <<
"SLAVE UNREACHABLE: " << wantHost;
946 QStringList &commands, QStringList &slist)
948 if (commands.size() != 2)
951 if (slist.size() < 2)
955 int recnum = commands[1].toInt();
962 if (slist[1] ==
"DONE")
966 LOG(VB_GENERAL, LOG_ERR,
967 QString(
"Unknown file transfer socket: %1").arg(recnum));
969 <<
"unknown_file_transfer_socket";
980 if (slist[1] ==
"REQUEST_BLOCK")
982 if (slist.size() != 3)
984 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER " 985 "REQUEST_BLOCK call");
986 res <<
"ERROR" <<
"invalid_call";
990 int size = slist[2].toInt();
994 else if (slist[1] ==
"WRITE_BLOCK")
996 if (slist.size() != 3)
998 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER " 1000 res <<
"ERROR" <<
"invalid_call";
1004 int size = slist[2].toInt();
1005 res << QString::number(ft->
WriteBlock(size));
1008 else if (slist[1] ==
"SEEK")
1010 if (slist.size() != 5)
1012 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER SEEK call");
1013 res <<
"ERROR" <<
"invalid_call";
1017 long long pos = slist[2].toLongLong();
1018 int whence = slist[3].toInt();
1019 long long curpos = slist[4].toLongLong();
1021 res << QString::number(ft->
Seek(curpos, pos, whence));
1024 else if (slist[1] ==
"IS_OPEN")
1026 res << QString::number(ft->
isOpen());
1028 else if (slist[1] ==
"DONE")
1033 else if (slist[1] ==
"SET_TIMEOUT")
1035 if (slist.size() != 3)
1037 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER " 1038 "SET_TIMEOUT call");
1039 res <<
"ERROR" <<
"invalid_call";
1043 bool fast = slist[2].toInt();
1048 else if (slist[1] ==
"REQUEST_SIZE")
1056 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER call");
1057 res <<
"ERROR" <<
"invalid_call";
1070 if (slist.size() != 4)
1072 res <<
"ERROR" << QString(
"Bad %1 command").arg(slist[0]);
1077 bool synchronous = (slist[0] ==
"DOWNLOAD_FILE_NOW");
1078 QString srcURL = slist[1];
1079 QString storageGroup = slist[2];
1080 QString filename = slist[3];
1084 QStringList retlist;
1086 if (filename.isEmpty())
1088 QFileInfo finfo(srcURL);
1089 filename = finfo.fileName();
1092 if (outDir.isEmpty())
1094 LOG(VB_GENERAL, LOG_ERR, QString(
"Unable to determine directory " 1095 "to write to in %1 write command").arg(slist[0]));
1096 res <<
"ERROR" <<
"downloadfile_directory_not_found";
1101 if ((filename.contains(
"/../")) ||
1102 (filename.startsWith(
"../")))
1104 LOG(VB_GENERAL, LOG_ERR, QString(
"ERROR: %1 write " 1105 "filename '%2' does not pass sanity checks.")
1106 .arg(slist[0]).arg(filename));
1107 res <<
"ERROR" <<
"downloadfile_filename_dangerous";
1112 outFile = outDir +
"/" + filename;
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
bool WriteStringList(const QStringList &strlist)
QList< FileSystemInfo > QueryFileSystems(void)
bool HandleGetFileList(SocketHandler *socket, QStringList &slist)
void bindValue(const QString &placeholder, const QVariant &val)
QString FileHash(QString filename)
DeleteThread * deletethread
void setBlockSize(int size)
QString GetFileName(void)
static void Consolidate(QList< FileSystemInfo > &disks, bool merge=true, int64_t fuzz=14000)
void Init(const QString &group="Default", const QString &hostname="", const bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
bool HandleDeleteFile(SocketHandler *socket, QStringList &slist)
int GetSocketDescriptor(void) const
static const QList< FileSystemInfo > RemoteGetInfo(MythSocket *sock=nullptr)
void queueDownload(const QString &url, const QString &dest, QObject *caller, const bool reload=false)
Adds a url to the download queue.
void setPath(QString path)
bool HandleQueryFileTransfer(SocketHandler *socket, QStringList &commands, QStringList &slist)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
void AllowStandardEvents(bool allow)
void PopulateFSProp(void)
int RequestBlock(int size)
void setHostname(QString hostname)
QStringList GetFileInfoList(const QString &Path)
bool HandleFileQuery(SocketHandler *socket, QStringList &slist)
Holds information on a TV Program one might wish to record.
bool DeleteFile(QString filename, QString storagegroup)
bool HandleQuery(SocketHandler *socket, QStringList &commands, QStringList &slist) override
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
static const QStringList kSpecialGroups
void connectionClosed(MythSocket *socket) override
void BlockShutdown(bool block)
void PopulateDiskSpace(void)
void AddSocketHandler(SocketHandler *socket)
bool IsThisHost(const QString &addr)
is this address mapped to this host
void connectionAnnounced(MythSocket *socket, QStringList &commands, QStringList &slist) override
void SetTimeout(bool fast)
QVariant value(int i) const
Holds information on recordings and videos.
bool HandleQueryFileHash(SocketHandler *socket, QStringList &slist)
QMap< QString, SocketHandler * > m_fsMap
virtual int IncrRef(void)
Increments reference count.
bool HandleQueryFreeSpaceList(SocketHandler *socket)
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
QString FindNextDirMostFree(void)
bool isRunning(void) const
bool isActive(void) const
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
static MSqlQueryInfo InitCon(ConnectionReuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
QMap< QString, QString > m_downloadURLs
bool HandleQueryFileExists(SocketHandler *socket, QStringList &slist)
bool HandleQueryCheckFile(SocketHandler *socket, QStringList &slist)
MythSocketManager * m_parent
uint64_t GetFileSize(void)
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
#define LOG(_MASK_, _LEVEL_, _STRING_)
long long Seek(long long curpos, long long pos, int whence)
QMap< int, FileTransfer * > m_ftMap
QString FindFile(const QString &filename)
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
bool HandleAnnounce(MythSocket *socket, QStringList &commands, QStringList &slist) override
void RunDeleteThread(void)
QList< FileSystemInfo > QueryAllFileSystems(void)
bool HandleQueryFreeSpaceSummary(SocketHandler *socket)
QString GetMasterHostPrefix(const QString &storageGroup=QString(), const QString &path=QString())
Class for communcating between myth backends and frontends.
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...
bool HandleQueryFreeSpace(SocketHandler *socket)
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
bool SendReceiveStringList(QStringList &strlist, uint min_reply_length=0)
bool WriteStringList(const QStringList &list)
QMutex m_downloadURLsLock
static void DBError(const QString &where, const MSqlQuery &query)
bool IsRegisteredFileForWrite(const QString &file)
void setLocal(bool local=true)
SocketHandler * GetConnectionBySocket(MythSocket *socket)
QString GetHostName(void)
void AllowSystemEvents(bool allow)
QString LocalFilePath(const QString &path, const QString &wantgroup)
QStringList GetFileInfo(const QString &filename)
bool HandleDownloadFile(SocketHandler *socket, QStringList &slist)
bool AddFile(QString path)