5 #include <QRegularExpression>
12 #include "mythconfig.h"
42 QString ann = QString(
"ANN Playback %1 0")
46 bool mismatch =
false;
49 addr, port, ann, &mismatch);
69 std::chrono::milliseconds
timeout,
70 const QStringList *possibleAuxiliaryFiles) :
71 m_path(std::move(url)),
72 m_useReadAhead(usereadahead), m_timeoutMs(
timeout),
80 else if (possibleAuxiliaryFiles)
88 LOG(VB_FILE, LOG_DEBUG, QString(
"RemoteFile(%1)").arg(
m_path));
108 bool is_local = !lpath.isEmpty() &&
109 !lpath.startsWith(
"myth:") &&
124 QString host = qurl.host();
125 int port = qurl.port();
130 dir +=
"?" + QUrl::fromPercentEncoding(
131 qurl.query(QUrl::FullyEncoded).toLocal8Bit());
133 if (qurl.hasFragment())
134 dir +=
"#" + qurl.fragment();
136 QString sgroup = qurl.userName();
139 QString stype = (control) ?
"control socket" :
"file data socket";
141 QString loc = QString(
"RemoteFile::openSocket(%1): ").arg(stype);
148 if (!lsock->ConnectToHost(host, port))
150 LOG(VB_GENERAL, LOG_ERR, loc +
151 QString(
"Could not connect to server %1:%2") .arg(host).arg(port));
160 #ifndef IGNORE_PROTO_VER_MISMATCH
163 LOG(VB_GENERAL, LOG_ERR, loc +
164 QString(
"Failed validation to server %1:%2").arg(host).arg(port));
172 strlist.append(QString(
"ANN Playback %1 %2")
173 .arg(
hostname).arg(
static_cast<int>(
false)));
174 if (!lsock->SendReceiveStringList(strlist))
176 LOG(VB_GENERAL, LOG_ERR, loc +
177 QString(
"Could not read string list from server %1:%2")
178 .arg(host).arg(port));
185 strlist.push_back(QString(
"ANN FileTransfer %1 %2 %3 %4")
188 strlist << QString(
"%1").arg(dir);
194 if (!lsock->SendReceiveStringList(strlist))
196 LOG(VB_GENERAL, LOG_ERR, loc +
197 QString(
"Did not get proper response from %1:%2")
198 .arg(host).arg(port));
200 strlist.push_back(
"ERROR");
201 strlist.push_back(
"invalid response");
204 if (strlist.size() >= 3)
206 auto it = strlist.begin(); ++it;
209 for (; it != strlist.end(); ++it)
212 else if (!strlist.isEmpty() && strlist.size() < 3 &&
213 strlist[0] !=
"ERROR")
215 LOG(VB_GENERAL, LOG_ERR, loc +
216 QString(
"Did not get proper response from %1:%2")
217 .arg(host).arg(port));
219 strlist.push_back(
"ERROR");
220 strlist.push_back(
"invalid response");
224 if (strlist.isEmpty() || strlist[0] ==
"ERROR")
228 if (strlist.isEmpty())
230 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket, timeout");
234 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket" +
235 ((strlist.size() >= 2) ?
236 QString(
", error was %1").arg(strlist[1]) :
237 QString(
", remote error")));
258 QMutexLocker locker(&
m_lock);
277 LOG(VB_FILE, LOG_WARNING, QString(
"RemoteFile::Open(%1) creating directories")
280 if (!dir.mkpath(fi.path()))
282 LOG(VB_GENERAL, LOG_ERR, QString(
"RemoteFile::Open(%1) failed to create the directories")
296 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) write mode error")
307 LOG(VB_FILE, LOG_ERR,
308 QString(
"RemoteFile::Open(%1) Error: Does not exist").arg(
m_path));
315 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) Error: %2")
316 .arg(
m_path, strerror(errno)));
350 QMutexLocker locker(&
m_lock);
354 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::ReOpen(): Couldn't connect");
360 strlist << newFilename;
367 if (!strlist.isEmpty())
368 retval = (strlist[0].toInt() != 0);
397 LOG(VB_GENERAL, LOG_ERR,
"Remote file timeout.");
422 return file.remove();
428 QString sgroup = qurl.userName();
430 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
436 if (
filename.isEmpty() || sgroup.isEmpty())
439 QStringList strlist(
"DELETE_FILE");
445 if (!strlist.isEmpty() && strlist[0] ==
"1")
456 struct stat fileinfo {};
457 return Exists(url, &fileinfo);
467 QString sgroup = qurl.userName();
468 QString host = qurl.host();
472 LOG(VB_FILE, LOG_INFO,
473 QString(
"RemoteFile::Exists(): looking for local file: %1").arg(url));
475 bool fileExists =
false;
476 QString fullFilePath =
"";
478 if (url.startsWith(
"myth:"))
482 if (!fullFilePath.isEmpty())
488 fileExists =
info.exists() ;
494 if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
496 LOG(VB_FILE, LOG_ERR,
497 QString(
"RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) +
ENO);
504 LOG(VB_FILE, LOG_INFO,
505 QString(
"RemoteFile::Exists(): looking for remote file: %1").arg(url));
507 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
516 QStringList strlist(
"QUERY_FILE_EXISTS");
518 if (!sgroup.isEmpty())
524 if ((strlist.size() >= 15) && fileinfo)
526 fileinfo->st_dev = strlist[2].toLongLong();
527 fileinfo->st_ino = strlist[3].toLongLong();
528 fileinfo->st_mode = strlist[4].toLongLong();
529 fileinfo->st_nlink = strlist[5].toLongLong();
530 fileinfo->st_uid = strlist[6].toLongLong();
531 fileinfo->st_gid = strlist[7].toLongLong();
532 fileinfo->st_rdev = strlist[8].toLongLong();
533 fileinfo->st_size = strlist[9].toLongLong();
535 fileinfo->st_blksize = strlist[10].toLongLong();
536 fileinfo->st_blocks = strlist[11].toLongLong();
538 fileinfo->st_atime = strlist[12].toLongLong();
539 fileinfo->st_mtime = strlist[13].toLongLong();
540 fileinfo->st_ctime = strlist[14].toLongLong();
562 QString sgroup = qurl.userName();
564 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
570 if (
filename.isEmpty() || sgroup.isEmpty())
573 QStringList strlist(
"QUERY_FILE_HASH");
587 bool overwrite,
bool verify)
589 LOG(VB_FILE, LOG_INFO,
590 QString(
"RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
595 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: Cannot copy a file to itself");
602 LOG(VB_GENERAL, LOG_ERR,
603 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
607 const int readSize = 2 * 1024 * 1024;
608 char *buf =
new char[readSize];
611 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
621 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: File already exists");
629 LOG(VB_GENERAL, LOG_ERR,
630 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
641 while ((srcLen = srcFile.
Read(buf, readSize)) > 0)
643 int dstLen = dstFile.
Write(buf, srcLen);
645 if (dstLen == -1 || srcLen != dstLen)
647 LOG(VB_GENERAL, LOG_ERR,
648 "RemoteFile::CopyFile: Error while trying to write to destination file.");
657 if (success && verify)
660 struct stat fileinfo {};
661 long long dstSize =
Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
663 if (dstSize != srcSize)
665 LOG(VB_GENERAL, LOG_ERR,
666 QString(
"RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
667 .arg(dstSize).arg(srcSize));
678 LOG(VB_FILE, LOG_INFO,
679 QString(
"RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
684 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: Cannot move a file to itself");
691 bool ok =
CopyFile(src, dst, overwrite,
true);
695 LOG(VB_FILE, LOG_ERR,
696 "RemoteFile::MoveFile: Failed to delete file after successful copy");
707 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: File already exists");
715 if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
718 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Rename failed");
726 if (srcUrl.userName() != dstUrl.userName())
728 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Cannot change a file's Storage Group");
732 QStringList strlist(
"MOVE_FILE");
733 strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
737 if (!strlist.isEmpty() && strlist[0] ==
"1")
740 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::MoveFile: MOVE_FILE failed with: %1")
741 .arg(strlist.join(
",")));
749 QMutexLocker locker(&
m_lock);
752 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Reset(): Called with no socket");
760 QMutexLocker locker(&
m_lock);
771 LOG(VB_FILE, LOG_ERR,
"RemoteFile::Seek(): Called with no file opened");
777 long long offset = 0LL;
778 if (whence == SEEK_SET)
781 offset = std::min(pos,
info.size());
783 else if (whence == SEEK_END)
786 offset =
info.size() + pos;
788 else if (whence == SEEK_CUR)
790 offset = ((curpos > 0) ? curpos : ::lseek64(
m_localFile, 0, SEEK_CUR)) + pos;
797 off64_t localpos = ::lseek64(
m_localFile, pos, whence);
800 LOG(VB_FILE, LOG_ERR,
801 QString(
"RemoteFile::Seek(): Couldn't seek to offset %1")
810 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Seek(): Couldn't connect");
816 strlist << QString::number(pos);
817 strlist << QString::number(whence);
819 strlist << QString::number(curpos);
825 if (ok && !strlist.isEmpty())
829 return strlist[0].toLongLong();
839 unsigned zerocnt = 0;
841 bool response =
false;
845 LOG(VB_NETWORK, LOG_ERR,
846 "RemoteFile::Write(): Called when not in write mode");
853 LOG(VB_FILE, LOG_ERR,
854 "RemoteFile::Write(): File not opened");
860 QMutexLocker locker(&
m_lock);
864 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Write(): Couldn't connect");
869 strlist <<
"WRITE_BLOCK";
870 strlist << QString::number(size);
874 LOG(VB_NETWORK, LOG_ERR,
875 "RemoteFile::Write(): Block notification failed");
880 while (sent < recv && !
error && zerocnt++ < 50)
882 int ret =
m_sock->
Write((
char*)data + sent, recv - sent);
889 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::Write(): socket error");
898 recv = strlist[0].toInt();
903 if (!
error && !response)
908 recv = strlist[0].toInt();
912 LOG(VB_GENERAL, LOG_ERR,
913 "RemoteFile::Write(): No response from control socket.");
918 LOG(VB_NETWORK, LOG_DEBUG,
919 QString(
"RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
920 .arg(size).arg(sent).arg(recv).arg(
error));
925 if (
error || recv != sent)
942 bool response =
false;
944 QMutexLocker locker(&
m_lock);
950 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called in writing mode");
957 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called when local file not opened");
963 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Couldn't connect");
969 LOG(VB_NETWORK, LOG_ERR,
970 "RemoteFile::Read(): Read socket not empty to start!");
976 LOG(VB_NETWORK, LOG_WARNING,
977 "RemoteFile::Read(): Control socket not empty to start!");
982 strlist <<
"REQUEST_BLOCK";
983 strlist << QString::number(size);
987 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Block request failed");
993 std::chrono::milliseconds waitms { 30ms };
999 int ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1006 waitms += (waitms < 200ms) ? 20ms : 0ms;
1012 sent = strlist[0].toInt();
1017 ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1026 if (!
error && !response)
1033 sent = strlist[0].toInt();
1037 LOG(VB_GENERAL, LOG_ERR,
1038 "RemoteFile::Read(): No response from control socket.");
1057 LOG(VB_NETWORK, LOG_DEBUG,
1058 QString(
"Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1059 .arg(size).arg(recv).arg(sent).arg(
error));
1064 if (
error || sent != recv)
1066 LOG(VB_GENERAL, LOG_WARNING,
1067 QString(
"RemoteFile::Read(): sent %1 != recv %2")
1068 .arg(sent).arg(recv));
1074 LOG(VB_GENERAL, LOG_WARNING,
"RemoteFile::Read(): Resume failed.");
1078 LOG(VB_GENERAL, LOG_NOTICE,
"RemoteFile::Read(): Resume success.");
1110 QMutexLocker locker(&
m_lock);
1129 QMutexLocker locker(&
m_lock);
1140 struct stat fileinfo {};
1150 strlist <<
"REQUEST_SIZE";
1154 if (ok && !strlist.isEmpty())
1156 bool validate =
false;
1157 long long size = strlist[0].toLongLong(&validate);
1161 if (strlist.count() >= 2)
1169 struct stat fileinfo {};
1191 Read(data.data(), fs);
1206 QMutexLocker locker(&
m_lock);
1217 LOG(VB_NETWORK, LOG_ERR,
1218 "RemoteFile::SetTimeout(): Couldn't connect");
1225 strlist <<
"SET_TIMEOUT";
1226 strlist << QString::number((
int)fast);
1237 QFileInfo
info(url);
1238 return info.lastModified();
1243 QString sgroup = qurl.userName();
1245 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
1251 if (
filename.isEmpty() || sgroup.isEmpty())
1254 QStringList strlist(
"QUERY_SG_FILEQUERY");
1255 strlist << qurl.host();
1261 if (strlist.size() > 1) {
1262 if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1265 result = QDateTime();;
1286 const QString& storageGroup,
bool useRegex,
1291 if (!files.isEmpty())
1307 const QString& storageGroup,
bool useRegex,
1310 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1311 "(useregex: %4, allowfallback: %5)")
1313 .arg(useRegex).arg(allowFallback));
1315 if (
filename.isEmpty() || storageGroup.isEmpty())
1318 QStringList strList;
1319 QString hostName = host;
1321 if (hostName.isEmpty())
1335 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
1337 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1338 .arg(fi.path(), fi.fileName()));
1340 for (
int x = 0; x < files.size(); x++)
1342 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Found '%1 - %2'")
1343 .arg(x).arg(files[x]));
1346 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1347 for (
const QString&
file : std::as_const(filteredFiles))
1351 fi.path() +
'/' +
file,
1365 if (!strList.isEmpty() || !allowFallback)
1372 strList <<
"QUERY_FINDFILE" << hostName << storageGroup <<
filename
1373 << (useRegex ?
"1" :
"0")
1378 if (!strList.empty() && !strList[0].isEmpty() &&
1379 strList[0] !=
"NOT FOUND" && !strList[0].startsWith(
"ERROR: "))
1447 LOG(VB_FILE, LOG_ERR,
1448 QString(
"RemoteFile::Resume: Enable to re-seek into last known "