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
204 }
206 QByteArray RemoteAccessManager::hashToJSONByteArray(QHash<QString, QVariant> hash)
207 {
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;
227 }