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));
189 strlist.push_back(QString(
"ANN FileTransfer %1 %2 %3 %4")
192 strlist << QString(
"%1").arg(dir);
198 if (!lsock->SendReceiveStringList(strlist))
200 LOG(VB_GENERAL, LOG_ERR, loc +
201 QString(
"Did not get proper response from %1:%2")
202 .arg(host).arg(port));
204 strlist.push_back(
"ERROR");
205 strlist.push_back(
"invalid response");
208 if (strlist.size() >= 3)
210 auto it = strlist.begin(); ++it;
213 for (; it != strlist.end(); ++it)
216 else if (!strlist.isEmpty() && strlist.size() < 3 &&
217 strlist[0] !=
"ERROR")
219 LOG(VB_GENERAL, LOG_ERR, loc +
220 QString(
"Did not get proper response from %1:%2")
221 .arg(host).arg(port));
223 strlist.push_back(
"ERROR");
224 strlist.push_back(
"invalid response");
228 if (strlist.isEmpty() || strlist[0] ==
"ERROR")
232 if (strlist.isEmpty())
234 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket, timeout");
238 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket" +
239 ((strlist.size() >= 2) ?
240 QString(
", error was %1").arg(strlist[1]) :
241 QString(
", remote error")));
262 QMutexLocker locker(&
m_lock);
281 LOG(VB_FILE, LOG_WARNING, QString(
"RemoteFile::Open(%1) creating directories")
284 if (!dir.mkpath(fi.path()))
286 LOG(VB_GENERAL, LOG_ERR, QString(
"RemoteFile::Open(%1) failed to create the directories")
300 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) write mode error")
311 LOG(VB_FILE, LOG_ERR,
312 QString(
"RemoteFile::Open(%1) Error: Does not exist").arg(
m_path));
319 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) Error: %2")
320 .arg(
m_path, strerror(errno)));
354 QMutexLocker locker(&
m_lock);
358 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::ReOpen(): Couldn't connect");
364 strlist << newFilename;
371 if (!strlist.isEmpty())
372 retval = (strlist[0].toInt() != 0);
401 LOG(VB_GENERAL, LOG_ERR,
"Remote file timeout.");
426 return file.remove();
432 QString sgroup = qurl.userName();
434 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
440 if (
filename.isEmpty() || sgroup.isEmpty())
443 QStringList strlist(
"DELETE_FILE");
449 if (!strlist.isEmpty() && strlist[0] ==
"1")
460 struct stat fileinfo {};
461 return Exists(url, &fileinfo);
471 QString sgroup = qurl.userName();
472 QString host = qurl.host();
476 LOG(VB_FILE, LOG_INFO,
477 QString(
"RemoteFile::Exists(): looking for local file: %1").arg(url));
479 bool fileExists =
false;
480 QString fullFilePath =
"";
482 if (url.startsWith(
"myth:"))
486 if (!fullFilePath.isEmpty())
492 fileExists =
info.exists() ;
498 if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
500 LOG(VB_FILE, LOG_ERR,
501 QString(
"RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) +
ENO);
508 LOG(VB_FILE, LOG_INFO,
509 QString(
"RemoteFile::Exists(): looking for remote file: %1").arg(url));
511 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
520 QStringList strlist(
"QUERY_FILE_EXISTS");
522 if (!sgroup.isEmpty())
528 if ((strlist.size() >= 15) && fileinfo)
530 fileinfo->st_dev = strlist[2].toLongLong();
531 fileinfo->st_ino = strlist[3].toLongLong();
532 fileinfo->st_mode = strlist[4].toLongLong();
533 fileinfo->st_nlink = strlist[5].toLongLong();
534 fileinfo->st_uid = strlist[6].toLongLong();
535 fileinfo->st_gid = strlist[7].toLongLong();
536 fileinfo->st_rdev = strlist[8].toLongLong();
537 fileinfo->st_size = strlist[9].toLongLong();
539 fileinfo->st_blksize = strlist[10].toLongLong();
540 fileinfo->st_blocks = strlist[11].toLongLong();
542 fileinfo->st_atime = strlist[12].toLongLong();
543 fileinfo->st_mtime = strlist[13].toLongLong();
544 fileinfo->st_ctime = strlist[14].toLongLong();
566 QString sgroup = qurl.userName();
568 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
574 if (
filename.isEmpty() || sgroup.isEmpty())
577 QStringList strlist(
"QUERY_FILE_HASH");
591 bool overwrite,
bool verify)
593 LOG(VB_FILE, LOG_INFO,
594 QString(
"RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
599 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: Cannot copy a file to itself");
606 LOG(VB_GENERAL, LOG_ERR,
607 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
611 const int readSize = 2 * 1024 * 1024;
612 char *buf =
new char[readSize];
615 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
625 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: File already exists");
633 LOG(VB_GENERAL, LOG_ERR,
634 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
645 while ((srcLen = srcFile.
Read(buf, readSize)) > 0)
647 int dstLen = dstFile.
Write(buf, srcLen);
649 if (dstLen == -1 || srcLen != dstLen)
651 LOG(VB_GENERAL, LOG_ERR,
652 "RemoteFile::CopyFile: Error while trying to write to destination file.");
661 if (success && verify)
664 struct stat fileinfo {};
665 long long dstSize =
Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
667 if (dstSize != srcSize)
669 LOG(VB_GENERAL, LOG_ERR,
670 QString(
"RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
671 .arg(dstSize).arg(srcSize));
682 LOG(VB_FILE, LOG_INFO,
683 QString(
"RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
688 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: Cannot move a file to itself");
695 bool ok =
CopyFile(src, dst, overwrite,
true);
699 LOG(VB_FILE, LOG_ERR,
700 "RemoteFile::MoveFile: Failed to delete file after successful copy");
711 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: File already exists");
719 if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
722 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Rename failed");
730 if (srcUrl.userName() != dstUrl.userName())
732 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Cannot change a file's Storage Group");
736 QStringList strlist(
"MOVE_FILE");
737 strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
741 if (!strlist.isEmpty() && strlist[0] ==
"1")
744 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::MoveFile: MOVE_FILE failed with: %1")
745 .arg(strlist.join(
",")));
753 QMutexLocker locker(&
m_lock);
756 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Reset(): Called with no socket");
764 QMutexLocker locker(&
m_lock);
775 LOG(VB_FILE, LOG_ERR,
"RemoteFile::Seek(): Called with no file opened");
781 long long offset = 0LL;
782 if (whence == SEEK_SET)
785 offset = std::min(pos,
info.size());
787 else if (whence == SEEK_END)
790 offset =
info.size() + pos;
792 else if (whence == SEEK_CUR)
794 offset = ((curpos > 0) ? curpos : lseek(
m_localFile, 0, SEEK_CUR)) + pos;
804 LOG(VB_FILE, LOG_ERR,
805 QString(
"RemoteFile::Seek(): Couldn't seek to offset %1")
814 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Seek(): Couldn't connect");
820 strlist << QString::number(pos);
821 strlist << QString::number(whence);
823 strlist << QString::number(curpos);
829 if (ok && !strlist.isEmpty())
833 return strlist[0].toLongLong();
843 unsigned zerocnt = 0;
845 bool response =
false;
849 LOG(VB_NETWORK, LOG_ERR,
850 "RemoteFile::Write(): Called when not in write mode");
857 LOG(VB_FILE, LOG_ERR,
858 "RemoteFile::Write(): File not opened");
864 QMutexLocker locker(&
m_lock);
868 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Write(): Couldn't connect");
873 strlist <<
"WRITE_BLOCK";
874 strlist << QString::number(size);
878 LOG(VB_NETWORK, LOG_ERR,
879 "RemoteFile::Write(): Block notification failed");
884 while (sent < recv && !
error && zerocnt++ < 50)
886 int ret =
m_sock->
Write((
char*)data + sent, recv - sent);
893 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::Write(): socket error");
902 recv = strlist[0].toInt();
907 if (!
error && !response)
912 recv = strlist[0].toInt();
916 LOG(VB_GENERAL, LOG_ERR,
917 "RemoteFile::Write(): No response from control socket.");
922 LOG(VB_NETWORK, LOG_DEBUG,
923 QString(
"RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
924 .arg(size).arg(sent).arg(recv).arg(
error));
929 if (
error || recv != sent)
946 bool response =
false;
948 QMutexLocker locker(&
m_lock);
954 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called in writing mode");
961 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called when local file not opened");
967 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Couldn't connect");
973 LOG(VB_NETWORK, LOG_ERR,
974 "RemoteFile::Read(): Read socket not empty to start!");
980 LOG(VB_NETWORK, LOG_WARNING,
981 "RemoteFile::Read(): Control socket not empty to start!");
986 strlist <<
"REQUEST_BLOCK";
987 strlist << QString::number(size);
991 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Block request failed");
997 std::chrono::milliseconds waitms { 30ms };
1001 while (recv < sent && !
error && mtimer.
elapsed() < 10s)
1003 int ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1010 waitms += (waitms < 200ms) ? 20ms : 0ms;
1016 sent = strlist[0].toInt();
1021 ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1030 if (!
error && !response)
1037 sent = strlist[0].toInt();
1041 LOG(VB_GENERAL, LOG_ERR,
1042 "RemoteFile::Read(): No response from control socket.");
1061 LOG(VB_NETWORK, LOG_DEBUG,
1062 QString(
"Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1063 .arg(size).arg(recv).arg(sent).arg(
error));
1068 if (
error || sent != recv)
1070 LOG(VB_GENERAL, LOG_WARNING,
1071 QString(
"RemoteFile::Read(): sent %1 != recv %2")
1072 .arg(sent).arg(recv));
1078 LOG(VB_GENERAL, LOG_WARNING,
"RemoteFile::Read(): Resume failed.");
1082 LOG(VB_GENERAL, LOG_NOTICE,
"RemoteFile::Read(): Resume success.");
1114 QMutexLocker locker(&
m_lock);
1133 QMutexLocker locker(&
m_lock);
1144 struct stat fileinfo {};
1154 strlist <<
"REQUEST_SIZE";
1158 if (ok && !strlist.isEmpty())
1160 bool validate =
false;
1161 long long size = strlist[0].toLongLong(&validate);
1165 if (strlist.count() >= 2)
1173 struct stat fileinfo {};
1195 Read(data.data(), fs);
1210 QMutexLocker locker(&
m_lock);
1221 LOG(VB_NETWORK, LOG_ERR,
1222 "RemoteFile::SetTimeout(): Couldn't connect");
1229 strlist <<
"SET_TIMEOUT";
1230 strlist << QString::number((
int)fast);
1241 QFileInfo
info(url);
1242 return info.lastModified();
1247 QString sgroup = qurl.userName();
1249 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
1255 if (
filename.isEmpty() || sgroup.isEmpty())
1258 QStringList strlist(
"QUERY_SG_FILEQUERY");
1259 strlist << qurl.host();
1265 if (strlist.size() > 1) {
1266 if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1269 result = QDateTime();;
1290 const QString& storageGroup,
bool useRegex,
1295 if (!files.isEmpty())
1311 const QString& storageGroup,
bool useRegex,
1314 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1315 "(useregex: %4, allowfallback: %5)")
1317 .arg(useRegex).arg(allowFallback));
1319 if (
filename.isEmpty() || storageGroup.isEmpty())
1322 QStringList strList;
1323 QString hostName = host;
1325 if (hostName.isEmpty())
1339 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
1341 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1342 .arg(fi.path(), fi.fileName()));
1344 for (
int x = 0; x < files.size(); x++)
1346 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Found '%1 - %2'")
1347 .arg(x).arg(files[x]));
1350 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1351 strList.reserve(filteredFiles.size());
1352 for (
const QString&
file : std::as_const(filteredFiles))
1356 fi.path() +
'/' +
file,
1370 if (!strList.isEmpty() || !allowFallback)
1377 strList <<
"QUERY_FINDFILE" << hostName << storageGroup <<
filename
1378 << (useRegex ?
"1" :
"0")
1383 if (!strList.empty() && !strList[0].isEmpty() &&
1384 strList[0] !=
"NOT FOUND" && !strList[0].startsWith(
"ERROR: "))
1452 LOG(VB_FILE, LOG_ERR,
1453 QString(
"RemoteFile::Resume: Enable to re-seek into last known "
1463 const QString &storageGroup,
1466 QStringList strlist(cmd);
1468 strlist << storageGroup;
1473 if (!ok || strlist.size() < 2 || strlist[0] !=
"OK")
1475 LOG(VB_GENERAL, LOG_ERR,
1476 "downloadRemoteFile(): " + cmd +
" returned ERROR!");
1484 const QString &storageGroup,
1491 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)