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
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
00190
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();
00493 response = true;
00494 }
00495 }
00496 }
00497
00498 if (!error && !response)
00499 {
00500 if (controlSock->readStringList(strlist, true))
00501 {
00502 recv = strlist[0].toInt();
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();
00601 response = true;
00602 }
00603 }
00604
00605 if (!error && !response)
00606 {
00607 if (controlSock->readStringList(strlist, true))
00608 {
00609 sent = strlist[0].toInt();
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