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 
11 #include "mythmiscutil.h"
12 #include "mythdb.h"
13 #include "io/mythmediabuffer.h"
14 #include "mythsocket.h"
15 #include "mythlogging.h"
16 #include "programinfo.h"
17 #include "recordinginfo.h"
18 #include "storagegroup.h"
19 #include "mythcorecontext.h"
20 #include "mythdownloadmanager.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).arg(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).arg(opath).arg(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  int timeout_ms = 2000;
215  switch (commands.size())
216  {
217  case 6:
218  timeout_ms = commands[5].toInt();
219  [[clang::fallthrough]];
220  case 5:
221  usereadahead = (commands[4].toInt() != 0);
222  [[clang::fallthrough]];
223  case 4:
224  writemode = (commands[3].toInt() != 0);
225  [[clang::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_ms);
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 : qAsConst(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 : qAsConst(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  QList<FileSystemInfo>::const_iterator i;
425  for (const auto & disk : qAsConst(disks))
426  if (!hosts.contains(disk.getHostname()))
427  hosts << disk.getHostname();
428 
429  // TODO: get max bitrate from encoderlink
430  FileSystemInfo::Consolidate(disks, true, 14000);
431 
432  long long total = 0;
433  long long used = 0;
434  for (const auto & disk : qAsConst(disks))
435  {
436  disk.ToStringList(res);
437  total += disk.getTotalSpace();
438  used += disk.getUsedSpace();
439  }
440 
441  res << hosts.join(",")
442  << "TotalDiskSpace"
443  << "0"
444  << "-2"
445  << "-2"
446  << "0"
447  << QString::number(total)
448  << QString::number(used);
449 
450  socket->WriteStringList(res);
451  return true;
452 }
453 
455 {
456  QStringList res;
457  QList<FileSystemInfo> disks = QueryAllFileSystems();
458  // TODO: get max bitrate from encoderlink
459  FileSystemInfo::Consolidate(disks, true, 14000);
460 
461  QList<FileSystemInfo>::const_iterator i;
462  long long total = 0;
463  long long used = 0;
464  for (const auto & disk : qAsConst(disks))
465  {
466  total += disk.getTotalSpace();
467  used += disk.getUsedSpace();
468  }
469 
470  res << QString::number(total) << QString::number(used);
471  socket->WriteStringList(res);
472  return true;
473 }
474 
475 QList<FileSystemInfo> FileServerHandler::QueryFileSystems(void)
476 {
477  QStringList groups(StorageGroup::kSpecialGroups);
478  groups.removeAll("LiveTV");
479  QString specialGroups = groups.join("', '");
480 
482  query.prepare(QString("SELECT MIN(id),dirname "
483  "FROM storagegroup "
484  "WHERE hostname = :HOSTNAME "
485  "AND groupname NOT IN ( '%1' ) "
486  "GROUP BY dirname;").arg(specialGroups));
487  query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
488 
489  QList<FileSystemInfo> disks;
490  if (query.exec() && query.isActive())
491  {
492  if (!query.size())
493  {
494  query.prepare("SELECT MIN(id),dirname "
495  "FROM storagegroup "
496  "WHERE groupname = :GROUP "
497  "GROUP BY dirname;");
498  query.bindValue(":GROUP", "Default");
499  if (!query.exec())
500  MythDB::DBError("BackendQueryFileSystems", query);
501  }
502 
503  QDir checkDir("");
504  QString currentDir;
505  FileSystemInfo disk;
506  QMap <QString, bool>foundDirs;
507 
508  while (query.next())
509  {
510  disk.clear();
512  disk.setLocal();
513  disk.setBlockSize(0);
514  disk.setGroupID(query.value(0).toInt());
515 
516  /* The storagegroup.dirname column uses utf8_bin collation, so Qt
517  * uses QString::fromAscii() for toString(). Explicitly convert the
518  * value using QString::fromUtf8() to prevent corruption. */
519  currentDir = QString::fromUtf8(query.value(1)
520  .toByteArray().constData());
521  disk.setPath(currentDir);
522 
523  if (currentDir.endsWith("/"))
524  currentDir.remove(currentDir.length() - 1, 1);
525 
526  checkDir.setPath(currentDir);
527  if (!foundDirs.contains(currentDir))
528  {
529  if (checkDir.exists())
530  {
531  disk.PopulateDiskSpace();
532  disk.PopulateFSProp();
533  disks << disk;
534 
535  foundDirs[currentDir] = true;
536  }
537  else
538  foundDirs[currentDir] = false;
539  }
540  }
541  }
542 
543  return disks;
544 }
545 
546 QList<FileSystemInfo> FileServerHandler::QueryAllFileSystems(void)
547 {
548  QList<FileSystemInfo> disks = QueryFileSystems();
549 
550  {
551  QReadLocker rlock(&m_fsLock);
552 
553  QMap<QString, SocketHandler*>::iterator i;
554  for (i = m_fsMap.begin(); i != m_fsMap.end(); ++i)
555  disks << FileSystemInfo::RemoteGetInfo((*i)->GetSocket());
556  }
557 
558  return disks;
559 }
560 
567  QStringList &slist)
568 {
569  QStringList::const_iterator it = slist.cbegin() + 2;
570  RecordingInfo recinfo(it, slist.cend());
571 
572  bool exists = false;
573 
574  QString pburl;
575  if (recinfo.HasPathname())
576  {
577  pburl = GetPlaybackURL(&recinfo);
578  exists = QFileInfo::exists(pburl);
579  if (!exists)
580  pburl.clear();
581  }
582 
583  QStringList res(QString::number(static_cast<int>(exists)));
584  res << pburl;
585  socket->WriteStringList(res);
586  return true;
587 }
588 
589 
595  QStringList &slist)
596 {
597  QString storageGroup = "Default";
598  QStringList res;
599 
600  if (slist.size() == 3)
601  {
602  if (!slist[2].isEmpty())
603  storageGroup = slist[2];
604  }
605  else if (slist.size() != 2)
606  return false;
607 
608  QString filename = slist[1];
609  if ((filename.isEmpty()) ||
610  (filename.contains("/../")) ||
611  (filename.startsWith("../")))
612  {
613  LOG(VB_GENERAL, LOG_ERR,
614  QString("ERROR checking for file, filename '%1' "
615  "fails sanity checks").arg(filename));
616  res << "";
617  socket->WriteStringList(res);
618  return true;
619  }
620 
621  StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
622  QString fullname = sgroup.FindFile(filename);
623 
624  if (!fullname.isEmpty())
625  {
626  res << "1"
627  << fullname;
628 
629  // TODO: convert me to QFile
630  struct stat fileinfo {};
631  if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
632  {
633  res << QString::number(fileinfo.st_dev)
634  << QString::number(fileinfo.st_ino)
635  << QString::number(fileinfo.st_mode)
636  << QString::number(fileinfo.st_nlink)
637  << QString::number(fileinfo.st_uid)
638  << QString::number(fileinfo.st_gid)
639  << QString::number(fileinfo.st_rdev)
640  << QString::number(fileinfo.st_size)
641 #ifdef _WIN32
642  << "0"
643  << "0"
644 #else
645  << QString::number(fileinfo.st_blksize)
646  << QString::number(fileinfo.st_blocks)
647 #endif
648  << QString::number(fileinfo.st_atime)
649  << QString::number(fileinfo.st_mtime)
650  << QString::number(fileinfo.st_ctime);
651  }
652  }
653  else
654  res << "0";
655 
656  socket->WriteStringList(res);
657  return true;
658 }
659 
665  QStringList &slist)
666 {
667  QString storageGroup = "Default";
668  QString hostname = gCoreContext->GetHostName();
669  QString filename = "";
670  QStringList res;
671 
672  switch (slist.size()) {
673  case 4:
674  if (!slist[3].isEmpty())
675  hostname = slist[3];
676  [[clang::fallthrough]];
677  case 3:
678  if (!slist[2].isEmpty())
679  storageGroup = slist[2];
680  [[clang::fallthrough]];
681  case 2:
682  filename = slist[1];
683  if (filename.isEmpty() ||
684  filename.contains("/../") ||
685  filename.startsWith("../"))
686  {
687  LOG(VB_GENERAL, LOG_ERR,
688  QString("ERROR checking for file, filename '%1' "
689  "fails sanity checks").arg(filename));
690  res << "";
691  socket->WriteStringList(res);
692  return true;
693  }
694  break;
695  default:
696  return false;
697  }
698 
699  QString hash = "";
700 
702  {
703  // looking for file on me, return directly
704  StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
705  QString fullname = sgroup.FindFile(filename);
706  hash = FileHash(fullname);
707  }
708  else
709  {
710  QReadLocker rlock(&m_fsLock);
711  if (m_fsMap.contains(hostname))
712  {
713  // looking for file on connected host, query from it
714  if (m_fsMap[hostname]->SendReceiveStringList(slist))
715  hash = slist[0];
716  }
717  // I deleted the incorrect SQL select that was supposed to get
718  // host name from ip address. Since it cannot work and has
719  // been there 6 years I assume it is not important.
720  }
721 
722 
723  res << hash;
724  socket->WriteStringList(res);
725 
726  return true;
727 }
728 
730  QStringList &slist)
731 {
732  if (slist.size() != 3)
733  return false;
734 
735  return HandleDeleteFile(socket, slist[1], slist[2]);
736 }
737 
738 bool FileServerHandler::DeleteFile(const QString& filename, const QString& storagegroup)
739 {
740  return HandleDeleteFile(nullptr, filename, storagegroup);
741 }
742 
744  const QString& filename, const QString& storagegroup)
745 {
746  StorageGroup sgroup(storagegroup, "", false);
747  QStringList res;
748 
749  if ((filename.isEmpty()) ||
750  (filename.contains("/../")) ||
751  (filename.startsWith("../")))
752  {
753  LOG(VB_GENERAL, LOG_ERR,
754  QString("ERROR deleting file, filename '%1' fails sanity checks")
755  .arg(filename));
756  if (socket)
757  {
758  res << "0";
759  socket->WriteStringList(res);
760  return true;
761  }
762  return false;
763  }
764 
765  QString fullfile = sgroup.FindFile(filename);
766 
767  if (fullfile.isEmpty())
768  {
769  LOG(VB_GENERAL, LOG_ERR,
770  QString("Unable to find %1 in HandleDeleteFile()") .arg(filename));
771  if (socket)
772  {
773  res << "0";
774  socket->WriteStringList(res);
775  return true;
776  }
777  return false;
778  }
779 
780  QFile checkFile(fullfile);
781  if (checkFile.exists())
782  {
783  if (socket)
784  {
785  res << "1";
786  socket->WriteStringList(res);
787  }
788  RunDeleteThread();
789  deletethread->AddFile(fullfile);
790  }
791  else
792  {
793  LOG(VB_GENERAL, LOG_ERR, QString("Error deleting file: '%1'")
794  .arg(fullfile));
795  if (socket)
796  {
797  res << "0";
798  socket->WriteStringList(res);
799  }
800  }
801 
802  return true;
803 }
804 
806 {
807  RunDeleteThread();
808  return deletethread->AddFile(handler);
809 }
810 
812  QStringList &slist)
813 {
814  QStringList res;
815 
816  bool fileNamesOnly = false;
817  if (slist.size() == 5)
818  fileNamesOnly = (slist[4].toInt() != 0);
819  else if (slist.size() != 4)
820  {
821  LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
822  .arg(slist.join("[]:[]")));
823  res << "EMPTY LIST";
824  socket->WriteStringList(res);
825  return true;
826  }
827 
828  QString host = gCoreContext->GetHostName();
829  QString wantHost = slist[1];
830  QString groupname = slist[2];
831  QString path = slist[3];
832 
833  LOG(VB_FILE, LOG_INFO,
834  QString("HandleSGGetFileList: group = %1 host = %2 "
835  "path = %3 wanthost = %4")
836  .arg(groupname).arg(host).arg(path).arg(wantHost));
837 
838  if (gCoreContext->IsThisHost(wantHost))
839  {
840  StorageGroup sg(groupname, host);
841  LOG(VB_FILE, LOG_INFO, "Getting local info");
842  if (fileNamesOnly)
843  res = sg.GetFileList(path);
844  else
845  res = sg.GetFileInfoList(path);
846 
847  if (res.count() == 0)
848  res << "EMPTY LIST";
849  }
850  else
851  {
852  // handle request on remote server
853  SocketHandler *remsock = nullptr;
854  {
855  QReadLocker rlock(&m_fsLock);
856  if (m_fsMap.contains(wantHost))
857  {
858  remsock = m_fsMap[wantHost];
859  remsock->IncrRef();
860  }
861  }
862 
863  if (remsock)
864  {
865  LOG(VB_FILE, LOG_INFO, "Getting remote info");
866  res << "QUERY_SG_GETFILELIST" << wantHost << groupname << path
867  << QString::number(static_cast<int>(fileNamesOnly));
868  remsock->SendReceiveStringList(res);
869  remsock->DecrRef();
870  }
871  else
872  {
873  LOG(VB_FILE, LOG_ERR, QString("Failed to grab slave socket : %1 :")
874  .arg(wantHost));
875  res << "SLAVE UNREACHABLE: " << wantHost;
876  }
877  }
878 
879  socket->WriteStringList(res);
880  return true;
881 }
882 
884  QStringList &slist)
885 {
886  QStringList res;
887 
888  if (slist.size() != 4)
889  {
890  LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
891  .arg(slist.join("[]:[]")));
892  res << "EMPTY LIST";
893  socket->WriteStringList(res);
894  return true;
895  }
896 
897  QString wantHost = slist[1];
898  QString groupname = slist[2];
899  QString filename = slist[3];
900 
901  LOG(VB_FILE, LOG_DEBUG, QString("HandleSGFileQuery: myth://%1@%2/%3")
902  .arg(groupname).arg(wantHost).arg(filename));
903 
904  if (gCoreContext->IsThisHost(wantHost))
905  {
906  // handle request locally
907  LOG(VB_FILE, LOG_DEBUG, QString("Getting local info"));
908  StorageGroup sg(groupname, gCoreContext->GetHostName());
909  res = sg.GetFileInfo(filename);
910 
911  if (res.count() == 0)
912  res << "EMPTY LIST";
913  }
914  else
915  {
916  // handle request on remote server
917  SocketHandler *remsock = nullptr;
918  {
919  QReadLocker rlock(&m_fsLock);
920  if (m_fsMap.contains(wantHost))
921  {
922  remsock = m_fsMap[wantHost];
923  remsock->IncrRef();
924  }
925  }
926 
927  if (remsock)
928  {
929  res << "QUERY_SG_FILEQUERY" << wantHost << groupname << filename;
930  remsock->SendReceiveStringList(res);
931  remsock->DecrRef();
932  }
933  else
934  {
935  res << "SLAVE UNREACHABLE: " << wantHost;
936  }
937  }
938 
939  socket->WriteStringList(res);
940  return true;
941 }
942 
944  QStringList &commands, QStringList &slist)
945 {
946  if (commands.size() != 2)
947  return false;
948 
949  if (slist.size() < 2)
950  return false;
951 
952  QStringList res;
953  int recnum = commands[1].toInt();
954  FileTransfer *ft = nullptr;
955 
956  {
957  QReadLocker rlock(&m_ftLock);
958  if (!m_ftMap.contains(recnum))
959  {
960  if (slist[1] == "DONE")
961  res << "OK";
962  else
963  {
964  LOG(VB_GENERAL, LOG_ERR,
965  QString("Unknown file transfer socket: %1").arg(recnum));
966  res << "ERROR"
967  << "unknown_file_transfer_socket";
968  }
969 
970  socket->WriteStringList(res);
971  return true;
972  }
973 
974  ft = m_ftMap[recnum];
975  ft->IncrRef();
976  }
977 
978  if (slist[1] == "REQUEST_BLOCK")
979  {
980  if (slist.size() != 3)
981  {
982  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
983  "REQUEST_BLOCK call");
984  res << "ERROR" << "invalid_call";
985  }
986  else
987  {
988  int size = slist[2].toInt();
989  res << QString::number(ft->RequestBlock(size));
990  }
991  }
992  else if (slist[1] == "WRITE_BLOCK")
993  {
994  if (slist.size() != 3)
995  {
996  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
997  "WRITE_BLOCK call");
998  res << "ERROR" << "invalid_call";
999  }
1000  else
1001  {
1002  int size = slist[2].toInt();
1003  res << QString::number(ft->WriteBlock(size));
1004  }
1005  }
1006  else if (slist[1] == "SEEK")
1007  {
1008  if (slist.size() != 5)
1009  {
1010  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER SEEK call");
1011  res << "ERROR" << "invalid_call";
1012  }
1013  else
1014  {
1015  long long pos = slist[2].toLongLong();
1016  int whence = slist[3].toInt();
1017  long long curpos = slist[4].toLongLong();
1018 
1019  res << QString::number(ft->Seek(curpos, pos, whence));
1020  }
1021  }
1022  else if (slist[1] == "IS_OPEN")
1023  {
1024  res << QString::number(static_cast<int>(ft->isOpen()));
1025  }
1026  else if (slist[1] == "DONE")
1027  {
1028  ft->Stop();
1029  res << "OK";
1030  }
1031  else if (slist[1] == "SET_TIMEOUT")
1032  {
1033  if (slist.size() != 3)
1034  {
1035  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
1036  "SET_TIMEOUT call");
1037  res << "ERROR" << "invalid_call";
1038  }
1039  else
1040  {
1041  bool fast = slist[2].toInt() != 0;
1042  ft->SetTimeout(fast);
1043  res << "OK";
1044  }
1045  }
1046  else if (slist[1] == "REQUEST_SIZE")
1047  {
1048  // return size and if the file is not opened for writing
1049  res << QString::number(ft->GetFileSize());
1050  res << QString::number(static_cast<int>(!gCoreContext->IsRegisteredFileForWrite(ft->GetFileName())));
1051  }
1052  else
1053  {
1054  LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER call");
1055  res << "ERROR" << "invalid_call";
1056  }
1057 
1058  ft->DecrRef();
1059  socket->WriteStringList(res);
1060  return true;
1061 }
1062 
1064  QStringList &slist)
1065 {
1066  QStringList res;
1067 
1068  if (slist.size() != 4)
1069  {
1070  res << "ERROR" << QString("Bad %1 command").arg(slist[0]);
1071  socket->WriteStringList(res);
1072  return true;
1073  }
1074 
1075  bool synchronous = (slist[0] == "DOWNLOAD_FILE_NOW");
1076  QString srcURL = slist[1];
1077  QString storageGroup = slist[2];
1078  QString filename = slist[3];
1079  StorageGroup sgroup(storageGroup, gCoreContext->GetHostName(), false);
1080  QString outDir = sgroup.FindNextDirMostFree();
1081  QString outFile;
1082  QStringList retlist;
1083 
1084  if (filename.isEmpty())
1085  {
1086  QFileInfo finfo(srcURL);
1087  filename = finfo.fileName();
1088  }
1089 
1090  if (outDir.isEmpty())
1091  {
1092  LOG(VB_GENERAL, LOG_ERR, QString("Unable to determine directory "
1093  "to write to in %1 write command").arg(slist[0]));
1094  res << "ERROR" << "downloadfile_directory_not_found";
1095  socket->WriteStringList(res);
1096  return true;
1097  }
1098 
1099  if ((filename.contains("/../")) ||
1100  (filename.startsWith("../")))
1101  {
1102  LOG(VB_GENERAL, LOG_ERR, QString("ERROR: %1 write "
1103  "filename '%2' does not pass sanity checks.")
1104  .arg(slist[0]).arg(filename));
1105  res << "ERROR" << "downloadfile_filename_dangerous";
1106  socket->WriteStringList(res);
1107  return true;
1108  }
1109 
1110  outFile = outDir + "/" + filename;
1111 
1112  if (synchronous)
1113  {
1114  if (GetMythDownloadManager()->download(srcURL, outFile))
1115  {
1116  res << "OK"
1117  << gCoreContext->GetMasterHostPrefix(storageGroup)
1118  + filename;
1119  }
1120  else
1121  res << "ERROR";
1122  }
1123  else
1124  {
1125  QMutexLocker locker(&m_downloadURLsLock);
1126  m_downloadURLs[outFile] =
1127  gCoreContext->GetMasterHostPrefix(storageGroup) +
1129 
1130  GetMythDownloadManager()->queueDownload(srcURL, outFile, this);
1131  res << "OK"
1132  << gCoreContext->GetMasterHostPrefix(storageGroup) + filename;
1133  }
1134 
1135  socket->WriteStringList(res);
1136  return true;
1137 }
1138 
MSqlQuery::isActive
bool isActive(void) const
Definition: mythdbcon.h:204
MSqlQuery::next
bool next(void)
Wrap QSqlQuery::next() so we can display the query results.
Definition: mythdbcon.cpp:783
MSqlQuery
QSqlQuery wrapper that fetches a DB connection from the connection pool.
Definition: mythdbcon.h:126
MSqlQuery::size
int size(void) const
Definition: mythdbcon.h:203
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:943
FileTransfer::Stop
void Stop(void)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:70
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:66
StorageGroup::FindFile
QString FindFile(const QString &filename)
Definition: storagegroup.cpp:602
FileServerHandler::HandleFileQuery
bool HandleFileQuery(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:883
FileSystemInfo::Consolidate
static void Consolidate(QList< FileSystemInfo > &disks, bool merge=true, int64_t fuzz=14000)
Definition: filesysteminfo.cpp:171
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:254
MSqlQuery::value
QVariant value(int i) const
Definition: mythdbcon.h:198
arg
arg(title).arg(filename).arg(doDelete))
DeleteThread
Definition: deletethread.h:20
MSqlQuery::exec
bool exec(void)
Wrap QSqlQuery::exec() so we can display SQL.
Definition: mythdbcon.cpp:603
deletethread
DeleteThread * deletethread
Definition: fileserverhandler.cpp:27
LOG
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:23
FileServerHandler::HandleDeleteFile
static bool HandleDeleteFile(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:729
build_compdb.file
file
Definition: build_compdb.py:55
FileTransfer::WriteBlock
int WriteBlock(int size)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:153
FileServerHandler::connectionAnnounced
void connectionAnnounced(MythSocket *socket, QStringList &commands, QStringList &slist) override
Definition: fileserverhandler.cpp:355
FileTransfer
Definition: libs/libmythprotoserver/sockethandler/filetransfer.h:20
FileServerHandler::m_fsLock
QReadWriteLock m_fsLock
Definition: fileserverhandler.h:61
FileServerHandler::HandleQueryFileHash
bool HandleQueryFileHash(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:664
MythSocket
Class for communcating between myth backends and frontends.
Definition: mythsocket.h:27
fileserverutil.h
MythCoreContext::IsRegisteredFileForWrite
bool IsRegisteredFileForWrite(const QString &file)
Definition: mythcorecontext.cpp:2138
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:58
mythlogging.h
FileSystemInfo::RemoteGetInfo
static QList< FileSystemInfo > RemoteGetInfo(MythSocket *sock=nullptr)
Definition: filesysteminfo.cpp:143
MythSocket::WriteStringList
bool WriteStringList(const QStringList &list)
Definition: mythsocket.cpp:317
DeleteHandler
Definition: fileserverutil.h:18
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:535
FileServerHandler::m_downloadURLs
QMap< QString, QString > m_downloadURLs
Definition: fileserverhandler.h:64
MythDB::DBError
static void DBError(const QString &where, const MSqlQuery &query)
Definition: mythdb.cpp:178
fileserverhandler.h
MythSocketManager::AddSocketHandler
void AddSocketHandler(SocketHandler *socket)
Definition: mythsocketmanager.cpp:135
GetPlaybackURL
QString GetPlaybackURL(ProgramInfo *pginfo, bool storePath)
Definition: fileserverutil.cpp:45
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:214
SocketHandler::WriteStringList
bool WriteStringList(const QStringList &strlist)
Definition: sockethandler.cpp:27
filename
QString filename
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:637
SocketHandler::SendReceiveStringList
bool SendReceiveStringList(QStringList &strlist, uint min_reply_length=0)
Definition: sockethandler.cpp:35
storagegroup.h
FileServerHandler::m_fsMap
QMap< QString, SocketHandler * > m_fsMap
Definition: fileserverhandler.h:60
filetransfer.h
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
FileServerHandler::HandleQueryFileExists
static bool HandleQueryFileExists(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:594
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:113
mythmediabuffer.h
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:230
FileServerHandler::QueryAllFileSystems
QList< FileSystemInfo > QueryAllFileSystems(void)
Definition: fileserverhandler.cpp:546
FileServerHandler::HandleQueryFreeSpaceSummary
bool HandleQueryFreeSpaceSummary(SocketHandler *socket)
Definition: fileserverhandler.cpp:454
recordinginfo.h
ProgramInfo::GetChanID
uint GetChanID(void) const
This is the unique key used in the database to locate tuning information.
Definition: programinfo.h:370
FileSystemInfo::PopulateDiskSpace
void PopulateDiskSpace(void)
Definition: filesysteminfo.cpp:221
ProgramInfo
Holds information on recordings and videos.
Definition: programinfo.h:68
kReadTestSize
#define kReadTestSize
Definition: mythmediabuffer.h:22
mythmiscutil.h
mythcorecontext.h
StorageGroup::FindNextDirMostFree
QString FindNextDirMostFree(void)
Definition: storagegroup.cpp:671
dir
QDir dir
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:1174
FileSystemInfo::setHostname
void setHostname(const QString &hostname)
Definition: filesysteminfo.h:47
MythSocketManager::GetConnectionBySocket
SocketHandler * GetConnectionBySocket(MythSocket *socket)
Definition: mythsocketmanager.cpp:145
MSqlQuery::bindValue
void bindValue(const QString &placeholder, const QVariant &val)
Add a single binding.
Definition: mythdbcon.cpp:864
FileSystemInfo
Definition: filesysteminfo.h:16
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:246
MythSocket::GetSocketDescriptor
int GetSocketDescriptor(void) const
Definition: mythsocket.cpp:591
FileServerHandler::m_ftMap
QMap< int, FileTransfer * > m_ftMap
Definition: fileserverhandler.h:57
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:566
StorageGroup::kSpecialGroups
static const QStringList kSpecialGroups
Definition: storagegroup.h:46
MThread::isRunning
bool isRunning(void) const
Definition: mthread.cpp:268
StorageGroup
Definition: storagegroup.h:12
SocketHandler::BlockShutdown
void BlockShutdown(bool block)
Definition: sockethandler.h:35
FileServerHandler::HandleDownloadFile
bool HandleDownloadFile(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:1063
SocketHandler
Definition: sockethandler.h:17
FileServerHandler::DeleteFile
static bool DeleteFile(const QString &filename, const QString &storagegroup)
Definition: fileserverhandler.cpp:738
MythCoreContext::GetHostName
QString GetHostName(void)
Definition: mythcorecontext.cpp:859
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:742
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:819
FileServerHandler::HandleGetFileList
bool HandleGetFileList(SocketHandler *socket, QStringList &slist)
Definition: fileserverhandler.cpp:811
DeleteThread::start
void start(void)
Definition: mainserver.h:82
mythdownloadmanager.h
FileTransfer::SetTimeout
void SetTimeout(bool fast)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:262
ReferenceCounter::IncrRef
virtual int IncrRef(void)
Increments reference count.
Definition: referencecounter.cpp:101
FileServerHandler::m_downloadURLsLock
QMutex m_downloadURLsLock
Definition: fileserverhandler.h:63
FileHash
QString FileHash(const QString &filename)
Definition: mythmiscutil.cpp:594
FileSystemInfo::clear
void clear(void)
Definition: filesysteminfo.cpp:90
mythsocket.h
query
MSqlQuery query(MSqlQuery::InitCon())
FileTransfer::isOpen
bool isOpen(void)
Definition: libs/libmythprotoserver/sockethandler/filetransfer.cpp:54
ProgramInfo::HasPathname
bool HasPathname(void) const
Definition: programinfo.h:356
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:475
MSqlQuery::prepare
bool prepare(const QString &query)
QSqlQuery::prepare() is not thread safe in Qt <= 3.3.2.
Definition: mythdbcon.cpp:808