4#if QT_VERSION >= QT_VERSION_CHECK(6,5,0)
5#include <QtSystemDetection>
9#include <QRegularExpression>
45 QString ann = QString(
"ANN Playback %1 0")
49 bool mismatch =
false;
52 addr, port, ann, &mismatch);
72 std::chrono::milliseconds
timeout,
73 const QStringList *possibleAuxiliaryFiles) :
74 m_path(
std::move(url)),
75 m_useReadAhead(usereadahead), m_timeoutMs(
timeout),
83 else if (possibleAuxiliaryFiles)
91 LOG(VB_FILE, LOG_DEBUG, QString(
"RemoteFile(%1)").arg(
m_path));
111 bool is_local = !lpath.isEmpty() &&
112 !lpath.startsWith(
"myth:") &&
127 QString host = qurl.host();
128 int port = qurl.port();
133 dir +=
"?" + QUrl::fromPercentEncoding(
134 qurl.query(QUrl::FullyEncoded).toLocal8Bit());
136 if (qurl.hasFragment())
137 dir +=
"#" + qurl.fragment();
139 QString sgroup = qurl.userName();
142 QString stype = (control) ?
"control socket" :
"file data socket";
144 QString loc = QString(
"RemoteFile::openSocket(%1): ").arg(stype);
151 if (!lsock->ConnectToHost(host, port))
153 LOG(VB_GENERAL, LOG_ERR, loc +
154 QString(
"Could not connect to server %1:%2") .arg(host).arg(port));
163#ifndef IGNORE_PROTO_VER_MISMATCH
166 LOG(VB_GENERAL, LOG_ERR, loc +
167 QString(
"Failed validation to server %1:%2").arg(host).arg(port));
175 strlist.append(QString(
"ANN Playback %1 %2")
176 .arg(
hostname).arg(
static_cast<int>(
false)));
177 if (!lsock->SendReceiveStringList(strlist))
179 LOG(VB_GENERAL, LOG_ERR, loc +
180 QString(
"Could not read string list from server %1:%2")
181 .arg(host).arg(port));
188 strlist.push_back(QString(
"ANN FileTransfer %1 %2 %3 %4")
191 strlist << QString(
"%1").arg(dir);
197 if (!lsock->SendReceiveStringList(strlist))
199 LOG(VB_GENERAL, LOG_ERR, loc +
200 QString(
"Did not get proper response from %1:%2")
201 .arg(host).arg(port));
203 strlist.push_back(
"ERROR");
204 strlist.push_back(
"invalid response");
207 if (strlist.size() >= 3)
209 auto it = strlist.begin(); ++it;
212 for (; it != strlist.end(); ++it)
215 else if (!strlist.isEmpty() && strlist.size() < 3 &&
216 strlist[0] !=
"ERROR")
218 LOG(VB_GENERAL, LOG_ERR, loc +
219 QString(
"Did not get proper response from %1:%2")
220 .arg(host).arg(port));
222 strlist.push_back(
"ERROR");
223 strlist.push_back(
"invalid response");
227 if (strlist.isEmpty() || strlist[0] ==
"ERROR")
231 if (strlist.isEmpty())
233 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket, timeout");
237 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket" +
238 ((strlist.size() >= 2) ?
239 QString(
", error was %1").arg(strlist[1]) :
240 QString(
", remote error")));
261 QMutexLocker locker(&
m_lock);
280 LOG(VB_FILE, LOG_WARNING, QString(
"RemoteFile::Open(%1) creating directories")
283 if (!dir.mkpath(fi.path()))
285 LOG(VB_GENERAL, LOG_ERR, QString(
"RemoteFile::Open(%1) failed to create the directories")
299 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) write mode error")
310 LOG(VB_FILE, LOG_ERR,
311 QString(
"RemoteFile::Open(%1) Error: Does not exist").arg(
m_path));
318 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) Error: %2")
319 .arg(
m_path, strerror(errno)));
353 QMutexLocker locker(&
m_lock);
357 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::ReOpen(): Couldn't connect");
363 strlist << newFilename;
370 if (!strlist.isEmpty())
371 retval = (strlist[0].toInt() != 0);
400 LOG(VB_GENERAL, LOG_ERR,
"Remote file timeout.");
425 return file.remove();
431 QString sgroup = qurl.userName();
433 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
439 if (
filename.isEmpty() || sgroup.isEmpty())
442 QStringList strlist(
"DELETE_FILE");
448 if (!strlist.isEmpty() && strlist[0] ==
"1")
459 struct stat fileinfo {};
460 return Exists(url, &fileinfo);
470 QString sgroup = qurl.userName();
471 QString host = qurl.host();
475 LOG(VB_FILE, LOG_INFO,
476 QString(
"RemoteFile::Exists(): looking for local file: %1").arg(url));
478 bool fileExists =
false;
479 QString fullFilePath =
"";
481 if (url.startsWith(
"myth:"))
485 if (!fullFilePath.isEmpty())
491 fileExists =
info.exists() ;
497 if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
499 LOG(VB_FILE, LOG_ERR,
500 QString(
"RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) +
ENO);
507 LOG(VB_FILE, LOG_INFO,
508 QString(
"RemoteFile::Exists(): looking for remote file: %1").arg(url));
510 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
519 QStringList strlist(
"QUERY_FILE_EXISTS");
521 if (!sgroup.isEmpty())
527 if ((strlist.size() >= 15) && fileinfo)
529 fileinfo->st_dev = strlist[2].toLongLong();
530 fileinfo->st_ino = strlist[3].toLongLong();
531 fileinfo->st_mode = strlist[4].toLongLong();
532 fileinfo->st_nlink = strlist[5].toLongLong();
533 fileinfo->st_uid = strlist[6].toLongLong();
534 fileinfo->st_gid = strlist[7].toLongLong();
535 fileinfo->st_rdev = strlist[8].toLongLong();
536 fileinfo->st_size = strlist[9].toLongLong();
538 fileinfo->st_blksize = strlist[10].toLongLong();
539 fileinfo->st_blocks = strlist[11].toLongLong();
541 fileinfo->st_atime = strlist[12].toLongLong();
542 fileinfo->st_mtime = strlist[13].toLongLong();
543 fileinfo->st_ctime = strlist[14].toLongLong();
565 QString sgroup = qurl.userName();
567 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
573 if (
filename.isEmpty() || sgroup.isEmpty())
576 QStringList strlist(
"QUERY_FILE_HASH");
590 bool overwrite,
bool verify)
592 LOG(VB_FILE, LOG_INFO,
593 QString(
"RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
598 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: Cannot copy a file to itself");
605 LOG(VB_GENERAL, LOG_ERR,
606 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
610 const int readSize = 2 * 1024 * 1024;
611 char *buf =
new char[readSize];
614 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
624 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: File already exists");
632 LOG(VB_GENERAL, LOG_ERR,
633 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
644 while ((srcLen = srcFile.
Read(buf, readSize)) > 0)
646 int dstLen = dstFile.
Write(buf, srcLen);
648 if (dstLen == -1 || srcLen != dstLen)
650 LOG(VB_GENERAL, LOG_ERR,
651 "RemoteFile::CopyFile: Error while trying to write to destination file.");
660 if (success && verify)
663 struct stat fileinfo {};
664 long long dstSize =
Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
666 if (dstSize != srcSize)
668 LOG(VB_GENERAL, LOG_ERR,
669 QString(
"RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
670 .arg(dstSize).arg(srcSize));
681 LOG(VB_FILE, LOG_INFO,
682 QString(
"RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
687 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: Cannot move a file to itself");
694 bool ok =
CopyFile(src, dst, overwrite,
true);
698 LOG(VB_FILE, LOG_ERR,
699 "RemoteFile::MoveFile: Failed to delete file after successful copy");
710 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: File already exists");
718 if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
721 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Rename failed");
729 if (srcUrl.userName() != dstUrl.userName())
731 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Cannot change a file's Storage Group");
735 QStringList strlist(
"MOVE_FILE");
736 strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
740 if (!strlist.isEmpty() && strlist[0] ==
"1")
743 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::MoveFile: MOVE_FILE failed with: %1")
744 .arg(strlist.join(
",")));
752 QMutexLocker locker(&
m_lock);
755 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Reset(): Called with no socket");
763 QMutexLocker locker(&
m_lock);
774 LOG(VB_FILE, LOG_ERR,
"RemoteFile::Seek(): Called with no file opened");
780 long long offset = 0LL;
781 if (whence == SEEK_SET)
784 offset = std::min(pos,
info.size());
786 else if (whence == SEEK_END)
789 offset =
info.size() + pos;
791 else if (whence == SEEK_CUR)
793 offset = ((curpos > 0) ? curpos : lseek(
m_localFile, 0, SEEK_CUR)) + pos;
803 LOG(VB_FILE, LOG_ERR,
804 QString(
"RemoteFile::Seek(): Couldn't seek to offset %1")
813 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Seek(): Couldn't connect");
819 strlist << QString::number(pos);
820 strlist << QString::number(whence);
822 strlist << QString::number(curpos);
828 if (ok && !strlist.isEmpty())
832 return strlist[0].toLongLong();
842 unsigned zerocnt = 0;
844 bool response =
false;
848 LOG(VB_NETWORK, LOG_ERR,
849 "RemoteFile::Write(): Called when not in write mode");
856 LOG(VB_FILE, LOG_ERR,
857 "RemoteFile::Write(): File not opened");
863 QMutexLocker locker(&
m_lock);
867 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Write(): Couldn't connect");
872 strlist <<
"WRITE_BLOCK";
873 strlist << QString::number(size);
877 LOG(VB_NETWORK, LOG_ERR,
878 "RemoteFile::Write(): Block notification failed");
883 while (sent < recv && !
error && zerocnt++ < 50)
885 int ret =
m_sock->
Write((
char*)data + sent, recv - sent);
892 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::Write(): socket error");
901 recv = strlist[0].toInt();
906 if (!
error && !response)
911 recv = strlist[0].toInt();
915 LOG(VB_GENERAL, LOG_ERR,
916 "RemoteFile::Write(): No response from control socket.");
921 LOG(VB_NETWORK, LOG_DEBUG,
922 QString(
"RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
923 .arg(size).arg(sent).arg(recv).arg(
error));
928 if (
error || recv != sent)
945 bool response =
false;
947 QMutexLocker locker(&
m_lock);
953 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called in writing mode");
960 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called when local file not opened");
966 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Couldn't connect");
972 LOG(VB_NETWORK, LOG_ERR,
973 "RemoteFile::Read(): Read socket not empty to start!");
979 LOG(VB_NETWORK, LOG_WARNING,
980 "RemoteFile::Read(): Control socket not empty to start!");
985 strlist <<
"REQUEST_BLOCK";
986 strlist << QString::number(size);
990 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Block request failed");
996 std::chrono::milliseconds waitms { 30ms };
1000 while (recv < sent && !
error && mtimer.
elapsed() < 10s)
1002 int ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1009 waitms += (waitms < 200ms) ? 20ms : 0ms;
1015 sent = strlist[0].toInt();
1020 ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1029 if (!
error && !response)
1036 sent = strlist[0].toInt();
1040 LOG(VB_GENERAL, LOG_ERR,
1041 "RemoteFile::Read(): No response from control socket.");
1060 LOG(VB_NETWORK, LOG_DEBUG,
1061 QString(
"Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1062 .arg(size).arg(recv).arg(sent).arg(
error));
1067 if (
error || sent != recv)
1069 LOG(VB_GENERAL, LOG_WARNING,
1070 QString(
"RemoteFile::Read(): sent %1 != recv %2")
1071 .arg(sent).arg(recv));
1077 LOG(VB_GENERAL, LOG_WARNING,
"RemoteFile::Read(): Resume failed.");
1081 LOG(VB_GENERAL, LOG_NOTICE,
"RemoteFile::Read(): Resume success.");
1113 QMutexLocker locker(&
m_lock);
1132 QMutexLocker locker(&
m_lock);
1143 struct stat fileinfo {};
1153 strlist <<
"REQUEST_SIZE";
1157 if (ok && !strlist.isEmpty())
1159 bool validate =
false;
1160 long long size = strlist[0].toLongLong(&validate);
1164 if (strlist.count() >= 2)
1172 struct stat fileinfo {};
1194 Read(data.data(), fs);
1209 QMutexLocker locker(&
m_lock);
1220 LOG(VB_NETWORK, LOG_ERR,
1221 "RemoteFile::SetTimeout(): Couldn't connect");
1228 strlist <<
"SET_TIMEOUT";
1229 strlist << QString::number((
int)fast);
1240 QFileInfo
info(url);
1241 return info.lastModified();
1246 QString sgroup = qurl.userName();
1248 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
1254 if (
filename.isEmpty() || sgroup.isEmpty())
1257 QStringList strlist(
"QUERY_SG_FILEQUERY");
1258 strlist << qurl.host();
1264 if (strlist.size() > 1) {
1265 if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1268 result = QDateTime();;
1289 const QString& storageGroup,
bool useRegex,
1294 if (!files.isEmpty())
1310 const QString& storageGroup,
bool useRegex,
1313 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1314 "(useregex: %4, allowfallback: %5)")
1316 .arg(useRegex).arg(allowFallback));
1318 if (
filename.isEmpty() || storageGroup.isEmpty())
1321 QStringList strList;
1322 QString hostName = host;
1324 if (hostName.isEmpty())
1338 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
1340 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1341 .arg(fi.path(), fi.fileName()));
1343 for (
int x = 0; x < files.size(); x++)
1345 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Found '%1 - %2'")
1346 .arg(x).arg(files[x]));
1349 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1350 for (
const QString&
file : std::as_const(filteredFiles))
1354 fi.path() +
'/' +
file,
1368 if (!strList.isEmpty() || !allowFallback)
1375 strList <<
"QUERY_FINDFILE" << hostName << storageGroup <<
filename
1376 << (useRegex ?
"1" :
"0")
1381 if (!strList.empty() && !strList[0].isEmpty() &&
1382 strList[0] !=
"NOT FOUND" && !strList[0].startsWith(
"ERROR: "))
1450 LOG(VB_FILE, LOG_ERR,
1451 QString(
"RemoteFile::Resume: Enable to re-seek into last known "
1461 const QString &storageGroup,
1464 QStringList strlist(cmd);
1466 strlist << storageGroup;
1471 if (!ok || strlist.size() < 2 || strlist[0] !=
"OK")
1473 LOG(VB_GENERAL, LOG_ERR,
1474 "downloadRemoteFile(): " + cmd +
" returned ERROR!");
1482 const QString &storageGroup,
1489 const QString &storageGroup,
QString GetHostName(void)
MythSocket * ConnectCommandSocket(const QString &hostname, int port, const QString &announcement, bool *proto_mismatch=nullptr, int maxConnTry=-1, std::chrono::milliseconds setup_timeout=-1ms)
bool CheckProtoVersion(MythSocket *socket, std::chrono::milliseconds timeout=kMythSocketLongTimeout, bool error_dialog_desired=false)
int GetBackendServerPort(void)
Returns the locally defined backend control port.
bool IsThisBackend(const QString &addr)
is this address mapped to this backend host
bool SendReceiveStringList(QStringList &strlist, bool quickTimeout=false, bool block=true)
Send a message to the backend and wait for a response.
static QString GenMythURL(const QString &host=QString(), int port=0, QString path=QString(), const QString &storageGroup=QString())
QString GetBackendServerIP(void)
Returns the IP address of the locally defined backend IP.
QString GetMasterHostName(void)
bool IsMasterBackend(void)
is this the actual MBE process
Class for communcating between myth backends and frontends.
bool SendReceiveStringList(QStringList &list, uint min_reply_length=0, std::chrono::milliseconds timeoutMS=kLongTimeout)
bool ReadStringList(QStringList &list, std::chrono::milliseconds timeoutMS=kShortTimeout)
bool IsConnected(void) const
bool IsDataAvailable(void)
static constexpr std::chrono::milliseconds kShortTimeout
int Read(char *data, int size, std::chrono::milliseconds max_wait)
int Write(const char *data, int size)
bool WriteStringList(const QStringList &list)
A QElapsedTimer based timer to replace use of QTime as a timer.
std::chrono::milliseconds restart(void)
Returns milliseconds elapsed since last start() or restart() and resets the count.
std::chrono::milliseconds elapsed(void)
Returns milliseconds elapsed since last start() or restart()
bool isRunning(void) const
Returns true if start() or restart() has been called at least once since construction and since any c...
void start(void)
starts measuring elapsed time.
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
MythSocket * m_controlSock
static bool CopyFile(const QString &src, const QString &dst, bool overwrite=false, bool verify=false)
bool ReOpen(const QString &newFilename)
int Read(void *data, int size)
bool OpenInternal(void)
Attempts to resume from a disconnected step.
static QString FindFile(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for a file in the give storage group.
static QString GetFileHash(const QString &url)
QStringList m_possibleAuxFiles
void Close(bool haslock=false)
static bool MoveFile(const QString &src, const QString &dst, bool overwrite=false)
long long Seek(long long pos, int whence, long long curpos=-1)
long long GetRealFileSize(void)
GetRealFileSize: returns the current remote file's size.
ThreadedFileWriter * m_fileWriter
static QStringList FindFileList(const QString &filename, const QString &host, const QString &storageGroup, bool useRegex=false, bool allowFallback=false)
Search all BE's for files in the give storage group.
bool CheckConnection(bool repos=true)
Check current connection and re-establish it if lost.
QDateTime LastModified(void) const
MythTimer m_lastSizeCheck
std::chrono::milliseconds m_timeoutMs
bool SaveAs(QByteArray &data)
bool SetBlocking(bool m_block=true)
Set write blocking mode for the ThreadedFileWriter instance.
MythSocket * openSocket(bool control)
static bool DeleteFile(const QString &url)
static bool Exists(const QString &url, struct stat *fileinfo)
long long SeekInternal(long long pos, int whence, long long curpos=-1)
int Write(const void *data, int size)
RemoteFile(QString url="", bool write=false, bool usereadahead=true, std::chrono::milliseconds timeout=2s, const QStringList *possibleAuxiliaryFiles=nullptr)
bool IsConnected(void)
Check if both the control and data sockets are currently connected.
bool Resume(bool repos=true)
Attempts to resume from a disconnected step.
void SetTimeout(bool fast)
long long GetFileSize(void) const
GetFileSize: returns the remote file's size at the time it was first opened Will query the server in ...
QString FindFile(const QString &filename)
QStringList GetFileList(const QString &Path, bool recursive=false)
This class supports the writing of recordings to disk.
bool SetBlocking(bool block=true)
Set write blocking mode While in blocking mode, ThreadedFileWriter::Write will wait for buffers to be...
long long Seek(long long pos, int whence)
Seek to a position within stream; May be unsafe.
bool Open(void)
Opens the file we will be writing to.
void Flush(void)
Allow DiskLoop() to flush buffer completely ignoring low watermark.
int Write(const void *data, uint count)
Writes data to the end of the write buffer.
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
#define ENO
This can be appended to the LOG args with "+".
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
QString FileHash(const QString &filename)
MBASE_PUBLIC QDateTime fromSecsSinceEpoch(int64_t seconds)
This function takes the number of seconds since the start of the epoch and returns a QDateTime with t...
def read(device=None, features=[])
def write(text, progress=True)
QString RemoteDownloadFile(const QString &url, const QString &storageGroup, const QString &filename)
QString RemoteDownloadFileNow(const QString &url, const QString &storageGroup, const QString &filename)
static bool RemoteSendReceiveStringList(const QString &host, QStringList &strlist)
static constexpr int8_t O_LARGEFILE
static constexpr std::chrono::milliseconds MAX_FILE_CHECK
static QString downloadRemoteFile(const QString &cmd, const QString &url, const QString &storageGroup, const QString &filename)