MythTV  0.26-pre
fileserverhandler.cpp
Go to the documentation of this file.
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     // iterate through transfer list and close if
00028     // socket matches connected transfer
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     // iterate through file server list and close 
00044     // if socket matched connected server
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         // This must be an icon request. Check channel.icon to be safe.
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             // For securities sake, make sure filename is really the pathless.
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         // were not going to handle these, but we still want to track them
00354         // for commands that need access to these sockets
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     // TODO: get max bitrate from encoderlink
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     // TODO: get max bitrate from encoderlink
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             /* The storagegroup.dirname column uses utf8_bin collation, so Qt
00509              * uses QString::fromAscii() for toString(). Explicitly convert the
00510              * value using QString::fromUtf8() to prevent corruption. */
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         // TODO: convert me to QFile
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         // looking for file on me, return directly
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             // looking for file on connected host, query from it
00675             if (m_fsMap[hostname]->SendReceiveStringList(slist))
00676                 hash = slist[0];
00677         }
00678         else
00679         {
00680             // looking for file on unknown host
00681             // assume host is an IP address, and look for matching
00682             // entry in database
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                 // address matches an entry
00693                 hostname = query.value(0).toString();
00694                 if (m_fsMap.contains(hostname))
00695                 {
00696                     // entry matches a connection
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         // handle request on remote server
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         // handle request locally
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         // handle request on remote server
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends