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
219 }
221 QByteArray RemoteAccessManager::hashToJSONByteArray(QHash<QString, QVariant> hash)
222 {
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;
253 }