5eebdef637b53b289790c62b5112df69852418e6
[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         {
106             QFile imageFile(":"+QString::fromAscii(requestParser[1]));
108             if(!imageFile.open(QFile::ReadOnly))
109                 ba = "HTTP/1.1 404 NOT FOUND";
110             else
111             {
112                 ba = "HTTP/1.1 200 OK\r\n";
113                 ba += "Date: "+currentDateTimeString+"\r\n";
114                 ba += "Content-Type: image/"+requestParser[1]+"\r\n";
115                 ba += "Content-Length: "+QByteArray::number(imageFile.size())+"\r\n";
116                 ba += "\r\n";
117                 ba+=imageFile.readAll();
118             }
119         }
120         else if(requestParser[1].contains(".js"))
121         {
122             QFile jsFile(":"+QString::fromAscii(requestParser[1]));
124             if(!jsFile.open(QFile::ReadOnly))
125                 ba = "HTTP/1.1 404 NOT FOUND";
126             else
127             {
128                 ba = "HTTP/1.1 200 OK\r\n";
129                 ba += "Date: "+currentDateTimeString+"\r\n";
130                 ba += "Content-Type: text/javascript; charset=utf-8\r\n";
131                 ba += "Content-Length: "+QByteArray::number(jsFile.size())+"\r\n";
132                 ba += "\r\n";
133                 ba+=jsFile.readAll();
134             }
135         }
136         else if(requestParser[1].contains(".css"))
137         {
138             QFile cssFile(":"+QString::fromAscii(requestParser[1]));
140             if(!cssFile.open(QFile::ReadOnly))
141                 ba = "HTTP/1.1 404 NOT FOUND";
142             else
143             {
144                 ba = "HTTP/1.1 200 OK\r\n";
145                 ba += "Date: "+currentDateTimeString+"\r\n";
146                 ba += "Content-Type: text/css; charset=utf-8\r\n";
147                 ba += "Content-Length: "+QByteArray::number(cssFile.size())+"\r\n";
148                 ba += "\r\n";
149                 ba+=cssFile.readAll();
150             }
151         }
152         else if(requestParser[1].contains("?"))
153         {
154             //create a hash table so we can lookup based on variable name
155             //ie getVar["command"] will give us the value set in the http request
156             QHash<QString, QVariant> getVar;
158             //let's parse the get variables
160             QString getVarsString = requestParser[1].mid(requestParser[1].lastIndexOf("?")+1);
162             foreach(QString keyValuePair, getVarsString.split("&"))
163             {
164                 QStringList splitKeyValue = keyValuePair.split("=");
165                 getVar[splitKeyValue[0]]=splitKeyValue[1];
166             }
168             QHash<QString, QVariant> data, optimizedData;
170             //QMetaObject::invokeMethod(parent(), Qt::BlockingQueuedConnection,  SLOT("remoteChangeReceived"), Q_RETURN_ARG(QHash<QString, QVariant>, data), Q_ARG(QHash<QString, QVariant>, getVar));
171             data = static_cast<MainWindow*>(parent())->processCommand(getVar);
175             optimizedData = data;
177             if(data["weatherCurrent"] == m_oldData["weatherCurrent"])
178                 optimizedData.remove("weatherCurrent");
179             if(data["weatherForecast"] == m_oldData["weatherForecast"])
180                 optimizedData.remove("weatherForecast");
181             if(getVar["command"] == "update")
182                 m_oldData = (data);
184             QByteArray jsonByteArray;
186             if(getVar["command"] == "update_forced")
187                 jsonByteArray=hashToJSONByteArray(data);
188             else
189                 jsonByteArray=hashToJSONByteArray(optimizedData);
191             ba = "HTTP/1.1 200 OK\r\n";
192             ba += "Date: "+currentDateTimeString+"\r\n";
193             ba += "Content-Type: text/html; charset=ISO-8859-1\r\n";
194             ba += "Content-Length: "+QByteArray::number(jsonByteArray.size())+"\r\n";
195             ba += "\r\n";
196             ba += jsonByteArray;
197         }
198         else
199             ba = "HTTP/1.1 404 NOT FOUND";
200     }
202     m_clientConnection->write(ba); //write the response
203     m_clientConnection->disconnectFromHost(); //http specifies completion with a closed connection
206 QByteArray RemoteAccessManager::hashToJSONByteArray(QHash<QString, QVariant> hash)
208     QByteArray jsonByteArray="{";
209     QHashIterator<QString, QVariant> it(hash);
212     while(it.hasNext())
213     {
214         it.next();
215         if(it.value().type() == QVariant::Hash)
216             jsonByteArray+="\""+it.key().toAscii()+"\""+QByteArray::fromRawData(":",1)+""+hashToJSONByteArray(it.value().toHash())+",";
217         else
218             jsonByteArray += "\""+it.key().toAscii()+"\""+QByteArray::fromRawData(":",1)+"\""+it.value().toByteArray()+"\""+QByteArray::fromRawData(",",1);
219     }
221     jsonByteArray = jsonByteArray.left(jsonByteArray.size()-1);
223     //terminate the json object
224     jsonByteArray += "}";
226     return jsonByteArray;