7#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
8#include <QtSystemDetection>
13#include <QWriteLocker>
40 QMap<int, FileTransfer*>::iterator i;
43 if ((*i)->GetSocket() == socket)
56 QMap<QString, SocketHandler*>::iterator i;
59 if ((*i)->GetSocket() == socket)
70 const QString &wantgroup)
72 QString lpath = QString(path);
74 if (lpath.section(
'/', -2, -2) ==
"channels")
77 QString
file = lpath.section(
'/', -1);
81 query.
prepare(
"SELECT icon FROM channel "
82 "WHERE icon LIKE :FILENAME ;");
87 lpath = query.
value(0).toString();
96 lpath = lpath.section(
'/', -1);
98 QString fpath = lpath;
99 if (fpath.endsWith(
".png"))
100 fpath = fpath.left(fpath.length() - 4);
106 if (pburl.startsWith(
"/"))
108 lpath = pburl.section(
'/', 0, -2) +
"/" + lpath;
109 LOG(VB_FILE, LOG_INFO,
110 QString(
"Local file path: %1").arg(lpath));
114 LOG(VB_GENERAL, LOG_ERR,
115 QString(
"LocalFilePath unable to find local "
116 "path for '%1', found '%2' instead.")
121 else if (!lpath.isEmpty())
124 QString opath = lpath;
127 if (!wantgroup.isEmpty())
129 sgroup.
Init(wantgroup);
130 lpath = QString(path);
134 lpath = QFileInfo(lpath).fileName();
137 QString tmpFile = sgroup.
FindFile(lpath);
138 if (!tmpFile.isEmpty())
141 LOG(VB_FILE, LOG_INFO,
142 QString(
"LocalFilePath(%1 '%2'), found through "
143 "exhaustive search at '%3'")
144 .arg(path, opath, lpath));
148 LOG(VB_GENERAL, LOG_ERR, QString(
"LocalFilePath unable to "
149 "find local path for '%1'.")
180 QStringList &commands, QStringList &slist)
182 if (commands[1] ==
"FileServer")
184 if (slist.size() >= 3)
188 handler->BlockShutdown(
true);
189 handler->AllowStandardEvents(
true);
190 handler->AllowSystemEvents(
true);
192 handler->WriteStringList(QStringList(
"OK"));
195 m_fsMap.insert(commands[2], handler);
205 if (commands[1] !=
"FileTransfer")
208 if (slist.size() < 3)
211 if ((commands.size() < 3) || (commands.size() > 6))
217 bool writemode =
false;
218 bool usereadahead =
true;
219 std::chrono::milliseconds
timeout = 2s;
220 switch (commands.size())
223 timeout = std::chrono::milliseconds(commands[5].toInt());
226 usereadahead = (commands[4].toInt() != 0);
229 writemode = (commands[3].toInt() != 0);
235 QStringList::const_iterator it = slist.cbegin();
236 QString path = *(++it);
237 QString wantgroup = *(++it);
239 QStringList checkfiles;
240 while (++it != slist.cend())
245 LOG(VB_GENERAL, LOG_DEBUG,
"FileServerHandler::HandleAnnounce");
246 LOG(VB_GENERAL, LOG_INFO, QString(
"adding: %1 as remote file transfer")
251 if (wantgroup.isEmpty())
252 wantgroup =
"Default";
258 LOG(VB_GENERAL, LOG_ERR,
"Unable to determine directory "
259 "to write to in FileTransfer write command");
261 slist <<
"ERROR" <<
"filetransfer_directory_not_found";
268 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer write "
269 "filename is empty in path '%1'.")
272 slist <<
"ERROR" <<
"filetransfer_filename_empty";
277 if ((path.contains(
"/../")) ||
278 (path.startsWith(
"../")))
280 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer write "
281 "filename '%1' does not pass sanity checks.")
284 slist <<
"ERROR" <<
"filetransfer_filename_dangerous";
299 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer filename "
300 "'%1' is actually a directory, cannot transfer.")
303 slist <<
"ERROR" <<
"filetransfer_filename_is_a_directory";
310 QString dirPath = finfo.absolutePath();
314 if (!qdir.mkpath(dirPath))
316 LOG(VB_GENERAL, LOG_ERR, QString(
"FileTransfer "
317 "filename '%1' is in a subdirectory which does "
318 "not exist, but can not be created.")
321 slist <<
"ERROR" <<
"filetransfer_unable_to_create_subdirectory";
345 if (!checkfiles.empty())
348 QDir dir = fi.absoluteDir();
349 for (
const auto &
file : std::as_const(checkfiles))
351 if (dir.exists(
file) &&
365 QStringList &commands, QStringList &slist)
367 if (commands[1] ==
"SlaveBackend")
371 if (slist.size() >= 3)
374 if (handler ==
nullptr)
378 m_fsMap.insert(commands[2], handler);
387 bool handled =
false;
388 QString command = commands[0];
390 if (command ==
"QUERY_FILETRANSFER")
392 else if (command ==
"QUERY_FREE_SPACE")
394 else if (command ==
"QUERY_FREE_SPACE_LIST")
396 else if (command ==
"QUERY_FREE_SPACE_SUMMARY")
398 else if (command ==
"QUERY_CHECKFILE")
400 else if (command ==
"QUERY_FILE_EXISTS")
402 else if (command ==
"QUERY_FILE_HASH")
404 else if (command ==
"DELETE_FILE")
406 else if (command ==
"QUERY_SG_GETFILELIST")
408 else if (command ==
"QUERY_SG_FILEQUERY")
410 else if (command ==
"DOWNLOAD_FILE" || command ==
"DOWNLOAD_FILE_NOW")
426 for (
const auto & disk : std::as_const(disks))
427 if (!hosts.contains(disk.getHostname()))
428 hosts << disk.getHostname();
443 socket->
WriteStringList({QString::number(disks.back().getTotalSpace()),
444 QString::number(disks.back().getUsedSpace())});
452 groups.removeAll(
"LiveTV");
453 QString specialGroups = groups.join(
"', '");
456 query.
prepare(QString(
"SELECT MIN(id),dirname "
458 "WHERE hostname = :HOSTNAME "
459 "AND groupname NOT IN ( '%1' ) "
460 "GROUP BY dirname;").arg(specialGroups));
461 query.
bindValue(
":HOSTNAME", localHostName);
470 query.
prepare(
"SELECT MIN(id),dirname "
472 "WHERE groupname = :GROUP "
473 "GROUP BY dirname;");
479 QMap<QString, bool> foundDirs;
486 QString currentDir {QString::fromUtf8(query.
value(1).toByteArray().constData())};
487 if (currentDir.endsWith(
"/"))
488 currentDir.remove(currentDir.length() - 1, 1);
490 if (!foundDirs.contains(currentDir))
492 if (QDir(currentDir).
exists())
496 foundDirs[currentDir] =
true;
500 foundDirs[currentDir] =
false;
515 for (
const auto* fs : std::as_const(
m_fsMap))
532 QStringList::const_iterator it = slist.cbegin() + 2;
546 QStringList res(QString::number(
static_cast<int>(
exists)));
560 QString storageGroup =
"Default";
563 if (slist.size() == 3)
565 if (!slist[2].isEmpty())
566 storageGroup = slist[2];
568 else if (slist.size() != 2)
578 LOG(VB_GENERAL, LOG_ERR,
579 QString(
"ERROR checking for file, filename '%1' "
580 "fails sanity checks").arg(
filename));
589 if (!fullname.isEmpty())
595 struct stat fileinfo {};
596 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
598 res << QString::number(fileinfo.st_dev)
599 << QString::number(fileinfo.st_ino)
600 << QString::number(fileinfo.st_mode)
601 << QString::number(fileinfo.st_nlink)
602 << QString::number(fileinfo.st_uid)
603 << QString::number(fileinfo.st_gid)
604 << QString::number(fileinfo.st_rdev)
605 << QString::number(fileinfo.st_size)
610 << QString::number(fileinfo.st_blksize)
611 << QString::number(fileinfo.st_blocks)
613 << QString::number(fileinfo.st_atime)
614 << QString::number(fileinfo.st_mtime)
615 << QString::number(fileinfo.st_ctime);
634 QString storageGroup =
"Default";
639 switch (slist.size()) {
641 if (!slist[3].isEmpty())
645 if (!slist[2].isEmpty())
646 storageGroup = slist[2];
654 LOG(VB_GENERAL, LOG_ERR,
655 QString(
"ERROR checking for file, filename '%1' "
656 "fails sanity checks").arg(
filename));
699 if (slist.size() != 3)
711 const QString&
filename,
const QString& storagegroup)
720 LOG(VB_GENERAL, LOG_ERR,
721 QString(
"ERROR deleting file, filename '%1' fails sanity checks")
734 if (fullfile.isEmpty())
736 LOG(VB_GENERAL, LOG_ERR,
737 QString(
"Unable to find %1 in HandleDeleteFile()") .arg(
filename));
747 QFile checkFile(fullfile);
748 if (checkFile.exists())
760 LOG(VB_GENERAL, LOG_ERR, QString(
"Error deleting file: '%1'")
783 bool fileNamesOnly =
false;
784 if (slist.size() == 5)
786 fileNamesOnly = (slist[4].toInt() != 0);
788 else if (slist.size() != 4)
790 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid Request. %1")
791 .arg(slist.join(
"[]:[]")));
798 QString wantHost = slist[1];
799 QString groupname = slist[2];
800 QString path = slist[3];
802 LOG(VB_FILE, LOG_INFO,
803 QString(
"HandleSGGetFileList: group = %1 host = %2 "
804 "path = %3 wanthost = %4")
805 .arg(groupname, host, path, wantHost));
810 LOG(VB_FILE, LOG_INFO,
"Getting local info");
816 if (res.count() == 0)
825 if (
m_fsMap.contains(wantHost))
827 remsock =
m_fsMap.value(wantHost);
834 LOG(VB_FILE, LOG_INFO,
"Getting remote info");
835 res <<
"QUERY_SG_GETFILELIST" << wantHost << groupname << path
836 << QString::number(
static_cast<int>(fileNamesOnly));
842 LOG(VB_FILE, LOG_ERR, QString(
"Failed to grab slave socket : %1 :")
844 res <<
"SLAVE UNREACHABLE: " << wantHost;
857 if (slist.size() != 4)
859 LOG(VB_GENERAL, LOG_ERR, QString(
"Invalid Request. %1")
860 .arg(slist.join(
"[]:[]")));
866 QString wantHost = slist[1];
867 QString groupname = slist[2];
870 LOG(VB_FILE, LOG_DEBUG, QString(
"HandleSGFileQuery: myth://%1@%2/%3")
871 .arg(groupname, wantHost,
filename));
876 LOG(VB_FILE, LOG_DEBUG, QString(
"Getting local info"));
880 if (res.count() == 0)
889 if (
m_fsMap.contains(wantHost))
891 remsock =
m_fsMap.value(wantHost);
898 res <<
"QUERY_SG_FILEQUERY" << wantHost << groupname <<
filename;
904 res <<
"SLAVE UNREACHABLE: " << wantHost;
913 QStringList &commands, QStringList &slist)
915 if (commands.size() != 2)
918 if (slist.size() < 2)
922 int recnum = commands[1].toInt();
929 if (slist[1] ==
"DONE")
935 LOG(VB_GENERAL, LOG_ERR,
936 QString(
"Unknown file transfer socket: %1").arg(recnum));
938 <<
"unknown_file_transfer_socket";
949 if (slist[1] ==
"REQUEST_BLOCK")
951 if (slist.size() != 3)
953 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER "
954 "REQUEST_BLOCK call");
955 res <<
"ERROR" <<
"invalid_call";
959 int size = slist[2].toInt();
963 else if (slist[1] ==
"WRITE_BLOCK")
965 if (slist.size() != 3)
967 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER "
969 res <<
"ERROR" <<
"invalid_call";
973 int size = slist[2].toInt();
977 else if (slist[1] ==
"SEEK")
979 if (slist.size() != 5)
981 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER SEEK call");
982 res <<
"ERROR" <<
"invalid_call";
986 long long pos = slist[2].toLongLong();
987 int whence = slist[3].toInt();
988 long long curpos = slist[4].toLongLong();
990 res << QString::number(ft->
Seek(curpos, pos, whence));
993 else if (slist[1] ==
"IS_OPEN")
995 res << QString::number(static_cast<int>(ft->
isOpen()));
997 else if (slist[1] ==
"DONE")
1002 else if (slist[1] ==
"SET_TIMEOUT")
1004 if (slist.size() != 3)
1006 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER "
1007 "SET_TIMEOUT call");
1008 res <<
"ERROR" <<
"invalid_call";
1012 bool fast = slist[2].toInt() != 0;
1017 else if (slist[1] ==
"REQUEST_SIZE")
1025 LOG(VB_GENERAL, LOG_ERR,
"Invalid QUERY_FILETRANSFER call");
1026 res <<
"ERROR" <<
"invalid_call";
1039 if (slist.size() != 4)
1041 res <<
"ERROR" << QString(
"Bad %1 command").arg(slist[0]);
1046 bool synchronous = (slist[0] ==
"DOWNLOAD_FILE_NOW");
1047 QString srcURL = slist[1];
1048 QString storageGroup = slist[2];
1056 QFileInfo finfo(srcURL);
1060 if (outDir.isEmpty())
1062 LOG(VB_GENERAL, LOG_ERR, QString(
"Unable to determine directory "
1063 "to write to in %1 write command").arg(slist[0]));
1064 res <<
"ERROR" <<
"downloadfile_directory_not_found";
1072 LOG(VB_GENERAL, LOG_ERR, QString(
"ERROR: %1 write "
1073 "filename '%2' does not pass sanity checks.")
1075 res <<
"ERROR" <<
"downloadfile_filename_dangerous";
1111#include "moc_fileserverhandler.cpp"
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={})