7#if QT_VERSION >= QT_VERSION_CHECK(6,0,0)
8#include <QtSystemDetection>
12#include <QWriteLocker>
39 QMap<int, FileTransfer*>::iterator i;
42 if ((*i)->GetSocket() == socket)
55 QMap<QString, SocketHandler*>::iterator i;
58 if ((*i)->GetSocket() == socket)
69 const QString &wantgroup)
71 QString lpath = QString(path);
73 if (lpath.section(
'/', -2, -2) ==
"channels")
76 QString
file = lpath.section(
'/', -1);
80 query.
prepare(
"SELECT icon FROM channel "
81 "WHERE icon LIKE :FILENAME ;");
86 lpath = query.
value(0).toString();
95 lpath = lpath.section(
'/', -1);
97 QString fpath = lpath;
98 if (fpath.endsWith(
".png"))
99 fpath = fpath.left(fpath.length() - 4);
105 if (pburl.startsWith(
"/"))
107 lpath = pburl.section(
'/', 0, -2) +
"/" + lpath;
108 LOG(VB_FILE, LOG_INFO,
109 QString(
"Local file path: %1").arg(lpath));
113 LOG(VB_GENERAL, LOG_ERR,
114 QString(
"LocalFilePath unable to find local "
115 "path for '%1', found '%2' instead.")
120 else if (!lpath.isEmpty())
123 QString opath = lpath;
126 if (!wantgroup.isEmpty())
128 sgroup.
Init(wantgroup);
129 lpath = QString(path);
133 lpath = QFileInfo(lpath).fileName();
136 QString tmpFile = sgroup.
FindFile(lpath);
137 if (!tmpFile.isEmpty())
140 LOG(VB_FILE, LOG_INFO,
141 QString(
"LocalFilePath(%1 '%2'), found through "
142 "exhaustive search at '%3'")
143 .arg(path, opath, lpath));
147 LOG(VB_GENERAL, LOG_ERR, QString(
"LocalFilePath unable to "
148 "find local path for '%1'.")
179 QStringList &commands, QStringList &slist)
181 if (commands[1] ==
"FileServer")
183 if (slist.size() >= 3)
187 handler->BlockShutdown(
true);
188 handler->AllowStandardEvents(
true);
189 handler->AllowSystemEvents(
true);
191 handler->WriteStringList(QStringList(
"OK"));
194 m_fsMap.insert(commands[2], handler);
204 if (commands[1] !=
"FileTransfer")
207 if (slist.size() < 3)
210 if ((commands.size() < 3) || (commands.size() > 6))
216 bool writemode =
false;
217 bool usereadahead =
true;
218 std::chrono::milliseconds
timeout = 2s;
219 switch (commands.size())
222 timeout = std::chrono::milliseconds(commands[5].toInt());
225 usereadahead = (commands[4].toInt() != 0);
228 writemode = (commands[3].toInt() != 0);
234 QStringList::const_iterator it = slist.cbegin();
235 QString path = *(++it);
236 QString wantgroup = *(++it);
238 QStringList checkfiles;
239 while (++it != slist.cend())
244 LOG(VB_GENERAL, LOG_DEBUG,
"FileServerHandler::HandleAnnounce");
245 LOG(VB_GENERAL, LOG_INFO, QString(
"adding: %1 as remote file transfer")
250 if (wantgroup.isEmpty())
251 wantgroup =
"Default";
257 LOG(VB_GENERAL, LOG_ERR,
"Unable to determine directory "
258 "to write to in FileTransfer write command");
260 slist <<
"ERROR" <<
"filetransfer_directory_not_found";
267 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer write "
268 "filename is empty in path '%1'.")
271 slist <<
"ERROR" <<
"filetransfer_filename_empty";
276 if ((path.contains(
"/../")) ||
277 (path.startsWith(
"../")))
279 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer write "
280 "filename '%1' does not pass sanity checks.")
283 slist <<
"ERROR" <<
"filetransfer_filename_dangerous";
298 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer filename "
299 "'%1' is actually a directory, cannot transfer.")
302 slist <<
"ERROR" <<
"filetransfer_filename_is_a_directory";
309 QString dirPath = finfo.absolutePath();
313 if (!qdir.mkpath(dirPath))
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.")
320 slist <<
"ERROR" <<
"filetransfer_unable_to_create_subdirectory";
344 if (!checkfiles.empty())
347 QDir dir = fi.absoluteDir();
348 for (
const auto &
file : std::as_const(checkfiles))
350 if (dir.exists(
file) &&
364 QStringList &commands, QStringList &slist)
366 if (commands[1] ==
"SlaveBackend")
370 if (slist.size() >= 3)
373 if (handler ==
nullptr)
377 m_fsMap.insert(commands[2], handler);
386 bool handled =
false;
387 QString command = commands[0];
389 if (command ==
"QUERY_FILETRANSFER")
391 else if (command ==
"QUERY_FREE_SPACE")
393 else if (command ==
"QUERY_FREE_SPACE_LIST")
395 else if (command ==
"QUERY_FREE_SPACE_SUMMARY")
397 else if (command ==
"QUERY_CHECKFILE")
399 else if (command ==
"QUERY_FILE_EXISTS")
401 else if (command ==
"QUERY_FILE_HASH")
403 else if (command ==
"DELETE_FILE")
405 else if (command ==
"QUERY_SG_GETFILELIST")
407 else if (command ==
"QUERY_SG_FILEQUERY")
409 else if (command ==
"DOWNLOAD_FILE" || command ==
"DOWNLOAD_FILE_NOW")
425 for (
const auto & disk : std::as_const(disks))
426 if (!hosts.contains(disk.getHostname()))
427 hosts << disk.getHostname();
442 socket->
WriteStringList({QString::number(disks.back().getTotalSpace()),
443 QString::number(disks.back().getUsedSpace())});
451 groups.removeAll(
"LiveTV");
452 QString specialGroups = groups.join(
"', '");
455 query.
prepare(QString(
"SELECT MIN(id),dirname "
457 "WHERE hostname = :HOSTNAME "
458 "AND groupname NOT IN ( '%1' ) "
459 "GROUP BY dirname;").arg(specialGroups));
460 query.
bindValue(
":HOSTNAME", localHostName);
469 query.
prepare(
"SELECT MIN(id),dirname "
471 "WHERE groupname = :GROUP "
472 "GROUP BY dirname;");
478 QMap<QString, bool> foundDirs;
485 QString currentDir {QString::fromUtf8(query.
value(1).toByteArray().constData())};
486 if (currentDir.endsWith(
"/"))
487 currentDir.remove(currentDir.length() - 1, 1);
489 if (!foundDirs.contains(currentDir))
491 if (QDir(currentDir).
exists())
495 foundDirs[currentDir] =
true;
499 foundDirs[currentDir] =
false;
514 for (
const auto* fs : std::as_const(
m_fsMap))
531 QStringList::const_iterator it = slist.cbegin() + 2;
545 QStringList res(QString::number(
static_cast<int>(
exists)));
559 QString storageGroup =
"Default";
562 if (slist.size() == 3)
564 if (!slist[2].isEmpty())
565 storageGroup = slist[2];
567 else if (slist.size() != 2)
577 LOG(VB_GENERAL, LOG_ERR,
578 QString(
"ERROR checking for file, filename '%1' "
579 "fails sanity checks").arg(
filename));
588 if (!fullname.isEmpty())
594 struct stat fileinfo {};
595 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
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)
609 << QString::number(fileinfo.st_blksize)
610 << QString::number(fileinfo.st_blocks)
612 << QString::number(fileinfo.st_atime)
613 << QString::number(fileinfo.st_mtime)
614 << QString::number(fileinfo.st_ctime);
633 QString storageGroup =
"Default";
638 switch (slist.size()) {
640 if (!slist[3].isEmpty())
644 if (!slist[2].isEmpty())
645 storageGroup = slist[2];
653 LOG(VB_GENERAL, LOG_ERR,
654 QString(
"ERROR checking for file, filename '%1' "
655 "fails sanity checks").arg(
filename));
698 if (slist.size() != 3)
710 const QString&
filename,
const QString& storagegroup)
719 LOG(VB_GENERAL, LOG_ERR,
720 QString(
"ERROR deleting file, filename '%1' fails sanity checks")
733 if (fullfile.isEmpty())
735 LOG(VB_GENERAL, LOG_ERR,
736 QString(
"Unable to find %1 in HandleDeleteFile()") .arg(
filename));
746 QFile checkFile(fullfile);
747 if (checkFile.exists())
759 LOG(VB_GENERAL, LOG_ERR, QString(
"Error deleting file: '%1'")
782 bool fileNamesOnly =
false;
783 if (slist.size() == 5)
784 fileNamesOnly = (slist[4].toInt() != 0);
785 else if (slist.size() != 4)
787 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid Request. %1")
788 .arg(slist.join(
"[]:[]")));
795 QString wantHost = slist[1];
796 QString groupname = slist[2];
797 QString path = slist[3];
799 LOG(VB_FILE, LOG_INFO,
800 QString(
"HandleSGGetFileList: group = %1 host = %2 "
801 "path = %3 wanthost = %4")
802 .arg(groupname, host, path, wantHost));
807 LOG(VB_FILE, LOG_INFO,
"Getting local info");
813 if (res.count() == 0)
822 if (
m_fsMap.contains(wantHost))
824 remsock =
m_fsMap.value(wantHost);
831 LOG(VB_FILE, LOG_INFO,
"Getting remote info");
832 res <<
"QUERY_SG_GETFILELIST" << wantHost << groupname << path
833 << QString::number(
static_cast<int>(fileNamesOnly));
839 LOG(VB_FILE, LOG_ERR, QString(
"Failed to grab slave socket : %1 :")
841 res <<
"SLAVE UNREACHABLE: " << wantHost;
854 if (slist.size() != 4)
856 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid Request. %1")
857 .arg(slist.join(
"[]:[]")));
863 QString wantHost = slist[1];
864 QString groupname = slist[2];
867 LOG(VB_FILE, LOG_DEBUG, QString(
"HandleSGFileQuery: myth://%1@%2/%3")
868 .arg(groupname, wantHost,
filename));
873 LOG(VB_FILE, LOG_DEBUG, QString(
"Getting local info"));
877 if (res.count() == 0)
886 if (
m_fsMap.contains(wantHost))
888 remsock =
m_fsMap.value(wantHost);
895 res <<
"QUERY_SG_FILEQUERY" << wantHost << groupname <<
filename;
901 res <<
"SLAVE UNREACHABLE: " << wantHost;
910 QStringList &commands, QStringList &slist)
912 if (commands.size() != 2)
915 if (slist.size() < 2)
919 int recnum = commands[1].toInt();
926 if (slist[1] ==
"DONE")
930 LOG(VB_GENERAL, LOG_ERR,
931 QString(
"Unknown file transfer socket: %1").arg(recnum));
933 <<
"unknown_file_transfer_socket";
944 if (slist[1] ==
"REQUEST_BLOCK")
946 if (slist.size() != 3)
948 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER "
949 "REQUEST_BLOCK call");
950 res <<
"ERROR" <<
"invalid_call";
954 int size = slist[2].toInt();
958 else if (slist[1] ==
"WRITE_BLOCK")
960 if (slist.size() != 3)
962 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER "
964 res <<
"ERROR" <<
"invalid_call";
968 int size = slist[2].toInt();
972 else if (slist[1] ==
"SEEK")
974 if (slist.size() != 5)
976 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER SEEK call");
977 res <<
"ERROR" <<
"invalid_call";
981 long long pos = slist[2].toLongLong();
982 int whence = slist[3].toInt();
983 long long curpos = slist[4].toLongLong();
985 res << QString::number(ft->
Seek(curpos, pos, whence));
988 else if (slist[1] ==
"IS_OPEN")
990 res << QString::number(static_cast<int>(ft->
isOpen()));
992 else if (slist[1] ==
"DONE")
997 else if (slist[1] ==
"SET_TIMEOUT")
999 if (slist.size() != 3)
1001 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER "
1002 "SET_TIMEOUT call");
1003 res <<
"ERROR" <<
"invalid_call";
1007 bool fast = slist[2].toInt() != 0;
1012 else if (slist[1] ==
"REQUEST_SIZE")
1020 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER call");
1021 res <<
"ERROR" <<
"invalid_call";
1034 if (slist.size() != 4)
1036 res <<
"ERROR" << QString(
"Bad %1 command").arg(slist[0]);
1041 bool synchronous = (slist[0] ==
"DOWNLOAD_FILE_NOW");
1042 QString srcURL = slist[1];
1043 QString storageGroup = slist[2];
1051 QFileInfo finfo(srcURL);
1055 if (outDir.isEmpty())
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";
1067 LOG(VB_GENERAL, LOG_ERR, QString(
"ERROR: %1 write "
1068 "filename '%2' does not pass sanity checks.")
1070 res <<
"ERROR" <<
"downloadfile_filename_dangerous";
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
QMutex m_downloadURLsLock
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
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)
long long Seek(long long curpos, long long pos, int whence)
QString GetFileName(void)
uint64_t GetFileSize(void)
void SetTimeout(bool fast)
int RequestBlock(int size)
QSqlQuery wrapper that fetches a DB connection from the connection pool.
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
QVariant value(int i) const
bool isActive(void) const
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
bool isRunning(void) const
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)
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.
int GetSocketDescriptor(void) const
bool WriteStringList(const QStringList &list)
Holds information on recordings and videos.
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
bool HasPathname(void) const
Holds information on a TV Program one might wish to record.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
virtual int IncrRef(void)
Increments reference count.
void BlockShutdown(bool block)
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
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_)
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={})