MythTV  0.26-pre
networkcontrol.cpp
Go to the documentation of this file.
00001 #include <unistd.h>
00002 
00003 #include <QCoreApplication>
00004 #include <QRegExp>
00005 #include <QStringList>
00006 #include <QTextStream>
00007 #include <QDir>
00008 #include <QKeyEvent>
00009 #include <QEvent>
00010 #include <QMap>
00011 
00012 #include "mythcorecontext.h"
00013 #include "mythversion.h"
00014 #include "networkcontrol.h"
00015 #include "programinfo.h"
00016 #include "remoteutil.h"
00017 #include "previewgenerator.h"
00018 #include "compat.h"
00019 #include "mythsystemevent.h"
00020 #include "mythdirs.h"
00021 #include "mythlogging.h"
00022 
00023 // libmythui
00024 #include "mythmainwindow.h"
00025 #include "mythuihelper.h"
00026 
00027 #define LOC QString("NetworkControl: ")
00028 #define LOC_ERR QString("NetworkControl Error: ")
00029 
00030 #define FE_SHORT_TO 2000
00031 #define FE_LONG_TO  10000
00032 
00033 static QEvent::Type kNetworkControlDataReadyEvent =
00034     (QEvent::Type) QEvent::registerEventType();
00035 QEvent::Type NetworkControlCloseEvent::kEventType =
00036     (QEvent::Type) QEvent::registerEventType();
00037 
00045 static bool is_abbrev(QString const& command,
00046                       QString const& test, int minchars = 1)
00047 {
00048     if (test.length() < minchars)
00049         return command.toLower() == test.toLower();
00050     else
00051         return test.toLower() == command.left(test.length()).toLower();
00052 }
00053 
00054 NetworkControl::NetworkControl() :
00055     ServerPool(), prompt("# "),
00056     gotAnswer(false), answer(""),
00057     clientLock(QMutex::Recursive),
00058     commandThread(new MThread("NetworkControl", this)),
00059     stopCommandThread(false)
00060 {
00061     // Eventually this map should be in the jumppoints table
00062     jumpMap["channelpriorities"]     = "Channel Recording Priorities";
00063     jumpMap["livetv"]                = "Live TV";
00064     jumpMap["livetvinguide"]         = "Live TV In Guide";
00065     jumpMap["mainmenu"]              = "Main Menu";
00066     jumpMap["managerecordings"]      = "Manage Recordings / Fix Conflicts";
00067     jumpMap["mythgallery"]           = "MythGallery";
00068     jumpMap["mythvideo"]             = "Video Default";
00069     jumpMap["mythweather"]           = "MythWeather";
00070     jumpMap["mythgame"]              = "MythGame";
00071     jumpMap["mythnews"]              = "MythNews";
00072     jumpMap["playdvd"]               = "Play Disc";
00073     jumpMap["playmusic"]             = "Play music";
00074     jumpMap["programfinder"]         = "Program Finder";
00075     jumpMap["programguide"]          = "Program Guide";
00076     jumpMap["recordingpriorities"]   = "Program Recording Priorities";
00077     jumpMap["ripcd"]                 = "Rip CD";
00078     jumpMap["musicplaylists"]        = "Select music playlists";
00079     jumpMap["deleterecordings"]      = "TV Recording Deletion";
00080     jumpMap["playbackrecordings"]    = "TV Recording Playback";
00081     jumpMap["videobrowser"]          = "Video Browser";
00082     jumpMap["videogallery"]          = "Video Gallery";
00083     jumpMap["videolistings"]         = "Video Listings";
00084     jumpMap["videomanager"]          = "Video Manager";
00085     jumpMap["zoneminderconsole"]     = "ZoneMinder Console";
00086     jumpMap["zoneminderliveview"]    = "ZoneMinder Live View";
00087     jumpMap["zoneminderevents"]      = "ZoneMinder Events";
00088 
00089     jumpMap["channelrecpriority"]    = "Channel Recording Priorities";
00090     jumpMap["viewscheduled"]         = "Manage Recordings / Fix Conflicts";
00091     jumpMap["previousbox"]           = "Previously Recorded";
00092     jumpMap["progfinder"]            = "Program Finder";
00093     jumpMap["guidegrid"]             = "Program Guide";
00094     jumpMap["programrecpriority"]    = "Program Recording Priorities";
00095     jumpMap["statusbox"]             = "Status Screen";
00096     jumpMap["deletebox"]             = "TV Recording Deletion";
00097     jumpMap["playbackbox"]           = "TV Recording Playback";
00098     jumpMap["pbb"]                   = "TV Recording Playback";
00099 
00100     keyMap["up"]                     = Qt::Key_Up;
00101     keyMap["down"]                   = Qt::Key_Down;
00102     keyMap["left"]                   = Qt::Key_Left;
00103     keyMap["right"]                  = Qt::Key_Right;
00104     keyMap["home"]                   = Qt::Key_Home;
00105     keyMap["end"]                    = Qt::Key_End;
00106     keyMap["enter"]                  = Qt::Key_Enter;
00107     keyMap["return"]                 = Qt::Key_Return;
00108     keyMap["pageup"]                 = Qt::Key_PageUp;
00109     keyMap["pagedown"]               = Qt::Key_PageDown;
00110     keyMap["escape"]                 = Qt::Key_Escape;
00111     keyMap["tab"]                    = Qt::Key_Tab;
00112     keyMap["backtab"]                = Qt::Key_Backtab;
00113     keyMap["space"]                  = Qt::Key_Space;
00114     keyMap["backspace"]              = Qt::Key_Backspace;
00115     keyMap["insert"]                 = Qt::Key_Insert;
00116     keyMap["delete"]                 = Qt::Key_Delete;
00117     keyMap["plus"]                   = Qt::Key_Plus;
00118     keyMap["+"]                      = Qt::Key_Plus;
00119     keyMap["comma"]                  = Qt::Key_Comma;
00120     keyMap[","]                      = Qt::Key_Comma;
00121     keyMap["minus"]                  = Qt::Key_Minus;
00122     keyMap["-"]                      = Qt::Key_Minus;
00123     keyMap["underscore"]             = Qt::Key_Underscore;
00124     keyMap["_"]                      = Qt::Key_Underscore;
00125     keyMap["period"]                 = Qt::Key_Period;
00126     keyMap["."]                      = Qt::Key_Period;
00127     keyMap["numbersign"]             = Qt::Key_NumberSign;
00128     keyMap["poundsign"]              = Qt::Key_NumberSign;
00129     keyMap["hash"]                   = Qt::Key_NumberSign;
00130     keyMap["#"]                      = Qt::Key_NumberSign;
00131     keyMap["bracketleft"]            = Qt::Key_BracketLeft;
00132     keyMap["["]                      = Qt::Key_BracketLeft;
00133     keyMap["bracketright"]           = Qt::Key_BracketRight;
00134     keyMap["]"]                      = Qt::Key_BracketRight;
00135     keyMap["backslash"]              = Qt::Key_Backslash;
00136     keyMap["\\"]                     = Qt::Key_Backslash;
00137     keyMap["dollar"]                 = Qt::Key_Dollar;
00138     keyMap["$"]                      = Qt::Key_Dollar;
00139     keyMap["percent"]                = Qt::Key_Percent;
00140     keyMap["%"]                      = Qt::Key_Percent;
00141     keyMap["ampersand"]              = Qt::Key_Ampersand;
00142     keyMap["&"]                      = Qt::Key_Ampersand;
00143     keyMap["parenleft"]              = Qt::Key_ParenLeft;
00144     keyMap["("]                      = Qt::Key_ParenLeft;
00145     keyMap["parenright"]             = Qt::Key_ParenRight;
00146     keyMap[")"]                      = Qt::Key_ParenRight;
00147     keyMap["asterisk"]               = Qt::Key_Asterisk;
00148     keyMap["*"]                      = Qt::Key_Asterisk;
00149     keyMap["question"]               = Qt::Key_Question;
00150     keyMap["?"]                      = Qt::Key_Question;
00151     keyMap["slash"]                  = Qt::Key_Slash;
00152     keyMap["/"]                      = Qt::Key_Slash;
00153     keyMap["colon"]                  = Qt::Key_Colon;
00154     keyMap[":"]                      = Qt::Key_Colon;
00155     keyMap["semicolon"]              = Qt::Key_Semicolon;
00156     keyMap[";"]                      = Qt::Key_Semicolon;
00157     keyMap["less"]                   = Qt::Key_Less;
00158     keyMap["<"]                      = Qt::Key_Less;
00159     keyMap["equal"]                  = Qt::Key_Equal;
00160     keyMap["="]                      = Qt::Key_Equal;
00161     keyMap["greater"]                = Qt::Key_Greater;
00162     keyMap[">"]                      = Qt::Key_Greater;
00163     keyMap["bar"]                    = Qt::Key_Bar;
00164     keyMap["pipe"]                   = Qt::Key_Bar;
00165     keyMap["|"]                      = Qt::Key_Bar;
00166     keyMap["f1"]                     = Qt::Key_F1;
00167     keyMap["f2"]                     = Qt::Key_F2;
00168     keyMap["f3"]                     = Qt::Key_F3;
00169     keyMap["f4"]                     = Qt::Key_F4;
00170     keyMap["f5"]                     = Qt::Key_F5;
00171     keyMap["f6"]                     = Qt::Key_F6;
00172     keyMap["f7"]                     = Qt::Key_F7;
00173     keyMap["f8"]                     = Qt::Key_F8;
00174     keyMap["f9"]                     = Qt::Key_F9;
00175     keyMap["f10"]                    = Qt::Key_F10;
00176     keyMap["f11"]                    = Qt::Key_F11;
00177     keyMap["f12"]                    = Qt::Key_F12;
00178     keyMap["f13"]                    = Qt::Key_F13;
00179     keyMap["f14"]                    = Qt::Key_F14;
00180     keyMap["f15"]                    = Qt::Key_F15;
00181     keyMap["f16"]                    = Qt::Key_F16;
00182     keyMap["f17"]                    = Qt::Key_F17;
00183     keyMap["f18"]                    = Qt::Key_F18;
00184     keyMap["f19"]                    = Qt::Key_F19;
00185     keyMap["f20"]                    = Qt::Key_F20;
00186     keyMap["f21"]                    = Qt::Key_F21;
00187     keyMap["f22"]                    = Qt::Key_F22;
00188     keyMap["f23"]                    = Qt::Key_F23;
00189     keyMap["f24"]                    = Qt::Key_F24;
00190 
00191     keyTextMap[Qt::Key_Plus]            = "+";
00192     keyTextMap[Qt::Key_Comma]           = ",";
00193     keyTextMap[Qt::Key_Minus]           = "-";
00194     keyTextMap[Qt::Key_Underscore]      = "_";
00195     keyTextMap[Qt::Key_Period]          = ".";
00196     keyTextMap[Qt::Key_NumberSign]      = "#";
00197     keyTextMap[Qt::Key_BracketLeft]     = "[";
00198     keyTextMap[Qt::Key_BracketRight]    = "]";
00199     keyTextMap[Qt::Key_Backslash]       = "\\";
00200     keyTextMap[Qt::Key_Dollar]          = "$";
00201     keyTextMap[Qt::Key_Percent]         = "%";
00202     keyTextMap[Qt::Key_Ampersand]       = "&";
00203     keyTextMap[Qt::Key_ParenLeft]       = "(";
00204     keyTextMap[Qt::Key_ParenRight]      = ")";
00205     keyTextMap[Qt::Key_Asterisk]        = "*";
00206     keyTextMap[Qt::Key_Question]        = "?";
00207     keyTextMap[Qt::Key_Slash]           = "/";
00208     keyTextMap[Qt::Key_Colon]           = ":";
00209     keyTextMap[Qt::Key_Semicolon]       = ";";
00210     keyTextMap[Qt::Key_Less]            = "<";
00211     keyTextMap[Qt::Key_Equal]           = "=";
00212     keyTextMap[Qt::Key_Greater]         = ">";
00213     keyTextMap[Qt::Key_Bar]             = "|";
00214 
00215     commandThread->start();
00216 
00217     gCoreContext->addListener(this);
00218 
00219     connect(this, SIGNAL(newConnection(QTcpSocket*)),
00220             this, SLOT(newConnection(QTcpSocket*)));
00221 }
00222 
00223 NetworkControl::~NetworkControl(void)
00224 {
00225     gCoreContext->removeListener(this);
00226 
00227     clientLock.lock();
00228     while (!clients.isEmpty())
00229     {
00230         NetworkControlClient *ncc = clients.takeFirst();
00231         delete ncc;
00232     }
00233     clientLock.unlock();
00234 
00235     nrLock.lock();
00236     networkControlReplies.push_back(new NetworkCommand(NULL,
00237         "mythfrontend shutting down, connection closing..."));
00238     nrLock.unlock();
00239 
00240     notifyDataAvailable();
00241 
00242     ncLock.lock();
00243     stopCommandThread = true;
00244     ncCond.wakeOne();
00245     ncLock.unlock();
00246     commandThread->wait();
00247     delete commandThread;
00248     commandThread = NULL;
00249 }
00250 
00251 void NetworkControl::run(void)
00252 {
00253     QMutexLocker locker(&ncLock);
00254     while (!stopCommandThread)
00255     {
00256         while (networkControlCommands.empty() && !stopCommandThread)
00257             ncCond.wait(&ncLock);
00258         if (!stopCommandThread)
00259         {
00260             NetworkCommand *nc = networkControlCommands.front();
00261             networkControlCommands.pop_front();
00262             locker.unlock();
00263             processNetworkControlCommand(nc);
00264             locker.relock();
00265         }
00266     }
00267 }
00268 
00269 void NetworkControl::processNetworkControlCommand(NetworkCommand *nc)
00270 {
00271     QMutexLocker locker(&clientLock);
00272     QString result;
00273 
00274     int clientID = clients.indexOf(nc->getClient());
00275 
00276     if (is_abbrev("jump", nc->getArg(0)))
00277         result = processJump(nc);
00278     else if (is_abbrev("key", nc->getArg(0)))
00279         result = processKey(nc);
00280     else if (is_abbrev("play", nc->getArg(0)))
00281         result = processPlay(nc, clientID);
00282     else if (is_abbrev("query", nc->getArg(0)))
00283         result = processQuery(nc);
00284     else if (is_abbrev("set", nc->getArg(0)))
00285         result = processSet(nc);
00286     else if (is_abbrev("screenshot", nc->getArg(0)))
00287         result = saveScreenshot(nc);
00288     else if (is_abbrev("help", nc->getArg(0)))
00289         result = processHelp(nc);
00290     else if (is_abbrev("message", nc->getArg(0)))
00291         result = processMessage(nc);
00292     else if ((nc->getArg(0).toLower() == "exit") || (nc->getArg(0).toLower() == "quit"))
00293         QCoreApplication::postEvent(this,
00294                                 new NetworkControlCloseEvent(nc->getClient()));
00295     else if (! nc->getArg(0).isEmpty())
00296         result = QString("INVALID command '%1', try 'help' for more info")
00297                          .arg(nc->getArg(0));
00298 
00299     nrLock.lock();
00300     networkControlReplies.push_back(new NetworkCommand(nc->getClient(),result));
00301     nrLock.unlock();
00302 
00303     notifyDataAvailable();
00304 }
00305 
00306 void NetworkControl::deleteClient(void)
00307 {
00308     LOG(VB_GENERAL, LOG_INFO, LOC + "Client Socket disconnected");
00309     QMutexLocker locker(&clientLock);
00310 
00311     gCoreContext->SendSystemEvent("NET_CTRL_DISCONNECTED");
00312 
00313     QList<NetworkControlClient *>::const_iterator it;
00314     for (it = clients.begin(); it != clients.end(); ++it)
00315     {
00316         NetworkControlClient *ncc = *it;
00317         if (ncc->getSocket()->state() == QTcpSocket::UnconnectedState)
00318         {
00319             deleteClient(ncc);
00320             return;
00321         }
00322     }
00323 }
00324 
00325 void NetworkControl::deleteClient(NetworkControlClient *ncc)
00326 {
00327     int index = clients.indexOf(ncc);
00328     if (index >= 0)
00329     {
00330         clients.removeAt(index);
00331 
00332         delete ncc;
00333     }
00334     else
00335         LOG(VB_GENERAL, LOG_ERR, LOC + QString("deleteClient(%1), unable to "
00336                 "locate specified NetworkControlClient").arg((long long)ncc));
00337 }
00338 
00339 void NetworkControl::newConnection(QTcpSocket *client)
00340 {
00341     QString welcomeStr;
00342 
00343     LOG(VB_GENERAL, LOG_INFO, LOC + QString("New connection established."));
00344 
00345     gCoreContext->SendSystemEvent("NET_CTRL_CONNECTED");
00346 
00347     NetworkControlClient *ncc = new NetworkControlClient(client);
00348 
00349     QMutexLocker locker(&clientLock);
00350     clients.push_back(ncc);
00351 
00352     connect(ncc, SIGNAL(commandReceived(QString&)), this,
00353             SLOT(receiveCommand(QString&)));
00354     connect(client, SIGNAL(disconnected()), this, SLOT(deleteClient()));
00355 
00356     welcomeStr = "MythFrontend Network Control\r\n";
00357     welcomeStr += "Type 'help' for usage information\r\n"
00358                   "---------------------------------";
00359     nrLock.lock();
00360     networkControlReplies.push_back(new NetworkCommand(ncc,welcomeStr));
00361     nrLock.unlock();
00362 
00363     notifyDataAvailable();
00364 }
00365 
00366 NetworkControlClient::NetworkControlClient(QTcpSocket *s)
00367 {
00368     m_socket = s;
00369     m_textStream = new QTextStream(s);
00370     m_textStream->setCodec("UTF-8");
00371     connect(m_socket, SIGNAL(readyRead()), this, SLOT(readClient()));
00372 }
00373 
00374 NetworkControlClient::~NetworkControlClient()
00375 {
00376     m_socket->close();
00377     m_socket->deleteLater();
00378 
00379     delete m_textStream;
00380 }
00381 
00382 void NetworkControlClient::readClient(void)
00383 {
00384     QTcpSocket *socket = (QTcpSocket *)sender();
00385     if (!socket)
00386         return;
00387 
00388     QString lineIn;
00389     while (socket->canReadLine())
00390     {
00391         lineIn = socket->readLine();
00392 #if 0
00393         lineIn.replace(QRegExp("[^-a-zA-Z0-9\\s\\.:_#/$%&()*+,;<=>?\\[\\]\\|]"),
00394                        "");
00395 #endif
00396 
00397         // TODO: can this be replaced with lineIn.simplify()?
00398         lineIn.replace(QRegExp("[\r\n]"), "");
00399         lineIn.replace(QRegExp("^\\s"), "");
00400 
00401         if (lineIn.isEmpty())
00402             continue;
00403 
00404         LOG(VB_NETWORK, LOG_INFO, LOC +
00405             QString("emit commandReceived(%1)").arg(lineIn));
00406         emit commandReceived(lineIn);
00407     }
00408 }
00409 
00410 void NetworkControl::receiveCommand(QString &command)
00411 {
00412     LOG(VB_NETWORK, LOG_INFO, LOC +
00413         QString("NetworkControl::receiveCommand(%1)").arg(command));
00414     NetworkControlClient *ncc = static_cast<NetworkControlClient *>(sender());
00415     if (!ncc)
00416          return;
00417 
00418     ncLock.lock();
00419     networkControlCommands.push_back(new NetworkCommand(ncc,command));
00420     ncCond.wakeOne();
00421     ncLock.unlock();
00422 }
00423 
00424 QString NetworkControl::processJump(NetworkCommand *nc)
00425 {
00426     QString result = "OK";
00427 
00428     if ((nc->getArgCount() < 2) || (!jumpMap.contains(nc->getArg(1))))
00429         return QString("ERROR: See 'help %1' for usage information")
00430                        .arg(nc->getArg(0));
00431 
00432     GetMythMainWindow()->JumpTo(jumpMap[nc->getArg(1)]);
00433 
00434     // Fixme, should do some better checking here, but that would
00435     // depend on all Locations matching their jumppoints
00436     QTime timer;
00437     timer.start();
00438     while ((timer.elapsed() < FE_SHORT_TO) &&
00439            (GetMythUI()->GetCurrentLocation().toLower() != nc->getArg(1)))
00440         usleep(10000);
00441 
00442     return result;
00443 }
00444 
00445 QString NetworkControl::processKey(NetworkCommand *nc)
00446 {
00447     QString result = "OK";
00448     QKeyEvent *event = NULL;
00449 
00450     if (nc->getArgCount() < 2)
00451         return QString("ERROR: See 'help %1' for usage information")
00452                        .arg(nc->getArg(0));
00453 
00454     QObject *keyDest = NULL;
00455 
00456     if (GetMythMainWindow())
00457         keyDest = GetMythMainWindow();
00458     else
00459         return QString("ERROR: Application has no main window!\n");
00460 
00461     if (GetMythMainWindow()->currentWidget())
00462         keyDest = GetMythMainWindow()->currentWidget()->focusWidget();
00463 
00464     int curToken = 1;
00465     int tokenLen = 0;
00466     while (curToken < nc->getArgCount())
00467     {
00468         tokenLen = nc->getArg(curToken).length();
00469 
00470         if (nc->getArg(curToken) == "sleep")
00471         {
00472             sleep(1);
00473         }
00474         else if (keyMap.contains(nc->getArg(curToken)))
00475         {
00476             int keyCode = keyMap[nc->getArg(curToken)];
00477             QString keyText;
00478 
00479             if (keyTextMap.contains(keyCode))
00480                 keyText = keyTextMap[keyCode];
00481 
00482             GetMythUI()->ResetScreensaver();
00483 
00484             event = new QKeyEvent(QEvent::KeyPress, keyCode, Qt::NoModifier,
00485                                   keyText);
00486             QCoreApplication::postEvent(keyDest, event);
00487 
00488             event = new QKeyEvent(QEvent::KeyRelease, keyCode, Qt::NoModifier,
00489                                   keyText);
00490             QCoreApplication::postEvent(keyDest, event);
00491         }
00492         else if (((tokenLen == 1) &&
00493                   (nc->getArg(curToken)[0].isLetterOrNumber())) ||
00494                  ((tokenLen >= 1) &&
00495                   (nc->getArg(curToken).contains("+"))))
00496         {
00497             QKeySequence a(nc->getArg(curToken));
00498             int keyCode = a[0];
00499             Qt::KeyboardModifiers modifiers = Qt::NoModifier;
00500 
00501             if (tokenLen > 1)
00502             {
00503                 QStringList tokenParts = nc->getArg(curToken).split('+');
00504 
00505                 int partNum = 0;
00506                 while (partNum < (tokenParts.size() - 1))
00507                 {
00508                     if (tokenParts[partNum].toUpper() == "CTRL")
00509                         modifiers |= Qt::ControlModifier;
00510                     if (tokenParts[partNum].toUpper() == "SHIFT")
00511                         modifiers |= Qt::ShiftModifier;
00512                     if (tokenParts[partNum].toUpper() == "ALT")
00513                         modifiers |= Qt::AltModifier;
00514                     if (tokenParts[partNum].toUpper() == "META")
00515                         modifiers |= Qt::MetaModifier;
00516 
00517                     partNum++;
00518                 }
00519             }
00520             else
00521             {
00522                 if (nc->getArg(curToken) == nc->getArg(curToken).toUpper())
00523                     modifiers = Qt::ShiftModifier;
00524             }
00525 
00526             GetMythUI()->ResetScreensaver();
00527 
00528             event = new QKeyEvent(QEvent::KeyPress, keyCode, modifiers,
00529                                   nc->getArg(curToken));
00530             QCoreApplication::postEvent(keyDest, event);
00531 
00532             event = new QKeyEvent(QEvent::KeyRelease, keyCode, modifiers,
00533                                   nc->getArg(curToken));
00534             QCoreApplication::postEvent(keyDest, event);
00535         }
00536         else
00537             return QString("ERROR: Invalid syntax at '%1', see 'help %2' for "
00538                            "usage information")
00539                            .arg(nc->getArg(curToken)).arg(nc->getArg(0));
00540 
00541         curToken++;
00542     }
00543 
00544     return result;
00545 }
00546 
00547 QString NetworkControl::processPlay(NetworkCommand *nc, int clientID)
00548 {
00549     QString result = "OK";
00550     QString message;
00551 
00552     if (nc->getArgCount() < 2)
00553         return QString("ERROR: See 'help %1' for usage information")
00554                        .arg(nc->getArg(0));
00555 
00556     if ((nc->getArgCount() >= 3) &&
00557         (is_abbrev("file", nc->getArg(1))))
00558     {
00559         if (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu")
00560         {
00561             GetMythMainWindow()->JumpTo(jumpMap["mainmenu"]);
00562 
00563             QTime timer;
00564             timer.start();
00565             while ((timer.elapsed() < FE_LONG_TO) &&
00566                    (GetMythUI()->GetCurrentLocation().toLower() != "mainmenu"))
00567                 usleep(10000);
00568         }
00569 
00570         if (GetMythUI()->GetCurrentLocation().toLower() == "mainmenu")
00571         {
00572             QStringList args;
00573             args << nc->getFrom(2);
00574             MythEvent *me = new MythEvent(ACTION_HANDLEMEDIA, args);
00575             qApp->postEvent(GetMythMainWindow(), me);
00576         }
00577         else
00578             return QString("Unable to change to main menu to start playback!");
00579     }
00580     else if ((nc->getArgCount() >= 4) &&
00581              (is_abbrev("program", nc->getArg(1))) &&
00582              (nc->getArg(2).contains(QRegExp("^\\d+$"))) &&
00583              (nc->getArg(3).contains(QRegExp(
00584                          "^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d$"))))
00585     {
00586         if (GetMythUI()->GetCurrentLocation().toLower() == "playback")
00587         {
00588             QString message = QString("NETWORK_CONTROL STOP");
00589             MythEvent me(message);
00590             gCoreContext->dispatch(me);
00591 
00592             QTime timer;
00593             timer.start();
00594             while ((timer.elapsed() < FE_LONG_TO) &&
00595                    (GetMythUI()->GetCurrentLocation().toLower() == "playback"))
00596                 usleep(10000);
00597         }
00598 
00599         if (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox")
00600         {
00601             GetMythMainWindow()->JumpTo(jumpMap["playbackbox"]);
00602 
00603             QTime timer;
00604             timer.start();
00605             while ((timer.elapsed() < 10000) &&
00606                    (GetMythUI()->GetCurrentLocation().toLower() != "playbackbox"))
00607                 usleep(10000);
00608 
00609             timer.start();
00610             while ((timer.elapsed() < 10000) &&
00611                    (!GetMythUI()->IsTopScreenInitialized()))
00612                 usleep(10000);
00613         }
00614 
00615         if (GetMythUI()->GetCurrentLocation().toLower() == "playbackbox")
00616         {
00617             QString action = "PLAY";
00618             if (nc->getArgCount() == 5 && nc->getArg(4) == "resume")
00619                 action = "RESUME";
00620 
00621             QString message = QString("NETWORK_CONTROL %1 PROGRAM %2 %3 %4")
00622                                       .arg(action).arg(nc->getArg(2))
00623                                       .arg(nc->getArg(3).toUpper()).arg(clientID);
00624 
00625             result.clear();
00626             gotAnswer = false;
00627             QTime timer;
00628             timer.start();
00629 
00630             MythEvent me(message);
00631             gCoreContext->dispatch(me);
00632 
00633             while (timer.elapsed() < FE_LONG_TO && !gotAnswer)
00634                 usleep(10000);
00635 
00636             if (gotAnswer)
00637                 result += answer;
00638             else
00639                 result = "ERROR: Timed out waiting for reply from player";
00640 
00641         }
00642         else
00643         {
00644             result = QString("ERROR: Unable to change to PlaybackBox from "
00645                              "%1, cannot play requested file.")
00646                              .arg(GetMythUI()->GetCurrentLocation());
00647         }
00648     }
00649     else if (is_abbrev("music", nc->getArg(1)))
00650     {
00651 #if 0
00652         if (GetMythUI()->GetCurrentLocation().toLower() != "playmusic")
00653         {
00654             return QString("ERROR: You are in %1 mode and this command is "
00655                            "only for MythMusic")
00656                         .arg(GetMythUI()->GetCurrentLocation());
00657         }
00658 #endif
00659 
00660         QString hostname = gCoreContext->GetHostName();
00661 
00662         if (nc->getArgCount() == 3)
00663         {
00664             if (is_abbrev("play", nc->getArg(2)))
00665                 message = QString("MUSIC_COMMAND %1 PLAY").arg(hostname);
00666             else if (is_abbrev("pause", nc->getArg(2)))
00667                 message = QString("MUSIC_COMMAND %1 PAUSE").arg(hostname);
00668             else if (is_abbrev("stop", nc->getArg(2)))
00669                 message = QString("MUSIC_COMMAND %1 STOP").arg(hostname);
00670             else if (is_abbrev("getvolume", nc->getArg(2)))
00671             {
00672                 gotAnswer = false;
00673 
00674                 MythEvent me(QString("MUSIC_COMMAND %1 GET_VOLUME").arg(hostname));
00675                 gCoreContext->dispatch(me);
00676 
00677                 QTime timer;
00678                 timer.start();
00679                 while (timer.elapsed() < FE_SHORT_TO && !gotAnswer)
00680                 {
00681                     qApp->processEvents();
00682                     usleep(10000);
00683                 }
00684 
00685                 if (gotAnswer)
00686                     return answer;
00687 
00688                 return "unknown";
00689             }
00690             else if (is_abbrev("getmeta", nc->getArg(2)))
00691             {
00692                 gotAnswer = false;
00693 
00694                 MythEvent me(QString("MUSIC_COMMAND %1 GET_METADATA").arg(hostname));
00695                 gCoreContext->dispatch(me);
00696 
00697                 QTime timer;
00698                 timer.start();
00699                 while (timer.elapsed() < FE_SHORT_TO && !gotAnswer)
00700                 {
00701                     qApp->processEvents();
00702                     usleep(10000);
00703                 }
00704 
00705                 if (gotAnswer)
00706                     return answer;
00707 
00708                 return "unknown";
00709             }
00710             else
00711                 return QString("ERROR: Invalid 'play music' command");
00712         }
00713         else if (nc->getArgCount() > 3)
00714         {
00715             if (is_abbrev("setvolume", nc->getArg(2)))
00716                 message = QString("MUSIC_COMMAND %1 SET_VOLUME %2")
00717                                 .arg(hostname)
00718                                 .arg(nc->getArg(3));
00719             else if (is_abbrev("track", nc->getArg(2)))
00720                 message = QString("MUSIC_COMMAND %1 PLAY_TRACK %2")
00721                                 .arg(hostname)
00722                                 .arg(nc->getArg(3));
00723             else if (is_abbrev("url", nc->getArg(2)))
00724                 message = QString("MUSIC_COMMAND %1 PLAY_URL %2")
00725                                 .arg(hostname)
00726                                 .arg(nc->getArg(3));
00727             else if (is_abbrev("file", nc->getArg(2)))
00728                 message = QString("MUSIC_COMMAND %1 PLAY_FILE '%2'")
00729                                 .arg(hostname)
00730                                 .arg(nc->getFrom(3));
00731             else
00732                 return QString("ERROR: Invalid 'play music' command");
00733         }
00734         else
00735             return QString("ERROR: Invalid 'play music' command");
00736     }
00737     // Everything below here requires us to be in playback mode so check to
00738     // see if we are
00739     else if (GetMythUI()->GetCurrentLocation().toLower() != "playback")
00740     {
00741         return QString("ERROR: You are in %1 mode and this command is only "
00742                        "for playback mode")
00743                        .arg(GetMythUI()->GetCurrentLocation());
00744     }
00745     else if (is_abbrev("chanid", nc->getArg(1), 5))
00746     {
00747         if (nc->getArg(2).contains(QRegExp("^\\d+$")))
00748             message = QString("NETWORK_CONTROL CHANID %1").arg(nc->getArg(2));
00749         else
00750             return QString("ERROR: See 'help %1' for usage information")
00751                            .arg(nc->getArg(0));
00752     }
00753     else if (is_abbrev("channel", nc->getArg(1), 5))
00754     {
00755         if (nc->getArgCount() < 3)
00756             return "ERROR: See 'help play' for usage information";
00757 
00758         if (is_abbrev("up", nc->getArg(2)))
00759             message = "NETWORK_CONTROL CHANNEL UP";
00760         else if (is_abbrev("down", nc->getArg(2)))
00761             message = "NETWORK_CONTROL CHANNEL DOWN";
00762         else if (nc->getArg(2).contains(QRegExp("^[-\\.\\d_#]+$")))
00763             message = QString("NETWORK_CONTROL CHANNEL %1").arg(nc->getArg(2));
00764         else
00765             return QString("ERROR: See 'help %1' for usage information")
00766                            .arg(nc->getArg(0));
00767     }
00768     else if (is_abbrev("seek", nc->getArg(1), 2))
00769     {
00770         if (nc->getArgCount() < 3)
00771             return QString("ERROR: See 'help %1' for usage information")
00772                            .arg(nc->getArg(0));
00773 
00774         if (is_abbrev("beginning", nc->getArg(2)))
00775             message = "NETWORK_CONTROL SEEK BEGINNING";
00776         else if (is_abbrev("forward", nc->getArg(2)))
00777             message = "NETWORK_CONTROL SEEK FORWARD";
00778         else if (is_abbrev("rewind",   nc->getArg(2)) ||
00779                  is_abbrev("backward", nc->getArg(2)))
00780             message = "NETWORK_CONTROL SEEK BACKWARD";
00781         else if (nc->getArg(2).contains(QRegExp("^\\d\\d:\\d\\d:\\d\\d$")))
00782         {
00783             int hours   = nc->getArg(2).mid(0, 2).toInt();
00784             int minutes = nc->getArg(2).mid(3, 2).toInt();
00785             int seconds = nc->getArg(2).mid(6, 2).toInt();
00786             message = QString("NETWORK_CONTROL SEEK POSITION %1")
00787                               .arg((hours * 3600) + (minutes * 60) + seconds);
00788         }
00789         else
00790             return QString("ERROR: See 'help %1' for usage information")
00791                            .arg(nc->getArg(0));
00792     }
00793     else if (is_abbrev("speed", nc->getArg(1), 2))
00794     {
00795         if (nc->getArgCount() < 3)
00796             return QString("ERROR: See 'help %1' for usage information")
00797                            .arg(nc->getArg(0));
00798 
00799         QString token2 = nc->getArg(2).toLower();
00800         if ((token2.contains(QRegExp("^\\-*\\d+x$"))) ||
00801             (token2.contains(QRegExp("^\\-*\\d+\\/\\d+x$"))) ||
00802             (token2.contains(QRegExp("^\\-*\\d*\\.\\d+x$"))))
00803             message = QString("NETWORK_CONTROL SPEED %1").arg(token2);
00804         else if (is_abbrev("normal", token2))
00805             message = QString("NETWORK_CONTROL SPEED 1x");
00806         else if (is_abbrev("pause", token2))
00807             message = QString("NETWORK_CONTROL SPEED 0x");
00808         else
00809             return QString("ERROR: See 'help %1' for usage information")
00810                            .arg(nc->getArg(0));
00811     }
00812     else if (is_abbrev("save", nc->getArg(1), 2))
00813     {
00814         if (is_abbrev("screenshot", nc->getArg(2), 2))
00815             return saveScreenshot(nc);
00816     }
00817     else if (is_abbrev("stop", nc->getArg(1), 2))
00818         message = QString("NETWORK_CONTROL STOP");
00819     else if (is_abbrev("volume", nc->getArg(1), 2))
00820     {
00821         if ((nc->getArgCount() < 3) ||
00822             (!nc->getArg(2).toLower().contains(QRegExp("^\\d+%?$"))))
00823         {
00824             return QString("ERROR: See 'help %1' for usage information")
00825                            .arg(nc->getArg(0));
00826         }
00827 
00828         message = QString("NETWORK_CONTROL VOLUME %1")
00829                           .arg(nc->getArg(2).toLower());
00830     }
00831     else
00832         return QString("ERROR: See 'help %1' for usage information")
00833                        .arg(nc->getArg(0));
00834 
00835     if (!message.isEmpty())
00836     {
00837         MythEvent me(message);
00838         gCoreContext->dispatch(me);
00839     }
00840 
00841     return result;
00842 }
00843 
00844 QString NetworkControl::processQuery(NetworkCommand *nc)
00845 {
00846     QString result = "OK";
00847 
00848     if (nc->getArgCount() < 2)
00849         return QString("ERROR: See 'help %1' for usage information")
00850                        .arg(nc->getArg(0));
00851 
00852     if (is_abbrev("location", nc->getArg(1)))
00853     {
00854         bool fullPath = false;
00855         bool mainStackOnly = true;
00856 
00857         if (nc->getArgCount() > 2)
00858             fullPath = (nc->getArg(2).toLower() == "true" || nc->getArg(2) == "1");
00859         if (nc->getArgCount() > 3)
00860             mainStackOnly = (nc->getArg(3).toLower() == "true" || nc->getArg(3) == "1");
00861 
00862         QString location = GetMythUI()->GetCurrentLocation(fullPath, mainStackOnly);
00863         result = location;
00864 
00865         // if we're playing something, then find out what
00866         if (location == "Playback")
00867         {
00868             result += " ";
00869             gotAnswer = false;
00870             QString message = QString("NETWORK_CONTROL QUERY POSITION");
00871             MythEvent me(message);
00872             gCoreContext->dispatch(me);
00873 
00874             QTime timer;
00875             timer.start();
00876             while (timer.elapsed() < FE_SHORT_TO  && !gotAnswer)
00877                 usleep(10000);
00878 
00879             if (gotAnswer)
00880                 result += answer;
00881             else
00882                 result = "ERROR: Timed out waiting for reply from player";
00883         }
00884     }
00885     else if (is_abbrev("verbose", nc->getArg(1)))
00886     {
00887         return verboseString;
00888     }
00889     else if (is_abbrev("liveTV", nc->getArg(1)))
00890     {
00891         if(nc->getArgCount() == 3) // has a channel ID
00892             return listSchedule(nc->getArg(2));
00893         else
00894             return listSchedule();
00895     }
00896     else if (is_abbrev("version", nc->getArg(1)))
00897     {
00898         int dbSchema = gCoreContext->GetNumSetting("DBSchemaVer");
00899 
00900         return QString("VERSION: %1/%2 %3 %4 QT/%5 DBSchema/%6")
00901                        .arg(MYTH_SOURCE_VERSION)
00902                        .arg(MYTH_SOURCE_PATH)
00903                        .arg(MYTH_BINARY_VERSION)
00904                        .arg(MYTH_PROTO_VERSION)
00905                        .arg(QT_VERSION_STR)
00906                        .arg(dbSchema);
00907 
00908     }
00909     else if(is_abbrev("time", nc->getArg(1)))
00910         return QDateTime::currentDateTime().toString(Qt::ISODate);
00911     else if (is_abbrev("uptime", nc->getArg(1)))
00912     {
00913         QString str;
00914         time_t  uptime;
00915 
00916         if (getUptime(uptime))
00917             str = QString::number(uptime);
00918         else
00919             str = QString("Could not determine uptime.");
00920         return str;
00921     }
00922     else if (is_abbrev("load", nc->getArg(1)))
00923     {
00924         QString str;
00925         double  loads[3];
00926 
00927         if (getloadavg(loads,3) == -1)
00928             str = QString("getloadavg() failed");
00929         else
00930             str = QString("%1 %2 %3").arg(loads[0]).arg(loads[1]).arg(loads[2]);
00931         return str;
00932     }
00933     else if (is_abbrev("memstats", nc->getArg(1)))
00934     {
00935         QString str;
00936         int     totalMB, freeMB, totalVM, freeVM;
00937 
00938         if (getMemStats(totalMB, freeMB, totalVM, freeVM))
00939             str = QString("%1 %2 %3 %4")
00940                           .arg(totalMB).arg(freeMB).arg(totalVM).arg(freeVM);
00941         else
00942             str = QString("Could not determine memory stats.");
00943         return str;
00944     }
00945     else if (is_abbrev("volume", nc->getArg(1)))
00946     {
00947         QString str = "0%";
00948 
00949         QString location = GetMythUI()->GetCurrentLocation(false, false);
00950 
00951         if (location != "Playback")
00952             return str;
00953 
00954         gotAnswer = false;
00955         QString message = QString("NETWORK_CONTROL QUERY VOLUME");
00956         MythEvent me(message);
00957         gCoreContext->dispatch(me);
00958 
00959         QTime timer;
00960         timer.start();
00961         while (timer.elapsed() < FE_SHORT_TO  && !gotAnswer)
00962             usleep(10000);
00963 
00964         if (gotAnswer)
00965             str = answer;
00966         else
00967             str = "ERROR: Timed out waiting for reply from player";
00968 
00969         return str;
00970     }
00971     else if ((nc->getArgCount() == 4) &&
00972              is_abbrev("recording", nc->getArg(1)) &&
00973              (nc->getArg(2).contains(QRegExp("^\\d+$"))) &&
00974              (nc->getArg(3).contains(QRegExp(
00975                          "^\\d\\d\\d\\d-\\d\\d-\\d\\dT\\d\\d:\\d\\d:\\d\\d$"))))
00976         return listRecordings(nc->getArg(2), nc->getArg(3).toUpper());
00977     else if (is_abbrev("recordings", nc->getArg(1)))
00978         return listRecordings();
00979     else if (is_abbrev("channels", nc->getArg(1)))
00980     {
00981         if (nc->getArgCount() == 2)
00982             return listChannels(0, 0);  // give us all you can
00983         else if (nc->getArgCount() == 4)
00984             return listChannels(nc->getArg(2).toLower().toUInt(),
00985                                 nc->getArg(3).toLower().toUInt());
00986         else
00987             return QString("ERROR: See 'help %1' for usage information "
00988                            "(parameters mismatch)").arg(nc->getArg(0));
00989     }
00990     else
00991         return QString("ERROR: See 'help %1' for usage information")
00992                        .arg(nc->getArg(0));
00993 
00994     return result;
00995 }
00996 
00997 QString NetworkControl::processSet(NetworkCommand *nc)
00998 {
00999     if (nc->getArgCount() == 1)
01000         return QString("ERROR: See 'help %1' for usage information")
01001                        .arg(nc->getArg(0));
01002 
01003     if (nc->getArg(1) == "verbose")
01004     {
01005         if (nc->getArgCount() > 3)
01006             return QString("ERROR: Separate filters with commas with no "
01007                            "space: playback,audio\r\n See 'help %1' for usage "
01008                            "information").arg(nc->getArg(0));
01009 
01010         QString oldVerboseString = verboseString;
01011         QString result = "OK";
01012 
01013         int pva_result = verboseArgParse(nc->getArg(2));
01014 
01015         if (pva_result != 0 /*GENERIC_EXIT_OK */)
01016             result = "Failed";
01017 
01018         result += "\r\n";
01019         result += " Previous filter: " + oldVerboseString + "\r\n";
01020         result += "      New Filter: " + verboseString + "\r\n";
01021 
01022         LOG(VB_GENERAL, LOG_NOTICE,
01023             QString("Verbose mask changed, new level is: %1")
01024                 .arg(verboseString));
01025 
01026         return result;
01027     }
01028 
01029     return QString("ERROR: See 'help %1' for usage information")
01030                    .arg(nc->getArg(0));
01031 }
01032 
01033 QString NetworkControl::processHelp(NetworkCommand *nc)
01034 {
01035     QString command, helpText;
01036 
01037     if (nc->getArgCount() >= 1)
01038     {
01039         if (is_abbrev("help", nc->getArg(0)))
01040         {
01041             if (nc->getArgCount() >= 2)
01042                 command = nc->getArg(1);
01043             else
01044                 command.clear();
01045         }
01046         else
01047         {
01048             command = nc->getArg(0);
01049         }
01050     }
01051 
01052     if (is_abbrev("jump", command))
01053     {
01054         QMap<QString, QString>::Iterator it;
01055         helpText +=
01056             "Usage: jump JUMPPOINT\r\n"
01057             "\r\n"
01058             "Where JUMPPOINT is one of the following:\r\n";
01059 
01060         for (it = jumpMap.begin(); it != jumpMap.end(); ++it)
01061         {
01062             helpText += it.key().leftJustified(20, ' ', true) + " - " +
01063                         *it + "\r\n";
01064         }
01065     }
01066     else if (is_abbrev("key", command))
01067     {
01068         helpText +=
01069             "key LETTER           - Send the letter key specified\r\n"
01070             "key NUMBER           - Send the number key specified\r\n"
01071             "key CODE             - Send one of the following key codes\r\n"
01072             "\r\n";
01073 
01074         QMap<QString, int>::Iterator it;
01075         bool first = true;
01076         for (it = keyMap.begin(); it != keyMap.end(); ++it)
01077         {
01078             if (first)
01079                 first = false;
01080             else
01081                 helpText += ", ";
01082 
01083             helpText += it.key();
01084         }
01085         helpText += "\r\n";
01086     }
01087     else if (is_abbrev("play", command))
01088     {
01089         helpText +=
01090             "play volume NUMBER%    - Change volume to given percentage value\r\n"
01091             "play channel up        - Change channel Up\r\n"
01092             "play channel down      - Change channel Down\r\n"
01093             "play channel NUMBER    - Change to a specific channel number\r\n"
01094             "play chanid NUMBER     - Change to a specific channel id (chanid)\r\n"
01095             "play file FILENAME     - Play FILENAME (FILENAME may be a file or a myth:// URL)\r\n"
01096             "play program CHANID yyyy-MM-ddThh:mm:ss\r\n"
01097             "                       - Play program with chanid & starttime\r\n"
01098             "play program CHANID yyyy-MM-ddThh:mm:ss resume\r\n"
01099             "                       - Resume program with chanid & starttime\r\n"
01100             "play save preview\r\n"
01101             "                       - Save preview image from current position\r\n"
01102             "play save preview FILENAME\r\n"
01103             "                       - Save preview image to FILENAME\r\n"
01104             "play save preview FILENAME WxH\r\n"
01105             "                       - Save preview image of size WxH\r\n"
01106             "play seek beginning    - Seek to the beginning of the recording\r\n"
01107             "play seek forward      - Skip forward in the video\r\n"
01108             "play seek backward     - Skip backwards in the video\r\n"
01109             "play seek HH:MM:SS     - Seek to a specific position\r\n"
01110             "play speed pause       - Pause playback\r\n"
01111             "play speed normal      - Playback at normal speed\r\n"
01112             "play speed 1x          - Playback at normal speed\r\n"
01113             "play speed SPEEDx      - Playback where SPEED must be a decimal\r\n"
01114             "play speed 1/8x        - Playback at 1/8x speed\r\n"
01115             "play speed 1/4x        - Playback at 1/4x speed\r\n"
01116             "play speed 1/3x        - Playback at 1/3x speed\r\n"
01117             "play speed 1/2x        - Playback at 1/2x speed\r\n"
01118             "play stop              - Stop playback\r\n"
01119             "play music play        - Resume playback (MythMusic)\r\n"
01120             "play music pause       - Pause playback (MythMusic)\r\n"
01121             "play music stop        - Stop Playback (MythMusic)\r\n"
01122             "play music setvolume N - Set volume to number (MythMusic)\r\n"
01123             "play music getvolume   - Get current volume (MythMusic)\r\n"
01124             "play music getmeta     - Get metadata for current track (MythMusic)\r\n"
01125             "play music file NAME   - Play specified file (MythMusic)\r\n"
01126             "play music track N     - Switch to specified track (MythMusic)\r\n"
01127             "play music url URL     - Play specified URL (MythMusic)\r\n";
01128     }
01129     else if (is_abbrev("query", command))
01130     {
01131         helpText +=
01132             "query location        - Query current screen or location\r\n"
01133             "query volume          - Query the current playback volume\r\n"
01134             "query recordings      - List currently available recordings\r\n"
01135             "query recording CHANID STARTTIME\r\n"
01136             "                      - List info about the specified program\r\n"
01137             "query liveTV          - List current TV schedule\r\n"
01138             "query liveTV CHANID   - Query current program for specified channel\r\n"
01139             "query load            - List 1/5/15 load averages\r\n"
01140             "query memstats        - List free and total, physical and swap memory\r\n"
01141             "query time            - Query current time on frontend\r\n"
01142             "query uptime          - Query machine uptime\r\n"
01143             "query verbose         - Get current VERBOSE mask\r\n"
01144             "query version         - Query Frontend version details\r\n"
01145             "query channels        - Query available channels\r\n"
01146             "query channels START LIMIT - Query available channels from START and limit results to LIMIT lines\r\n";
01147     }
01148     else if (is_abbrev("set", command))
01149     {
01150         helpText +=
01151             "set verbose debug-mask - "
01152             "Change the VERBOSE mask to 'debug-mask'\r\n"
01153             "                         (i.e. 'set verbose playback,audio')\r\n"
01154             "                         use 'set verbose default' to revert\r\n"
01155             "                         back to the default level of\r\n";
01156     }
01157     else if (is_abbrev("screenshot", command))
01158     {
01159         helpText +=
01160             "screenshot               - Takes a screenshot and saves it as screenshot.png\r\n"
01161             "screenshot WxH           - Saves the screenshot as a WxH size image\r\n";
01162     }
01163     else if (command == "exit")
01164     {
01165         helpText +=
01166             "exit                  - Terminates session\r\n\r\n";
01167     }
01168     else if ((is_abbrev("message", command)))
01169     {
01170         helpText +=
01171             "message               - Displays a simple text message popup\r\n";
01172     }
01173 
01174     if (!helpText.isEmpty())
01175         return helpText;
01176 
01177     if (!command.isEmpty())
01178             helpText += QString("Unknown command '%1'\r\n\r\n").arg(command);
01179 
01180     helpText +=
01181         "Valid Commands:\r\n"
01182         "---------------\r\n"
01183         "jump               - Jump to a specified location in Myth\r\n"
01184         "key                - Send a keypress to the program\r\n"
01185         "play               - Playback related commands\r\n"
01186         "query              - Queries\r\n"
01187         "set                - Changes\r\n"
01188         "screenshot         - Capture screenshot\r\n"
01189         "message            - Display a simple text message\r\n"
01190         "exit               - Exit Network Control\r\n"
01191         "\r\n"
01192         "Type 'help COMMANDNAME' for help on any specific command.\r\n";
01193 
01194     return helpText;
01195 }
01196 
01197 QString NetworkControl::processMessage(NetworkCommand *nc)
01198 {
01199     if (nc->getArgCount() < 2)
01200         return QString("ERROR: See 'help %1' for usage information")
01201                        .arg(nc->getArg(0));
01202 
01203     QString message = nc->getCommand().remove(0, 7).trimmed();
01204     MythMainWindow *window = GetMythMainWindow();
01205     MythEvent* me = new MythEvent(MythEvent::MythUserMessage, message);
01206     qApp->postEvent(window, me);
01207     return QString("OK");
01208 }
01209 
01210 void NetworkControl::notifyDataAvailable(void)
01211 {
01212     QCoreApplication::postEvent(
01213         this, new QEvent(kNetworkControlDataReadyEvent));
01214 }
01215 
01216 void NetworkControl::sendReplyToClient(NetworkControlClient *ncc,
01217                                        QString &reply)
01218 {
01219     if (!clients.contains(ncc))
01220         // NetworkControl instance is unaware of control client
01221         // assume connection to client has been terminated and bail
01222         return;
01223 
01224     QRegExp crlfRegEx("\r\n$");
01225     QRegExp crlfcrlfRegEx("\r\n.*\r\n");
01226 
01227     QTcpSocket  *client = ncc->getSocket();
01228     QTextStream *clientStream = ncc->getTextStream();
01229 
01230     if (client && clientStream && client->state() == QTcpSocket::ConnectedState)
01231     {
01232         *clientStream << reply;
01233 
01234         if ((!reply.contains(crlfRegEx)) ||
01235             ( reply.contains(crlfcrlfRegEx)))
01236             *clientStream << "\r\n" << prompt;
01237 
01238         clientStream->flush();
01239         client->flush();
01240     }
01241 }
01242 
01243 void NetworkControl::customEvent(QEvent *e)
01244 {
01245     if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
01246     {
01247         MythEvent *me = (MythEvent *)e;
01248         QString message = me->Message();
01249 
01250         if (message.left(13) == "MUSIC_CONTROL")
01251         {
01252             QStringList tokens = message.simplified().split(" ");
01253             if ((tokens.size() >= 4) &&
01254                 (tokens[1] == "ANSWER") &&
01255                 (tokens[2] == gCoreContext->GetHostName()))
01256             {
01257                 answer = tokens[3];
01258                 for (int i = 4; i < tokens.size(); i++)
01259                     answer += QString(" ") + tokens[i];
01260                 gotAnswer = true;
01261             } 
01262 
01263         }
01264         else if (message.left(15) == "NETWORK_CONTROL")
01265         {
01266             QStringList tokens = message.simplified().split(" ");
01267             if ((tokens.size() >= 3) &&
01268                 (tokens[1] == "ANSWER"))
01269             {
01270                 answer = tokens[2];
01271                 for (int i = 3; i < tokens.size(); i++)
01272                     answer += QString(" ") + tokens[i];
01273                 gotAnswer = true;
01274             }
01275             else if ((tokens.size() >= 4) &&
01276                      (tokens[1] == "RESPONSE"))
01277             {
01278 //                int clientID = tokens[2].toInt();
01279                 answer = tokens[3];
01280                 for (int i = 4; i < tokens.size(); i++)
01281                     answer += QString(" ") + tokens[i];
01282                 gotAnswer = true;
01283             }
01284         }
01285     }
01286     else if (e->type() == kNetworkControlDataReadyEvent)
01287     {
01288         NetworkCommand *nc;
01289         QString reply;
01290 
01291         QMutexLocker locker(&clientLock);
01292         QMutexLocker nrLocker(&nrLock);
01293 
01294         while (!networkControlReplies.isEmpty())
01295         {
01296             nc = networkControlReplies.front();
01297             networkControlReplies.pop_front();
01298 
01299             reply = nc->getCommand();
01300 
01301             NetworkControlClient * ncc = nc->getClient();
01302             if (ncc)
01303             {
01304                 sendReplyToClient(ncc, reply);
01305             }
01306             else //send to all clients
01307             {
01308                 QList<NetworkControlClient *>::const_iterator it;
01309                 for (it = clients.begin(); it != clients.end(); ++it)
01310                 {
01311                     NetworkControlClient *ncc = *it;
01312                     if (ncc)
01313                         sendReplyToClient(ncc, reply);
01314                 }
01315             }
01316             delete nc;
01317         }
01318     }
01319     else if (e->type() == NetworkControlCloseEvent::kEventType)
01320     {
01321         NetworkControlCloseEvent *ncce = static_cast<NetworkControlCloseEvent*>(e);
01322         NetworkControlClient     *ncc  = ncce->getClient();
01323 
01324         deleteClient(ncc);
01325     }
01326 }
01327 
01328 QString NetworkControl::listSchedule(const QString& chanID) const
01329 {
01330     QString result("");
01331     MSqlQuery query(MSqlQuery::InitCon());
01332     bool appendCRLF = true;
01333     QString queryStr("SELECT chanid, starttime, endtime, title, subtitle "
01334                          "FROM program "
01335                          "WHERE starttime < :START AND endtime > :END ");
01336 
01337     if (!chanID.isEmpty())
01338     {
01339         queryStr += " AND chanid = :CHANID";
01340         appendCRLF = false;
01341     }
01342 
01343     queryStr += " ORDER BY starttime, endtime, chanid";
01344 
01345     query.prepare(queryStr);
01346     query.bindValue(":START", QDateTime::currentDateTime());
01347     query.bindValue(":END", QDateTime::currentDateTime());
01348     if (!chanID.isEmpty())
01349     {
01350         query.bindValue(":CHANID", chanID);
01351     }
01352 
01353     if (query.exec())
01354     {
01355         while (query.next())
01356         {
01357             QString title = query.value(3).toString();
01358             QString subtitle = query.value(4).toString();
01359 
01360             if (!subtitle.isEmpty())
01361                 title += QString(" -\"%1\"").arg(subtitle);
01362             QByteArray atitle = title.toLocal8Bit();
01363 
01364             result +=
01365                 QString("%1 %2 %3 %4")
01366                         .arg(QString::number(query.value(0).toInt())
01367                              .rightJustified(5, ' '))
01368                         .arg(query.value(1).toDateTime().toString(Qt::ISODate))
01369                         .arg(query.value(2).toDateTime().toString(Qt::ISODate))
01370                         .arg(atitle.constData());
01371 
01372             if (appendCRLF)
01373                 result += "\r\n";
01374         }
01375     }
01376     else
01377     {
01378        result = "ERROR: Unable to retrieve current schedule list.";
01379     }
01380     return result;
01381 }
01382 
01383 QString NetworkControl::listRecordings(QString chanid, QString starttime)
01384 {
01385     QString result;
01386     MSqlQuery query(MSqlQuery::InitCon());
01387     QString queryStr;
01388     bool appendCRLF = true;
01389 
01390     queryStr = "SELECT chanid, starttime, title, subtitle "
01391                "FROM recorded WHERE deletepending = 0 ";
01392 
01393     if ((!chanid.isEmpty()) && (!starttime.isEmpty()))
01394     {
01395         queryStr += "AND chanid = " + chanid + " "
01396                     "AND starttime = '" + starttime + "' ";
01397         appendCRLF = false;
01398     }
01399 
01400     queryStr += "ORDER BY starttime, title;";
01401 
01402     query.prepare(queryStr);
01403     if (query.exec())
01404     {
01405         QString episode, title, subtitle;
01406         while (query.next())
01407         {
01408             title = query.value(2).toString();
01409             subtitle = query.value(3).toString();
01410 
01411             if (!subtitle.isEmpty())
01412                 episode = QString("%1 -\"%2\"")
01413                                   .arg(title)
01414                                   .arg(subtitle);
01415             else
01416                 episode = title;
01417 
01418             result +=
01419                 QString("%1 %2 %3").arg(query.value(0).toInt())
01420                         .arg(query.value(1).toDateTime().toString(Qt::ISODate))
01421                         .arg(episode);
01422 
01423             if (appendCRLF)
01424                 result += "\r\n";
01425         }
01426     }
01427     else
01428         result = "ERROR: Unable to retrieve recordings list.";
01429 
01430     return result;
01431 }
01432 
01433 QString NetworkControl::listChannels(const uint start, const uint limit) const
01434 {
01435     QString result;
01436     MSqlQuery query(MSqlQuery::InitCon());
01437     QString queryStr;
01438     uint cnt;
01439     uint maxcnt;
01440     uint sqlStart = start;
01441 
01442     // sql starts at zero, we want to start at 1
01443     if (sqlStart > 0)
01444         sqlStart--;
01445 
01446     queryStr = "select chanid, callsign, name from channel where visible=1";
01447     queryStr += " ORDER BY callsign";
01448 
01449     if (limit > 0)  // only if a limit is specified, we limit the results
01450     {
01451       QString limitStr = QString(" LIMIT %1,%2").arg(sqlStart).arg(limit);
01452       queryStr += limitStr;
01453     }
01454 
01455     query.prepare(queryStr);
01456     if (!query.exec())
01457     {
01458         result = "ERROR: Unable to retrieve channel list.";
01459         return result;
01460     }
01461 
01462     maxcnt = query.size();
01463     cnt = 0;
01464     if (maxcnt == 0)    // Feedback we have no usefull information
01465     {
01466         result += QString("0:0 0 \"Invalid\" \"Invalid\"");
01467         return result;
01468     }
01469 
01470     while (query.next())
01471     {
01472         // Feedback is as follow:
01473         // <current line count>:<max line count to expect> <channelid> <callsign name> <channel name>\r\n
01474         cnt++;
01475         result += QString("%1:%2 %3 \"%4\" \"%5\"\r\n")
01476                           .arg(cnt).arg(maxcnt).arg(query.value(0).toInt())
01477                           .arg(query.value(1).toString())
01478                           .arg(query.value(2).toString());
01479     }
01480 
01481     return result;
01482 }
01483 
01484 QString NetworkControl::saveScreenshot(NetworkCommand *nc)
01485 {
01486     int width = 0;
01487     int height = 0;
01488 
01489     if (nc->getArgCount() == 2)
01490     {
01491         QStringList size = nc->getArg(1).split('x');
01492         if (size.size() == 2)
01493         {
01494             width  = size[0].toInt();
01495             height = size[1].toInt();
01496         }
01497     }
01498 
01499     MythMainWindow *window = GetMythMainWindow();
01500     QStringList args;
01501     if (width && height)
01502     {
01503         args << QString::number(width);
01504         args << QString::number(height);
01505     }
01506     MythEvent* me = new MythEvent(MythEvent::MythEventMessage,
01507                                   ACTION_SCREENSHOT, args);
01508     qApp->postEvent(window, me);
01509     return "OK";
01510 }
01511 
01512 QString NetworkCommand::getFrom(int arg)
01513 {
01514     QString c = m_command;
01515     for(int i=0 ; i<arg ; i++) {
01516         QString arg = c.simplified().split(" ")[0];
01517         c = c.mid(arg.length()).trimmed();
01518     }
01519     return c;
01520 }
01521 
01522 /* vim: set expandtab tabstop=4 shiftwidth=4: */
01523 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends