00001 #include <unistd.h>
00002
00003 #include <QFileInfo>
00004 #include <QFile>
00005 #include <QDir>
00006 #include <QList>
00007
00008 #include "compat.h"
00009 #include "remoteutil.h"
00010 #include "programinfo.h"
00011 #include "mythcorecontext.h"
00012 #include "storagegroup.h"
00013 #include "mythevent.h"
00014 #include "mythsocket.h"
00015
00016 vector<ProgramInfo *> *RemoteGetRecordedList(int sort)
00017 {
00018 QString str = "QUERY_RECORDINGS ";
00019 if (sort < 0)
00020 str += "Descending";
00021 else if (sort > 0)
00022 str += "Ascending";
00023 else
00024 str += "Unsorted";
00025
00026 QStringList strlist(str);
00027
00028 vector<ProgramInfo *> *info = new vector<ProgramInfo *>;
00029
00030 if (!RemoteGetRecordingList(*info, strlist))
00031 {
00032 delete info;
00033 return NULL;
00034 }
00035
00036 return info;
00037 }
00038
00039 bool RemoteGetLoad(float load[3])
00040 {
00041 QStringList strlist(QString("QUERY_LOAD"));
00042
00043 if (gCoreContext->SendReceiveStringList(strlist))
00044 {
00045 load[0] = strlist[0].toFloat();
00046 load[1] = strlist[1].toFloat();
00047 load[2] = strlist[2].toFloat();
00048 return true;
00049 }
00050
00051 return false;
00052 }
00053
00054 bool RemoteGetUptime(time_t &uptime)
00055 {
00056 QStringList strlist(QString("QUERY_UPTIME"));
00057
00058 if (!gCoreContext->SendReceiveStringList(strlist))
00059 return false;
00060
00061 if (!strlist[0].at(0).isNumber())
00062 return false;
00063
00064 if (sizeof(time_t) == sizeof(int))
00065 uptime = strlist[0].toUInt();
00066 else if (sizeof(time_t) == sizeof(long))
00067 uptime = strlist[0].toULong();
00068 else if (sizeof(time_t) == sizeof(long long))
00069 uptime = strlist[0].toULongLong();
00070
00071 return true;
00072 }
00073
00074 bool RemoteGetMemStats(int &totalMB, int &freeMB, int &totalVM, int &freeVM)
00075 {
00076 QStringList strlist(QString("QUERY_MEMSTATS"));
00077
00078 if (gCoreContext->SendReceiveStringList(strlist))
00079 {
00080 totalMB = strlist[0].toInt();
00081 freeMB = strlist[1].toInt();
00082 totalVM = strlist[2].toInt();
00083 freeVM = strlist[3].toInt();
00084 return true;
00085 }
00086
00087 return false;
00088 }
00089
00090 bool RemoteCheckFile(const ProgramInfo *pginfo, bool checkSlaves)
00091 {
00092 QStringList strlist("QUERY_CHECKFILE");
00093 strlist << QString::number((int)checkSlaves);
00094 pginfo->ToStringList(strlist);
00095
00096 if ((!gCoreContext->SendReceiveStringList(strlist)) ||
00097 (!strlist[0].toInt()))
00098 return false;
00099
00100
00101
00102 QString localpath = strlist[1];
00103 QFile checkFile(localpath);
00104 if (checkFile.exists())
00105 pginfo->SetPathname(localpath);
00106
00107 return true;
00108 }
00109
00110 bool RemoteDeleteRecording(
00111 uint chanid, const QDateTime &recstartts, bool forceMetadataDelete,
00112 bool forgetHistory)
00113 {
00114 bool result = true;
00115 QString cmd =
00116 QString("DELETE_RECORDING %1 %2 %3 %4")
00117 .arg(chanid)
00118 .arg(recstartts.toString(Qt::ISODate))
00119 .arg(forceMetadataDelete ? "FORCE" : "NO_FORCE")
00120 .arg(forgetHistory ? "FORGET" : "NO_FORGET");
00121 QStringList strlist(cmd);
00122
00123 if (!gCoreContext->SendReceiveStringList(strlist) || strlist.empty())
00124 result = false;
00125 else if (strlist[0].toInt() == -2)
00126 result = false;
00127
00128 if (!result)
00129 {
00130 LOG(VB_GENERAL, LOG_ALERT,
00131 QString("Failed to delete recording %1:%2")
00132 .arg(chanid).arg(recstartts.toString(Qt::ISODate)));
00133 }
00134
00135 return result;
00136 }
00137
00138 bool RemoteUndeleteRecording(uint chanid, const QDateTime &recstartts)
00139 {
00140 bool result = false;
00141
00142 #if 0
00143 if (!gCoreContext->GetNumSetting("AutoExpireInsteadOfDelete", 0))
00144 return result;
00145 #endif
00146
00147 QStringList strlist(QString("UNDELETE_RECORDING"));
00148 strlist.push_back(QString::number(chanid));
00149 strlist.push_back(recstartts.toString(Qt::ISODate));
00150
00151 gCoreContext->SendReceiveStringList(strlist);
00152
00153 if (strlist[0].toInt() == 0)
00154 result = true;
00155
00156 return result;
00157 }
00158
00159 void RemoteGetAllScheduledRecordings(vector<ProgramInfo *> &scheduledlist)
00160 {
00161 QStringList strList(QString("QUERY_GETALLSCHEDULED"));
00162 RemoteGetRecordingList(scheduledlist, strList);
00163 }
00164
00165 void RemoteGetAllExpiringRecordings(vector<ProgramInfo *> &expiringlist)
00166 {
00167 QStringList strList(QString("QUERY_GETEXPIRING"));
00168 RemoteGetRecordingList(expiringlist, strList);
00169 }
00170
00171 uint RemoteGetRecordingList(
00172 vector<ProgramInfo *> &reclist, QStringList &strList)
00173 {
00174 if (!gCoreContext->SendReceiveStringList(strList))
00175 return 0;
00176
00177 int numrecordings = strList[0].toInt();
00178 if (numrecordings <= 0)
00179 return 0;
00180
00181 if (numrecordings * NUMPROGRAMLINES + 1 > (int)strList.size())
00182 {
00183 LOG(VB_GENERAL, LOG_ERR,
00184 "RemoteGetRecordingList() list size appears to be incorrect.");
00185 return 0;
00186 }
00187
00188 uint reclist_initial_size = (uint) reclist.size();
00189 QStringList::const_iterator it = strList.begin() + 1;
00190 for (int i = 0; i < numrecordings; i++)
00191 {
00192 ProgramInfo *pginfo = new ProgramInfo(it, strList.end());
00193 reclist.push_back(pginfo);
00194 }
00195
00196 return ((uint) reclist.size()) - reclist_initial_size;
00197 }
00198
00199 vector<ProgramInfo *> *RemoteGetConflictList(const ProgramInfo *pginfo)
00200 {
00201 QString cmd = QString("QUERY_GETCONFLICTING");
00202 QStringList strlist( cmd );
00203 pginfo->ToStringList(strlist);
00204
00205 vector<ProgramInfo *> *retlist = new vector<ProgramInfo *>;
00206
00207 RemoteGetRecordingList(*retlist, strlist);
00208 return retlist;
00209 }
00210
00211 QDateTime RemoteGetPreviewLastModified(const ProgramInfo *pginfo)
00212 {
00213 QDateTime retdatetime;
00214
00215 QStringList strlist( "QUERY_PIXMAP_LASTMODIFIED" );
00216 pginfo->ToStringList(strlist);
00217
00218 if (!gCoreContext->SendReceiveStringList(strlist))
00219 return retdatetime;
00220
00221 if (!strlist.empty() && strlist[0] != "BAD")
00222 {
00223 uint timet = strlist[0].toUInt();
00224 retdatetime.setTime_t(timet);
00225 }
00226
00227 return retdatetime;
00228 }
00229
00232 QDateTime RemoteGetPreviewIfModified(
00233 const ProgramInfo &pginfo, const QString &cachefile)
00234 {
00235 QString loc("RemoteGetPreviewIfModified: ");
00236 QDateTime cacheLastModified;
00237 QFileInfo cachefileinfo(cachefile);
00238 if (cachefileinfo.exists())
00239 cacheLastModified = cachefileinfo.lastModified();
00240
00241 QStringList strlist("QUERY_PIXMAP_GET_IF_MODIFIED");
00242 strlist << ((cacheLastModified.isValid()) ?
00243 QString::number(cacheLastModified.toTime_t()) : QString("-1"));
00244 strlist << QString::number(200 * 1024);
00245 pginfo.ToStringList(strlist);
00246
00247 if (!gCoreContext->SendReceiveStringList(strlist) ||
00248 strlist.empty() || strlist[0] == "ERROR")
00249 {
00250 LOG(VB_GENERAL, LOG_ERR, loc + "Remote error" +
00251 ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
00252
00253 return QDateTime();
00254 }
00255
00256 if (strlist[0] == "WARNING")
00257 {
00258 LOG(VB_NETWORK, LOG_WARNING, loc + "Remote warning" +
00259 ((strlist.size() >= 2) ? (":\n\t\t\t" + strlist[1]) : ""));
00260
00261 return QDateTime();
00262 }
00263
00264 QDateTime retdatetime;
00265 qlonglong timet = strlist[0].toLongLong();
00266 if (timet >= 0)
00267 retdatetime.setTime_t(timet);
00268
00269 if (strlist.size() < 4)
00270 {
00271 return retdatetime;
00272 }
00273
00274 size_t length = strlist[1].toULongLong();
00275 quint16 checksum16 = strlist[2].toUInt();
00276 QByteArray data = QByteArray::fromBase64(strlist[3].toAscii());
00277 if ((size_t) data.size() < length)
00278 {
00279 LOG(VB_GENERAL, LOG_ERR, loc +
00280 QString("Preview size check failed %1 < %2")
00281 .arg(data.size()).arg(length));
00282 return QDateTime();
00283 }
00284 data.resize(length);
00285
00286 if (checksum16 != qChecksum(data.constData(), data.size()))
00287 {
00288 LOG(VB_GENERAL, LOG_ERR, loc + "Preview checksum failed");
00289 return QDateTime();
00290 }
00291
00292 QString pdir(cachefile.section("/", 0, -2));
00293 QDir cfd(pdir);
00294 if (!cfd.exists() && !cfd.mkdir(pdir))
00295 {
00296 LOG(VB_GENERAL, LOG_ERR, loc +
00297 QString("Unable to create remote cache directory '%1'")
00298 .arg(pdir));
00299
00300 return QDateTime();
00301 }
00302
00303 QFile file(cachefile);
00304 if (!file.open(QIODevice::WriteOnly|QIODevice::Truncate))
00305 {
00306 LOG(VB_GENERAL, LOG_ERR, loc +
00307 QString("Unable to open cached preview file for writing '%1'")
00308 .arg(cachefile));
00309
00310 return QDateTime();
00311 }
00312
00313 off_t offset = 0;
00314 size_t remaining = length;
00315 uint failure_cnt = 0;
00316 while ((remaining > 0) && (failure_cnt < 5))
00317 {
00318 ssize_t written = file.write(data.data() + offset, remaining);
00319 if (written < 0)
00320 {
00321 failure_cnt++;
00322 usleep(50000);
00323 continue;
00324 }
00325
00326 failure_cnt = 0;
00327 offset += written;
00328 remaining -= written;
00329 }
00330
00331 if (remaining)
00332 {
00333 LOG(VB_GENERAL, LOG_ERR, loc +
00334 QString("Failed to write cached preview file '%1'")
00335 .arg(cachefile));
00336
00337 file.resize(0);
00338 file.remove();
00339 return QDateTime();
00340 }
00341
00342 file.close();
00343
00344 return retdatetime;
00345 }
00346
00347 bool RemoteFillProgramInfo(ProgramInfo &pginfo, const QString &playbackhost)
00348 {
00349 QStringList strlist( "FILL_PROGRAM_INFO" );
00350 strlist << playbackhost;
00351 pginfo.ToStringList(strlist);
00352
00353 if (gCoreContext->SendReceiveStringList(strlist))
00354 {
00355 ProgramInfo tmp(strlist);
00356 if (tmp.HasPathname() || tmp.GetChanID())
00357 {
00358 pginfo = tmp;
00359 return true;
00360 }
00361 }
00362
00363 return false;
00364 }
00365
00366 QStringList RemoteRecordings(void)
00367 {
00368 QStringList strlist("QUERY_ISRECORDING");
00369
00370 if (!gCoreContext->SendReceiveStringList(strlist, false, false))
00371 {
00372 QStringList empty;
00373 empty << "0" << "0";
00374 return empty;
00375 }
00376
00377 return strlist;
00378 }
00379
00380 int RemoteGetRecordingMask(void)
00381 {
00382 int mask = 0;
00383
00384 QString cmd = "QUERY_ISRECORDING";
00385
00386 QStringList strlist( cmd );
00387
00388 if (!gCoreContext->SendReceiveStringList(strlist))
00389 return mask;
00390
00391 if (strlist.empty())
00392 return 0;
00393
00394 int recCount = strlist[0].toInt();
00395
00396 for (int i = 0, j = 0; j < recCount; i++)
00397 {
00398 cmd = QString("QUERY_RECORDER %1").arg(i + 1);
00399
00400 strlist = QStringList( cmd );
00401 strlist << "IS_RECORDING";
00402
00403 if (gCoreContext->SendReceiveStringList(strlist) && !strlist.empty())
00404 {
00405 if (strlist[0].toInt())
00406 {
00407 mask |= 1<<i;
00408 j++;
00409 }
00410 }
00411 else
00412 {
00413 break;
00414 }
00415 }
00416
00417 return mask;
00418 }
00419
00420 int RemoteGetFreeRecorderCount(void)
00421 {
00422 QStringList strlist( "GET_FREE_RECORDER_COUNT" );
00423
00424 if (!gCoreContext->SendReceiveStringList(strlist, true))
00425 return 0;
00426
00427 if (strlist.empty())
00428 return 0;
00429
00430 if (strlist[0] == "UNKNOWN_COMMAND")
00431 {
00432 LOG(VB_GENERAL, LOG_EMERG,
00433 "Unknown command GET_FREE_RECORDER_COUNT, upgrade your "
00434 "backend version.");
00435 return 0;
00436 }
00437
00438 return strlist[0].toInt();
00439 }
00440
00441 bool RemoteGetFileList(QString host, QString path, QStringList* list,
00442 QString sgroup, bool fileNamesOnly)
00443 {
00444
00445
00446 list->clear();
00447
00448 if (sgroup.isEmpty())
00449 sgroup = "Videos";
00450
00451 *list << "QUERY_SG_GETFILELIST";
00452 *list << host;
00453 *list << StorageGroup::GetGroupToUse(host, sgroup);
00454 *list << path;
00455 *list << QString::number(fileNamesOnly);
00456
00457 bool ok = false;
00458
00459 if (gCoreContext->IsMasterBackend())
00460 {
00461
00462
00463
00464
00465
00466 QString ann = QString("ANN Playback %1 0")
00467 .arg(gCoreContext->GetHostName());
00468 QString addr = gCoreContext->GetBackendServerIP(host);
00469 int port = gCoreContext->GetNumSettingOnHost("BackendServerPort",
00470 host, 6543);
00471 bool mismatch = false;
00472
00473 MythSocket *sock = gCoreContext->ConnectCommandSocket(
00474 addr, port, ann, &mismatch);
00475 if (sock)
00476 {
00477 ok = sock->SendReceiveStringList(*list);
00478 sock->DownRef();
00479 }
00480 else
00481 list->clear();
00482 }
00483 else
00484 ok = gCoreContext->SendReceiveStringList(*list);
00485
00486
00487 return ok;
00488 }
00489
00495 int RemoteCheckForRecording(const ProgramInfo *pginfo)
00496 {
00497 QStringList strlist( QString("CHECK_RECORDING") );
00498 pginfo->ToStringList(strlist);
00499
00500 if (gCoreContext->SendReceiveStringList(strlist) && !strlist.empty())
00501 return strlist[0].toInt();
00502
00503 return 0;
00504 }
00505
00514 int RemoteGetRecordingStatus(
00515 const ProgramInfo *pginfo, int overrecsecs, int underrecsecs)
00516 {
00517 QDateTime curtime = QDateTime::currentDateTime();
00518
00519 int retval = 0;
00520
00521 if (pginfo)
00522 {
00523 if (curtime >= pginfo->GetScheduledStartTime().addSecs(-underrecsecs) &&
00524 curtime < pginfo->GetScheduledEndTime().addSecs(overrecsecs))
00525 {
00526 if (curtime >= pginfo->GetScheduledStartTime() &&
00527 curtime < pginfo->GetScheduledEndTime())
00528 retval = 1;
00529 else if (curtime < pginfo->GetScheduledStartTime() &&
00530 RemoteCheckForRecording(pginfo) > 0)
00531 retval = 2;
00532 else if (curtime > pginfo->GetScheduledEndTime() &&
00533 RemoteCheckForRecording(pginfo) > 0)
00534 retval = 3;
00535 }
00536 }
00537
00538 return retval;
00539 }
00540
00544 vector<ProgramInfo *> *RemoteGetCurrentlyRecordingList(void)
00545 {
00546 QString str = "QUERY_RECORDINGS ";
00547 str += "Recording";
00548 QStringList strlist( str );
00549
00550 vector<ProgramInfo *> *reclist = new vector<ProgramInfo *>;
00551 vector<ProgramInfo *> *info = new vector<ProgramInfo *>;
00552 if (!RemoteGetRecordingList(*info, strlist))
00553 {
00554 if (info)
00555 delete info;
00556 return reclist;
00557 }
00558
00559 ProgramInfo *p = NULL;
00560 vector<ProgramInfo *>::iterator it = info->begin();
00561
00562
00563 for ( ; it != info->end(); ++it)
00564 {
00565 p = *it;
00566 if (p->GetRecordingStatus() == rsRecording ||
00567 (p->GetRecordingStatus() == rsRecorded &&
00568 p->GetRecordingGroup() == "LiveTV"))
00569 {
00570 reclist->push_back(new ProgramInfo(*p));
00571 }
00572 }
00573
00574 while (!info->empty())
00575 {
00576 delete info->back();
00577 info->pop_back();
00578 }
00579 if (info)
00580 delete info;
00581
00582 return reclist;
00583 }
00584
00588 bool RemoteGetActiveBackends(QStringList *list)
00589 {
00590 list->clear();
00591 *list << "QUERY_ACTIVE_BACKENDS";
00592
00593 if (!gCoreContext->SendReceiveStringList(*list))
00594 return false;
00595
00596 list->removeFirst();
00597 return true;
00598 }
00599
00600