MythTV  master
fileserverhandler.cpp
Go to the documentation of this file.
1 
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <unistd.h>
5 
6 #include <QReadLocker>
7 #include <QString>
8 #include <QWriteLocker>
9 #include <utility>
10 
12 #include "libmythbase/mythdb.h"
16 #include "libmythbase/mythsocket.h"
21 
26 
28 
30 {
31  // iterate through transfer list and close if
32  // socket matches connected transfer
33  {
34  QWriteLocker wlock(&m_ftLock);
35  QMap<int, FileTransfer*>::iterator i;
36  for (i = m_ftMap.begin(); i != m_ftMap.end(); ++i)
37  {
38  if ((*i)->GetSocket() == socket)
39  {
40  (*i)->DecrRef();
41  m_ftMap.remove(i.key());
42  return;
43  }
44  }
45  }
46 
47  // iterate through file server list and close
48  // if socket matched connected server
49  {
50  QWriteLocker wlock(&m_fsLock);
51  QMap<QString, SocketHandler*>::iterator i;
52  for (i = m_fsMap.begin(); i != m_fsMap.end(); ++i)
53  {
54  if ((*i)->GetSocket() == socket)
55  {
56  (*i)->DecrRef();
57  m_fsMap.remove(i.key());
58  return;
59  }
60  }
61  }
62 }
63 
64 QString FileServerHandler::LocalFilePath(const QString &path,
65  const QString &wantgroup)
66 {
67  QString lpath = QString(path);
68 
69  if (lpath.section('/', -2, -2) == "channels")
70  {
71  // This must be an icon request. Check channel.icon to be safe.
72  QString file = lpath.section('/', -1);
73  lpath = "";
74 
76  query.prepare("SELECT icon FROM channel "
77  "WHERE icon LIKE :FILENAME ;");
78  query.bindValue(":FILENAME", QString("%/") + file);
79 
80  if (query.exec() && query.next())
81  {
82  lpath = query.value(0).toString();
83  }
84  else
85  {
86  MythDB::DBError("Icon path", query);
87  }
88  }
89  else
90  {
91  lpath = lpath.section('/', -1);
92 
93  QString fpath = lpath;
94  if (fpath.endsWith(".png"))
95  fpath = fpath.left(fpath.length() - 4);
96 
97  ProgramInfo pginfo(fpath);
98  if (pginfo.GetChanID())
99  {
100  QString pburl = GetPlaybackURL(&pginfo);
101  if (pburl.startsWith("/"))
102  {
103  lpath = pburl.section('/', 0, -2) + "/" + lpath;
104  LOG(VB_FILE, LOG_INFO,
105  QString("Local file path: %1").arg(lpath));
106  }
107  else
108  {
109  LOG(VB_GENERAL, LOG_ERR,
110  QString("LocalFilePath unable to find local "
111  "path for '%1', found '%2' instead.")
112  .arg(lpath, pburl));
113  lpath = "";
114  }
115  }
116  else if (!lpath.isEmpty())
117  {
118  // For securities sake, make sure filename is really the pathless.
119  QString opath = lpath;
120  StorageGroup sgroup;
121 
122  if (!wantgroup.isEmpty())
123  {
124  sgroup.Init(wantgroup);
125  lpath = QString(path);
126  }
127  else
128  {
129  lpath = QFileInfo(lpath).fileName();
130  }
131 
132  QString tmpFile = sgroup.FindFile(lpath);
133  if (!tmpFile.isEmpty())
134  {
135  lpath = tmpFile;
136  LOG(VB_FILE, LOG_INFO,
137  QString("LocalFilePath(%1 '%2'), found through "
138  "exhaustive search at '%3'")
139  .arg(path, opath, lpath));
140  }
141  else
142  {
143  LOG(VB_GENERAL, LOG_ERR, QString("LocalFilePath unable to "
144  "find local path for '%1'.")
145  .arg(opath));
146  lpath = "";
147  }
148 
149  }
150  else
151  {
152  lpath = "";
153  }
154  }
155 
156  return lpath;
157 }
158 
160 {
161  if (deletethread != nullptr)
162  {
163  if (deletethread->isRunning())
164  return;
165 
166  delete deletethread;
167  deletethread = nullptr;
168  }
169 
170  deletethread = new DeleteThread();
171  deletethread->start();
172 }
173 
175  QStringList &commands, QStringList &slist)
176 {
177  if (commands[1] == "FileServer")
178  {
179  if (slist.size() >= 3)
180  {
181  auto *handler = new SocketHandler(socket, m_parent, commands[2]);
182 
183  handler->BlockShutdown(true);
184  handler->AllowStandardEvents(true);
185  handler->AllowSystemEvents(true);
186 
187  handler->WriteStringList(QStringList("OK"));
188 
189  QWriteLocker wlock(&m_fsLock);
190  m_fsMap.insert(commands[2], handler);
191  m_parent->AddSocketHandler(handler);
192 
193  handler->DecrRef();
194 
195  return true;
196  }
197  return false;
198  }
199 
200  if (commands[1] != "FileTransfer")
201  return false;
202 
203  if (slist.size() < 3)
204  return false;
205 
206  if ((commands.size() < 3) || (commands.size() > 6))
207  return false;
208 
209  FileTransfer *ft = nullptr;
210  QString hostname = "";
211  QString filename = "";
212  bool writemode = false;
213  bool usereadahead = true;
214  std::chrono::milliseconds timeout = 2s;
215  switch (commands.size())
216  {
217  case 6:
218  timeout = std::chrono::milliseconds(commands[5].toInt());
219  [[fallthrough]];
220  case 5:
221  usereadahead = (commands[4].toInt() != 0);
222  [[fallthrough]];
223  case 4:
224  writemode = (commands[3].toInt() != 0);
225  [[fallthrough]];
226  default:
227  hostname = commands[2];
228  }
229 
230  QStringList::const_iterator it = slist.cbegin();
231  QString path = *(++it);
232  QString wantgroup = *(++it);
233 
234  QStringList checkfiles;
235  while (++it != slist.cend())
236  checkfiles += *(it);
237 
238  slist.clear();
239 
240  LOG(VB_GENERAL, LOG_DEBUG, "FileServerHandler::HandleAnnounce");
241  LOG(VB_GENERAL, LOG_INFO, QString("adding: %1 as remote file transfer")
242  .arg(hostname));
243 
244  if (writemode)
245  {
246  if (wantgroup.isEmpty())
247  wantgroup = "Default";
248 
249  StorageGroup sgroup(wantgroup, gCoreContext->GetHostName(), false);
250  QString dir = sgroup.FindNextDirMostFree();
251  if (dir.isEmpty())
252  {
253  LOG(VB_GENERAL, LOG_ERR, "Unable to determine directory "
254  "to write to in FileTransfer write command");
255 
256  slist << "ERROR" << "filetransfer_directory_not_found";
257  socket->WriteStringList(slist);
258  return true;
259  }
260 
261  if (path.isEmpty())
262  {
263  LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write "
264  "filename is empty in path '%1'.")
265  .arg(path));
266 
267  slist << "ERROR" << "filetransfer_filename_empty";
268  socket->WriteStringList(slist);
269  return true;
270  }
271 
272  if ((path.contains("/../")) ||
273  (path.startsWith("../")))
274  {
275  LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write "
276  "filename '%1' does not pass sanity checks.")
277  .arg(path));
278 
279  slist << "ERROR" << "filetransfer_filename_dangerous";
280  socket->WriteStringList(slist);
281  return true;
282  }
283 
284  filename = dir + "/" + path;
285  }
286  else
287  filename = LocalFilePath(path, wantgroup);
288 
289  QFileInfo finfo(filename);
290  if (finfo.isDir())
291  {
292  LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer filename "
293  "'%1' is actually a directory, cannot transfer.")
294  .arg(filename));
295 
296  slist << "ERROR" << "filetransfer_filename_is_a_directory";
297  socket->WriteStringList(slist);
298  return true;
299  }
300 
301  if (writemode)
302  {
303  QString dirPath = finfo.absolutePath();
304  QDir qdir(dirPath);
305  if (!qdir.exists())
306  {
307  if (!qdir.mkpath(dirPath))
308  {
309  LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer "
310  "filename '%1' is in a subdirectory which does "
311  "not exist, but can not be created.")
312  .arg(filename));
313 
314  slist << "ERROR" << "filetransfer_unable_to_create_subdirectory";
315  socket->WriteStringList(slist);
316  return true;
317  }
318  }
319 
320  ft = new FileTransfer(filename, socket, m_parent, writemode);
321  }
322  else
323  ft = new FileTransfer(filename, socket, m_parent, usereadahead, timeout);
324 
325  ft->BlockShutdown(true);
326 
327  {
328  QWriteLocker wlock(&m_ftLock);
329  m_ftMap.insert(socket->GetSocketDescriptor(), ft);
330  }
331 
332  slist << "OK"
333  << QString::number(socket->GetSocketDescriptor())
334  << QString::number(ft->GetFileSize());
335 
336  if (!checkfiles.empty())
337  {
338  QFileInfo fi(filename);
339  QDir dir = fi.absoluteDir();
340  for (const auto & file : std::as_const(checkfiles))
341  {
342  if (dir.exists(file) &&
343  QFileInfo(dir, file).size() >= kReadTestSize)
344  slist << file;
345  }
346  }
347 
348  socket->WriteStringList(slist);
350  ft->DecrRef(); ft = nullptr;
351 
352  return true;
353 }
354 
356  QStringList &commands, QStringList &slist)
357 {
358  if (commands[1] == "SlaveBackend")
359  {
360  // were not going to handle these, but we still want to track them
361  // for commands that need access to these sockets
362  if (slist.size() >= 3)
363  {
364  SocketHandler *handler = m_parent->GetConnectionBySocket(socket);
365  if (handler == nullptr)
366  return;
367 
368  QWriteLocker wlock(&m_fsLock);
369  m_fsMap.insert(commands[2], handler);
370  }
371  }
372 
373 }
374 
375 bool FileServerHandler::HandleQuery(SocketHandler *socket, QStringList &commands,
376  QStringList &slist)
377 {
378  bool handled = false;
379  QString command = commands[0];
380 
381  if (command == "QUERY_FILETRANSFER")
382  handled = HandleQueryFileTransfer(socket, commands, slist);
383  else if (command == "QUERY_FREE_SPACE")
384  handled = HandleQueryFreeSpace(socket);
385  else if (command == "QUERY_FREE_SPACE_LIST")
386  handled = HandleQueryFreeSpaceList(socket);
387  else if (command == "QUERY_FREE_SPACE_SUMMARY")
388  handled = HandleQueryFreeSpaceSummary(socket);
389  else if (command == "QUERY_CHECKFILE")
390  handled = HandleQueryCheckFile(socket, slist);
391  else if (command == "QUERY_FILE_EXISTS")
392  handled = HandleQueryFileExists(socket, slist);
393  else if (command == "QUERY_FILE_HASH")
394  handled = HandleQueryFileHash(socket, slist);
395  else if (command == "DELETE_FILE")
396  handled = HandleDeleteFile(socket, slist);
397  else if (command == "QUERY_SG_GETFILELIST")
398  handled = HandleGetFileList(socket, slist);
399  else if (command == "QUERY_SG_FILEQUERY")
400  handled = HandleFileQuery(socket, slist);
401  else if (command == "DOWNLOAD_FILE" || command == "DOWNLOAD_FILE_NOW")
402  handled = HandleDownloadFile(socket, slist);
403  return handled;
404 }
405 
407 {
408  QStringList res;
409 
410  QList<FileSystemInfo> disks = QueryFileSystems();
411  for (const auto & disk : std::as_const(disks))
412  disk.ToStringList(res);
413 
414  socket->WriteStringList(res);
415  return true;
416 }
417 
419 {
420  QStringList res;
421  QStringList hosts;
422 
423  QList<FileSystemInfo> disks = QueryAllFileSystems();
424  for (const auto & disk : std::as_const(disks))
425  if (!hosts.contains(disk.getHostname()))
426  hosts << disk.getHostname();
427 
428  // TODO: get max bitrate from encoderlink
429  FileSystemInfo::Consolidate(disks, true, 14000);
430 
431  long long total = 0;
432  long long used = 0;
433  for (const auto & disk : std::as_const(disks))
434  {
435  disk.ToStringList(res);
436  total += disk.getTotalSpace();
437  used += disk.getUsedSpace();
438  }
439 
440  res << hosts.join(",")
441  << "TotalDiskSpace"
442  << "0"
443  << "-2"
444  << "-2"
445  << "0"
446  << QString::number(total)
447  << QString::number(used);
448 
449  socket->WriteStringList(res);
450  return true;
451 }
452 
454 {
455  QStringList res;
456  QList<FileSystemInfo> disks = QueryAllFileSystems();
457  // TODO: get max bitrate from encoderlink
458  FileSystemInfo::Consolidate(disks, true, 14000);
459 
460  long long total = 0;
461  long long used = 0;
462  for (const auto & disk : std::as_const(disks))
463  {
464  total += disk.getTotalSpace();
465  used += disk.getUsedSpace();
466  }
467 
468  res << QString::number(total) << QString::number(used);
469  socket->WriteStringList(res);
470  return true;
471 }
472 
473 QList<FileSystemInfo> FileServerHandler::QueryFileSystems(void)
474 {
475  QStringList groups(StorageGroup::kSpecialGroups);
476  groups.removeAll("LiveTV");
477  QString specialGroups = groups.join("', '");
478 
479  MSqlQuery query(MSqlQuery::InitCon());
480  query.prepare(QString("SELECT MIN(id),dirname "
481  "FROM storagegroup "
482  "WHERE hostname = :HOSTNAME "
483  "AND groupname NOT IN ( '%1' ) "
484  "GROUP BY dirname;").arg(specialGroups));
485  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
486 
487  QList<FileSystemInfo> disks;
488  if (query.exec() && query.isActive())
489  {
490  if (!query.size())
491  {
492  query.prepare("SELECT MIN(id),dirname "
493  "FROM storagegroup "
494  "WHERE groupname = :GROUP "
495  "GROUP BY dirname;");
496  query.bindValue(":GROUP", "Default");
497  if (!query.exec())
498  MythDB::DBError("BackendQueryFileSystems", query);
499  }
500 
501  QDir checkDir("");
502  QString currentDir;
503  FileSystemInfo disk;
504  QMap <QString, bool>foundDirs;
505 
506  while (query.next())
507  {
508  disk.clear();
510  disk.setLocal();
511  disk.setBlockSize(0);
512  disk.setGroupID(query.value(0).toInt());
513 
514  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
515  * uses QString::fromAscii() for toString(). Explicitly convert the
516  * value using QString::fromUtf8() to prevent corruption. */
517  currentDir = QString::fromUtf8(query.value(1)
518  .toByteArray().constData());
519  disk.setPath(currentDir);
520 
521  if (currentDir.endsWith("/"))
522  currentDir.remove(currentDir.length() - 1, 1);
523 
524  checkDir.setPath(currentDir);
525  if (!foundDirs.contains(currentDir))
526  {
527  if (checkDir.exists())
528  {
529  disk.PopulateDiskSpace();
530  disk.PopulateFSProp();
531  disks << disk;
532 
533  foundDirs[currentDir] = true;
534  }
535  else
536  foundDirs[currentDir] = false;
537  }
538  }
539  }
540 
541  return disks;
542 }
543 
544 QList<FileSystemInfo> FileServerHandler::QueryAllFileSystems(void)
545 {
546  QList<FileSystemInfo> disks = QueryFileSystems();
547 
548  {
549  QReadLocker rlock(&m_fsLock);
550 
551  QMap<QString, SocketHandler*>::iterator i;
552  for (i = m_fsMap.begin(); i != m_fsMap.end(); ++i)
553  disks << FileSystemInfo::RemoteGetInfo((*i)->GetSocket());
554  }
555 
556  return disks;
557 }
558 
565  QStringList &slist)
566 {
567  QStringList::const_iterator it = slist.cbegin() + 2;
568  RecordingInfo recinfo(it, slist.cend());
569 
570  bool exists = false;
571 
572  QString pburl;
573  if (recinfo.HasPathname())
574  {
575  pburl = GetPlaybackURL(&recinfo);
576  exists = QFileInfo::exists(pburl);
577  if (!exists)
578  pburl.clear();
579  }
580 
581  QStringList res(QString::number(static_cast<int>(exists)));
582  res << pburl;
583  socket->WriteStringList(res);
584  return true;
585 }
586 
587 
593  QStringList &slist)
594 {
595  QString storageGroup = "Default";
596  QStringList res;
597 
598  if (slist.size() == 3)
599  {
600  if (!slist[2].isEmpty())
601  storageGroup = slist[2];
602  }
603  else if (slist.size() != 2)
604  return false;
605 
606  QString filename = slist[1];
607  if ((filename.isEmpty()) ||
608  (filename.contains("/../")) ||
609  (filename.startsWith("../")))
610  {
611  LOG(VB_GENERAL, LOG_ERR,
612  QString("ERROR checking for file, filename '%1' "
613  "fails sanity checks").arg(filename));
614  res << "";
615  socket->WriteStringList(res);
616  return true;
617  }
618 
619  StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
620  QString fullname = sgroup.FindFile(filename);
621 
622  if (!fullname.isEmpty())
623  {
624  res << "1"
625  << fullname;
626 
627  // TODO: convert me to QFile
628  struct stat fileinfo {};
629  if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
630  {
631  res << QString::number(fileinfo.st_dev)
632  << QString::number(fileinfo.st_ino)
633  << QString::number(fileinfo.st_mode)
634  << QString::number(fileinfo.st_nlink)
635  << QString::number(fileinfo.st_uid)
636  << QString::number(fileinfo.st_gid)
637  << QString::number(fileinfo.st_rdev)
638  << QString::number(fileinfo.st_size)
639 #ifdef _WIN32
640  << "0"
641  << "0"
642 #else
643  << QString::number(fileinfo.st_blksize)
644  << QString::number(fileinfo.st_blocks)
645 #endif
646  << QString::number(fileinfo.st_atime)
647  << QString::number(fileinfo.st_mtime)
648  << QString::number(fileinfo.st_ctime);
649  }
650  }
651  else
652  res << "0";
653 
654  socket->WriteStringList(res);
655  return true;
656 }
657 
663  QStringList &slist)
664 {
665  QString storageGroup = "Default";
666  QString hostname = gCoreContext->GetHostName();
667  QString filename = "";
668  QStringList res;
669 
670  switch (slist.size()) {
671  case 4:
672  if (!slist[3].isEmpty())
673  hostname = slist[3];
674  [[fallthrough]];
675  case 3:
676  if (!slist[2].isEmpty())
677  storageGroup = slist[2];
678  [[fallthrough]];
679  case 2:
680  filename = slist[1];
681  if (filename.isEmpty() ||
682  filename.contains("/../") ||
683  filename.startsWith("../"))
684  {
685  LOG(VB_GENERAL, LOG_ERR,
686  QString("ERROR checking for file, filename '%1' "
687  "fails sanity checks").arg(filename));
688  res << "";
689  socket->WriteStringList(res);
690  return true;
691  }
692  break;
693  default:
694  return false;
695  }
696 
697  QString hash = "";
698 
700  {
701  // looking for file on me, return directly
702  StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
703  QString fullname = sgroup.FindFile(filename);
704  hash = FileHash(fullname);
705  }
706  else
707  {
708  QReadLocker rlock(&m_fsLock);
709  if (m_fsMap.contains(hostname))
710  {
711  // looking for file on connected host, query from it
712  if (m_fsMap[hostname]->SendReceiveStringList(slist))
713  hash = slist[0];
714  }
715  // I deleted the incorrect SQL select that was supposed to get
716  // host name from ip address. Since it cannot work and has
717  // been there 6 years I assume it is not important.
718  }
719 
720 
721  res << hash;
722  socket->WriteStringList(res);
723 
724  return true;
725 }
726 
728  QStringList &slist)
729 {
730  if (slist.size() != 3)
731  return false;
732 
733  return HandleDeleteFile(socket, slist[1], slist[2]);
734 }
735 
736 bool FileServerHandler::DeleteFile(const QString& filename, const QString& storagegroup)
737 {
738  return HandleDeleteFile(nullptr, filename, storagegroup);
739 }
740 
742  const QString& filename, const QString& storagegroup)
743 {
744  StorageGroup sgroup(storagegroup, "", false);
745  QStringList res;
746 
747  if ((filename.isEmpty()) ||
748  (filename.contains("/../")) ||
749  (filename.startsWith("../")))
750  {
751  LOG(VB_GENERAL, LOG_ERR,
752  QString("ERROR deleting file, filename '%1' fails sanity checks")
753  .arg(filename));
754  if (socket)
755  {
756  res << "0";
757  socket->WriteStringList(res);
758  return true;
759  }
760  return false;
761  }
762 
763  QString fullfile = sgroup.FindFile(filename);
764 
765  if (fullfile.isEmpty())
766  {
767  LOG(VB_GENERAL, LOG_ERR,
768  QString("Unable to find %1 in HandleDeleteFile()") .arg(filename));
769  if (socket)
770  {
771  res << "0";
772  socket->WriteStringList(res);
773  return true;
774  }
775  return false;
776  }
777 
778  QFile checkFile(fullfile);
779  if (checkFile.exists())
780  {
781  if (socket)
782  {
783  res << "1";
784  socket->WriteStringList(res);
785  }
786  RunDeleteThread();
787  deletethread->AddFile(fullfile);
788  }
789  else
790  {
791  LOG(VB_GENERAL, LOG_ERR, QString("Error deleting file: '%1'")
792  .arg(fullfile));
793  if (socket)
794  {
795  res << "0";
796  socket->WriteStringList(res);
797  }
798  }
799 
800  return true;
801 }
802 
804 {
805  RunDeleteThread();
806  return deletethread->AddFile(handler);
807 }
808 
810  QStringList &slist)
811 {
812  QStringList res;
813 
814  bool fileNamesOnly = false;
815  if (slist.size() == 5)
816  fileNamesOnly = (slist[4].toInt() != 0);
817  else if (slist.size() != 4)
818  {
819  LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
820  .arg(slist.join("[]:[]")));
821  res << "EMPTY LIST";
822  socket->WriteStringList(res);
823  return true;
824  }
825 
826  QString host = gCoreContext->GetHostName();
827  QString wantHost = slist[1];
828  QString groupname = slist[2];
829  QString path = slist[3];
830 
831  LOG(VB_FILE, LOG_INFO,
832  QString("HandleSGGetFileList: group = %1 host = %2 "
833  "path = %3 wanthost = %4")
834  .arg(groupname, host, path, wantHost));
835 
836  if (gCoreContext->IsThisHost(wantHost))
837  {
838  StorageGroup sg(groupname, host);
839  LOG(VB_FILE, LOG_INFO, "Getting local info");
840  if (fileNamesOnly)
841  res = sg.GetFileList(path);
842  else
843  res = sg.GetFileInfoList(path);
844 
845  if (res.count() == 0)
846  res << "EMPTY LIST";
847  }
848  else
849  {
850  // handle request on remote server
851  SocketHandler *remsock = nullptr;
852  {
853  QReadLocker rlock(&m_fsLock);
854  if (m_fsMap.contains(wantHost))
855  {
856  remsock = m_fsMap[wantHost];
857  remsock->IncrRef();
858  }
859  }
860 
861  if (remsock)
862  {
863  LOG(VB_FILE, LOG_INFO, "Getting remote info");
864  res << "QUERY_SG_GETFILELIST" << wantHost << groupname << path
865  << QString::number(static_cast<int>(fileNamesOnly));
866  remsock->SendReceiveStringList(res);
867  remsock->DecrRef();
868  }
869  else
870  {
871  LOG(VB_FILE, LOG_ERR, QString("Failed to grab slave socket : %1 :")
872  .arg(wantHost));
873  res << "SLAVE UNREACHABLE: " << wantHost;
874  }
875  }
876 
877  socket->WriteStringList(res);
878  return true;
879 }
880 
882  QStringList &slist)
883 {
884  QStringList res;
885 
886  if (slist.size() != 4)
887  {
888  LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
889  .arg(slist.join("[]:[]")));
890  res << "EMPTY LIST";
891  socket->WriteStringList(res);
892  return true;
893  }
894 
895  QString wantHost = slist[1];
896  QString groupname = slist[2];
897  QString filename = slist[3];
898 
899  LOG(VB_FILE, LOG_DEBUG, QString("HandleSGFileQuery: myth://%1@%2/%3")
900  .arg(groupname, wantHost, filename));
901 
902  if (gCoreContext->IsThisHost(wantHost))
903  {
904  // handle request locally
905  LOG(VB_FILE, LOG_DEBUG, QString("Getting local info"));
906  StorageGroup sg(groupname, gCoreContext->GetHostName());
907  res = sg.GetFileInfo(filename);
908 
909  if (res.count() == 0)
910  res << "EMPTY LIST";
911  }
912  else
913  {
914  // handle request on remote server
915  SocketHandler *remsock = nullptr;
916  {
917  QReadLocker rlock(&m_fsLock);
918  if (m_fsMap.contains(wantHost))
919  {
920  remsock = m_fsMap[wantHost];
921  remsock->IncrRef();
922  }
923  }
924 
925  if (remsock)
926  {
927  res << "QUERY_SG_FILEQUERY" << wantHost << groupname << filename;
928  remsock->SendReceiveStringList(res);
929  remsock->DecrRef();
930  }
931  else
932  {
933  res << "SLAVE UNREACHABLE: " << wantHost;
934  }
935  }
936 
937  socket->WriteStringList(res);
938  return true;
939 }
940 
942  QStringList &commands, QStringList &slist)
943 {
944  if (commands.size() != 2)
945  return false;
946 
947  if (slist.size() < 2)
948  return false;
949 
950  QStringList res;
951  int recnum = commands[1].toInt();
952  FileTransfer *ft = nullptr;
953 
954  {
955  QReadLocker rlock(&m_ftLock);
956  if (!m_ftMap.contains(recnum))
957  {
958  if (slist[1] == "DONE")
959  res << "OK";
960  else
961  {
962  LOG(VB_GENERAL, LOG_ERR,
963  QString("Unknown file transfer socket: %1").arg(recnum));
964  res << "ERROR"
965  << "unknown_file_transfer_socket";
966  }
967 
968  socket->WriteStringList(res);
969  return true;
970  }
971 
972  ft = m_ftMap[recnum];
973  ft->IncrRef();
974  }
975 
976  if (slist[1] == "REQUEST_BLOCK")
977  {
978  if (slist.size() != 3)
979  {
980  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
981  "REQUEST_BLOCK call");
982  res << "ERROR" << "invalid_call";
983  }
984  else
985  {
986  int size = slist[2].toInt();
987  res << QString::number(ft->RequestBlock(size));
988  }
989  }
990  else if (slist[1] == "WRITE_BLOCK")
991  {
992  if (slist.size() != 3)
993  {
994  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
995  "WRITE_BLOCK call");
996  res << "ERROR" << "invalid_call";
997  }
998  else
999  {
1000  int size = slist[2].toInt();
1001  res << QString::number(ft->WriteBlock(size));
1002  }
1003  }
1004  else if (slist[1] == "SEEK")
1005  {
1006  if (slist.size() != 5)
1007  {
1008  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER SEEK call");
1009  res << "ERROR" << "invalid_call";
1010  }
1011  else
1012  {
1013  long long pos = slist[2].toLongLong();
1014  int whence = slist[3].toInt();
1015  long long curpos = slist[4].toLongLong();
1016 
1017  res << QString::number(ft->Seek(curpos, pos, whence));
1018  }
1019  }
1020  else if (slist[1] == "IS_OPEN")
1021  {
1022  res << QString::number(static_cast<int>(ft->isOpen()));
1023  }
1024  else if (slist[1] == "DONE")
1025  {
1026  ft->Stop();
1027  res << "OK";
1028  }
1029  else if (slist[1] == "SET_TIMEOUT")
1030  {
1031  if (slist.size() != 3)
1032  {
1033  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
1034  "SET_TIMEOUT call");
1035  res << "ERROR" << "invalid_call";
1036  }
1037  else
1038  {
1039  bool fast = slist[2].toInt() != 0;
1040  ft->SetTimeout(fast);
1041  res << "OK";
1042  }
1043  }
1044  else if (slist[1] == "REQUEST_SIZE")
1045  {
1046  // return size and if the file is not opened for writing
1047  res << QString::number(ft->GetFileSize());
1048  res << QString::number(static_cast<int>(!gCoreContext->IsRegisteredFileForWrite(ft->GetFileName())));
1049  }
1050  else
1051  {
1052  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER call");
1053  res << "ERROR" << "invalid_call";
1054  }
1055 
1056  ft->DecrRef();
1057  socket->WriteStringList(res);
1058  return true;
1059 }
1060 
1062  QStringList &slist)
1063 {
1064  QStringList res;
1065 
1066  if (slist.size() != 4)
1067  {
1068  res << "ERROR" << QString("Bad %1 command").arg(slist[0]);
1069  socket->WriteStringList(res);
1070  return true;
1071  }
1072 
1073  bool synchronous = (slist[0] == "DOWNLOAD_FILE_NOW");
1074  QString srcURL = slist[1];
1075  QString storageGroup = slist[2];
1076  QString filename = slist[3];
1077  StorageGroup sgroup(storageGroup, gCoreContext->GetHostName(), false);
1078  QString outDir = sgroup.FindNextDirMostFree();
1079  QString outFile;
1080 
1081  if (filename.isEmpty())
1082  {
1083  QFileInfo finfo(srcURL);
1084  filename = finfo.fileName();
1085  }
1086 
1087  if (outDir.isEmpty())
1088  {
1089  LOG(VB_GENERAL, LOG_ERR, QString("Unable to determine directory "
1090  "to write to in %1 write command").arg(slist[0]));
1091  res << "ERROR" << "downloadfile_directory_not_found";
1092  socket->WriteStringList(res);
1093  return true;
1094  }
1095 
1096  if ((filename.contains("/../")) ||
1097  (filename.startsWith("../")))
1098  {
1099  LOG(VB_GENERAL, LOG_ERR, QString("ERROR: %1 write "
1100  "filename '%2' does not pass sanity checks.")
1101  .arg(slist[0], filename));
1102  res << "ERROR" << "downloadfile_filename_dangerous";
1103  socket->WriteStringList(res);
1104  return true;
1105  }
1106 
1107  outFile = outDir + "/" + filename;
1108 
1109  if (synchronous)
1110  {
1111  if (GetMythDownloadManager()->download(srcURL, outFile))
1112  {
1113  res << "OK"
1114  << gCoreContext->GetMasterHostPrefix(storageGroup)
1115  + filename;
1116  }
1117  else
1118  res << "ERROR";
1119  }
1120  else
1121  {
1122  QMutexLocker locker(&m_downloadURLsLock);
1123  m_downloadURLs[outFile] =
1124  gCoreContext->GetMasterHostPrefix(storageGroup) +
1126 
1127  GetMythDownloadManager()->queueDownload(srcURL, outFile, this);
1128  res << "OK"
1129  << gCoreContext->GetMasterHostPrefix(storageGroup) + filename;
1130  }
1131 
1132  socket->WriteStringList(res);
1133  return true;
1134 }
1135 
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:215
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:813
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:127
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:214
hardwareprofile.smolt.timeout
float timeout
Definition: smolt.py:102
ReferenceCounter::DecrRef
virtual int DecrRef(void)
Decrements reference count and deletes on 0.
Definition: referencecounter.cpp:125
mythdb.h
FileServerHandler::HandleQueryFileTransfer
bool HandleQueryFileTransfer(SocketHandler *socket, QStringList &commands, QStringList &slist)
Definition: fileserverhandler.cpp:941
FileTransfer::Stop
void Stop(void)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:71
FileSystemInfo::setGroupID
void setGroupID(int id)
Definition: filesysteminfo.h:51
RecordingInfo
Holds information on a TV Program one might wish to record.
Definition: recordinginfo.h:35
DeleteThread::AddFile
bool AddFile(const QString &path)
Definition: deletethread.cpp:67
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:597
FileServerHandler::HandleFileQuery
bool HandleFileQuery(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:881
FileSystemInfo::Consolidate
static void Consolidate(QList< FileSystemInfo > &disks, bool merge=true, int64_t fuzz=14000)
Definition: filesysteminfo.cpp:165
MythDownloadManager::queueDownload
void queueDownload(const QString &url, const QString &dest, QObject *caller, bool reload=false)
Adds a url to the download queue.
Definition: mythdownloadmanager.cpp:393
SocketRequestHandler::m_parent
MythSocketManager * m_parent
Definition: socketrequesthandler.h:33
FileTransfer::GetFileName
QString GetFileName(void)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:255
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:204
DeleteThread
Definition: deletethread.h:19
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:619
deletethread
DeleteThread * deletethread
Definition: fileserverhandler.cpp:27
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
FileServerHandler::HandleDeleteFile
static bool HandleDeleteFile(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:727
build_compdb.file
file
Definition: build_compdb.py:55
FileTransfer::WriteBlock
int WriteBlock(int size)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:154
FileServerHandler::connectionAnnounced
void connectionAnnounced(MythSocket *socket, QStringList &commands, QStringList &slist) override
Definition: fileserverhandler.cpp:355
FileTransfer
Definition: libs/libmythprotoserver/sockethandler/filetransfer.h:18
FileServerHandler::m_fsLock
QReadWriteLock m_fsLock
Definition: fileserverhandler.h:60
FileServerHandler::HandleQueryFileHash
bool HandleQueryFileHash(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:662
MythSocket
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:25
fileserverutil.h
MythCoreContext::IsRegisteredFileForWrite
bool IsRegisteredFileForWrite(const QString &file)
Definition: mythcorecontext.cpp:2157
deletethread.h
StorageGroup::GetFileList
QStringList GetFileList(const QString &Path, bool recursive=false)
Definition: storagegroup.cpp:271
programinfo.h
FileServerHandler::HandleQuery
bool HandleQuery(SocketHandler *socket, QStringList &commands, QStringList &slist) override
Definition: fileserverhandler.cpp:375
FileServerHandler::m_ftLock
QReadWriteLock m_ftLock
Definition: fileserverhandler.h:57
mythlogging.h
FileSystemInfo::RemoteGetInfo
static QList< FileSystemInfo > RemoteGetInfo(MythSocket *sock=nullptr)
Definition: filesysteminfo.cpp:137
MythSocket::WriteStringList
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:301
DeleteHandler
Definition: fileserverutil.h:17
FileSystemInfo::setLocal
void setLocal(bool local=true)
Definition: filesysteminfo.h:49
FileServerHandler::connectionClosed
void connectionClosed(MythSocket *socket) override
Definition: fileserverhandler.cpp:29
MSqlQuery::InitCon
static MSqlQueryInfo InitCon(ConnectionReuse _reuse=kNormalConnection)
Only use this in combination with MSqlQuery constructor.
Definition: mythdbcon.cpp:551
FileServerHandler::m_downloadURLs
QMap< QString, QString > m_downloadURLs
Definition: fileserverhandler.h:63
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:226
fileserverhandler.h
MythSocketManager::AddSocketHandler
void AddSocketHandler(SocketHandler *socket)
Definition: mythsocketmanager.cpp:128
GetPlaybackURL
QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
Definition: fileserverutil.cpp:46
StorageGroup::GetFileInfo
QStringList GetFileInfo(const QString &filename)
Definition: storagegroup.cpp:380
FileTransfer::Seek
long long Seek(long long curpos, long long pos, int whence)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:215
SocketHandler::WriteStringList
bool WriteStringList(const QStringList &strlist)
Definition: sockethandler.cpp:26
SocketHandler::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, uint min_reply_length=0)
Definition: sockethandler.cpp:34
storagegroup.h
FileServerHandler::m_fsMap
QMap< QString, SocketHandler * > m_fsMap
Definition: fileserverhandler.h:59
filetransfer.h
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:55
FileServerHandler::HandleQueryFileExists
static bool HandleQueryFileExists(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:592
FileServerHandler::HandleQueryFreeSpaceList
bool HandleQueryFreeSpaceList(SocketHandler *socket)
Definition: fileserverhandler.cpp:418
FileSystemInfo::setPath
void setPath(const QString &path)
Definition: filesysteminfo.h:48
FileTransfer::RequestBlock
int RequestBlock(int size)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:114
mythmediabuffer.h
kReadTestSize
static constexpr qint64 kReadTestSize
Definition: mythmediabuffer.h:23
StorageGroup::GetRelativePathname
static QString GetRelativePathname(const QString &filename)
Returns the relative pathname of a file by comparing the filename against all Storage Group directori...
Definition: storagegroup.cpp:420
FileSystemInfo::PopulateFSProp
void PopulateFSProp(void)
Definition: filesysteminfo.cpp:224
FileServerHandler::QueryAllFileSystems
QList< FileSystemInfo > QueryAllFileSystems(void)
Definition: fileserverhandler.cpp:544
FileServerHandler::HandleQueryFreeSpaceSummary
bool HandleQueryFreeSpaceSummary(SocketHandler *socket)
Definition: fileserverhandler.cpp:453
recordinginfo.h
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:372
FileSystemInfo::PopulateDiskSpace
void PopulateDiskSpace(void)
Definition: filesysteminfo.cpp:215
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:67
mythmiscutil.h
mythcorecontext.h
StorageGroup::FindNextDirMostFree
QString FindNextDirMostFree(void)
Definition: storagegroup.cpp:666
FileSystemInfo::setHostname
void setHostname(const QString &hostname)
Definition: filesysteminfo.h:47
MythSocketManager::GetConnectionBySocket
SocketHandler * GetConnectionBySocket(MythSocket *socket)
Definition: mythsocketmanager.cpp:138
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:889
FileSystemInfo
Definition: filesysteminfo.h:15
StorageGroup::Init
void Init(const QString &group="Default", const QString &hostname="", bool allowFallback=true)
Initilizes the groupname, hostname, and dirlist.
Definition: storagegroup.cpp:106
FileServerHandler::RunDeleteThread
static void RunDeleteThread(void)
Definition: fileserverhandler.cpp:159
StorageGroup::GetFileInfoList
QStringList GetFileInfoList(const QString &Path)
Definition: storagegroup.cpp:289
FileTransfer::GetFileSize
uint64_t GetFileSize(void)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:247
MythSocket::GetSocketDescriptor
int GetSocketDescriptor(void) const
Definition: mythsocket.cpp:575
FileServerHandler::m_ftMap
QMap< int, FileTransfer * > m_ftMap
Definition: fileserverhandler.h:56
FileSystemInfo::setBlockSize
void setBlockSize(int size)
Definition: filesysteminfo.h:52
FileServerHandler::HandleQueryFreeSpace
static bool HandleQueryFreeSpace(SocketHandler *socket)
Definition: fileserverhandler.cpp:406
FileServerHandler::HandleQueryCheckFile
static bool HandleQueryCheckFile(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:564
StorageGroup::kSpecialGroups
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:263
StorageGroup
Definition: storagegroup.h:11
SocketHandler::BlockShutdown
void BlockShutdown(bool block)
Definition: sockethandler.h:35
FileServerHandler::HandleDownloadFile
bool HandleDownloadFile(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:1061
SocketHandler
Definition: sockethandler.h:16
FileServerHandler::DeleteFile
static bool DeleteFile(const QString &filename, const QString &storagegroup)
Definition: fileserverhandler.cpp:736
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:838
FileServerHandler::HandleAnnounce
bool HandleAnnounce(MythSocket *socket, QStringList &commands, QStringList &slist) override
Definition: fileserverhandler.cpp:174
MythCoreContext::IsThisHost
bool IsThisHost(const QString &addr)
is this address mapped to this host
Definition: mythcorecontext.cpp:721
musicbrainzngs.caa.hostname
string hostname
Definition: caa.py:17
FileServerHandler::LocalFilePath
static QString LocalFilePath(const QString &path, const QString &wantgroup)
Definition: fileserverhandler.cpp:64
MythCoreContext::GetMasterHostPrefix
QString GetMasterHostPrefix(const QString &storageGroup=QString(), const QString &path=QString())
Definition: mythcorecontext.cpp:798
FileServerHandler::HandleGetFileList
bool HandleGetFileList(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:809
DeleteThread::start
void start(void)
Definition: mainserver.h:84
mythdownloadmanager.h
FileTransfer::SetTimeout
void SetTimeout(bool fast)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:263
ReferenceCounter::IncrRef
virtual int IncrRef(void)
Increments reference count.
Definition: referencecounter.cpp:101
build_compdb.filename
filename
Definition: build_compdb.py:21
FileServerHandler::m_downloadURLsLock
QMutex m_downloadURLsLock
Definition: fileserverhandler.h:62
FileHash
QString FileHash(const QString &filename)
Definition: mythmiscutil.cpp:548
FileSystemInfo::clear
void clear(void)
Definition: filesysteminfo.cpp:84
mythsocket.h
FileTransfer::isOpen
bool isOpen(void)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:55
ProgramInfo::HasPathname
bool HasPathname(void) const
Definition: programinfo.h:358
GetMythDownloadManager
MythDownloadManager * GetMythDownloadManager(void)
Gets the pointer to the MythDownloadManager singleton.
Definition: mythdownloadmanager.cpp:145
FileServerHandler::QueryFileSystems
static QList< FileSystemInfo > QueryFileSystems(void)
Definition: fileserverhandler.cpp:473
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:838