00001
00002 using namespace std;
00003
00004 #include <QString>
00005 #include <QWriteLocker>
00006 #include <QReadLocker>
00007
00008 #include "mythmiscutil.h"
00009 #include "mythdb.h"
00010 #include "ringbuffer.h"
00011 #include "mythsocket.h"
00012 #include "mythlogging.h"
00013 #include "programinfo.h"
00014 #include "storagegroup.h"
00015 #include "mythcorecontext.h"
00016 #include "mythdownloadmanager.h"
00017
00018 #include "sockethandler/filetransfer.h"
00019 #include "requesthandler/deletethread.h"
00020 #include "requesthandler/fileserverhandler.h"
00021 #include "requesthandler/fileserverutil.h"
00022
00023 DeleteThread *deletethread = NULL;
00024
00025 void FileServerHandler::connectionClosed(MythSocket *socket)
00026 {
00027
00028
00029 {
00030 QWriteLocker wlock(&m_ftLock);
00031 QMap<int, FileTransfer*>::iterator i;
00032 for (i = m_ftMap.begin(); i != m_ftMap.end(); ++i)
00033 {
00034 if ((*i)->GetSocket() == socket)
00035 {
00036 (*i)->DownRef();
00037 m_ftMap.remove(i.key());
00038 return;
00039 }
00040 }
00041 }
00042
00043
00044
00045 {
00046 QWriteLocker wlock(&m_fsLock);
00047 QMap<QString, SocketHandler*>::iterator i;
00048 for (i = m_fsMap.begin(); i != m_fsMap.end(); ++i)
00049 {
00050 if ((*i)->GetSocket() == socket)
00051 {
00052 (*i)->DownRef();
00053 m_fsMap.remove(i.key());
00054 return;
00055 }
00056 }
00057 }
00058 }
00059
00060 QString FileServerHandler::LocalFilePath(const QUrl &url,
00061 const QString &wantgroup)
00062 {
00063 QString lpath = url.path();
00064
00065 if (lpath.section('/', -2, -2) == "channels")
00066 {
00067
00068 QString querytext;
00069
00070 QString file = lpath.section('/', -1);
00071 lpath = "";
00072
00073 MSqlQuery query(MSqlQuery::InitCon());
00074 query.prepare("SELECT icon FROM channel WHERE icon LIKE :FILENAME ;");
00075 query.bindValue(":FILENAME", QString("%/") + file);
00076
00077 if (query.exec() && query.next())
00078 {
00079 lpath = query.value(0).toString();
00080 }
00081 else
00082 {
00083 MythDB::DBError("Icon path", query);
00084 }
00085 }
00086 else
00087 {
00088 lpath = lpath.section('/', -1);
00089
00090 QString fpath = lpath;
00091 if (fpath.right(4) == ".png")
00092 fpath = fpath.left(fpath.length() - 4);
00093
00094 ProgramInfo pginfo(fpath);
00095 if (pginfo.GetChanID())
00096 {
00097 QString pburl = GetPlaybackURL(&pginfo);
00098 if (pburl.left(1) == "/")
00099 {
00100 lpath = pburl.section('/', 0, -2) + "/" + lpath;
00101 LOG(VB_FILE, LOG_INFO,
00102 QString("Local file path: %1").arg(lpath));
00103 }
00104 else
00105 {
00106 LOG(VB_GENERAL, LOG_ERR,
00107 QString("LocalFilePath unable to find local "
00108 "path for '%1', found '%2' instead.")
00109 .arg(lpath).arg(pburl));
00110 lpath = "";
00111 }
00112 }
00113 else if (!lpath.isEmpty())
00114 {
00115
00116 QString opath = lpath;
00117 StorageGroup sgroup;
00118
00119 if (!wantgroup.isEmpty())
00120 {
00121 sgroup.Init(wantgroup);
00122 lpath = url.toString();
00123 }
00124 else
00125 {
00126 lpath = QFileInfo(lpath).fileName();
00127 }
00128
00129 QString tmpFile = sgroup.FindFile(lpath);
00130 if (!tmpFile.isEmpty())
00131 {
00132 lpath = tmpFile;
00133 LOG(VB_FILE, LOG_INFO,
00134 QString("LocalFilePath(%1 '%2'), found through "
00135 "exhaustive search at '%3'")
00136 .arg(url.toString()).arg(opath).arg(lpath));
00137 }
00138 else
00139 {
00140 LOG(VB_GENERAL, LOG_ERR, QString("LocalFilePath unable to "
00141 "find local path for '%1'.")
00142 .arg(opath));
00143 lpath = "";
00144 }
00145
00146 }
00147 else
00148 {
00149 lpath = "";
00150 }
00151 }
00152
00153 return lpath;
00154 }
00155
00156 void FileServerHandler::RunDeleteThread(void)
00157 {
00158 if (deletethread != NULL)
00159 {
00160 if (deletethread->isRunning())
00161 return
00162
00163 delete deletethread;
00164 deletethread = NULL;
00165 }
00166
00167 deletethread = new DeleteThread();
00168 deletethread->start();
00169 }
00170
00171 bool FileServerHandler::HandleAnnounce(MythSocket *socket,
00172 QStringList &commands, QStringList &slist)
00173 {
00174 if (commands[1] == "FileServer")
00175 {
00176 if (slist.size() >= 3)
00177 {
00178 SocketHandler *handler =
00179 new SocketHandler(socket, m_parent, commands[2]);
00180
00181 handler->BlockShutdown(true);
00182 handler->AllowStandardEvents(true);
00183 handler->AllowSystemEvents(true);
00184
00185 QWriteLocker wlock(&m_fsLock);
00186 m_fsMap.insert(commands[2], handler);
00187 m_parent->AddSocketHandler(handler);
00188
00189 slist.clear();
00190 slist << "OK";
00191 handler->SendStringList(slist);
00192 return true;
00193 }
00194 return false;
00195 }
00196
00197 if (commands[1] != "FileTransfer")
00198 return false;
00199
00200 if (slist.size() < 3)
00201 return false;
00202
00203 if ((commands.size() < 3) || (commands.size() > 6))
00204 return false;
00205
00206 FileTransfer *ft = NULL;
00207 QString hostname = "";
00208 QString filename = "";
00209 bool writemode = false;
00210 bool usereadahead = true;
00211 int timeout_ms = 2000;
00212 switch (commands.size())
00213 {
00214 case 6:
00215 timeout_ms = commands[5].toInt();
00216 case 5:
00217 usereadahead = commands[4].toInt();
00218 case 4:
00219 writemode = commands[3].toInt();
00220 default:
00221 hostname = commands[2];
00222 }
00223
00224 QStringList::const_iterator it = slist.begin();
00225 QUrl qurl = *(++it);
00226 QString wantgroup = *(++it);
00227
00228 QStringList checkfiles;
00229 while (++it != slist.end())
00230 checkfiles += *(it);
00231
00232 slist.clear();
00233
00234 LOG(VB_GENERAL, LOG_DEBUG, "FileServerHandler::HandleAnnounce");
00235 LOG(VB_GENERAL, LOG_INFO, QString("adding: %1 as remote file transfer")
00236 .arg(hostname));
00237
00238 if (writemode)
00239 {
00240 if (wantgroup.isEmpty())
00241 wantgroup = "Default";
00242
00243 StorageGroup sgroup(wantgroup, gCoreContext->GetHostName(), false);
00244 QString dir = sgroup.FindNextDirMostFree();
00245 if (dir.isEmpty())
00246 {
00247 LOG(VB_GENERAL, LOG_ERR, "Unable to determine directory "
00248 "to write to in FileTransfer write command");
00249
00250 slist << "ERROR" << "filetransfer_directory_not_found";
00251 socket->writeStringList(slist);
00252 return true;
00253 }
00254
00255 QString basename = qurl.path();
00256 if (basename.isEmpty())
00257 {
00258 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write "
00259 "filename is empty in url '%1'.")
00260 .arg(qurl.toString()));
00261
00262 slist << "ERROR" << "filetransfer_filename_empty";
00263 socket->writeStringList(slist);
00264 return true;
00265 }
00266
00267 if ((basename.contains("/../")) ||
00268 (basename.startsWith("../")))
00269 {
00270 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer write "
00271 "filename '%1' does not pass sanity checks.")
00272 .arg(basename));
00273
00274 slist << "ERROR" << "filetransfer_filename_dangerous";
00275 socket->writeStringList(slist);
00276 return true;
00277 }
00278
00279 filename = dir + "/" + basename;
00280 }
00281 else
00282 filename = LocalFilePath(qurl, wantgroup);
00283
00284 QFileInfo finfo(filename);
00285 if (finfo.isDir())
00286 {
00287 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer filename "
00288 "'%1' is actually a directory, cannot transfer.")
00289 .arg(filename));
00290
00291 slist << "ERROR" << "filetransfer_filename_is_a_directory";
00292 socket->writeStringList(slist);
00293 return true;
00294 }
00295
00296 if (writemode)
00297 {
00298 QString dirPath = finfo.absolutePath();
00299 QDir qdir(dirPath);
00300 if (!qdir.exists())
00301 {
00302 if (!qdir.mkpath(dirPath))
00303 {
00304 LOG(VB_GENERAL, LOG_ERR, QString("FileTransfer "
00305 "filename '%1' is in a subdirectory which does "
00306 "not exist, but can not be created.")
00307 .arg(filename));
00308
00309 slist << "ERROR" << "filetransfer_unable_to_create_subdirectory";
00310 socket->writeStringList(slist);
00311 return true;
00312 }
00313 }
00314
00315 ft = new FileTransfer(filename, socket, m_parent, writemode);
00316 }
00317 else
00318 ft = new FileTransfer(filename, socket, m_parent, usereadahead, timeout_ms);
00319
00320 ft->BlockShutdown(true);
00321
00322 {
00323 QWriteLocker wlock(&m_ftLock);
00324 m_ftMap.insert(socket->socket(), ft);
00325 }
00326
00327 slist << "OK"
00328 << QString::number(socket->socket())
00329 << QString::number(ft->GetFileSize());
00330
00331 if (checkfiles.size())
00332 {
00333 QFileInfo fi(filename);
00334 QDir dir = fi.absoluteDir();
00335 for (it = checkfiles.begin(); it != checkfiles.end(); ++it)
00336 {
00337 if (dir.exists(*it) &&
00338 QFileInfo(dir, *it).size() >= kReadTestSize)
00339 slist << *it;
00340 }
00341 }
00342
00343 socket->writeStringList(slist);
00344 m_parent->AddSocketHandler(ft);
00345 return true;
00346 }
00347
00348 void FileServerHandler::connectionAnnounced(MythSocket *socket,
00349 QStringList &commands, QStringList &slist)
00350 {
00351 if (commands[1] == "SlaveBackend")
00352 {
00353
00354
00355 if (slist.size() >= 3)
00356 {
00357 SocketHandler *handler = m_parent->GetConnectionBySocket(socket);
00358 if (handler == NULL)
00359 return;
00360
00361 QWriteLocker wlock(&m_fsLock);
00362 m_fsMap.insert(commands[2], handler);
00363 }
00364 }
00365
00366 }
00367
00368 bool FileServerHandler::HandleQuery(SocketHandler *socket, QStringList &commands,
00369 QStringList &slist)
00370 {
00371 bool handled = false;
00372 QString command = commands[0];
00373
00374 if (command == "QUERY_FREE_SPACE")
00375 handled = HandleQueryFreeSpace(socket);
00376 else if (command == "QUERY_FREE_SPACE_LIST")
00377 handled = HandleQueryFreeSpaceList(socket);
00378 else if (command == "QUERY_FREE_SPACE_SUMMARY")
00379 handled = HandleQueryFreeSpaceSummary(socket);
00380 else if (command == "QUERY_FILE_EXISTS")
00381 handled = HandleQueryFileExists(socket, slist);
00382 else if (command == "QUERY_FILE_HASH")
00383 handled = HandleQueryFileHash(socket, slist);
00384 else if (command == "DELETE_FILE")
00385 handled = HandleDeleteFile(socket, slist);
00386 else if (command == "QUERY_SG_GETFILELIST")
00387 handled = HandleGetFileList(socket, slist);
00388 else if (command == "QUERY_SG_FILEQUERY")
00389 handled = HandleFileQuery(socket, slist);
00390 else if (command == "QUERY_FILETRANSFER")
00391 handled = HandleQueryFileTransfer(socket, commands, slist);
00392 else if (command == "DOWNLOAD_FILE" || command == "DOWNLOAD_FILE_NOW")
00393 handled = HandleDownloadFile(socket, slist);
00394 return handled;
00395 }
00396
00397 bool FileServerHandler::HandleQueryFreeSpace(SocketHandler *socket)
00398 {
00399 QStringList res;
00400
00401 QList<FileSystemInfo> disks = QueryFileSystems();
00402 QList<FileSystemInfo>::const_iterator i;
00403 for (i = disks.begin(); i != disks.end(); ++i)
00404 i->ToStringList(res);
00405
00406 socket->SendStringList(res);
00407 return true;
00408 }
00409
00410 bool FileServerHandler::HandleQueryFreeSpaceList(SocketHandler *socket)
00411 {
00412 QStringList res;
00413 QStringList hosts;
00414
00415 QList<FileSystemInfo> disks = QueryAllFileSystems();
00416 QList<FileSystemInfo>::const_iterator i;
00417 for (i = disks.begin(); i != disks.end(); ++i)
00418 if (!hosts.contains(i->getHostname()))
00419 hosts << i->getHostname();
00420
00421
00422 FileSystemInfo::Consolidate(disks, true, 14000);
00423
00424 long long total = 0;
00425 long long used = 0;
00426 for (i = disks.begin(); i != disks.end(); ++i)
00427 {
00428 i->ToStringList(res);
00429 total += i->getTotalSpace();
00430 used += i->getUsedSpace();
00431 }
00432
00433 res << hosts.join(",")
00434 << "TotalDiskSpace"
00435 << "0"
00436 << "-2"
00437 << "-2"
00438 << "0"
00439 << QString::number(total)
00440 << QString::number(used);
00441
00442 socket->SendStringList(res);
00443 return true;
00444 }
00445
00446 bool FileServerHandler::HandleQueryFreeSpaceSummary(SocketHandler *socket)
00447 {
00448 QStringList res;
00449 QList<FileSystemInfo> disks = QueryAllFileSystems();
00450
00451 FileSystemInfo::Consolidate(disks, true, 14000);
00452
00453 QList<FileSystemInfo>::const_iterator i;
00454 long long total = 0;
00455 long long used = 0;
00456 for (i = disks.begin(); i != disks.end(); ++i)
00457 {
00458 total += i->getTotalSpace();
00459 used += i->getUsedSpace();
00460 }
00461
00462 res << QString::number(total) << QString::number(used);
00463 socket->SendStringList(res);
00464 return true;
00465 }
00466
00467 QList<FileSystemInfo> FileServerHandler::QueryFileSystems(void)
00468 {
00469 QStringList groups(StorageGroup::kSpecialGroups);
00470 groups.removeAll("LiveTV");
00471 QString specialGroups = groups.join("', '");
00472
00473 MSqlQuery query(MSqlQuery::InitCon());
00474 query.prepare(QString("SELECT MIN(id),dirname "
00475 "FROM storagegroup "
00476 "WHERE hostname = :HOSTNAME "
00477 "AND groupname NOT IN ( '%1' ) "
00478 "GROUP BY dirname;").arg(specialGroups));
00479 query.bindValue(":HOSTNAME", gCoreContext->GetHostName());
00480
00481 QList<FileSystemInfo> disks;
00482 if (query.exec() && query.isActive())
00483 {
00484 if (!query.size())
00485 {
00486 query.prepare("SELECT MIN(id),dirname "
00487 "FROM storagegroup "
00488 "WHERE groupname = :GROUP "
00489 "GROUP BY dirname;");
00490 query.bindValue(":GROUP", "Default");
00491 if (!query.exec())
00492 MythDB::DBError("BackendQueryFileSystems", query);
00493 }
00494
00495 QDir checkDir("");
00496 QString currentDir;
00497 FileSystemInfo disk;
00498 QMap <QString, bool>foundDirs;
00499
00500 while (query.next())
00501 {
00502 disk.clear();
00503 disk.setHostname(gCoreContext->GetHostName());
00504 disk.setLocal();
00505 disk.setBlockSize(0);
00506 disk.setGroupID(query.value(0).toInt());
00507
00508
00509
00510
00511 currentDir = QString::fromUtf8(query.value(1)
00512 .toByteArray().constData());
00513 disk.setPath(currentDir);
00514
00515 if (currentDir.right(1) == "/")
00516 currentDir.remove(currentDir.length() - 1, 1);
00517
00518 checkDir.setPath(currentDir);
00519 if (!foundDirs.contains(currentDir))
00520 {
00521 if (checkDir.exists())
00522 {
00523 disk.PopulateDiskSpace();
00524 disk.PopulateFSProp();
00525 disks << disk;
00526
00527 foundDirs[currentDir] = true;
00528 }
00529 else
00530 foundDirs[currentDir] = false;
00531 }
00532 }
00533 }
00534
00535 return disks;
00536 }
00537
00538 QList<FileSystemInfo> FileServerHandler::QueryAllFileSystems(void)
00539 {
00540 QList<FileSystemInfo> disks = QueryFileSystems();
00541
00542 {
00543 QReadLocker rlock(&m_fsLock);
00544
00545 QMap<QString, SocketHandler*>::iterator i;
00546 for (i = m_fsMap.begin(); i != m_fsMap.end(); ++i)
00547 disks << FileSystemInfo::RemoteGetInfo((*i)->GetSocket());
00548 }
00549
00550 return disks;
00551 }
00552
00557 bool FileServerHandler::HandleQueryFileExists(SocketHandler *socket,
00558 QStringList &slist)
00559 {
00560 QString storageGroup = "Default";
00561 QStringList res;
00562
00563 if (slist.size() == 3)
00564 {
00565 if (!slist[2].isEmpty())
00566 storageGroup = slist[2];
00567 }
00568 else if (slist.size() != 2)
00569 return false;
00570
00571 QString filename = slist[1];
00572 if ((filename.isEmpty()) ||
00573 (filename.contains("/../")) ||
00574 (filename.startsWith("../")))
00575 {
00576 LOG(VB_GENERAL, LOG_ERR,
00577 QString("ERROR checking for file, filename '%1' "
00578 "fails sanity checks").arg(filename));
00579 res << "";
00580 socket->SendStringList(res);
00581 return true;
00582 }
00583
00584 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
00585 QString fullname = sgroup.FindFile(filename);
00586
00587 if (!fullname.isEmpty())
00588 {
00589 res << "1"
00590 << fullname;
00591
00592
00593 struct stat fileinfo;
00594 if (stat(fullname.toLocal8Bit().constData(), &fileinfo) >= 0)
00595 {
00596 res << QString::number(fileinfo.st_dev)
00597 << QString::number(fileinfo.st_ino)
00598 << QString::number(fileinfo.st_mode)
00599 << QString::number(fileinfo.st_nlink)
00600 << QString::number(fileinfo.st_uid)
00601 << QString::number(fileinfo.st_gid)
00602 << QString::number(fileinfo.st_rdev)
00603 << QString::number(fileinfo.st_size)
00604 #ifdef USING_MINGW
00605 << "0"
00606 << "0"
00607 #else
00608 << QString::number(fileinfo.st_blksize)
00609 << QString::number(fileinfo.st_blocks)
00610 #endif
00611 << QString::number(fileinfo.st_atime)
00612 << QString::number(fileinfo.st_mtime)
00613 << QString::number(fileinfo.st_ctime);
00614 }
00615 }
00616 else
00617 res << "0";
00618
00619 socket->SendStringList(res);
00620 return true;
00621 }
00622
00627 bool FileServerHandler::HandleQueryFileHash(SocketHandler *socket,
00628 QStringList &slist)
00629 {
00630 QString storageGroup = "Default";
00631 QString hostname = gCoreContext->GetHostName();
00632 QString filename = "";
00633 QStringList res;
00634
00635 switch (slist.size()) {
00636 case 4:
00637 if (!slist[3].isEmpty())
00638 hostname = slist[3];
00639 case 3:
00640 if (!slist[2].isEmpty())
00641 storageGroup = slist[2];
00642 case 2:
00643 filename = slist[1];
00644 if (filename.isEmpty() ||
00645 filename.contains("/../") ||
00646 filename.startsWith("../"))
00647 {
00648 LOG(VB_GENERAL, LOG_ERR,
00649 QString("ERROR checking for file, filename '%1' "
00650 "fails sanity checks").arg(filename));
00651 res << "";
00652 socket->SendStringList(res);
00653 return true;
00654 }
00655 break;
00656 default:
00657 return false;
00658 }
00659
00660 QString hash = "";
00661
00662 if (hostname == gCoreContext->GetHostName())
00663 {
00664
00665 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName());
00666 QString fullname = sgroup.FindFile(filename);
00667 hash = FileHash(fullname);
00668 }
00669 else
00670 {
00671 QReadLocker rlock(&m_fsLock);
00672 if (m_fsMap.contains(hostname))
00673 {
00674
00675 if (m_fsMap[hostname]->SendReceiveStringList(slist))
00676 hash = slist[0];
00677 }
00678 else
00679 {
00680
00681
00682
00683 MSqlQuery query(MSqlQuery::InitCon());
00684 query.prepare("SELECT hostname FROM settings "
00685 "WHERE value='BackendServerIP' "
00686 " OR value='BackendServerIP6' "
00687 "AND data=:HOSTNAME;");
00688 query.bindValue(":HOSTNAME", hostname);
00689
00690 if (query.exec() && query.next())
00691 {
00692
00693 hostname = query.value(0).toString();
00694 if (m_fsMap.contains(hostname))
00695 {
00696
00697 slist.clear();
00698 slist << "QUERY_FILE_HASH"
00699 << filename
00700 << storageGroup;
00701
00702 if (m_fsMap[hostname]->SendReceiveStringList(slist))
00703 hash = slist[0];
00704 }
00705 }
00706 }
00707 }
00708
00709
00710 res << hash;
00711 socket->SendStringList(res);
00712
00713 return true;
00714 }
00715
00716 bool FileServerHandler::HandleDeleteFile(SocketHandler *socket,
00717 QStringList &slist)
00718 {
00719 if (slist.size() != 3)
00720 return false;
00721
00722 return HandleDeleteFile(socket, slist[1], slist[2]);
00723 }
00724
00725 bool FileServerHandler::DeleteFile(QString filename, QString storagegroup)
00726 {
00727 return HandleDeleteFile( (SocketHandler *)NULL, filename, storagegroup);
00728 }
00729
00730 bool FileServerHandler::HandleDeleteFile(SocketHandler *socket,
00731 QString filename, QString storagegroup)
00732 {
00733 StorageGroup sgroup(storagegroup, "", false);
00734 QStringList res;
00735
00736 if ((filename.isEmpty()) ||
00737 (filename.contains("/../")) ||
00738 (filename.startsWith("../")))
00739 {
00740 LOG(VB_GENERAL, LOG_ERR,
00741 QString("ERROR deleting file, filename '%1' fails sanity checks")
00742 .arg(filename));
00743 if (socket)
00744 {
00745 res << "0";
00746 socket->SendStringList(res);
00747 return true;
00748 }
00749 return false;
00750 }
00751
00752 QString fullfile = sgroup.FindFile(filename);
00753
00754 if (fullfile.isEmpty())
00755 {
00756 LOG(VB_GENERAL, LOG_ERR,
00757 QString("Unable to find %1 in HandleDeleteFile()") .arg(filename));
00758 if (socket)
00759 {
00760 res << "0";
00761 socket->SendStringList(res);
00762 return true;
00763 }
00764 return false;
00765 }
00766
00767 QFile checkFile(fullfile);
00768 if (checkFile.exists())
00769 {
00770 if (socket)
00771 {
00772 res << "1";
00773 socket->SendStringList(res);
00774 }
00775 RunDeleteThread();
00776 deletethread->AddFile(fullfile);
00777 }
00778 else
00779 {
00780 LOG(VB_GENERAL, LOG_ERR, QString("Error deleting file: '%1'")
00781 .arg(fullfile));
00782 if (socket)
00783 {
00784 res << "0";
00785 socket->SendStringList(res);
00786 }
00787 }
00788
00789 return true;
00790 }
00791
00792 bool FileServerHandler::HandleDeleteFile(DeleteHandler *handler)
00793 {
00794 RunDeleteThread();
00795 return deletethread->AddFile(handler);
00796 }
00797
00798 bool FileServerHandler::HandleGetFileList(SocketHandler *socket,
00799 QStringList &slist)
00800 {
00801 QStringList res;
00802
00803 bool fileNamesOnly = false;
00804 if (slist.size() == 5)
00805 fileNamesOnly = slist[4].toInt();
00806 else if (slist.size() != 4)
00807 {
00808 LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
00809 .arg(slist.join("[]:[]")));
00810 res << "EMPTY LIST";
00811 socket->SendStringList(res);
00812 return true;
00813 }
00814
00815 QString host = gCoreContext->GetHostName();
00816 QString wantHost = slist[1];
00817 QString groupname = slist[2];
00818 QString path = slist[3];
00819
00820 LOG(VB_FILE, LOG_INFO,
00821 QString("HandleSGGetFileList: group = %1 host = %2 "
00822 "path = %3 wanthost = %4")
00823 .arg(groupname).arg(host).arg(path).arg(wantHost));
00824
00825 if ((host.toLower() == wantHost.toLower()) ||
00826 gCoreContext->IsThisHost(wantHost))
00827 {
00828 StorageGroup sg(groupname, host);
00829 LOG(VB_FILE, LOG_INFO, "Getting local info");
00830 if (fileNamesOnly)
00831 res = sg.GetFileList(path);
00832 else
00833 res = sg.GetFileInfoList(path);
00834
00835 if (res.count() == 0)
00836 res << "EMPTY LIST";
00837 }
00838 else
00839 {
00840
00841 SocketHandler *remsock = NULL;
00842 {
00843 QReadLocker rlock(&m_fsLock);
00844 if (m_fsMap.contains(wantHost))
00845 {
00846 remsock = m_fsMap[wantHost];
00847 remsock->UpRef();
00848 }
00849 }
00850
00851 if (remsock)
00852 {
00853 LOG(VB_FILE, LOG_INFO, "Getting remote info");
00854 res << "QUERY_SG_GETFILELIST" << wantHost << groupname << path
00855 << QString::number(fileNamesOnly);
00856 remsock->SendReceiveStringList(res);
00857 remsock->DownRef();
00858 }
00859 else
00860 {
00861 LOG(VB_FILE, LOG_ERR, QString("Failed to grab slave socket : %1 :")
00862 .arg(wantHost));
00863 res << "SLAVE UNREACHABLE: " << wantHost;
00864 }
00865 }
00866
00867 socket->SendStringList(res);
00868 return true;
00869 }
00870
00871 bool FileServerHandler::HandleFileQuery(SocketHandler *socket,
00872 QStringList &slist)
00873 {
00874 QStringList res;
00875
00876 if (slist.size() != 4)
00877 {
00878 LOG(VB_GENERAL, LOG_ERR, QString("Invalid Request. %1")
00879 .arg(slist.join("[]:[]")));
00880 res << "EMPTY LIST";
00881 socket->SendStringList(res);
00882 return true;
00883 }
00884
00885 QString wantHost = slist[1];
00886 QString groupname = slist[2];
00887 QString filename = slist[3];
00888
00889 LOG(VB_FILE, LOG_DEBUG, QString("HandleSGFileQuery: myth://%1@%2/%3")
00890 .arg(groupname).arg(wantHost).arg(filename));
00891
00892 if ((wantHost.toLower() == gCoreContext->GetHostName().toLower()) ||
00893 gCoreContext->IsThisHost(wantHost))
00894 {
00895
00896 LOG(VB_FILE, LOG_DEBUG, QString("Getting local info"));
00897 StorageGroup sg(groupname, gCoreContext->GetHostName());
00898 res = sg.GetFileInfo(filename);
00899
00900 if (res.count() == 0)
00901 res << "EMPTY LIST";
00902 }
00903 else
00904 {
00905
00906 SocketHandler *remsock = NULL;
00907 {
00908 QReadLocker rlock(&m_fsLock);
00909 if (m_fsMap.contains(wantHost))
00910 {
00911 remsock = m_fsMap[wantHost];
00912 remsock->UpRef();
00913 }
00914 }
00915
00916 if (remsock)
00917 {
00918 res << "QUERY_SG_FILEQUERY" << wantHost << groupname << filename;
00919 remsock->SendReceiveStringList(res);
00920 remsock->DownRef();
00921 }
00922 else
00923 {
00924 res << "SLAVE UNREACHABLE: " << wantHost;
00925 }
00926 }
00927
00928 socket->SendStringList(res);
00929 return true;
00930 }
00931
00932 bool FileServerHandler::HandleQueryFileTransfer(SocketHandler *socket,
00933 QStringList &commands, QStringList &slist)
00934 {
00935 if (commands.size() != 2)
00936 return false;
00937
00938 if (slist.size() < 2)
00939 return false;
00940
00941 QStringList res;
00942 int recnum = commands[1].toInt();
00943 FileTransfer *ft;
00944
00945 {
00946 QReadLocker rlock(&m_ftLock);
00947 if (!m_ftMap.contains(recnum))
00948 {
00949 if (slist[1] == "DONE")
00950 res << "ok";
00951 else
00952 {
00953 LOG(VB_GENERAL, LOG_ERR,
00954 QString("Unknown file transfer socket: %1").arg(recnum));
00955 res << "ERROR"
00956 << "unknown_file_transfer_socket";
00957 }
00958
00959 socket->SendStringList(res);
00960 return true;
00961 }
00962
00963 ft = m_ftMap[recnum];
00964 ft->UpRef();
00965 }
00966
00967 if (slist[1] == "IS_OPEN")
00968 {
00969 res << QString::number(ft->isOpen());
00970 }
00971 else if (slist[1] == "DONE")
00972 {
00973 ft->Stop();
00974 res << "ok";
00975 }
00976 else if (slist[1] == "REQUEST_BLOCK")
00977 {
00978 if (slist.size() != 3)
00979 {
00980 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
00981 "REQUEST_BLOCK call");
00982 res << "ERROR" << "invalid_call";
00983 }
00984 else
00985 {
00986 int size = slist[2].toInt();
00987 res << QString::number(ft->RequestBlock(size));
00988 }
00989 }
00990 else if (slist[1] == "WRITE_BLOCK")
00991 {
00992 if (slist.size() != 3)
00993 {
00994 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
00995 "WRITE_BLOCK call");
00996 res << "ERROR" << "invalid_call";
00997 }
00998 else
00999 {
01000 int size = slist[2].toInt();
01001 res << QString::number(ft->WriteBlock(size));
01002 }
01003 }
01004 else if (slist[1] == "SEEK")
01005 {
01006 if (slist.size() != 5)
01007 {
01008 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER SEEK call");
01009 res << "ERROR" << "invalid_call";
01010 }
01011 else
01012 {
01013 long long pos = slist[2].toLongLong();
01014 int whence = slist[3].toInt();
01015 long long curpos = slist[4].toLongLong();
01016
01017 res << QString::number(ft->Seek(curpos, pos, whence));
01018 }
01019 }
01020 else if (slist[1] == "SET_TIMEOUT")
01021 {
01022 if (slist.size() != 3)
01023 {
01024 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER "
01025 "SET_TIMEOUT call");
01026 res << "ERROR" << "invalid_call";
01027 }
01028 else
01029 {
01030 bool fast = slist[2].toInt();
01031 ft->SetTimeout(fast);
01032 res << "ok";
01033 }
01034 }
01035 else
01036 {
01037 LOG(VB_GENERAL, LOG_ERR, "Invalid QUERY_FILETRANSFER call");
01038 res << "ERROR" << "invalid_call";
01039 }
01040
01041 ft->DownRef();
01042 socket->SendStringList(res);
01043 return true;
01044 }
01045
01046 bool FileServerHandler::HandleDownloadFile(SocketHandler *socket,
01047 QStringList &slist)
01048 {
01049 QStringList res;
01050
01051 if (slist.size() != 4)
01052 {
01053 res << "ERROR" << QString("Bad %1 command").arg(slist[0]);
01054 socket->SendStringList(res);
01055 return true;
01056 }
01057
01058 bool synchronous = (slist[0] == "DOWNLOAD_FILE_NOW");
01059 QString srcURL = slist[1];
01060 QString storageGroup = slist[2];
01061 QString filename = slist[3];
01062 StorageGroup sgroup(storageGroup, gCoreContext->GetHostName(), false);
01063 QString outDir = sgroup.FindNextDirMostFree();
01064 QString outFile;
01065 QStringList retlist;
01066
01067 if (filename.isEmpty())
01068 {
01069 QFileInfo finfo(srcURL);
01070 filename = finfo.fileName();
01071 }
01072
01073 if (outDir.isEmpty())
01074 {
01075 LOG(VB_GENERAL, LOG_ERR, QString("Unable to determine directory "
01076 "to write to in %1 write command").arg(slist[0]));
01077 res << "ERROR" << "downloadfile_directory_not_found";
01078 socket->SendStringList(res);
01079 return true;
01080 }
01081
01082 if ((filename.contains("/../")) ||
01083 (filename.startsWith("../")))
01084 {
01085 LOG(VB_GENERAL, LOG_ERR, QString("ERROR: %1 write "
01086 "filename '%2' does not pass sanity checks.")
01087 .arg(slist[0]).arg(filename));
01088 res << "ERROR" << "downloadfile_filename_dangerous";
01089 socket->SendStringList(res);
01090 return true;
01091 }
01092
01093 outFile = outDir + "/" + filename;
01094
01095 if (synchronous)
01096 {
01097 if (GetMythDownloadManager()->download(srcURL, outFile))
01098 {
01099 res << "OK"
01100 << gCoreContext->GetMasterHostPrefix(storageGroup)
01101 + filename;
01102 }
01103 else
01104 res << "ERROR";
01105 }
01106 else
01107 {
01108 QMutexLocker locker(&m_downloadURLsLock);
01109 m_downloadURLs[outFile] =
01110 gCoreContext->GetMasterHostPrefix(storageGroup) +
01111 StorageGroup::GetRelativePathname(outFile);
01112
01113 GetMythDownloadManager()->queueDownload(srcURL, outFile, this);
01114 res << "OK"
01115 << gCoreContext->GetMasterHostPrefix(storageGroup) + filename;
01116 }
01117
01118 socket->SendStringList(res);
01119 return true;
01120 }
01121