Ticket #5831: networkcontrol.diff

File networkcontrol.diff, 17.3 KB (added by Xavier Hervy <xavier.hervy@…>, 12 years ago)
  • main.cpp

     
    13561356    if (gContext->GetNumSetting("NetworkControlEnabled", 0))
    13571357    {
    13581358        int networkPort = gContext->GetNumSetting("NetworkControlPort", 6545);
    1359         networkControl = new NetworkControl(networkPort);
    1360         if (!networkControl->ok())
     1359        networkControl = new NetworkControl();
     1360        if (!networkControl->listen(QHostAddress::Any,networkPort))
    13611361            VERBOSE(VB_IMPORTANT,
    13621362                    QString("NetworkControl failed to bind to port %1.")
    13631363                    .arg(networkPort));
  • networkcontrol.h

     
    33
    44#include <pthread.h>
    55
    6 #include <q3serversocket.h>
    7 #include <q3textstream.h>
    8 #include <q3socket.h>
    9 #include <q3valuelist.h>
     6#include <qtcpserver.h>
     7#include <qtextstream.h>
     8#include <qtcpsocket.h>
     9#include <qlinkedlist.h>
    1010#include <qobject.h>
    1111#include <qmutex.h>
    1212#include <qwaitcondition.h>
     13#include <qevent.h>
    1314
    1415class MainServer;
    1516
    16 class NetworkControl : public Q3ServerSocket
     17const int kNetworkControlCloseEvent     = 35672;
     18
     19class NetworkControlClient : public QObject
    1720{
    1821    Q_OBJECT
    1922  public:
    20     NetworkControl(int port);
     23    NetworkControlClient(QTcpSocket *);
     24    ~NetworkControlClient();
     25    QTcpSocket * getSocket()
     26    {
     27        return m_socket;
     28    };
     29    QTextStream * getTextStream()
     30    {
     31        return m_textStream;
     32    };
     33  signals:
     34        void commandReceived(QString&);
     35  public slots:
     36        void readClient();
     37  private:
     38        QTcpSocket * m_socket;
     39        QTextStream *m_textStream;
     40};
     41
     42class NetworkControlCloseEvent : public QEvent
     43{
     44  public:
     45    NetworkControlCloseEvent(NetworkControlClient * ncc)
     46        :QEvent((QEvent::Type)kNetworkControlCloseEvent)
     47        ,m_networkControlClient(ncc){};
     48    NetworkControlClient * getClient()
     49    {
     50        return m_networkControlClient;
     51    };
     52    private:
     53        NetworkControlClient * m_networkControlClient;
     54   
     55};
     56
     57
     58class NetworkCommand : public QObject
     59{
     60  public:
     61    NetworkCommand(NetworkControlClient * cli, QString c)
     62    {
     63        m_command = c;
     64        m_client = cli;
     65    }
     66   
     67    NetworkCommand &operator=(NetworkCommand const &nc)
     68    {
     69        m_command = nc.m_command;
     70        m_client = nc.m_client;
     71        return *this;
     72    }
     73    QString getCommand()
     74    {
     75        return m_command;
     76    }
     77    NetworkControlClient * getClient()
     78    {
     79        return m_client;
     80    }
     81  private:
     82    QString m_command;
     83    NetworkControlClient * m_client;
     84};
     85
     86class NetworkControl : public QTcpServer
     87{
     88    Q_OBJECT
     89  public:
     90    NetworkControl();
    2191    ~NetworkControl();
     92    bool listen ( const QHostAddress & address = QHostAddress::Any, quint16 port = 0 );
    2293
    23     void newConnection(int socket);
    24 
    2594  private slots:
    26     void readClient();
    27     void discardClient();
     95    void newConnection();
     96    void receiveCommand(QString&);
    2897
    2998  protected:
    30     static void *SocketThread(void *param);
    31     void RunSocketThread(void);
    3299    static void *CommandThread(void *param);
    33100    void RunCommandThread(void);
    34101
     
    47114    QString listSchedule(const QString& chanID = "") const;
    48115    QString saveScreenshot(QStringList tokens);
    49116
    50     void processNetworkControlCommand(QString command);
     117    void processNetworkControlCommand(NetworkCommand *nc);
    51118
    52119
    53120    QString prompt;
     
    57124    QMap <QString, int> keyMap;
    58125
    59126    QMutex clientLock;
    60     Q3Socket *client;
    61     Q3TextStream *cs;
     127    QList<NetworkControlClient*> clients;
    62128
    63     Q3ValueList<QString> networkControlCommands;
     129    QLinkedList<NetworkCommand*> networkControlCommands;
    64130    QMutex ncLock;
    65131    QWaitCondition ncCond;
    66132
    67     Q3ValueList<QString> networkControlReplies;
     133    QLinkedList<NetworkCommand*> networkControlReplies;
    68134    QMutex nrLock;
    69135
    70136    pthread_t command_thread;
    71137    bool stopCommandThread;
     138
    72139};
    73140
    74141#endif
  • networkcontrol.cpp

     
    55#include <QApplication>
    66#include <QRegExp>
    77#include <QStringList>
    8 #include <q3textstream.h>
     8#include <qtextstream.h>
    99#include <QDir>
    1010#include <QKeyEvent>
    1111#include <QEvent>
     
    1313
    1414#include "mythcontext.h"
    1515#include "mythdialogs.h"
     16#include "mythversion.h"
    1617#include "networkcontrol.h"
    1718#include "programinfo.h"
    1819#include "remoteutil.h"
     
    2627#define LOC_ERR QString("NetworkControl Error: ")
    2728
    2829const int kNetworkControlDataReadyEvent = 35671;
    29 const int kNetworkControlCloseEvent     = 35672;
    3030
     31
    3132/** Is @p test an abbreviation of @p command ?
    3233 * The @p test substring must be at least @p minchars long.
    3334 * @param command the full command name
     
    3536 * @param minchars the minimum length of test in order to declare a match
    3637 * @return true if @p test is the initial substring of @p command
    3738 */
    38 static bool is_abbrev(QString const& command, QString const& test, int minchars = 1)
     39static bool is_abbrev(QString const& command,
     40                      QString const& test, int minchars = 1)
    3941{
    4042    if (test.length() < minchars)
    4143        return command.lower() == test.lower();
     
    4345        return test.lower() == command.left(test.length()).lower();
    4446}
    4547
    46 NetworkControl::NetworkControl(int port)
    47           : Q3ServerSocket(port, 1),
     48NetworkControl::NetworkControl()
     49          : QTcpServer(),
    4850            prompt("# "),
    49             gotAnswer(false), answer(""),
    50             client(NULL), cs(NULL)
     51            gotAnswer(false), answer("")/*,
     52            client(NULL), cs(NULL)*/
    5153{
    52     VERBOSE(VB_IMPORTANT, LOC +
    53             QString("Listening for remote connections on port %1").arg(port));
    54 
    5554    // Eventually this map should be in the jumppoints table
    5655    jumpMap["channelpriorities"]     = "Channel Recording Priorities";
    5756    jumpMap["livetv"]                = "Live TV";
     
    8584    jumpMap["zoneminderconsole"]     = "ZoneMinder Console";
    8685    jumpMap["zoneminderliveview"]    = "ZoneMinder Live View";
    8786    jumpMap["zoneminderevents"]      = "ZoneMinder Events";
    88     jumpMap["snapshot"]              = "ScreenShot"; 
     87    jumpMap["snapshot"]              = "ScreenShot";
    8988
    9089    // These jump point names match the (lowercased) locations from gContext
    9190    jumpMap["channelrecpriority"]    = "Channel Recording Priorities";
     
    194193    pthread_create(&command_thread, NULL, CommandThread, this);
    195194
    196195    gContext->addListener(this);
     196   
     197    connect(this, SIGNAL(newConnection()), this, SLOT(newConnection()));
    197198}
    198199
    199200NetworkControl::~NetworkControl(void)
    200201{
    201202    nrLock.lock();
    202     networkControlReplies.push_back(
    203         "mythfrontend shutting down, connection closing...");
     203    networkControlReplies.push_back(new NetworkCommand(NULL,
     204        "mythfrontend shutting down, connection closing..."));
    204205    nrLock.unlock();
    205206
    206207    notifyDataAvailable();
     
    212213    pthread_join(command_thread, NULL);
    213214}
    214215
     216bool NetworkControl::listen(const QHostAddress & address, quint16 port)
     217{
     218    if (QTcpServer::listen(address,port))
     219    {
     220        VERBOSE(VB_IMPORTANT, LOC +
     221            QString("Listening for remote connections on port %1").arg(port));
     222        return true;
     223    }
     224    return false;
     225}
     226
    215227void *NetworkControl::CommandThread(void *param)
    216228{
    217229    NetworkControl *networkControl = (NetworkControl *)param;
     
    222234
    223235void NetworkControl::RunCommandThread(void)
    224236{
    225     QString command;
     237    NetworkCommand *nc;
    226238
    227239    while (!stopCommandThread)
    228240    {
     
    235247                return;
    236248            }
    237249        }
    238         command = networkControlCommands.front();
     250        nc = networkControlCommands.front();
    239251        networkControlCommands.pop_front();
    240252        ncLock.unlock();
    241253
    242         processNetworkControlCommand(command);
     254        processNetworkControlCommand(nc);
    243255    }
    244256}
    245257
    246 void NetworkControl::processNetworkControlCommand(QString command)
     258void NetworkControl::processNetworkControlCommand(NetworkCommand *nc)
    247259{
    248260    QMutexLocker locker(&clientLock);
    249261    QString result = "";
    250     QStringList tokens = QStringList::split(" ", command);
     262    QStringList tokens = QStringList::split(" ", nc->getCommand());
    251263
    252264    if (is_abbrev("jump", tokens[0]))
    253265        result = processJump(tokens);
     
    260272    else if (is_abbrev("help", tokens[0]))
    261273        result = processHelp(tokens);
    262274    else if ((tokens[0].lower() == "exit") || (tokens[0].lower() == "quit"))
    263         QApplication::postEvent(this,
    264                                 new QEvent((QEvent::Type)kNetworkControlCloseEvent));
     275        QApplication::postEvent(this, 
     276                                new NetworkControlCloseEvent (nc->getClient()));
    265277    else if (! tokens[0].isEmpty())
    266278        result = QString("INVALID command '%1', try 'help' for more info")
    267279                         .arg(tokens[0]);
    268280
    269281    if (result != "")
    270282    {
    271         nrLock.lock();
    272         networkControlReplies.push_back(result);
     283        nrLock.lock();       
     284        networkControlReplies.push_back(new NetworkCommand(nc->getClient(),result));
    273285        nrLock.unlock();
    274286
    275287        notifyDataAvailable();
    276288    }
    277289}
    278290
    279 void NetworkControl::newConnection(int socket)
     291void NetworkControl::newConnection()
    280292{
    281293    QString welcomeStr = "";
    282     bool closedOldConn = false;
    283     Q3Socket *s = new Q3Socket(this);
    284     connect(s, SIGNAL(readyRead()), this, SLOT(readClient()));
    285     connect(s, SIGNAL(delayedCloseFinished()), this, SLOT(discardClient()));
    286     connect(s, SIGNAL(connectionClosed()), this, SLOT(discardClient()));
    287     s->setSocket(socket);
     294    QTcpSocket *s = this->nextPendingConnection();
     295    NetworkControlClient * ncc = new NetworkControlClient(s);
     296    connect(ncc, SIGNAL(commandReceived(QString&)), this, SLOT(receiveCommand(QString&)));
     297    connect(s, SIGNAL(disconnected()), s, SLOT(deleteLater()));
    288298
    289299    VERBOSE(VB_IMPORTANT, LOC +
    290             QString("New connection established. (%1)").arg(socket));
     300            QString("New connection established."));
    291301
    292     Q3TextStream *os = new Q3TextStream(s);
    293     os->setEncoding(Q3TextStream::UnicodeUTF8);
    294 
    295302    QMutexLocker locker(&clientLock);
    296     if (client)
    297     {
    298         closedOldConn = true;
    299         client->close();
    300         delete client;
    301         delete cs;
    302     }
    303303
    304     client = s;
    305     cs = os;
    306 
    307     ncLock.lock();
    308     networkControlCommands.clear();
    309     ncLock.unlock();
    310 
    311     nrLock.lock();
    312     networkControlReplies.clear();
    313     nrLock.unlock();
    314 
    315304    welcomeStr = "MythFrontend Network Control\r\n";
    316     if (closedOldConn)
    317     {
    318         welcomeStr +=
    319             "WARNING: mythfrontend was already under network control.\r\n";
    320         welcomeStr +=
    321             "         Previous session is being disconnected.\r\n";
    322     }
    323305
    324306    welcomeStr += "Type 'help' for usage information\r\n"
    325307                  "---------------------------------";
    326308    nrLock.lock();
    327     networkControlReplies.push_back(welcomeStr);
     309    networkControlReplies.push_back(new NetworkCommand(ncc,welcomeStr));
    328310    nrLock.unlock();
    329311
    330312    notifyDataAvailable();
    331313}
    332314
    333 void NetworkControl::readClient(void)
     315NetworkControlClient::NetworkControlClient(QTcpSocket *s)
    334316{
    335     Q3Socket *socket = (Q3Socket *)sender();
     317    m_socket = s;
     318    m_textStream = new QTextStream(s);
     319    m_textStream->setEncoding(QTextStream::UnicodeUTF8);
     320    connect(m_socket, SIGNAL(readyRead()), this, SLOT(readClient()));
     321}
     322
     323NetworkControlClient::~NetworkControlClient()
     324{
     325    m_socket->close();
     326    delete m_socket;
     327    delete m_textStream;
     328}
     329
     330
     331void NetworkControlClient::readClient(void)
     332{
     333        VERBOSE(VB_IMPORTANT, LOC +
     334            QString("NetworkControlClient::readClient"));
     335    QTcpSocket *socket = (QTcpSocket *)sender();
    336336    if (!socket)
    337337        return;
    338338
     
    349349            continue;
    350350
    351351        tokens = QStringList::split(" ", lineIn);
    352 
    353         ncLock.lock();
    354         networkControlCommands.push_back(lineIn);
    355         ncCond.wakeOne();
    356         ncLock.unlock();
     352       
     353        VERBOSE(VB_IMPORTANT, LOC +
     354            QString("emit commandReceived(%1)").arg(lineIn));
     355        emit commandReceived(lineIn);
    357356    }
    358357}
    359358
    360 void NetworkControl::discardClient(void)
     359void NetworkControl::receiveCommand(QString &command)
    361360{
    362     Q3Socket *socket = (Q3Socket *)sender();
    363     if (!socket)
     361    VERBOSE(VB_IMPORTANT, LOC +
     362            QString("NetworkControl::receiveCommand(%1)").arg(command));
     363    NetworkControlClient *ncc = (NetworkControlClient *)sender();
     364    if (!ncc)
    364365        return;
    365366
    366     QMutexLocker locker(&clientLock);
    367     if (client == socket)
    368     {
    369         delete cs;
    370         delete client;
    371         client = NULL;
    372         cs = NULL;
    373     }
    374     else
    375         delete socket;
     367    ncLock.lock();
     368    networkControlCommands.push_back(new NetworkCommand(ncc,command));
     369    ncCond.wakeOne();
     370    ncLock.unlock();
    376371}
    377372
     373
    378374QString NetworkControl::processJump(QStringList tokens)
    379375{
    380376    QString result = "OK";
     
    744740            command = tokens[0];
    745741        }
    746742    }
    747        
     743
    748744    if (is_abbrev("jump", command))
    749745    {
    750746        QMap<QString, QString>::Iterator it;
     
    851847
    852848void NetworkControl::notifyDataAvailable(void)
    853849{
    854     QApplication::postEvent(this,
    855                             new QEvent((QEvent::Type)kNetworkControlDataReadyEvent));
     850    QApplication::postEvent(this, new QEvent(
     851        (QEvent::Type)kNetworkControlDataReadyEvent));
    856852}
    857853
    858854void NetworkControl::customEvent(QEvent *e)
    859 {       
     855{
    860856    if ((MythEvent::Type)(e->type()) == MythEvent::MythEventMessage)
    861     {   
     857    {
    862858        MythEvent *me = (MythEvent *)e;
    863859        QString message = me->Message();
    864860
     
    881877            for (int i = 3; i < tokens.size(); i++)
    882878                response += QString(" ") + tokens[i];
    883879            nrLock.lock();
    884             networkControlReplies.push_back(response);
     880            //networkControlReplies.push_back(response);
     881            //FIXME send to which client ?
     882            networkControlReplies.push_back(new NetworkCommand(NULL,response));
    885883            nrLock.unlock();
    886884
    887885            notifyDataAvailable();
     
    889887    }
    890888    else if (e->type() == kNetworkControlDataReadyEvent)
    891889    {
     890        NetworkCommand *nc;
    892891        QString reply;
    893892        int replies;
    894893        QRegExp crlfRegEx("\r\n$");
     
    896895
    897896        nrLock.lock();
    898897        replies = networkControlReplies.size();
    899         while (client && cs && replies > 0 &&
    900                client->state() == Q3Socket::Connected)
     898        while (replies > 0 )
    901899        {
    902             reply = networkControlReplies.front();
     900            nc = networkControlReplies.front();
    903901            networkControlReplies.pop_front();
    904             *cs << reply;
    905             if (!reply.contains(crlfRegEx) || reply.contains(crlfcrlfRegEx))
    906                 *cs << "\r\n" << prompt;
    907             client->flush();
    908 
     902            NetworkControlClient * ncc = nc->getClient();
     903            if (ncc)
     904            {
     905                QTcpSocket *client = ncc->getSocket();
     906                QTextStream *cs = ncc->getTextStream();
     907                reply = nc->getCommand();
     908                if (client && cs && client->state() == QTcpSocket::Connected)
     909                {
     910                    *cs << reply;
     911                    if (!reply.contains(crlfRegEx) || reply.contains(crlfcrlfRegEx))
     912                        *cs << "\r\n" << prompt;
     913                    cs->flush();
     914                    client->flush();
     915                }
     916            }
     917            else //send to all clients
     918            {
     919                reply = nc->getCommand();
     920                if (!reply.contains(crlfRegEx) || reply.contains(crlfcrlfRegEx))
     921                        reply = QString("%1\r\n%2").arg(reply).arg(prompt);
     922                QList<NetworkControlClient *>::const_iterator it;
     923                for (it = clients.begin(); it != clients.end(); ++it)
     924                {
     925                    NetworkControlClient *ncc = *it;
     926                    if (ncc)
     927                    {
     928                        QTcpSocket *client = ncc->getSocket();
     929                        QTextStream *cs = ncc->getTextStream();
     930                        if (client && cs && client->state() == QTcpSocket::Connected)
     931                        {
     932                            *cs << reply;
     933                            if (!reply.contains(crlfRegEx) || reply.contains(crlfcrlfRegEx))
     934                                *cs << "\r\n" << prompt;
     935                            cs->flush();
     936                            client->flush();
     937                        }
     938                    }
     939                }
     940            }
     941            delete nc;
    909942            replies = networkControlReplies.size();
    910943        }
    911944        nrLock.unlock();
    912945    }
    913946    else if (e->type() == kNetworkControlCloseEvent)
    914947    {
    915         if (client && client->state() == Q3Socket::Connected)
     948        NetworkControlCloseEvent *ncce = (NetworkControlCloseEvent*)e;
     949        NetworkControlClient *client = ncce->getClient();
     950        if (client)
    916951        {
    917952            clientLock.lock();
    918             client->close();
     953            int index = clients.indexOf(client);
     954            clients.removeAt(index);
    919955            delete client;
    920             delete cs;
    921             client = NULL;
    922             cs = NULL;
    923956            clientLock.unlock();
    924957        }
    925958    }