Update to support both Qt 4 and Qt 5
[apps/thermostat-demo.git] / remoteaccessmanager.cpp
1 #include "remoteaccessmanager.h"
3 #include <QDateTime>
4 #include <QFile>
5 #include <QHash>
6 #include <QStringList>
7 #include <QTcpServer>
8 #include <QTcpSocket>
10 #include "globalsettings.h"
11 #include "mainwindow.h"
13 /***********************************************************************************************************
14 * RemoteAccessManager
15 *   Class that has instantiates an HTTP server using a QTcpServer and listens for connections.
16 *   Serves a page that allows for remote configuration of the thermostat though ajax calls
17 ************************************************************************************************************/
19 RemoteAccessManager::RemoteAccessManager(QObject *parent) :
20     QObject(parent)
21 {
22     m_globalSettings = GlobalSettings::getInstance();
24     m_listenPort = 8081;
25     m_tcpServer = new QTcpServer;
27 }
29 RemoteAccessManager::~RemoteAccessManager()
30 {
31     stop();
32     if(m_tcpServer) delete m_tcpServer;
33 }
35 //Function: start()
36 //
37 //  starts the tcpserver listening on specified port and make connections for incoming connections
39 void RemoteAccessManager::start()
40 {
41     m_tcpServer->listen(QHostAddress::Any, m_listenPort);
42     m_tcpServer->errorString();
43     connect(m_tcpServer, SIGNAL(newConnection()), this, SLOT(handleIncomingConnection()));
44 }
46 void RemoteAccessManager::stop()
47 {
48     m_tcpServer->close();
49 }
51 //Function: handleIncomingConnection()
52 //
53 //  accepts an incoming connection from the tcpserver and makes a connection for when the socket
54 //  has received data from the client (presumably a web server)
56 void RemoteAccessManager::handleIncomingConnection()
57 {
58     m_clientConnection = m_tcpServer->nextPendingConnection();
59     connect(m_clientConnection, SIGNAL(readyRead()), this, SLOT(processIncomingConnection()));
60     if(!m_clientConnection)
61     {
62         qDebug() << "Error connecting to client";
63         return;
64     }
66 }
68 //Function: processIncomingConnection()
69 //
70 //  once a new socket has received data, parse that data and thenserve back what was requested
72 void RemoteAccessManager::processIncomingConnection()
73 {
74     QList<QByteArray> request;
75     m_clientConnection = static_cast<QTcpSocket *>(sender());
77     while(m_clientConnection->bytesAvailable())
78         request << m_clientConnection->readLine(4096);
80     QList<QByteArray> requestParser = request[0].split(0x20);
81     QByteArray ba;
83     QDateTime currentDateTime(QDateTime::currentDateTime());
84     QString currentDateTimeString = currentDateTime.toString("ddd, dd MMM yyyy hh:mm:ss");
86     if(requestParser[0] == "GET")
87     {
88         if(requestParser[1] == "/")
89         {
90             QFile htmlFile(":/remote/index.html");
92             if(!htmlFile.open(QFile::ReadOnly))
93                 ba = "HTTP/1.1 404 NOT FOUND";
94             else
95             {
96                 ba = "HTTP/1.1 200 OK\r\n";
97                 ba += "Date: "+currentDateTimeString+"\r\n";
98                 ba += "Content-Type: text/html; charset=utf-8\r\n";
99                 ba += "Content-Length: "+QByteArray::number(htmlFile.size())+"\r\n";
100                 ba += "\r\n";
101                 ba += htmlFile.readAll();
102             }
103         }
104         else if(requestParser[1].contains(".jpg") || requestParser[1].contains(".png"))
105         {
107             #if QT_VERSION >= 0x050000
108                 QFile imageFile(":"+QString::fromLatin1(requestParser[1]));
109             #else
110                 QFile imageFile(":"+QString::fromAscii(requestParser[1]));
111             #endif
113             if(!imageFile.open(QFile::ReadOnly))
114                 ba = "HTTP/1.1 404 NOT FOUND";
115             else
116             {
117                 ba = "HTTP/1.1 200 OK\r\n";
118                 ba += "Date: "+currentDateTimeString+"\r\n";
119                 ba += "Content-Type: image/"+requestParser[1]+"\r\n";
120                 ba += "Content-Length: "+QByteArray::number(imageFile.size())+"\r\n";
121                 ba += "\r\n";
122                 ba+=imageFile.readAll();
123             }
124         }
125         else if(requestParser[1].contains(".js"))
126         {
128             #if QT_VERSION >= 0x050000
129                 QFile jsFile(":"+QString::fromLatin1(requestParser[1]));
130             #else
131                 QFile jsFile(":"+QString::fromAscii(requestParser[1]));
132             #endif
134             if(!jsFile.open(QFile::ReadOnly))
135                 ba = "HTTP/1.1 404 NOT FOUND";
136             else
137             {
138                 ba = "HTTP/1.1 200 OK\r\n";
139                 ba += "Date: "+currentDateTimeString+"\r\n";
140                 ba += "Content-Type: text/javascript; charset=utf-8\r\n";
141                 ba += "Content-Length: "+QByteArray::number(jsFile.size())+"\r\n";
142                 ba += "\r\n";
143                 ba+=jsFile.readAll();
144             }
145         }
146         else if(requestParser[1].contains(".css"))
147         {
149             #if QT_VERSION >= 0x050000
150                 QFile cssFile(":"+QString::fromLatin1(requestParser[1]));
151             #else
152                 QFile cssFile(":"+QString::fromAscii(requestParser[1]));
153             #endif
155             if(!cssFile.open(QFile::ReadOnly))
156                 ba = "HTTP/1.1 404 NOT FOUND";
157             else
158             {
159                 ba = "HTTP/1.1 200 OK\r\n";
160                 ba += "Date: "+currentDateTimeString+"\r\n";
161                 ba += "Content-Type: text/css; charset=utf-8\r\n";
162                 ba += "Content-Length: "+QByteArray::number(cssFile.size())+"\r\n";
163                 ba += "\r\n";
164                 ba+=cssFile.readAll();
165             }
166         }
167         else if(requestParser[1].contains("?"))
168         {
169             //create a hash table so we can lookup based on variable name
170             //ie getVar["command"] will give us the value set in the http request
171             QHash<QString, QVariant> getVar;
173             //let's parse the get variables
175             QString getVarsString = requestParser[1].mid(requestParser[1].lastIndexOf("?")+1);
177             foreach(QString keyValuePair, getVarsString.split("&"))
178             {
179                 QStringList splitKeyValue = keyValuePair.split("=");
180                 getVar[splitKeyValue[0]]=splitKeyValue[1];
181             }
183             QHash<QString, QVariant> data, optimizedData;
185             //QMetaObject::invokeMethod(parent(), Qt::BlockingQueuedConnection,  SLOT("remoteChangeReceived"), Q_RETURN_ARG(QHash<QString, QVariant>, data), Q_ARG(QHash<QString, QVariant>, getVar));
186             data = static_cast<MainWindow*>(parent())->processCommand(getVar);
190             optimizedData = data;
192             if(data["weatherCurrent"] == m_oldData["weatherCurrent"])
193                 optimizedData.remove("weatherCurrent");
194             if(data["weatherForecast"] == m_oldData["weatherForecast"])
195                 optimizedData.remove("weatherForecast");
196             if(getVar["command"] == "update")
197                 m_oldData = (data);
199             QByteArray jsonByteArray;
201             if(getVar["command"] == "update_forced")
202                 jsonByteArray=hashToJSONByteArray(data);
203             else
204                 jsonByteArray=hashToJSONByteArray(optimizedData);
206             ba = "HTTP/1.1 200 OK\r\n";
207             ba += "Date: "+currentDateTimeString+"\r\n";
208             ba += "Content-Type: text/html; charset=ISO-8859-1\r\n";
209             ba += "Content-Length: "+QByteArray::number(jsonByteArray.size())+"\r\n";
210             ba += "\r\n";
211             ba += jsonByteArray;
212         }
213         else
214             ba = "HTTP/1.1 404 NOT FOUND";
215     }
217     m_clientConnection->write(ba); //write the response
218     m_clientConnection->disconnectFromHost(); //http specifies completion with a closed connection
221 QByteArray RemoteAccessManager::hashToJSONByteArray(QHash<QString, QVariant> hash)
223     QByteArray jsonByteArray="{";
224     QHashIterator<QString, QVariant> it(hash);
227     while(it.hasNext())
228     {
230         it.next();
231         if(it.value().type() == QVariant::Hash) {
232             #if QT_VERSION >= 0x050000
233                 jsonByteArray+="\""+it.key().toLatin1()+"\""+QByteArray::fromRawData(":",1)+""+hashToJSONByteArray(it.value().toHash())+",";
234             #else
235                 jsonByteArray+="\""+it.key().toAscii()+"\""+QByteArray::fromRawData(":",1)+""+hashToJSONByteArray(it.value().toHash())+",";
236             #endif
237         }
238         else {
239             #if QT_VERSION >= 0x050000
240                 jsonByteArray += "\""+it.key().toLatin1()+"\""+QByteArray::fromRawData(":",1)+"\""+it.value().toByteArray()+"\""+QByteArray::fromRawData(",",1);
241             #else
242                 jsonByteArray += "\""+it.key().toAscii()+"\""+QByteArray::fromRawData(":",1)+"\""+it.value().toByteArray()+"\""+QByteArray::fromRawData(",",1);
243             #endif
244         }
245     }
247     jsonByteArray = jsonByteArray.left(jsonByteArray.size()-1);
249     //terminate the json object
250     jsonByteArray += "}";
252     return jsonByteArray;