5 #include <QRegularExpression>
12 #include "mythconfig.h"
42 QString ann = QString(
"ANN Playback %1 0")
46 bool mismatch =
false;
49 addr, port, ann, &mismatch);
65 std::chrono::milliseconds
timeout,
66 const QStringList *possibleAuxiliaryFiles) :
67 m_path(
std::move(url)),
68 m_useReadAhead(usereadahead), m_timeoutMs(
timeout),
76 else if (possibleAuxiliaryFiles)
82 LOG(VB_FILE, LOG_DEBUG, QString(
"RemoteFile(%1)").arg(
m_path));
102 bool is_local = !lpath.isEmpty() &&
103 !lpath.startsWith(
"myth:") &&
104 (lpath.startsWith(
"/") || QFile::exists(lpath));
118 QString host = qurl.host();
119 int port = qurl.port();
124 dir +=
"?" + QUrl::fromPercentEncoding(
125 qurl.query(QUrl::FullyEncoded).toLocal8Bit());
127 if (qurl.hasFragment())
128 dir +=
"#" + qurl.fragment();
130 QString sgroup = qurl.userName();
133 QString stype = (control) ?
"control socket" :
"file data socket";
135 QString loc = QString(
"RemoteFile::openSocket(%1): ").arg(stype);
142 if (!lsock->ConnectToHost(host, port))
144 LOG(VB_GENERAL, LOG_ERR, loc +
145 QString(
"Could not connect to server %1:%2") .arg(host).arg(port));
154 #ifndef IGNORE_PROTO_VER_MISMATCH
157 LOG(VB_GENERAL, LOG_ERR, loc +
158 QString(
"Failed validation to server %1:%2").arg(host).arg(port));
166 strlist.append(QString(
"ANN Playback %1 %2")
167 .arg(
hostname).arg(
static_cast<int>(
false)));
168 if (!lsock->SendReceiveStringList(strlist))
170 LOG(VB_GENERAL, LOG_ERR, loc +
171 QString(
"Could not read string list from server %1:%2")
172 .arg(host).arg(port));
179 strlist.push_back(QString(
"ANN FileTransfer %1 %2 %3 %4")
182 strlist << QString(
"%1").arg(dir);
188 if (!lsock->SendReceiveStringList(strlist))
190 LOG(VB_GENERAL, LOG_ERR, loc +
191 QString(
"Did not get proper response from %1:%2")
192 .arg(host).arg(port));
194 strlist.push_back(
"ERROR");
195 strlist.push_back(
"invalid response");
198 if (strlist.size() >= 3)
200 auto it = strlist.begin(); ++it;
203 for (; it != strlist.end(); ++it)
206 else if (!strlist.isEmpty() && strlist.size() < 3 &&
207 strlist[0] !=
"ERROR")
209 LOG(VB_GENERAL, LOG_ERR, loc +
210 QString(
"Did not get proper response from %1:%2")
211 .arg(host).arg(port));
213 strlist.push_back(
"ERROR");
214 strlist.push_back(
"invalid response");
218 if (strlist.isEmpty() || strlist[0] ==
"ERROR")
222 if (strlist.isEmpty())
224 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket, timeout");
228 LOG(VB_GENERAL, LOG_ERR, loc +
"Failed to open socket" +
229 ((strlist.size() >= 2) ?
230 QString(
", error was %1").arg(strlist[1]) :
231 QString(
", remote error")));
252 QMutexLocker locker(&
m_lock);
271 LOG(VB_FILE, LOG_WARNING, QString(
"RemoteFile::Open(%1) creating directories")
274 if (!dir.mkpath(fi.path()))
276 LOG(VB_GENERAL, LOG_ERR, QString(
"RemoteFile::Open(%1) failed to create the directories")
290 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) write mode error")
301 LOG(VB_FILE, LOG_ERR,
302 QString(
"RemoteFile::Open(%1) Error: Does not exist").arg(
m_path));
309 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::Open(%1) Error: %2")
310 .arg(
m_path, strerror(errno)));
344 QMutexLocker locker(&
m_lock);
348 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::ReOpen(): Couldn't connect");
354 strlist << newFilename;
361 if (!strlist.isEmpty())
362 retval = (strlist[0].toInt() != 0);
391 LOG(VB_GENERAL, LOG_ERR,
"Remote file timeout.");
416 return file.remove();
422 QString sgroup = qurl.userName();
424 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
430 if (
filename.isEmpty() || sgroup.isEmpty())
433 QStringList strlist(
"DELETE_FILE");
439 if (!strlist.isEmpty() && strlist[0] ==
"1")
450 struct stat fileinfo {};
451 return Exists(url, &fileinfo);
461 QString sgroup = qurl.userName();
462 QString host = qurl.host();
466 LOG(VB_FILE, LOG_INFO,
467 QString(
"RemoteFile::Exists(): looking for local file: %1").arg(url));
469 bool fileExists =
false;
470 QString fullFilePath =
"";
472 if (url.startsWith(
"myth:"))
476 if (!fullFilePath.isEmpty())
482 fileExists = info.exists() ;
488 if (stat(fullFilePath.toLocal8Bit().constData(), fileinfo) == -1)
490 LOG(VB_FILE, LOG_ERR,
491 QString(
"RemoteFile::Exists(): failed to stat file: %1").arg(fullFilePath) +
ENO);
498 LOG(VB_FILE, LOG_INFO,
499 QString(
"RemoteFile::Exists(): looking for remote file: %1").arg(url));
501 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
510 QStringList strlist(
"QUERY_FILE_EXISTS");
512 if (!sgroup.isEmpty())
518 if ((strlist.size() >= 15) && fileinfo)
520 fileinfo->st_dev = strlist[2].toLongLong();
521 fileinfo->st_ino = strlist[3].toLongLong();
522 fileinfo->st_mode = strlist[4].toLongLong();
523 fileinfo->st_nlink = strlist[5].toLongLong();
524 fileinfo->st_uid = strlist[6].toLongLong();
525 fileinfo->st_gid = strlist[7].toLongLong();
526 fileinfo->st_rdev = strlist[8].toLongLong();
527 fileinfo->st_size = strlist[9].toLongLong();
529 fileinfo->st_blksize = strlist[10].toLongLong();
530 fileinfo->st_blocks = strlist[11].toLongLong();
532 fileinfo->st_atime = strlist[12].toLongLong();
533 fileinfo->st_mtime = strlist[13].toLongLong();
534 fileinfo->st_ctime = strlist[14].toLongLong();
556 QString sgroup = qurl.userName();
558 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
564 if (
filename.isEmpty() || sgroup.isEmpty())
567 QStringList strlist(
"QUERY_FILE_HASH");
581 bool overwrite,
bool verify)
583 LOG(VB_FILE, LOG_INFO,
584 QString(
"RemoteFile::CopyFile: Copying file from '%1' to '%2'").arg(src, dst));
589 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: Cannot copy a file to itself");
596 LOG(VB_GENERAL, LOG_ERR,
597 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for reading.").arg(src));
601 const int readSize = 2 * 1024 * 1024;
602 char *buf =
new char[readSize];
605 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: ERROR, unable to allocate copy buffer");
615 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::CopyFile: File already exists");
623 LOG(VB_GENERAL, LOG_ERR,
624 QString(
"RemoteFile::CopyFile: Failed to open file (%1) for writing.").arg(dst));
635 while ((srcLen = srcFile.
Read(buf, readSize)) > 0)
637 int dstLen = dstFile.
Write(buf, srcLen);
639 if (dstLen == -1 || srcLen != dstLen)
641 LOG(VB_GENERAL, LOG_ERR,
642 "RemoteFile::CopyFile: Error while trying to write to destination file.");
651 if (success && verify)
654 struct stat fileinfo {};
655 long long dstSize =
Exists(dst, &fileinfo) ? fileinfo.st_size : -1;
657 if (dstSize != srcSize)
659 LOG(VB_GENERAL, LOG_ERR,
660 QString(
"RemoteFile::CopyFile: Copied file is wrong size (%1 rather than %2)")
661 .arg(dstSize).arg(srcSize));
672 LOG(VB_FILE, LOG_INFO,
673 QString(
"RemoteFile::MoveFile: Moving file from '%1' to '%2'").arg(src, dst));
678 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: Cannot move a file to itself");
685 bool ok =
CopyFile(src, dst, overwrite,
true);
689 LOG(VB_FILE, LOG_ERR,
690 "RemoteFile::MoveFile: Failed to delete file after successful copy");
701 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::MoveFile: File already exists");
709 if (QDir().mkpath(fi.path()) && QFile::rename(src, dst))
712 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Rename failed");
720 if (srcUrl.userName() != dstUrl.userName())
722 LOG(VB_FILE, LOG_ERR,
"RemoteFile::MoveFile: Cannot change a file's Storage Group");
726 QStringList strlist(
"MOVE_FILE");
727 strlist << srcUrl.userName() << srcUrl.path() << dstUrl.path();
731 if (!strlist.isEmpty() && strlist[0] ==
"1")
734 LOG(VB_FILE, LOG_ERR, QString(
"RemoteFile::MoveFile: MOVE_FILE failed with: %1")
735 .arg(strlist.join(
",")));
743 QMutexLocker locker(&
m_lock);
746 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Reset(): Called with no socket");
754 QMutexLocker locker(&
m_lock);
765 LOG(VB_FILE, LOG_ERR,
"RemoteFile::Seek(): Called with no file opened");
771 long long offset = 0LL;
772 if (whence == SEEK_SET)
775 offset = std::min(pos, info.size());
777 else if (whence == SEEK_END)
780 offset = info.size() + pos;
782 else if (whence == SEEK_CUR)
784 offset = ((curpos > 0) ? curpos : ::lseek64(
m_localFile, 0, SEEK_CUR)) + pos;
789 off64_t localpos = ::lseek64(
m_localFile, pos, whence);
792 LOG(VB_FILE, LOG_ERR,
793 QString(
"RemoteFile::Seek(): Couldn't seek to offset %1")
802 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Seek(): Couldn't connect");
808 strlist << QString::number(pos);
809 strlist << QString::number(whence);
811 strlist << QString::number(curpos);
817 if (ok && !strlist.isEmpty())
821 return strlist[0].toLongLong();
831 unsigned zerocnt = 0;
833 bool response =
false;
837 LOG(VB_NETWORK, LOG_ERR,
838 "RemoteFile::Write(): Called when not in write mode");
845 LOG(VB_FILE, LOG_ERR,
846 "RemoteFile::Write(): File not opened");
852 QMutexLocker locker(&
m_lock);
856 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Write(): Couldn't connect");
861 strlist <<
"WRITE_BLOCK";
862 strlist << QString::number(size);
866 LOG(VB_NETWORK, LOG_ERR,
867 "RemoteFile::Write(): Block notification failed");
872 while (sent < recv && !
error && zerocnt++ < 50)
874 int ret =
m_sock->
Write((
char*)data + sent, recv - sent);
881 LOG(VB_GENERAL, LOG_ERR,
"RemoteFile::Write(): socket error");
890 recv = strlist[0].toInt();
895 if (!
error && !response)
900 recv = strlist[0].toInt();
904 LOG(VB_GENERAL, LOG_ERR,
905 "RemoteFile::Write(): No response from control socket.");
910 LOG(VB_NETWORK, LOG_DEBUG,
911 QString(
"RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
912 .arg(size).arg(sent).arg(recv).arg(
error));
917 if (
error || recv != sent)
934 bool response =
false;
936 QMutexLocker locker(&
m_lock);
942 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called in writing mode");
949 LOG(VB_FILE, LOG_ERR,
"RemoteFile:Read() called when local file not opened");
955 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Couldn't connect");
961 LOG(VB_NETWORK, LOG_ERR,
962 "RemoteFile::Read(): Read socket not empty to start!");
968 LOG(VB_NETWORK, LOG_WARNING,
969 "RemoteFile::Read(): Control socket not empty to start!");
974 strlist <<
"REQUEST_BLOCK";
975 strlist << QString::number(size);
979 LOG(VB_NETWORK, LOG_ERR,
"RemoteFile::Read(): Block request failed");
985 std::chrono::milliseconds waitms { 30ms };
991 int ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
998 waitms += (waitms < 200ms) ? 20ms : 0ms;
1004 sent = strlist[0].toInt();
1009 ret =
m_sock->
Read(((
char *)data) + recv, sent - recv, waitms);
1018 if (!
error && !response)
1025 sent = strlist[0].toInt();
1029 LOG(VB_GENERAL, LOG_ERR,
1030 "RemoteFile::Read(): No response from control socket.");
1049 LOG(VB_NETWORK, LOG_DEBUG,
1050 QString(
"Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
1051 .arg(size).arg(recv).arg(sent).arg(
error));
1056 if (
error || sent != recv)
1058 LOG(VB_GENERAL, LOG_WARNING,
1059 QString(
"RemoteFile::Read(): sent %1 != recv %2")
1060 .arg(sent).arg(recv));
1066 LOG(VB_GENERAL, LOG_WARNING,
"RemoteFile::Read(): Resume failed.");
1069 LOG(VB_GENERAL, LOG_NOTICE,
"RemoteFile::Read(): Resume success.");
1100 QMutexLocker locker(&
m_lock);
1119 QMutexLocker locker(&
m_lock);
1130 struct stat fileinfo {};
1140 strlist <<
"REQUEST_SIZE";
1144 if (ok && !strlist.isEmpty())
1146 bool validate =
false;
1147 long long size = strlist[0].toLongLong(&validate);
1151 if (strlist.count() >= 2)
1159 struct stat fileinfo {};
1181 Read(data.data(), fs);
1196 QMutexLocker locker(&
m_lock);
1207 LOG(VB_NETWORK, LOG_ERR,
1208 "RemoteFile::SetTimeout(): Couldn't connect");
1215 strlist <<
"SET_TIMEOUT";
1216 strlist << QString::number((
int)fast);
1227 QFileInfo info(url);
1228 return info.lastModified();
1233 QString sgroup = qurl.userName();
1235 if (!qurl.fragment().isEmpty() || url.endsWith(
"#"))
1241 if (
filename.isEmpty() || sgroup.isEmpty())
1244 QStringList strlist(
"QUERY_SG_FILEQUERY");
1245 strlist << qurl.host();
1251 if (strlist.size() > 1) {
1252 if (!strlist[1].isEmpty() && (strlist[1].toInt() != -1))
1255 result = QDateTime();;
1276 const QString& storageGroup,
bool useRegex,
1281 if (!files.isEmpty())
1297 const QString& storageGroup,
bool useRegex,
1300 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFile(): looking for '%1' on '%2' in group '%3' "
1301 "(useregex: %4, allowfallback: %5)")
1303 .arg(useRegex).arg(allowFallback));
1305 if (
filename.isEmpty() || storageGroup.isEmpty())
1308 QStringList strList;
1309 QString hostName = host;
1311 if (hostName.isEmpty())
1325 QStringList files = sgroup.
GetFileList(
'/' + fi.path());
1327 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Looking in dir '%1' for '%2'")
1328 .arg(fi.path(), fi.fileName()));
1330 for (
int x = 0; x < files.size(); x++)
1332 LOG(VB_FILE, LOG_INFO, QString(
"RemoteFile::FindFileList: Found '%1 - %2'")
1333 .arg(x).arg(files[x]));
1336 QStringList filteredFiles = files.filter(QRegularExpression(fi.fileName()));
1337 for (
const QString&
file : qAsConst(filteredFiles))
1341 fi.path() +
'/' +
file,
1355 if (!strList.isEmpty() || !allowFallback)
1362 strList <<
"QUERY_FINDFILE" << hostName << storageGroup <<
filename
1363 << (useRegex ?
"1" :
"0")
1368 if (!strList.empty() && !strList[0].isEmpty() &&
1369 strList[0] !=
"NOT FOUND" && !strList[0].startsWith(
"ERROR: "))
1437 LOG(VB_FILE, LOG_ERR,
1438 QString(
"RemoteFile::Resume: Enable to re-seek into last known "