MythTV  0.26-pre
remotefile.cpp
Go to the documentation of this file.
00001 #include <iostream>
00002 using namespace std;
00003 
00004 #include <QUrl>
00005 
00006 #include "mythconfig.h"
00007 #include "mythdb.h"
00008 #include "remotefile.h"
00009 #include "mythcorecontext.h"
00010 #include "mythsocket.h"
00011 #include "compat.h"
00012 #include "mythtimer.h"
00013 
00014 RemoteFile::RemoteFile(const QString &_path, bool write, bool useRA,
00015                        int _timeout_ms,
00016                        const QStringList *possibleAuxiliaryFiles) :
00017     path(_path),
00018     usereadahead(useRA),  timeout_ms(_timeout_ms),
00019     filesize(-1),         timeoutisfast(false),
00020     readposition(0),      recordernum(0),
00021     lock(QMutex::NonRecursive),
00022     controlSock(NULL),    sock(NULL),
00023     query("QUERY_FILETRANSFER %1"),
00024     writemode(write)
00025 {
00026     if (writemode)
00027     {
00028         usereadahead = false;
00029         timeout_ms = -1;
00030     }
00031     else if (possibleAuxiliaryFiles)
00032         possibleauxfiles = *possibleAuxiliaryFiles;
00033 
00034     if (!path.isEmpty())
00035         Open();
00036 
00037     LOG(VB_FILE, LOG_DEBUG, QString("RemoteFile(%1)").arg(path));
00038 }
00039 
00040 RemoteFile::~RemoteFile()
00041 {
00042     Close();
00043     if (controlSock)
00044         controlSock->DownRef();
00045     if (sock)
00046         sock->DownRef();
00047 }
00048 
00049 MythSocket *RemoteFile::openSocket(bool control)
00050 {
00051     QUrl qurl(path);
00052     QString dir;
00053 
00054     QString host = qurl.host();
00055     int port = qurl.port();
00056 
00057     dir = qurl.path();
00058 
00059     if (qurl.hasQuery())
00060         dir += "?" + QUrl::fromPercentEncoding(qurl.encodedQuery());
00061 
00062     if (qurl.hasFragment())
00063         dir += "#" + qurl.fragment();
00064 
00065     QString sgroup = qurl.userName();
00066 
00067     MythSocket *lsock = new MythSocket();
00068     QString stype = (control) ? "control socket" : "file data socket";
00069 
00070     QString loc = QString("RemoteFile::openSocket(%1): ").arg(stype);
00071 
00072     if (port <= 0)
00073     {
00074         port = GetMythDB()->GetSettingOnHost("BackendServerPort", host).toInt();
00075 
00076         // if we still have no port use the default
00077         if (port <= 0)
00078             port = 6543;
00079     }
00080 
00081     if (!lsock->connect(host, port))
00082     {
00083         LOG(VB_GENERAL, LOG_ERR, loc +
00084             QString("Could not connect to server %1:%2") .arg(host).arg(port));
00085         lsock->DownRef();
00086         return NULL;
00087     }
00088 
00089     QString hostname = GetMythDB()->GetHostName();
00090 
00091     QStringList strlist;
00092 
00093 #ifndef IGNORE_PROTO_VER_MISMATCH
00094     if (!gCoreContext->CheckProtoVersion(lsock))
00095     {
00096         LOG(VB_GENERAL, LOG_ERR, loc +
00097             QString("Failed validation to server %1:%2").arg(host).arg(port));
00098         lsock->DownRef();
00099         return NULL;
00100     }
00101 #endif
00102 
00103     if (control)
00104     {
00105         strlist.append(QString("ANN Playback %1 %2").arg(hostname).arg(false));
00106         lsock->writeStringList(strlist);
00107         if (!lsock->readStringList(strlist, true))
00108         {
00109             LOG(VB_GENERAL, LOG_ERR, loc +
00110                 QString("Could not read string list from server %1:%2")
00111                     .arg(host).arg(port));
00112             lsock->DownRef();
00113             return NULL;
00114         }
00115     }
00116     else
00117     {
00118         strlist.push_back(QString("ANN FileTransfer %1 %2 %3 %4")
00119                           .arg(hostname).arg(writemode)
00120                           .arg(usereadahead).arg(timeout_ms));
00121         strlist << QString("%1").arg(dir);
00122         strlist << sgroup;
00123 
00124         QStringList::const_iterator it = possibleauxfiles.begin();
00125         for (; it != possibleauxfiles.end(); ++it)
00126             strlist << *it;
00127 
00128         if (!lsock->writeStringList(strlist) ||
00129             !lsock->readStringList(strlist, true))
00130         {
00131             LOG(VB_GENERAL, LOG_ERR, loc +
00132                 QString("Did not get proper response from %1:%2")
00133                     .arg(host).arg(port));
00134             strlist.clear();
00135             strlist.push_back("ERROR");
00136             strlist.push_back("invalid response");
00137         }
00138 
00139         if (strlist.size() >= 3)
00140         {
00141             it = strlist.begin(); ++it;
00142             recordernum = (*it).toInt(); ++it;
00143             filesize = (*(it)).toLongLong(); ++it;
00144             for (; it != strlist.end(); ++it)
00145                 auxfiles << *it;
00146         }
00147         else if (0 < strlist.size() && strlist.size() < 3 &&
00148                  strlist[0] != "ERROR")
00149         {
00150             LOG(VB_GENERAL, LOG_ERR, loc +
00151                 QString("Did not get proper response from %1:%2")
00152                     .arg(host).arg(port));
00153             strlist.clear();
00154             strlist.push_back("ERROR");
00155             strlist.push_back("invalid response");
00156         }
00157     }
00158 
00159     if (strlist.empty() || strlist[0] == "ERROR")
00160     {
00161         lsock->DownRef();
00162         lsock = NULL;
00163         if (strlist.empty())
00164         {
00165             LOG(VB_GENERAL, LOG_ERR, loc + "Failed to open socket, timeout");
00166         }
00167         else
00168         {
00169             LOG(VB_GENERAL, LOG_ERR, loc + "Failed to open socket" +
00170                 ((strlist.size() >= 2) ?
00171                 QString(", error was %1").arg(strlist[1]) :
00172                 QString(", remote error")));
00173         }
00174     }
00175 
00176     return lsock;
00177 }
00178 
00179 bool RemoteFile::Open(void)
00180 {
00181     QMutexLocker locker(&lock);
00182     controlSock = openSocket(true);
00183     if (!controlSock)
00184         return false;
00185 
00186     sock = openSocket(false);
00187     if (!sock)
00188     {
00189         // Close the sockets if we received an error so that isOpen() will
00190         // return false if the caller tries to use the RemoteFile.
00191         locker.unlock();
00192         Close();
00193         return false;
00194     }
00195     return true;
00196 }
00197 
00198 bool RemoteFile::ReOpen(QString newFilename)
00199 {
00200     lock.lock();
00201     if (!sock)
00202     {
00203         LOG(VB_NETWORK, LOG_ERR, "RemoteFile::ReOpen(): Called with no socket");
00204         return false;
00205     }
00206 
00207     if (!sock->isOpen() || sock->error())
00208         return false;
00209 
00210     if (!controlSock->isOpen() || controlSock->error())
00211         return false;
00212 
00213     QStringList strlist( QString(query).arg(recordernum) );
00214     strlist << "REOPEN";
00215     strlist << newFilename;
00216 
00217     controlSock->writeStringList(strlist);
00218     controlSock->readStringList(strlist);
00219 
00220     lock.unlock();
00221 
00222     bool retval = false;
00223     if (!strlist.empty())
00224         retval = strlist[0].toInt();
00225 
00226     return retval;
00227 }
00228 
00229 void RemoteFile::Close(void)
00230 {
00231     if (!controlSock)
00232         return;
00233 
00234     QStringList strlist( QString(query).arg(recordernum) );
00235     strlist << "DONE";
00236 
00237     lock.lock();
00238     controlSock->writeStringList(strlist);
00239     if (!controlSock->readStringList(strlist, true))
00240     {
00241         LOG(VB_GENERAL, LOG_ERR, "Remote file timeout.");
00242     }
00243 
00244     if (sock)
00245     {
00246         sock->DownRef();
00247         sock = NULL;
00248     }
00249     if (controlSock)
00250     {
00251         controlSock->DownRef();
00252         controlSock = NULL;
00253     }
00254 
00255     lock.unlock();
00256 }
00257 
00258 bool RemoteFile::DeleteFile(const QString &url)
00259 {
00260     bool result      = false;
00261     QUrl qurl(url);
00262     QString filename = qurl.path();
00263     QString sgroup   = qurl.userName();
00264 
00265     if (!qurl.fragment().isEmpty() || url.right(1) == "#")
00266         filename = filename + "#" + qurl.fragment();
00267 
00268     if (filename.left(1) == "/")
00269         filename = filename.right(filename.length()-1);
00270 
00271     if (filename.isEmpty() || sgroup.isEmpty())
00272         return false;
00273 
00274     QStringList strlist("DELETE_FILE");
00275     strlist << filename;
00276     strlist << sgroup;
00277 
00278     gCoreContext->SendReceiveStringList(strlist);
00279 
00280     if (strlist[0] == "1")
00281         result = true;
00282 
00283     return result;
00284 }
00285 
00286 bool RemoteFile::Exists(const QString &url)
00287 {
00288     struct stat fileinfo;
00289     return Exists(url, &fileinfo);
00290 }
00291 
00292 bool RemoteFile::Exists(const QString &url, struct stat *fileinfo)
00293 {
00294     QUrl qurl(url);
00295     QString filename = qurl.path();
00296     QString sgroup   = qurl.userName();
00297 
00298     if (!qurl.fragment().isEmpty() || url.right(1) == "#")
00299         filename = filename + "#" + qurl.fragment();
00300 
00301     if (filename.left(1) == "/")
00302         filename = filename.right(filename.length()-1);
00303 
00304     if (filename.isEmpty())
00305         return false;
00306 
00307     QStringList strlist("QUERY_FILE_EXISTS");
00308     strlist << filename;
00309     if (!sgroup.isEmpty())
00310         strlist << sgroup;
00311 
00312     gCoreContext->SendReceiveStringList(strlist);
00313 
00314     bool result = false;
00315     if ((strlist.size() >= 1) && strlist[0] == "1")
00316     {
00317         if ((strlist.size() >= 15) && fileinfo)
00318         {
00319             fileinfo->st_dev       = strlist[2].toLongLong();
00320             fileinfo->st_ino       = strlist[3].toLongLong();
00321             fileinfo->st_mode      = strlist[4].toLongLong();
00322             fileinfo->st_nlink     = strlist[5].toLongLong();
00323             fileinfo->st_uid       = strlist[6].toLongLong();
00324             fileinfo->st_gid       = strlist[7].toLongLong();
00325             fileinfo->st_rdev      = strlist[8].toLongLong();
00326             fileinfo->st_size      = strlist[9].toLongLong();
00327 #ifndef USING_MINGW
00328             fileinfo->st_blksize   = strlist[10].toLongLong();
00329             fileinfo->st_blocks    = strlist[11].toLongLong();
00330 #endif
00331             fileinfo->st_atime     = strlist[12].toLongLong();
00332             fileinfo->st_mtime     = strlist[13].toLongLong();
00333             fileinfo->st_ctime     = strlist[14].toLongLong();
00334             result = true;
00335         }
00336         else if (!fileinfo)
00337         {
00338             result = true;
00339         }
00340     }
00341 
00342     return result;
00343 }
00344 
00345 QString RemoteFile::GetFileHash(const QString &url)
00346 {
00347     QString result;
00348     QUrl qurl(url);
00349     QString filename = qurl.path();
00350     QString hostname = qurl.host();
00351     QString sgroup   = qurl.userName();
00352 
00353     if (!qurl.fragment().isEmpty() || url.right(1) == "#")
00354         filename = filename + "#" + qurl.fragment();
00355 
00356     if (filename.left(1) == "/")
00357         filename = filename.right(filename.length()-1);
00358 
00359     if (filename.isEmpty() || sgroup.isEmpty())
00360         return QString();
00361 
00362     QStringList strlist("QUERY_FILE_HASH");
00363     strlist << filename;
00364     strlist << sgroup;
00365     strlist << hostname;
00366 
00367     gCoreContext->SendReceiveStringList(strlist);
00368     result = strlist[0];
00369 
00370     return result;
00371 }
00372 
00373 void RemoteFile::Reset(void)
00374 {
00375     QMutexLocker locker(&lock);
00376     if (!sock)
00377     {
00378         LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Reset(): Called with no socket");
00379         return;
00380     }
00381 
00382     while (sock && (sock->bytesAvailable() > 0))
00383     {
00384         int avail;
00385         char *trash;
00386 
00387         avail = sock->bytesAvailable();
00388         trash = new char[avail + 1];
00389         sock->readBlock(trash, avail);
00390         delete [] trash;
00391 
00392         LOG(VB_NETWORK, LOG_INFO,
00393             QString ("%1 bytes available during reset.") .arg(avail));
00394         locker.unlock();
00395         usleep(30000);
00396         locker.relock();
00397     }
00398 }
00399 
00400 long long RemoteFile::Seek(long long pos, int whence, long long curpos)
00401 {
00402     lock.lock();
00403     if (!sock)
00404     {
00405         LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Seek(): Called with no socket");
00406         return 0;
00407     }
00408 
00409     if (!sock->isOpen() || sock->error())
00410         return 0;
00411 
00412     if (!controlSock->isOpen() || controlSock->error())
00413         return 0;
00414 
00415     QStringList strlist( QString(query).arg(recordernum) );
00416     strlist << "SEEK";
00417     strlist << QString::number(pos);
00418     strlist << QString::number(whence);
00419     if (curpos > 0)
00420         strlist << QString::number(curpos);
00421     else
00422         strlist << QString::number(readposition);
00423 
00424     controlSock->writeStringList(strlist);
00425     controlSock->readStringList(strlist);
00426     lock.unlock();
00427 
00428     long long retval = -1;
00429     if (!strlist.empty())
00430     {
00431         retval = strlist[0].toLongLong();
00432         readposition = retval;
00433     }
00434 
00435     Reset();
00436 
00437     return retval;
00438 }
00439 
00440 int RemoteFile::Write(const void *data, int size)
00441 {
00442     int recv = 0;
00443     int sent = 0;
00444     unsigned zerocnt = 0;
00445     bool error = false;
00446     bool response = false;
00447 
00448     if (!writemode)
00449     {
00450         LOG(VB_NETWORK, LOG_ERR,
00451                 "RemoteFile::Write(): Called when not in write mode");
00452         return -1;
00453     }
00454 
00455     QMutexLocker locker(&lock);
00456     if (!sock)
00457     {
00458         LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Write(): Called with no socket");
00459         return -1;
00460     }
00461 
00462     if (!sock->isOpen() || sock->error())
00463         return -1;
00464 
00465     if (!controlSock->isOpen() || controlSock->error())
00466         return -1;
00467 
00468     QStringList strlist( QString(query).arg(recordernum) );
00469     strlist << "WRITE_BLOCK";
00470     strlist << QString::number(size);
00471     controlSock->writeStringList(strlist);
00472 
00473     recv = size;
00474     while (sent < recv && !error && zerocnt++ < 50)
00475     {
00476         int ret = sock->writeBlock((char *)data + sent, recv - sent);
00477         if (ret > 0)
00478         {
00479             sent += ret;
00480         }
00481         else
00482         {
00483             LOG(VB_GENERAL, LOG_ERR, "RemoteFile::Write(): socket error");
00484             error = true;
00485             break;
00486         }
00487 
00488         if (controlSock->bytesAvailable() > 0)
00489         {
00490             if (controlSock->readStringList(strlist, true))
00491             {
00492                 recv = strlist[0].toInt(); // -1 on backend error
00493                 response = true;
00494             }
00495         }
00496     }
00497 
00498     if (!error && !response)
00499     {
00500         if (controlSock->readStringList(strlist, true))
00501         {
00502             recv = strlist[0].toInt(); // -1 on backend error
00503         }
00504         else
00505         {
00506             LOG(VB_GENERAL, LOG_ERR,
00507                     "RemoteFile::Write(): No response from control socket.");
00508             recv = -1;
00509         }
00510     }
00511 
00512     LOG(VB_NETWORK, LOG_DEBUG,
00513             QString("RemoteFile::Write(): reqd=%1, sent=%2, rept=%3, error=%4")
00514                     .arg(size).arg(sent).arg(recv).arg(error));
00515 
00516     if (recv < 0)
00517         return recv;
00518 
00519     if (error || recv != sent)
00520         sent = -1;
00521 
00522     return sent;
00523 }
00524 
00525 int RemoteFile::Read(void *data, int size)
00526 {
00527     int recv = 0;
00528     int sent = 0;
00529     bool error = false;
00530     bool response = false;
00531 
00532     QMutexLocker locker(&lock);
00533     if (!sock)
00534     {
00535         LOG(VB_NETWORK, LOG_ERR, "RemoteFile::Read(): Called with no socket");
00536         return -1;
00537     }
00538 
00539     if (!sock->isOpen() || sock->error())
00540         return -1;
00541 
00542     if (!controlSock->isOpen() || controlSock->error())
00543         return -1;
00544 
00545     if (sock->bytesAvailable() > 0)
00546     {
00547         LOG(VB_NETWORK, LOG_ERR,
00548                 "RemoteFile::Read(): Read socket not empty to start!");
00549         while (sock->waitForMore(5) > 0)
00550         {
00551             int avail = sock->bytesAvailable();
00552             char *trash = new char[avail + 1];
00553             sock->readBlock(trash, avail);
00554             delete [] trash;
00555         }
00556     }
00557 
00558     if (controlSock->bytesAvailable() > 0)
00559     {
00560         LOG(VB_NETWORK, LOG_ERR,
00561                 "RemoteFile::Read(): Control socket not empty to start!");
00562         QStringList tempstrlist;
00563         controlSock->readStringList(tempstrlist);
00564     }
00565 
00566     QStringList strlist( QString(query).arg(recordernum) );
00567     strlist << "REQUEST_BLOCK";
00568     strlist << QString::number(size);
00569     controlSock->writeStringList(strlist);
00570 
00571     sent = size;
00572 
00573     int waitms = 10;
00574     MythTimer mtimer;
00575     mtimer.start();
00576 
00577     while (recv < sent && !error && mtimer.elapsed() < 10000)
00578     {
00579         while (recv < sent && sock->waitForMore(waitms) > 0)
00580         {
00581             int ret = sock->readBlock(((char *)data) + recv, sent - recv);
00582             if (ret > 0)
00583             {
00584                 recv += ret;
00585             }
00586             else if (sock->error() != MythSocket::NoError)
00587             {
00588                 LOG(VB_GENERAL, LOG_ERR, "RemoteFile::Read(): socket error");
00589                 error = true;
00590                 break;
00591             }
00592 
00593             if (waitms < 200)
00594                 waitms += 20;
00595         }
00596 
00597         if (controlSock->bytesAvailable() > 0)
00598         {
00599             controlSock->readStringList(strlist, true);
00600             sent = strlist[0].toInt(); // -1 on backend error
00601             response = true;
00602         }
00603     }
00604 
00605     if (!error && !response)
00606     {
00607         if (controlSock->readStringList(strlist, true))
00608         {
00609             sent = strlist[0].toInt(); // -1 on backend error
00610         }
00611         else
00612         {
00613             LOG(VB_GENERAL, LOG_ERR,
00614                    "RemoteFile::Read(): No response from control socket.");
00615             sent = -1;
00616         }
00617     }
00618 
00619     LOG(VB_NETWORK, LOG_DEBUG,
00620         QString("Read(): reqd=%1, rcvd=%2, rept=%3, error=%4")
00621             .arg(size).arg(recv).arg(sent).arg(error));
00622 
00623     if (sent < 0)
00624         return sent;
00625 
00626     if (error || sent != recv)
00627         recv = -1;
00628 
00629     return recv;
00630 }
00631 
00632 bool RemoteFile::SaveAs(QByteArray &data)
00633 {
00634     if (filesize < 0)
00635         return false;
00636 
00637     data.resize(filesize);
00638     Read(data.data(), filesize);
00639 
00640     return true;
00641 }
00642 
00643 void RemoteFile::SetTimeout(bool fast)
00644 {
00645     if (timeoutisfast == fast)
00646         return;
00647 
00648     QMutexLocker locker(&lock);
00649     if (!sock)
00650     {
00651         LOG(VB_NETWORK, LOG_ERR,
00652             "RemoteFile::SetTimeout(): Called with no socket");
00653         return;
00654     }
00655 
00656     if (!sock->isOpen() || sock->error())
00657         return;
00658 
00659     if (!controlSock->isOpen() || controlSock->error())
00660         return;
00661 
00662     QStringList strlist( QString(query).arg(recordernum) );
00663     strlist << "SET_TIMEOUT";
00664     strlist << QString::number((int)fast);
00665 
00666     controlSock->writeStringList(strlist);
00667     controlSock->readStringList(strlist);
00668 
00669     timeoutisfast = fast;
00670 }
00671 
00672 QDateTime RemoteFile::LastModified(const QString &url)
00673 {
00674     QDateTime result;
00675     QUrl qurl(url);
00676     QString filename = qurl.path();
00677     QString sgroup   = qurl.userName();
00678 
00679     if (!qurl.fragment().isEmpty() || url.right(1) == "#")
00680         filename = filename + "#" + qurl.fragment();
00681 
00682     if (filename.left(1) == "/")
00683         filename = filename.right(filename.length()-1);
00684 
00685     if (filename.isEmpty() || sgroup.isEmpty())
00686         return result;
00687 
00688     QStringList strlist("QUERY_SG_FILEQUERY");
00689     strlist << qurl.host();
00690     strlist << sgroup;
00691     strlist << filename;
00692 
00693     gCoreContext->SendReceiveStringList(strlist);
00694 
00695     if (strlist.size() > 1)
00696         result = QDateTime::fromTime_t(strlist[1].toUInt());
00697 
00698     return result;
00699 }
00700 
00701 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends