|
MythTV
0.26-pre
|
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: */
1.7.6.1