5 #include <QRegularExpression>
12 #include "mythconfig.h"
43 QString ann = QString(
"ANN Playback %1 0")
47 bool mismatch =
false;
50 addr, port, ann, &mismatch);
70 std::chrono::milliseconds
timeout,
71 const QStringList *possibleAuxiliaryFiles) :
72 m_path(std::move(url)),
73 m_useReadAhead(usereadahead), m_timeoutMs(
timeout),
81 else if (possibleAuxiliaryFiles)
89 LOG(VB_FILE, LOG_DEBUG, QString(
"RemoteFile(%1)").arg(
m_path));
109 bool is_local = !lpath.isEmpty() &&
110 !lpath.startsWith(
"myth:") &&
125 QString host = qurl.host();
126 int port = qurl.port();
131 dir +=
"?" + QUrl::fromPercentEncoding(
132 qurl.query(QUrl::FullyEncoded).toLocal8Bit());
134 if (qurl.hasFragment())
135 dir +=
"#" + qurl.fragment();
137 QString sgroup = qurl.userName();
140 QString stype = (control) ?
"control socket" :
"file data socket";
142 QString loc = QString(
"RemoteFile::openSocket(%1): ").arg(stype);
149 if (!lsock->ConnectToHost(host, port))
151 LOG(VB_GENERAL, LOG_ERR, loc +
152 QString(
"Could not connect to server %1:%2") .arg(host).arg(port));
161 #ifndef IGNORE_PROTO_VER_MISMATCH
164 LOG(VB_GENERAL, LOG_ERR, loc +
165 QString(
"Failed validation to server %1:%2").arg(host).arg(port));
173 strlist.append(QString(
"ANN Playback %1 %2")
174 .arg(
hostname).arg(
static_cast<int>(
false)));
175 if (!lsock->SendReceiveStringList(strlist))
177 LOG(VB_GENERAL, LOG_ERR, loc +
178 QString(
"Could not read string list from server %1:%2")
179 .arg(host).arg(port));
186 strlist.push_back(QString(
"ANN FileTransfer %1 %2 %3 %4")
189 strlist << QString(
"%1").arg(dir);
195 if (!lsock->SendReceiveStringList(strlist))
197 LOG(VB_GENERAL, LOG_ERR, loc +
198 QString(
"Did not get proper response from %1:%2")
199 .arg(host).arg(port));
201 strlist.push_back(
"ERROR");
202 strlist.push_back(
"invalid response");
205 if (strlist.size() >= 3)
207 auto it = strlist.begin(); ++it;
210 for (; it != strlist.end(); ++it)
213 else if (!strlist.isEmpty() && strlist.size() < 3 &&
214 strlist[0] !=
"ERROR")
216 LOG(VB_GENERAL, LOG_ERR, loc +
217 QString(
"Did not get proper response from %1:%2")
218 .arg(host).arg(port));
220 strlist.push_back(
"ERROR");
221 strlist.push_back(
"invalid response");
225 if (strlist.isEmpty() || strlist[0] ==
"ERROR")
229 if (strlist.isEmpty())
231 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket, timeout");
235 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket" +
236 ((strlist.size() >= 2) ?
237 QString(
", error was %1").arg(strlist[1]) :
238 QString(
", remote error")));
259 QMutexLocker locker(&
m_lock);
278 LOG(VB_FILE, LOG_WARNING, QString(
"RemoteFile::Open(%1) creating directories")
281 if (!dir.mkpath(fi.path()))
283 LOG(VB_GENERAL, LOG_ERR, QString(
"RemoteFile::Open(%1) failed to create the directories")
297 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) write mode error")
308 LOG(VB_FILE, LOG_ERR,
309 QString(
"RemoteFile::Open(%1) Error: Does not exist").arg(
m_path));
316 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) Error: %2")
317 .arg(
m_path, strerror(errno)));
351 QMutexLocker locker(&
m_lock);
355 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::ReOpen(): Couldn't connect");
361 strlist << newFilename;
368 if (!strlist.isEmpty())
369 retval = (strlist[0].toInt() != 0);
398 LOG(VB_GENERAL, LOG_ERR,
"Remote file timeout.");
423 return file.remove();
429 QString sgroup = qurl.userName();
431 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
437 if (
filename.isEmpty() || sgroup.isEmpty())
440 QStringList strlist(
"DELETE_FILE");
446 if (!strlist.isEmpty() && strlist[0] ==
"1")
457 struct stat fileinfo {};
458 return Exists(url, &fileinfo);
468 QString sgroup = qurl.userName();
469 QString host = qurl.host();
473 LOG(VB_FILE, LOG_INFO,
474 QString(
"RemoteFile::Exists(): looking for local file: %1").arg(url));
476 bool fileExists =
false;
477 QString fullFilePath =
"";
479 if (url.startsWith(
"myth:"))
483 if (!fullFilePath.isEmpty())
489 fileExists =
info.exists() ;
495 if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
497 LOG(VB_FILE, LOG_ERR,
498 QString(
"RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) +
ENO);
505 LOG(VB_FILE, LOG_INFO,
506 QString(
"RemoteFile::Exists(): looking for remote file: %1").arg(url));
508 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
517 QStringList strlist(
"QUERY_FILE_EXISTS");
519 if (!sgroup.isEmpty())
525 if ((strlist.size() >= 15) && fileinfo)
527 fileinfo->st_dev = strlist[2].toLongLong();
528 fileinfo->st_ino = strlist[3].toLongLong();
529 fileinfo->st_mode = strlist[4].toLongLong();
530 fileinfo->st_nlink = strlist[5].toLongLong();
531 fileinfo->st_uid = strlist[6].toLongLong();
532 fileinfo->st_gid = strlist[7].toLongLong();
533 fileinfo->st_rdev = strlist[8].toLongLong();
534 fileinfo->st_size = strlist[9].toLongLong();
536 fileinfo->st_blksize = strlist[10].toLongLong();
537 fileinfo->st_blocks = strlist[11].toLongLong();
539 fileinfo->st_atime = strlist[12].toLongLong();
540 fileinfo->st_mtime = strlist[13].toLongLong();
541 fileinfo->st_ctime = strlist[14].toLongLong();
563 QString sgroup = qurl.userName();
565 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
571 if (
filename.isEmpty() || sgroup.isEmpty())
574 QStringList strlist(
"QUERY_FILE_HASH");
588 bool overwrite,
bool verify)
590 LOG(VB_FILE, LOG_INFO,
591 QString(
"RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
596 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: Cannot copy a file to itself");
603 LOG(VB_GENERAL, LOG_ERR,
604 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
608 const int readSize = 2 * 1024 * 1024;
609 char *buf =
new char[readSize];
612 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
622 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: File already exists");
630 LOG(VB_GENERAL, LOG_ERR,
631 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
642 while ((srcLen = srcFile.
Read(buf, readSize)) > 0)
644 int dstLen = dstFile.
Write(buf, srcLen);
646 if (dstLen == -1 || srcLen != dstLen)
648 LOG(VB_GENERAL, LOG_ERR,
649 "RemoteFile::CopyFile: Error while trying to write to destination file.");
658 if (success && verify)
661 struct stat fileinfo {};
662 long long dstSize =
Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
664 if (dstSize != srcSize)
666 LOG(VB_GENERAL, LOG_ERR,
667 QString(
"RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
668 .arg(dstSize).arg(srcSize));
679 LOG(VB_FILE, LOG_INFO,
680 QString(
"RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
685 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: Cannot move a file to itself");
692 bool ok =
CopyFile(src, dst, overwrite,
true);
696 LOG(VB_FILE, LOG_ERR,
697 "RemoteFile::MoveFile: Failed to delete file after successful copy");
708 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: File already exists");
716 if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
719 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Rename failed");
727 if (srcUrl.userName() != dstUrl.userName())
729 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Cannot change a file's Storage Group");
733 QStringList strlist(
"MOVE_FILE");
734 strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
738 if (!strlist.isEmpty() && strlist[0] ==
"1")
741 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::MoveFile: MOVE_FILE failed with: %1")
742 .arg(strlist.join(
",")));
750 QMutexLocker locker(&
m_lock);
753 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Reset(): Called with no socket");
761 QMutexLocker locker(&
m_lock);
772 LOG(VB_FILE, LOG_ERR,
"RemoteFile::Seek(): Called with no file opened");
778 long long offset = 0LL;
779 if (whence == SEEK_SET)
782 offset = std::min(pos,
info.size());
784 else if (whence == SEEK_END)
787 offset =
info.size() + pos;
789 else if (whence == SEEK_CUR)
791 offset = ((curpos > 0) ? curpos : ::lseek64(
m_localFile, 0, SEEK_CUR)) + pos;
798 off64_t localpos = ::lseek64(
m_localFile, pos, whence);
801 LOG(VB_FILE, LOG_ERR,
802 QString(
"RemoteFile::Seek(): Couldn't seek to offset %1")
811 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Seek(): Couldn't connect");
817 strlist << QString::number(pos);
818 strlist << QString::number(whence);
820 strlist << QString::number(curpos);
826 if (ok && !strlist.isEmpty())
830 return strlist[0].toLongLong();
840 unsigned zerocnt = 0;
842 bool response =
false;
846 LOG(VB_NETWORK, LOG_ERR,
847 "RemoteFile::Write(): Called when not in write mode");
854 LOG(VB_FILE, LOG_ERR,
855 "RemoteFile::Write(): File not opened");
861 QMutexLocker locker(&
m_lock);
865 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Write(): Couldn't connect");
870 strlist <<
"WRITE_BLOCK";
871 strlist << QString::number(size);
875 LOG(VB_NETWORK, LOG_ERR,
876 "RemoteFile::Write(): Block notification failed");
881 while (sent < recv && !
error && zerocnt++ < 50)
883 int ret =
m_sock->
Write((
char*)data + sent, recv - sent);
890 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::Write(): socket error");
899 recv = strlist[0].toInt();
904 if (!
error && !response)
909 recv = strlist[0].toInt();
913 LOG(VB_GENERAL, LOG_ERR,
914 "RemoteFile::Write(): No response from control socket.");
919 LOG(VB_NETWORK, LOG_DEBUG,
920 QString(
"RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
921 .arg(size).arg(sent).arg(recv).arg(
error));
926 if (
error || recv != sent)
943 bool response =
false;
945 QMutexLocker locker(&
m_lock);
951 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called in writing mode");
958 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called when local file not opened");
964 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Couldn't connect");
970 LOG(VB_NETWORK, LOG_ERR,
971 "RemoteFile::Read(): Read socket not empty to start!");
977 LOG(VB_NETWORK, LOG_WARNING,
978 "RemoteFile::Read(): Control socket not empty to start!");
983 strlist <<
"REQUEST_BLOCK";
984 strlist << QString::number(size);
988 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Block request failed");
994 std::chrono::milliseconds waitms { 30ms };
1000 int ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1007 waitms += (waitms < 200ms) ? 20ms : 0ms;
1013 sent = strlist[0].toInt();
1018 ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1027 if (!
error && !response)
1034 sent = strlist[0].toInt();
1038 LOG(VB_GENERAL, LOG_ERR,
1039 "RemoteFile::Read(): No response from control socket.");
1058 LOG(VB_NETWORK, LOG_DEBUG,
1059 QString(
"Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1060 .arg(size).arg(recv).arg(sent).arg(
error));
1065 if (
error || sent != recv)
1067 LOG(VB_GENERAL, LOG_WARNING,
1068 QString(
"RemoteFile::Read(): sent %1 != recv %2")
1069 .arg(sent).arg(recv));
1075 LOG(VB_GENERAL, LOG_WARNING,
"RemoteFile::Read(): Resume failed.");
1079 LOG(VB_GENERAL, LOG_NOTICE,
"RemoteFile::Read(): Resume success.");
1111 QMutexLocker locker(&
m_lock);
1130 QMutexLocker locker(&
m_lock);
1141 struct stat fileinfo {};
1151 strlist <<
"REQUEST_SIZE";
1155 if (ok && !strlist.isEmpty())
1157 bool validate =
false;
1158 long long size = strlist[0].toLongLong(&validate);
1162 if (strlist.count() >= 2)
1170 struct stat fileinfo {};
1192 Read(data.data(), fs);
1207 QMutexLocker locker(&
m_lock);
1218 LOG(VB_NETWORK, LOG_ERR,
1219 "RemoteFile::SetTimeout(): Couldn't connect");
1226 strlist <<
"SET_TIMEOUT";
1227 strlist << QString::number((
int)fast);
1238 QFileInfo
info(url);
1239 return info.lastModified();
1244 QString sgroup = qurl.userName();
1246 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
1252 if (
filename.isEmpty() || sgroup.isEmpty())
1255 QStringList strlist(
"QUERY_SG_FILEQUERY");
1256 strlist << qurl.host();
1262 if (strlist.size() > 1) {
1263 if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1266 result = QDateTime();;
1287 const QString& storageGroup,
bool useRegex,
1292 if (!files.isEmpty())
1308 const QString& storageGroup,
bool useRegex,
1311 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1312 "(useregex: %4, allowfallback: %5)")
1314 .arg(useRegex).arg(allowFallback));
1316 if (
filename.isEmpty() || storageGroup.isEmpty())
1319 QStringList strList;
1320 QString hostName = host;
1322 if (hostName.isEmpty())
1336 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
1338 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1339 .arg(fi.path(), fi.fileName()));
1341 for (
int x = 0; x < files.size(); x++)
1343 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Found '%1 - %2'")
1344 .arg(x).arg(files[x]));
1347 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1348 for (
const QString&
file : std::as_const(filteredFiles))
1352 fi.path() +
'/' +
file,
1366 if (!strList.isEmpty() || !allowFallback)
1373 strList <<
"QUERY_FINDFILE" << hostName << storageGroup <<
filename
1374 << (useRegex ?
"1" :
"0")
1379 if (!strList.empty() && !strList[0].isEmpty() &&
1380 strList[0] !=
"NOT FOUND" && !strList[0].startsWith(
"ERROR: "))
1448 LOG(VB_FILE, LOG_ERR,
1449 QString(
"RemoteFile::Resume: Enable to re-seek into last known "