1 #include "stdafx.h"
2 
3 //
4 //	Domoticz Plugin System - Dnpwwo, 2016
5 //
6 #ifdef ENABLE_PYTHON
7 
8 #include "PluginMessages.h"
9 #include "PluginProtocols.h"
10 #include "../../main/Helper.h"
11 #include "../../main/json_helper.h"
12 #include "../../main/Logger.h"
13 #include "../../webserver/Base64.h"
14 #include "icmp_header.hpp"
15 #include "ipv4_header.hpp"
16 
17 namespace Plugins {
18 
Create(std::string sProtocol)19 	CPluginProtocol* CPluginProtocol::Create(std::string sProtocol)
20 	{
21 		if (sProtocol == "Line") return (CPluginProtocol*) new CPluginProtocolLine();
22 		else if (sProtocol == "XML") return (CPluginProtocol*) new CPluginProtocolXML();
23 		else if (sProtocol == "JSON") return (CPluginProtocol*) new CPluginProtocolJSON();
24 		else if ((sProtocol == "HTTP") || (sProtocol == "HTTPS"))
25 		{
26 			CPluginProtocolHTTP* pProtocol = new CPluginProtocolHTTP(sProtocol == "HTTPS");
27 			return (CPluginProtocol*)pProtocol;
28 		}
29 		else if (sProtocol == "ICMP") return (CPluginProtocol*) new CPluginProtocolICMP();
30 		else if ((sProtocol == "MQTT") || (sProtocol == "MQTTS"))
31 		{
32 			CPluginProtocolMQTT* pProtocol = new CPluginProtocolMQTT(sProtocol == "MQTTS");
33 			return (CPluginProtocol*)pProtocol;
34 		}
35 		else if ((sProtocol == "WS") || (sProtocol == "WSS"))
36 		{
37 			CPluginProtocolWS* pProtocol = new CPluginProtocolWS(sProtocol == "WSS");
38 			return (CPluginProtocol*)pProtocol;
39 		}
40 		else return new CPluginProtocol();
41 	}
42 
ProcessInbound(const ReadEvent * Message)43 	void CPluginProtocol::ProcessInbound(const ReadEvent* Message)
44 	{
45 		// Raw protocol is to just always dispatch data to plugin without interpretation
46 		Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, Message->m_Buffer));
47 	}
48 
ProcessOutbound(const WriteDirective * WriteMessage)49 	std::vector<byte> CPluginProtocol::ProcessOutbound(const WriteDirective* WriteMessage)
50 	{
51 		std::vector<byte>	retVal;
52 
53 		// Handle Bytes objects
54 		if ((((PyObject*)WriteMessage->m_Object)->ob_type->tp_flags & (Py_TPFLAGS_BYTES_SUBCLASS)) != 0)
55 		{
56 			const char* pData = PyBytes_AsString(WriteMessage->m_Object);
57 			int			iSize = PyBytes_Size(WriteMessage->m_Object);
58 			retVal.reserve((size_t)iSize);
59 			retVal.assign(pData, pData + iSize);
60 		}
61 		// Handle ByteArray objects
62 		else if ((((PyObject*)WriteMessage->m_Object)->ob_type->tp_name == std::string("bytearray")))
63 		{
64 			size_t	len = PyByteArray_Size(WriteMessage->m_Object);
65 			char* data = PyByteArray_AsString(WriteMessage->m_Object);
66 			retVal.reserve(len);
67 			retVal.assign((const byte*)data, (const byte*)data + len);
68 		}
69 		// Handle String objects
70 		else if ((((PyObject*)WriteMessage->m_Object)->ob_type->tp_flags & (Py_TPFLAGS_UNICODE_SUBCLASS)) != 0)
71 		{
72 			std::string	sData = PyUnicode_AsUTF8(WriteMessage->m_Object);
73 			retVal.reserve((size_t)sData.length());
74 			retVal.assign((const byte*)sData.c_str(), (const byte*)sData.c_str() + sData.length());
75 		}
76 		else
77 			_log.Log(LOG_ERROR, "(%s) Send request Python object parameter was not of type Unicode or Byte, ignored.", __func__);
78 
79 		return retVal;
80 	}
81 
Flush(CPlugin * pPlugin,PyObject * pConnection)82 	void CPluginProtocol::Flush(CPlugin* pPlugin, PyObject* pConnection)
83 	{
84 		if (m_sRetainedData.size())
85 		{
86 			// Forced buffer clear, make sure the plugin gets a look at the data in case it wants it
87 			pPlugin->MessagePlugin(new onMessageCallback(pPlugin, pConnection, m_sRetainedData));
88 			m_sRetainedData.clear();
89 		}
90 	}
91 
ProcessInbound(const ReadEvent * Message)92 	void CPluginProtocolLine::ProcessInbound(const ReadEvent* Message)
93 	{
94 		//
95 		//	Handles the cases where a read contains a partial message or multiple messages
96 		//
97 		std::vector<byte>	vData = m_sRetainedData;										// if there was some data left over from last time add it back in
98 		vData.insert(vData.end(), Message->m_Buffer.begin(), Message->m_Buffer.end());		// add the new data
99 
100 		std::string		sData(vData.begin(), vData.end());
101 		int iPos = sData.find_first_of('\r');		//  Look for message terminator
102 		while (iPos != std::string::npos)
103 		{
104 			Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, std::vector<byte>(&sData[0], &sData[iPos])));
105 
106 			if (sData[iPos + 1] == '\n') iPos++;				//  Handle \r\n
107 			sData = sData.substr(iPos + 1);
108 			iPos = sData.find_first_of('\r');
109 		}
110 
111 		m_sRetainedData.assign(sData.c_str(), sData.c_str() + sData.length()); // retain any residual for next time
112 	}
113 
AddBytesToDict(PyObject * pDict,const char * key,const std::string & value)114 	static void AddBytesToDict(PyObject* pDict, const char* key, const std::string& value)
115 	{
116 		PyObject* pObj = Py_BuildValue("y#", value.c_str(), value.length());
117 		if (PyDict_SetItemString(pDict, key, pObj) == -1)
118 			_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, key, value.c_str());
119 		Py_DECREF(pObj);
120 	}
121 
AddStringToDict(PyObject * pDict,const char * key,const std::string & value)122 	static void AddStringToDict(PyObject* pDict, const char* key, const std::string& value)
123 	{
124 		PyObject* pObj = Py_BuildValue("s#", value.c_str(), value.length());
125 		if (PyDict_SetItemString(pDict, key, pObj) == -1)
126 			_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, key, value.c_str());
127 		Py_DECREF(pObj);
128 	}
129 
AddIntToDict(PyObject * pDict,const char * key,const int value)130 	static void AddIntToDict(PyObject* pDict, const char* key, const int value)
131 	{
132 		PyObject* pObj = Py_BuildValue("i", value);
133 		if (PyDict_SetItemString(pDict, key, pObj) == -1)
134 			_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%d' to dictionary.", __func__, key, value);
135 		Py_DECREF(pObj);
136 	}
137 
AddUIntToDict(PyObject * pDict,const char * key,const unsigned int value)138 	static void AddUIntToDict(PyObject* pDict, const char* key, const unsigned int value)
139 	{
140 		PyObject* pObj = Py_BuildValue("I", value);
141 		if (PyDict_SetItemString(pDict, key, pObj) == -1)
142 			_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%d' to dictionary.", __func__, key, value);
143 		Py_DECREF(pObj);
144 	}
145 
AddDoubleToDict(PyObject * pDict,const char * key,const double value)146 	static void AddDoubleToDict(PyObject* pDict, const char* key, const double value)
147 	{
148 		PyObject* pObj = Py_BuildValue("d", value);
149 		if (PyDict_SetItemString(pDict, key, pObj) == -1)
150 			_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%f' to dictionary.", __func__, key, value);
151 		Py_DECREF(pObj);
152 	}
153 
AddBoolToDict(PyObject * pDict,const char * key,const bool value)154 	static void AddBoolToDict(PyObject* pDict, const char* key, const bool value)
155 	{
156 		PyObject* pObj = Py_BuildValue("N", PyBool_FromLong(value));
157 		if (PyDict_SetItemString(pDict, key, pObj) == -1)
158 			_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%d' to dictionary.", __func__, key, value);
159 		Py_DECREF(pObj);
160 	}
161 
JSONtoPython(Json::Value * pJSON)162 	PyObject* CPluginProtocolJSON::JSONtoPython(Json::Value* pJSON)
163 	{
164 		PyObject* pRetVal = NULL;
165 
166 		if (pJSON->isArray())
167 		{
168 			pRetVal = PyList_New(pJSON->size());
169 			Py_ssize_t	Index = 0;
170 			for (Json::ValueIterator it = pJSON->begin(); it != pJSON->end(); ++it)
171 			{
172 				Json::ValueIterator::reference	pRef = *it;
173 				if (it->isArray() || it->isObject())
174 				{
175 					PyObject* pObj = JSONtoPython(&pRef);
176 					if (!pObj || (PyList_SetItem(pRetVal, Index++, pObj) == -1))
177 						_log.Log(LOG_ERROR, "(%s) failed to add item '%zd', to list for object.", __func__, Index - 1);
178 				}
179 				else if (it->isUInt())
180 				{
181 					PyObject* pObj = Py_BuildValue("I", it->asUInt());
182 					if (!pObj || (PyList_SetItem(pRetVal, Index++, pObj) == -1))
183 						_log.Log(LOG_ERROR, "(%s) failed to add item '%zd', to list for unsigned integer.", __func__, Index - 1);
184 				}
185 				else if (it->isInt())
186 				{
187 					PyObject* pObj = Py_BuildValue("i", it->asInt());
188 					if (!pObj || (PyList_SetItem(pRetVal, Index++, pObj) == -1))
189 						_log.Log(LOG_ERROR, "(%s) failed to add item '%zd', to list for integer.", __func__, Index - 1);
190 				}
191 				else if (it->isDouble())
192 				{
193 					PyObject* pObj = Py_BuildValue("d", it->asDouble());
194 					if (!pObj || (PyList_SetItem(pRetVal, Index++, pObj) == -1))
195 						_log.Log(LOG_ERROR, "(%s) failed to add item '%zd', to list for double.", __func__, Index - 1);
196 				}
197 				else if (it->isConvertibleTo(Json::stringValue))
198 				{
199 					std::string	sString = it->asString();
200 					PyObject* pObj = Py_BuildValue("s#", sString.c_str(), sString.length());
201 					if (!pObj || (PyList_SetItem(pRetVal, Index++, pObj) == -1))
202 						_log.Log(LOG_ERROR, "(%s) failed to add item '%zd', to list for string.", __func__, Index - 1);
203 				}
204 				else
205 					_log.Log(LOG_ERROR, "(%s) failed to process entry.", __func__);
206 			}
207 		}
208 		else if (pJSON->isObject())
209 		{
210 			pRetVal = PyDict_New();
211 			for (Json::ValueIterator it = pJSON->begin(); it != pJSON->end(); ++it)
212 			{
213 				std::string						KeyName = it.name();
214 				Json::ValueIterator::reference	pRef = *it;
215 				if (it->isArray() || it->isObject())
216 				{
217 					PyObject* pObj = JSONtoPython(&pRef);
218 					if (!pObj || (PyDict_SetItemString(pRetVal, KeyName.c_str(), pObj) == -1))
219 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s', to dictionary for object.", __func__, KeyName.c_str());
220 				}
221 				else if (it->isUInt()) AddUIntToDict(pRetVal, KeyName.c_str(), it->asUInt());
222 				else if (it->isInt()) AddIntToDict(pRetVal, KeyName.c_str(), it->asInt());
223 				else if (it->isBool()) AddBoolToDict(pRetVal, KeyName.c_str(), it->asInt());
224 				else if (it->isDouble()) AddDoubleToDict(pRetVal, KeyName.c_str(), it->asDouble());
225 				else if (it->isConvertibleTo(Json::stringValue)) AddStringToDict(pRetVal, KeyName.c_str(), it->asString());
226 				else _log.Log(LOG_ERROR, "(%s) failed to process entry for '%s'.", __func__, KeyName.c_str());
227 			}
228 		}
229 		return pRetVal;
230 	}
231 
JSONtoPython(std::string sData)232 	PyObject* CPluginProtocolJSON::JSONtoPython(std::string	sData)
233 	{
234 		Json::Value		root;
235 		PyObject* pRetVal = Py_None;
236 
237 		bool bRet = ParseJSon(sData, root);
238 		if ((!bRet) || (!root.isObject()))
239 		{
240 			_log.Log(LOG_ERROR, "JSON Protocol: Parse Error on '%s'", sData.c_str());
241 			Py_INCREF(Py_None);
242 		}
243 		else
244 		{
245 			pRetVal = JSONtoPython(&root);
246 		}
247 
248 		return pRetVal;
249 	}
250 
PythontoJSON(PyObject * pObject)251 	std::string CPluginProtocolJSON::PythontoJSON(PyObject* pObject)
252 	{
253 		std::string	sJson;
254 
255 		if (PyUnicode_Check(pObject))
256 		{
257 			sJson += '"' + std::string(PyUnicode_AsUTF8(pObject)) + '"';
258 		}
259 		else if (pObject->ob_type->tp_name == std::string("bool"))
260 		{
261 			sJson += (PyObject_IsTrue(pObject) ? "true" : "false");
262 		}
263 		else if (PyLong_Check(pObject))
264 		{
265 			sJson += std::to_string(PyLong_AsLong(pObject));
266 		}
267 		else if (PyBytes_Check(pObject))
268 		{
269 			sJson += '"' + std::string(PyBytes_AsString(pObject)) + '"';
270 		}
271 		else if (pObject->ob_type->tp_name == std::string("bytearray"))
272 		{
273 			sJson += '"' + std::string(PyByteArray_AsString(pObject)) + '"';
274 		}
275 		else if (pObject->ob_type->tp_name == std::string("float"))
276 		{
277 			sJson += std::to_string(PyFloat_AsDouble(pObject));
278 		}
279 		else if (PyDict_Check(pObject))
280 		{
281 			sJson += "{ ";
282 			PyObject* key, * value;
283 			Py_ssize_t pos = 0;
284 			while (PyDict_Next(pObject, &pos, &key, &value))
285 			{
286 				sJson += PythontoJSON(key) + ':' + PythontoJSON(value) + ',';
287 			}
288 			sJson[sJson.length() - 1] = '}';
289 		}
290 		else if (PyList_Check(pObject))
291 		{
292 			sJson += "[ ";
293 			for (Py_ssize_t i = 0; i < PyList_Size(pObject); i++)
294 			{
295 				sJson += PythontoJSON(PyList_GetItem(pObject, i)) + ',';
296 			}
297 			sJson[sJson.length() - 1] = ']';
298 		}
299 		else if (PyTuple_Check(pObject))
300 		{
301 			sJson += "[ ";
302 			for (Py_ssize_t i = 0; i < PyTuple_Size(pObject); i++)
303 			{
304 				sJson += PythontoJSON(PyTuple_GetItem(pObject, i)) + ',';
305 			}
306 			sJson[sJson.length() - 1] = ']';
307 		}
308 
309 		return sJson;
310 	}
311 
ProcessInbound(const ReadEvent * Message)312 	void CPluginProtocolJSON::ProcessInbound(const ReadEvent* Message)
313 	{
314 		//
315 		//	Handles the cases where a read contains a partial message or multiple messages
316 		//
317 		std::vector<byte>	vData = m_sRetainedData;										// if there was some data left over from last time add it back in
318 		vData.insert(vData.end(), Message->m_Buffer.begin(), Message->m_Buffer.end());		// add the new data
319 
320 		std::string		sData(vData.begin(), vData.end());
321 		int iPos = 1;
322 		while (iPos) {
323 			Json::Value		root;
324 			iPos = sData.find("}{", 0) + 1;		//  Look for message separater in case there is more than one
325 			if (!iPos) // no, just one or part of one
326 			{
327 				if ((sData.substr(sData.length() - 1, 1) == "}") &&
328 					(std::count(sData.begin(), sData.end(), '{') == std::count(sData.begin(), sData.end(), '}'))) // whole message so queue the whole buffer
329 				{
330 					bool bRet = ParseJSon(sData, root);
331 					if ((!bRet) || (!root.isObject()))
332 					{
333 						_log.Log(LOG_ERROR, "JSON Protocol: Parse Error on '%s'", sData.c_str());
334 						Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, sData));
335 					}
336 					else
337 					{
338 						PyObject* pMessage = JSONtoPython(&root);
339 						Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, pMessage));
340 					}
341 					sData.clear();
342 				}
343 			}
344 			else  // more than one message so queue the first one
345 			{
346 				std::string sMessage = sData.substr(0, iPos);
347 				sData = sData.substr(iPos);
348 				bool bRet = ParseJSon(sMessage, root);
349 				if ((!bRet) || (!root.isObject()))
350 				{
351 					_log.Log(LOG_ERROR, "JSON Protocol: Parse Error on '%s'", sData.c_str());
352 					Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, sMessage));
353 				}
354 				else
355 				{
356 					PyObject* pMessage = JSONtoPython(&root);
357 					Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, pMessage));
358 				}
359 			}
360 		}
361 
362 		m_sRetainedData.assign(sData.c_str(), sData.c_str() + sData.length()); // retain any residual for next time
363 	}
364 
ProcessInbound(const ReadEvent * Message)365 	void CPluginProtocolXML::ProcessInbound(const ReadEvent* Message)
366 	{
367 		//
368 		//	Only returns whole XML messages. Does not handle <tag /> as the top level tag
369 		//	Handles the cases where a read contains a partial message or multiple messages
370 		//
371 		std::vector<byte>	vData = m_sRetainedData;										// if there was some data left over from last time add it back in
372 		vData.insert(vData.end(), Message->m_Buffer.begin(), Message->m_Buffer.end());		// add the new data
373 
374 		std::string		sData(vData.begin(), vData.end());
375 		try
376 		{
377 			while (true)
378 			{
379 				//
380 				//	Find the top level tag name if it is not set
381 				//
382 				if (!m_Tag.length())
383 				{
384 					if (sData.find("<?xml") != std::string::npos)	// step over '<?xml version="1.0" encoding="utf-8"?>' if present
385 					{
386 						int iEnd = sData.find("?>");
387 						sData = sData.substr(iEnd + 2);
388 					}
389 
390 					int iStart = sData.find_first_of('<');
391 					if (iStart == std::string::npos)
392 					{
393 						// start of a tag not found so discard
394 						m_sRetainedData.clear();
395 						break;
396 					}
397 					if (iStart) sData = sData.substr(iStart);		// remove any leading data
398 					int iEnd = sData.find_first_of(" >");
399 					if (iEnd != std::string::npos)
400 					{
401 						m_Tag = sData.substr(1, (iEnd - 1));
402 					}
403 				}
404 
405 				int	iPos = sData.find("</" + m_Tag + ">");
406 				if (iPos != std::string::npos)
407 				{
408 					int iEnd = iPos + m_Tag.length() + 3;
409 					Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, sData.substr(0, iEnd)));
410 
411 					if (iEnd == sData.length())
412 					{
413 						sData.clear();
414 					}
415 					else
416 					{
417 						sData = sData.substr(++iEnd);
418 					}
419 					m_Tag = "";
420 				}
421 				else
422 					break;
423 			}
424 		}
425 		catch (std::exception const& exc)
426 		{
427 			_log.Log(LOG_ERROR, "(CPluginProtocolXML::ProcessInbound) Unexpected exception thrown '%s', Data '%s'.", exc.what(), sData.c_str());
428 		}
429 
430 		m_sRetainedData.assign(sData.c_str(), sData.c_str() + sData.length()); // retain any residual for next time
431 	}
432 
ExtractHeaders(std::string * pData)433 	void CPluginProtocolHTTP::ExtractHeaders(std::string* pData)
434 	{
435 		// Remove headers
436 		if (m_Headers)
437 		{
438 			Py_DECREF(m_Headers);
439 		}
440 		m_Headers = (PyObject*)PyDict_New();
441 
442 		*pData = pData->substr(pData->find_first_of('\n') + 1);
443 		while (pData->length() && ((*pData)[0] != '\r'))
444 		{
445 			std::string		sHeaderLine = pData->substr(0, pData->find_first_of('\r'));
446 			std::string		sHeaderName = pData->substr(0, sHeaderLine.find_first_of(':'));
447 			std::string		uHeaderName = sHeaderName;
448 			stdupper(uHeaderName);
449 			std::string		sHeaderText = sHeaderLine.substr(sHeaderName.length() + 2);
450 			if (uHeaderName == "CONTENT-LENGTH")
451 			{
452 				m_ContentLength = atoi(sHeaderText.c_str());
453 			}
454 			if (uHeaderName == "TRANSFER-ENCODING")
455 			{
456 				std::string		uHeaderText = sHeaderText;
457 				stdupper(uHeaderText);
458 				if (uHeaderText == "CHUNKED")
459 					m_Chunked = true;
460 			}
461 			PyObject* pObj = Py_BuildValue("s", sHeaderText.c_str());
462 			PyObject* pPrevObj = PyDict_GetItemString((PyObject*)m_Headers, sHeaderName.c_str());
463 			// Encode multi headers in a list
464 			if (pPrevObj != NULL) {
465 				PyObject* pListObj = pPrevObj;
466 				// First duplicate? Create a list and add previous value
467 				if (!PyList_Check(pListObj))
468 				{
469 					pListObj = PyList_New(1);
470 					if (!pListObj)
471 					{
472 						_log.Log(LOG_ERROR, "(%s) failed to create list to handle duplicate header. Name '%s'.", __func__, sHeaderName.c_str());
473 						return;
474 					}
475 					PyList_SetItem(pListObj, 0, pPrevObj);
476 					Py_INCREF(pPrevObj);
477 					PyDict_SetItemString((PyObject*)m_Headers, sHeaderName.c_str(), pListObj);
478 					Py_DECREF(pListObj);
479 				}
480 				// Append new value to the list
481 				if (PyList_Append(pListObj, pObj) == -1) {
482 					_log.Log(LOG_ERROR, "(%s) failed to append to list key '%s', value '%s' to headers.", __func__, sHeaderName.c_str(), sHeaderText.c_str());
483 				}
484 			}
485 			else if (PyDict_SetItemString((PyObject*)m_Headers, sHeaderName.c_str(), pObj) == -1) {
486 				_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to headers.", __func__, sHeaderName.c_str(), sHeaderText.c_str());
487 			}
488 			Py_DECREF(pObj);
489 			*pData = pData->substr(pData->find_first_of('\n') + 1);
490 		}
491 	}
492 
Flush(CPlugin * pPlugin,PyObject * pConnection)493 	void CPluginProtocolHTTP::Flush(CPlugin* pPlugin, PyObject* pConnection)
494 	{
495 		if (m_sRetainedData.size())
496 		{
497 			// Forced buffer clear, make sure the plugin gets a look at the data in case it wants it
498 			ProcessInbound(new ReadEvent(pPlugin, pConnection, 0, NULL));
499 			m_sRetainedData.clear();
500 		}
501 	}
502 
ProcessInbound(const ReadEvent * Message)503 	void CPluginProtocolHTTP::ProcessInbound(const ReadEvent* Message)
504 	{
505 		// There won't be a buffer if the connection closed
506 		if (Message->m_Buffer.size())
507 		{
508 			m_sRetainedData.insert(m_sRetainedData.end(), Message->m_Buffer.begin(), Message->m_Buffer.end());
509 		}
510 
511 		// HTML is non binary so use strings
512 		std::string		sData(m_sRetainedData.begin(), m_sRetainedData.end());
513 
514 		m_ContentLength = -1;
515 		m_Chunked = false;
516 		m_RemainingChunk = 0;
517 
518 		// Need at least the whole of the first line before going any further; otherwise attempting to parse it
519 		// will end badly.
520 		if (sData.find("\r\n") == std::string::npos)
521 		{
522 		        return;
523 		}
524 
525 		//
526 		//	Process server responses
527 		//
528 		if (sData.substr(0, 4) == "HTTP")
529 		{
530 			// HTTP/1.0 404 Not Found
531 			// Content-Type: text/html; charset=UTF-8
532 			// Content-Length: 1570
533 			// Date: Thu, 05 Jan 2017 05:50:33 GMT
534 			//
535 			// <!DOCTYPE html>
536 			// <html lang=en>
537 			//   <meta charset=utf-8>
538 			//   <meta name=viewport...
539 
540 			// HTTP/1.1 200 OK
541 			// Content-Type: text/html; charset=UTF-8
542 			// Transfer-Encoding: chunked
543 			// Date: Thu, 05 Jan 2017 05:50:33 GMT
544 			//
545 			// 40d
546 			// <!DOCTYPE html>
547 			// <html lang=en>
548 			//   <meta charset=utf-8>
549 			// ...
550 			// </html>
551 			// 0
552 
553 			// Process response header (HTTP/1.1 200 OK)
554 			std::string		sFirstLine = sData.substr(0, sData.find_first_of('\r'));
555 			sFirstLine = sFirstLine.substr(sFirstLine.find_first_of(' ') + 1);
556 			m_Status = sFirstLine.substr(0, sFirstLine.find_first_of(' '));
557 
558 			ExtractHeaders(&sData);
559 
560 			// not enough data arrived to complete header processing
561 			if (!sData.length())
562 			{
563 				return;
564 			}
565 
566 			sData = sData.substr(sData.find_first_of('\n') + 1);		// skip over 2nd new line char
567 
568 			// Process the message body
569 			if (m_Status.length())
570 			{
571 				if (!m_Chunked)
572 				{
573 					// If full message then return it
574 					if ((m_ContentLength == sData.length()) || (!Message->m_Buffer.size()))
575 					{
576 						PyObject* pDataDict = PyDict_New();
577 						PyObject* pObj = Py_BuildValue("s", m_Status.c_str());
578 						if (PyDict_SetItemString(pDataDict, "Status", pObj) == -1)
579 							_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "HTTP", "Status", m_Status.c_str());
580 						Py_DECREF(pObj);
581 
582 						if (m_Headers)
583 						{
584 							if (PyDict_SetItemString(pDataDict, "Headers", (PyObject*)m_Headers) == -1)
585 								_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", "HTTP", "Headers");
586 							Py_DECREF((PyObject*)m_Headers);
587 							m_Headers = NULL;
588 						}
589 
590 						if (sData.length())
591 						{
592 							pObj = Py_BuildValue("y#", sData.c_str(), sData.length());
593 							if (PyDict_SetItemString(pDataDict, "Data", pObj) == -1)
594 								_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "HTTP", "Data", sData.c_str());
595 							Py_DECREF(pObj);
596 						}
597 
598 						Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, pDataDict));
599 						m_sRetainedData.clear();
600 					}
601 				}
602 				else
603 				{
604 					// Process available chunks
605 					std::string		sPayload;
606 					while (sData.length() >= 2 && (sData != "\r\n"))
607 					{
608 						if (!m_RemainingChunk)	// Not processing a chunk so we should be at the start of one
609 						{
610 						        // Skip terminating \r\n from previous chunk
611 							if (sData[0] == '\r')
612 							{
613 								sData = sData.substr(sData.find_first_of('\n') + 1);
614 							}
615 							// Stop if we have not received the complete chunk size terminator yet
616 							size_t uSizeEnd = sData.find_first_of('\r');
617 							if (uSizeEnd == std::string::npos || sData.find_first_of('\n', uSizeEnd + 1) == std::string::npos)
618 							{
619 							        break;
620 							}
621 							std::string		sChunkLine = sData.substr(0, uSizeEnd);
622 							m_RemainingChunk = strtol(sChunkLine.c_str(), NULL, 16);
623 							sData = sData.substr(sData.find_first_of('\n') + 1);
624 
625 							// last chunk is zero length, but still has a terminator.  We aren't done until we have received the terminator as well
626 							if (m_RemainingChunk == 0 && (sData.find_first_of('\n') != std::string::npos))
627 							{
628 								PyObject* pDataDict = PyDict_New();
629 								PyObject* pObj = Py_BuildValue("s", m_Status.c_str());
630 								if (PyDict_SetItemString(pDataDict, "Status", pObj) == -1)
631 									_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "HTTP", "Status", m_Status.c_str());
632 								Py_DECREF(pObj);
633 
634 								if (m_Headers)
635 								{
636 									if (PyDict_SetItemString(pDataDict, "Headers", (PyObject*)m_Headers) == -1)
637 										_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", "HTTP", "Headers");
638 									Py_DECREF((PyObject*)m_Headers);
639 									m_Headers = NULL;
640 								}
641 
642 								if (sPayload.length())
643 								{
644 									pObj = Py_BuildValue("y#", sPayload.c_str(), sPayload.length());
645 									if (PyDict_SetItemString(pDataDict, "Data", pObj) == -1)
646 										_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "HTTP", "Data", sPayload.c_str());
647 									Py_DECREF(pObj);
648 								}
649 
650 								Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, pDataDict));
651 								m_sRetainedData.clear();
652 								break;
653 							}
654 						}
655 
656 						if (sData.length() <= m_RemainingChunk)		// Read data is just part of a chunk
657 						{
658 							break;
659 						}
660 
661 						sPayload += sData.substr(0, m_RemainingChunk);
662 						sData = sData.substr(m_RemainingChunk);
663 						m_RemainingChunk = 0;
664 					}
665 				}
666 			}
667 		}
668 
669 		//
670 		//	Process client requests
671 		//
672 		else
673 		{
674 			// GET / HTTP / 1.1\r\n
675 			// Host: 127.0.0.1 : 9090\r\n
676 			// User - Agent: Mozilla / 5.0 (Windows NT 10.0; WOW64; rv:53.0) Gecko / 20100101 Firefox / 53.0\r\n
677 			// Accept: text / html, application / xhtml + xml, application / xml; q = 0.9, */*;q=0.8\r\n
678 			std::string		sFirstLine = sData.substr(0, sData.find_first_of('\r'));
679 			sFirstLine = sFirstLine.substr(0, sFirstLine.find_last_of(' '));
680 
681 			ExtractHeaders(&sData);
682 			if (sData.substr(0, 2) == "\r\n")
683 			{
684 				std::string		sPayload = sData.substr(2);
685 				// No payload || we have the payload || the connection has closed
686 				if ((m_ContentLength == -1) || (m_ContentLength == sPayload.length()) || !Message->m_Buffer.size())
687 				{
688 					PyObject* DataDict = PyDict_New();
689 					std::string		sVerb = sFirstLine.substr(0, sFirstLine.find_first_of(' '));
690 					PyObject* pObj = Py_BuildValue("s", sVerb.c_str());
691 					if (PyDict_SetItemString(DataDict, "Verb", pObj) == -1)
692 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "HTTP", "Verb", sVerb.c_str());
693 					Py_DECREF(pObj);
694 
695 					std::string		sURL = sFirstLine.substr(sVerb.length() + 1, sFirstLine.find_first_of(' ', sVerb.length() + 1));
696 					pObj = Py_BuildValue("s", sURL.c_str());
697 					if (PyDict_SetItemString(DataDict, "URL", pObj) == -1)
698 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "HTTP", "URL", sURL.c_str());
699 					Py_DECREF(pObj);
700 
701 					if (m_Headers)
702 					{
703 						if (PyDict_SetItemString(DataDict, "Headers", (PyObject*)m_Headers) == -1)
704 							_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", "HTTP", "Headers");
705 						Py_DECREF((PyObject*)m_Headers);
706 						m_Headers = NULL;
707 					}
708 
709 					if (sPayload.length())
710 					{
711 						pObj = Py_BuildValue("y#", sPayload.c_str(), sPayload.length());
712 						if (PyDict_SetItemString(DataDict, "Data", pObj) == -1)
713 							_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "HTTP", "Data", sPayload.c_str());
714 						Py_DECREF(pObj);
715 					}
716 
717 					Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, DataDict));
718 					m_sRetainedData.clear();
719 				}
720 			}
721 		}
722 	}
723 
ProcessOutbound(const WriteDirective * WriteMessage)724 	std::vector<byte>	CPluginProtocolHTTP::ProcessOutbound(const WriteDirective* WriteMessage)
725 	{
726 		std::vector<byte>	retVal;
727 		std::string	sHttp;
728 
729 		// Sanity check input
730 		if (!WriteMessage->m_Object || !PyDict_Check(WriteMessage->m_Object))
731 		{
732 			_log.Log(LOG_ERROR, "(%s) HTTP Send parameter was not a dictionary, ignored. See Python Plugin wiki page for help.", __func__);
733 			return retVal;
734 		}
735 
736 		// Extract potential values.  Failures return NULL, success returns borrowed reference
737 		PyObject* pVerb = PyDict_GetItemString(WriteMessage->m_Object, "Verb");
738 		PyObject* pStatus = PyDict_GetItemString(WriteMessage->m_Object, "Status");
739 		PyObject* pChunk = PyDict_GetItemString(WriteMessage->m_Object, "Chunk");
740 		PyObject* pHeaders = PyDict_GetItemString(WriteMessage->m_Object, "Headers");
741 		PyObject* pData = PyDict_GetItemString(WriteMessage->m_Object, "Data");
742 
743 		//
744 		//	Assume Request if 'Verb' specified
745 		//
746 		if (pVerb)
747 		{
748 			// GET /path/file.html HTTP/1.1
749 			// Connection: "keep-alive"
750 			// Accept: "text/html"
751 			//
752 
753 			// POST /path/test.cgi HTTP/1.1
754 			// From: info@domoticz.com
755 			// User-Agent: Domoticz/1.0
756 			// Content-Type : application/x-www-form-urlencoded
757 			// Content-Length : 32
758 			//
759 			// param1=value&param2=other+value
760 
761 			if (!PyUnicode_Check(pVerb))
762 			{
763 				_log.Log(LOG_ERROR, "(%s) HTTP 'Verb' dictionary entry not a string, ignored. See Python Plugin wiki page for help.", __func__);
764 				return retVal;
765 			}
766 			sHttp = PyUnicode_AsUTF8(pVerb);
767 			stdupper(sHttp);
768 			sHttp += " ";
769 
770 			PyObject* pURL = PyDict_GetItemString(WriteMessage->m_Object, "URL");
771 			std::string	sHttpURL = "/";
772 			if (pURL && PyUnicode_Check(pURL))
773 			{
774 				sHttpURL = PyUnicode_AsUTF8(pURL);
775 			}
776 			sHttp += sHttpURL;
777 			sHttp += " HTTP/1.1\r\n";
778 
779 			// If username &/or password specified then add a basic auth header (if one was not supplied)
780 			PyObject* pHead = NULL;
781 			if (pHeaders) pHead = PyDict_GetItemString(pHeaders, "Authorization");
782 			if (!pHead)
783 			{
784 				std::string		User;
785 				std::string		Pass;
786 				PyObject* pModule = (PyObject*)WriteMessage->m_pPlugin->PythonModule();
787 				PyObject* pDict = PyObject_GetAttrString(pModule, "Parameters");
788 				if (pDict)
789 				{
790 					PyObject* pUser = PyDict_GetItemString(pDict, "Username");
791 					if (pUser) User = PyUnicode_AsUTF8(pUser);
792 					PyObject* pPass = PyDict_GetItemString(pDict, "Password");
793 					if (pPass) Pass = PyUnicode_AsUTF8(pPass);
794 					Py_DECREF(pDict);
795 				}
796 				if (User.length() > 0 || Pass.length() > 0)
797 				{
798 					std::string auth;
799 					if (User.length() > 0)
800 					{
801 						auth += User;
802 					}
803 					auth += ":";
804 					if (Pass.length() > 0)
805 					{
806 						auth += Pass;
807 					}
808 					std::string encodedAuth = base64_encode(auth);
809 					sHttp += "Authorization: Basic " + encodedAuth + "\r\n";
810 				}
811 			}
812 
813 			// Add Server header if it is not supplied
814 			if (pHeaders) pHead = PyDict_GetItemString(pHeaders, "User-Agent");
815 			if (!pHead)
816 			{
817 				sHttp += "User-Agent: Domoticz/1.0\r\n";
818 			}
819 		}
820 		//
821 		//	Assume Response if 'Status' specified
822 		//
823 		else if (pStatus)
824 		{
825 			//	HTTP/1.1 200 OK
826 			//	Date: Mon, 27 Jul 2009 12:28:53 GMT
827 			//	Server: Apache/2.2.14 (Win32)
828 			//	Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
829 			//	Content-Length: 88
830 			//	Content-Type: text/html
831 			//	Connection: Closed
832 			//
833 			//	<html>
834 			//	<body>
835 			//	<h1>Hello, World!</h1>
836 			//	</body>
837 			//	</html>
838 
839 			if (!PyUnicode_Check(pStatus))
840 			{
841 				_log.Log(LOG_ERROR, "(%s) HTTP 'Status' dictionary entry was not a string, ignored. See Python Plugin wiki page for help.", __func__);
842 				return retVal;
843 			}
844 
845 			sHttp = "HTTP/1.1 ";
846 			sHttp += PyUnicode_AsUTF8(pStatus);
847 			sHttp += "\r\n";
848 
849 			// Add Date header if it is not supplied
850 			PyObject* pHead = NULL;
851 			if (pHeaders) pHead = PyDict_GetItemString(pHeaders, "Date");
852 			if (!pHead)
853 			{
854 				char szDate[100];
855 				time_t rawtime;
856 				struct tm* info;
857 				time(&rawtime);
858 				info = gmtime(&rawtime);
859 				if (0 < strftime(szDate, sizeof(szDate), "Date: %a, %d %b %Y %H:%M:%S GMT\r\n", info))	sHttp += szDate;
860 			}
861 
862 			// Add Server header if it is not supplied
863 			pHead = NULL;
864 			if (pHeaders) pHead = PyDict_GetItemString(pHeaders, "Server");
865 			if (!pHead)
866 			{
867 				sHttp += "Server: Domoticz/1.0\r\n";
868 			}
869 		}
870 		//
871 		//	Only other valid options is a chunk response
872 		//
873 		else if (!pChunk)
874 		{
875 			_log.Log(LOG_ERROR, "(%s) HTTP unable to determine send type. 'Verb', 'Status' or 'Chunk' dictionary entries not found, ignored. See Python Plugin wiki page for help.", __func__);
876 			return retVal;
877 		}
878 
879 		// Handle headers for normal Requests & Responses
880 		if (pVerb || pStatus)
881 		{
882 			// Did we get headers to send?
883 			if (pHeaders)
884 			{
885 				if (PyDict_Check(pHeaders))
886 				{
887 					PyObject* key, * value;
888 					Py_ssize_t pos = 0;
889 					while (PyDict_Next(pHeaders, &pos, &key, &value))
890 					{
891 						std::string	sKey = PyUnicode_AsUTF8(key);
892 						if (PyUnicode_Check(value))
893 						{
894 							std::string	sValue = PyUnicode_AsUTF8(value);
895 							sHttp += sKey + ": " + sValue + "\r\n";
896 						}
897 						else if (PyBytes_Check(value))
898 						{
899 							const char* pBytes = PyBytes_AsString(value);
900 							sHttp += sKey + ": " + pBytes + "\r\n";
901 						}
902 						else if (value->ob_type->tp_name == std::string("bytearray"))
903 						{
904 							const char* pByteArray = PyByteArray_AsString(value);
905 							sHttp += sKey + ": " + pByteArray + "\r\n";
906 						}
907 						else if (PyList_Check(value))
908 						{
909 							PyObject* iterator = PyObject_GetIter(value);
910 							PyObject* item;
911 							while (item = PyIter_Next(iterator)) {
912 								if (PyUnicode_Check(item))
913 								{
914 									std::string	sValue = PyUnicode_AsUTF8(item);
915 									sHttp += sKey + ": " + sValue + "\r\n";
916 								}
917 								else if (PyBytes_Check(item))
918 								{
919 									const char* pBytes = PyBytes_AsString(item);
920 									sHttp += sKey + ": " + pBytes + "\r\n";
921 								}
922 								else if (item->ob_type->tp_name == std::string("bytearray"))
923 								{
924 									const char* pByteArray = PyByteArray_AsString(item);
925 									sHttp += sKey + ": " + pByteArray + "\r\n";
926 								}
927 								Py_DECREF(item);
928 							}
929 
930 							Py_DECREF(iterator);
931 						}
932 					}
933 				}
934 				else
935 				{
936 					_log.Log(LOG_ERROR, "(%s) HTTP 'Headers' parameter was not a dictionary, ignored.", __func__);
937 				}
938 			}
939 
940 			// Add Content-Length header if it is required but not supplied
941 			PyObject* pLength = NULL;
942 			if (pHeaders)
943 				pLength = PyDict_GetItemString(pHeaders, "Content-Length");
944 			if (!pLength && pData && !pChunk)
945 			{
946 				Py_ssize_t iLength = 0;
947 				if (PyUnicode_Check(pData))
948 					iLength = PyUnicode_GetLength(pData);
949 				else if (pData->ob_type->tp_name == std::string("bytearray"))
950 					iLength = PyByteArray_Size(pData);
951 				else if (PyBytes_Check(pData))
952 					iLength = PyBytes_Size(pData);
953 				sHttp += "Content-Length: " + std::to_string(iLength) + "\r\n";
954 			}
955 
956 			// Add Transfer-Encoding header if required but not supplied
957 			if (pChunk)
958 			{
959 				PyObject* pHead = NULL;
960 				if (pHeaders) pHead = PyDict_GetItemString(pHeaders, "Transfer-Encoding");
961 				if (!pHead)
962 				{
963 					sHttp += "Transfer-Encoding: chunked\r\n";
964 				}
965 			}
966 
967 			// Terminate preamble
968 			sHttp += "\r\n";
969 		}
970 
971 		// Chunks require hex encoded chunk length instead of normal response
972 		if (pChunk)
973 		{
974 			long	lChunkLength = 0;
975 			if (pData)
976 			{
977 				if (PyUnicode_Check(pData))
978 					lChunkLength = PyUnicode_GetLength(pData);
979 				else if (pData->ob_type->tp_name == std::string("bytearray"))
980 					lChunkLength = PyByteArray_Size(pData);
981 				else if (PyBytes_Check(pData))
982 					lChunkLength = PyBytes_Size(pData);
983 			}
984 			std::stringstream stream;
985 			stream << std::hex << lChunkLength;
986 			sHttp += std::string(stream.str());
987 			sHttp += "\r\n";
988 		}
989 
990 		// Append data if supplied (for POST) or Response
991 		if (pData && PyUnicode_Check(pData))
992 		{
993 			sHttp += PyUnicode_AsUTF8(pData);
994 			retVal.reserve(sHttp.length() + 2);
995 			retVal.assign(sHttp.c_str(), sHttp.c_str() + sHttp.length());
996 		}
997 		else if (pData && (pData->ob_type->tp_name == std::string("bytearray")))
998 		{
999 			retVal.reserve(sHttp.length() + PyByteArray_Size(pData) + 2);
1000 			retVal.assign(sHttp.c_str(), sHttp.c_str() + sHttp.length());
1001 			const char* pByteArray = PyByteArray_AsString(pData);
1002 			int iStop = PyByteArray_Size(pData);
1003 			for (int i = 0; i < iStop; i++)
1004 			{
1005 				retVal.push_back(pByteArray[i]);
1006 			}
1007 		}
1008 		else if (pData && PyBytes_Check(pData))
1009 		{
1010 			retVal.reserve(sHttp.length() + PyBytes_Size(pData) + 2);
1011 			retVal.assign(sHttp.c_str(), sHttp.c_str() + sHttp.length());
1012 			const char* pBytes = PyBytes_AsString(pData);
1013 			int iStop = PyBytes_Size(pData);
1014 			for (int i = 0; i < iStop; i++)
1015 			{
1016 				retVal.push_back(pBytes[i]);
1017 			}
1018 		}
1019 		else
1020 		{
1021 			retVal.reserve(sHttp.length() + 2);
1022 			retVal.assign(sHttp.c_str(), sHttp.c_str() + sHttp.length());
1023 		}
1024 
1025 		// Chunks require additional CRLF (hence '+2' on all vector reserves to make sure there is room)
1026 		if (pChunk)
1027 		{
1028 			retVal.push_back('\r');
1029 			retVal.push_back('\n');
1030 		}
1031 
1032 		return retVal;
1033 	}
1034 
ProcessInbound(const ReadEvent * Message)1035 	void CPluginProtocolICMP::ProcessInbound(const ReadEvent* Message)
1036 	{
1037 		PyObject* pObj = NULL;
1038 		PyObject* pDataDict = PyDict_New();
1039 		int			iTotalData = 0;
1040 		int			iDataOffset = 0;
1041 
1042 		// Handle response
1043 		if (Message->m_Buffer.size())
1044 		{
1045 			PyObject* pIPv4Dict = PyDict_New();
1046 			if (pDataDict && pIPv4Dict)
1047 			{
1048 				if (PyDict_SetItemString(pDataDict, "IPv4", (PyObject*)pIPv4Dict) == -1)
1049 					_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", "ICMP", "IPv4");
1050 				else
1051 				{
1052 					Py_XDECREF((PyObject*)pIPv4Dict);
1053 
1054 					ipv4_header* pIPv4 = (ipv4_header*)(&Message->m_Buffer[0]);
1055 
1056 					pObj = Py_BuildValue("s", pIPv4->source_address().to_string().c_str());
1057 					PyDict_SetItemString(pIPv4Dict, "Source", pObj);
1058 					Py_DECREF(pObj);
1059 
1060 					pObj = Py_BuildValue("s", pIPv4->destination_address().to_string().c_str());
1061 					PyDict_SetItemString(pIPv4Dict, "Destination", pObj);
1062 					Py_DECREF(pObj);
1063 
1064 					pObj = Py_BuildValue("b", pIPv4->version());
1065 					PyDict_SetItemString(pIPv4Dict, "Version", pObj);
1066 					Py_DECREF(pObj);
1067 
1068 					pObj = Py_BuildValue("b", pIPv4->protocol());
1069 					PyDict_SetItemString(pIPv4Dict, "Protocol", pObj);
1070 					Py_DECREF(pObj);
1071 
1072 					pObj = Py_BuildValue("b", pIPv4->type_of_service());
1073 					PyDict_SetItemString(pIPv4Dict, "TypeOfService", pObj);
1074 					Py_DECREF(pObj);
1075 
1076 					pObj = Py_BuildValue("h", pIPv4->header_length());
1077 					PyDict_SetItemString(pIPv4Dict, "HeaderLength", pObj);
1078 					Py_DECREF(pObj);
1079 
1080 					pObj = Py_BuildValue("h", pIPv4->total_length());
1081 					PyDict_SetItemString(pIPv4Dict, "TotalLength", pObj);
1082 					Py_DECREF(pObj);
1083 
1084 					pObj = Py_BuildValue("h", pIPv4->identification());
1085 					PyDict_SetItemString(pIPv4Dict, "Identification", pObj);
1086 					Py_DECREF(pObj);
1087 
1088 					pObj = Py_BuildValue("h", pIPv4->header_checksum());
1089 					PyDict_SetItemString(pIPv4Dict, "HeaderChecksum", pObj);
1090 					Py_DECREF(pObj);
1091 
1092 					pObj = Py_BuildValue("i", pIPv4->time_to_live());
1093 					PyDict_SetItemString(pIPv4Dict, "TimeToLive", pObj);
1094 					Py_DECREF(pObj);
1095 
1096 					iTotalData = pIPv4->total_length();
1097 					iDataOffset = pIPv4->header_length();
1098 				}
1099 				pIPv4Dict = NULL;
1100 			}
1101 
1102 			PyObject* pIcmpDict = PyDict_New();
1103 			if (pDataDict && pIcmpDict)
1104 			{
1105 				if (PyDict_SetItemString(pDataDict, "ICMP", (PyObject*)pIcmpDict) == -1)
1106 					_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", "ICMP", "ICMP");
1107 				else
1108 				{
1109 					Py_XDECREF((PyObject*)pIcmpDict);
1110 
1111 					icmp_header* pICMP = (icmp_header*)(&Message->m_Buffer[0] + 20);
1112 					if ((pICMP->type() == icmp_header::echo_reply) && (Message->m_ElapsedMs >= 0))
1113 					{
1114 						pObj = Py_BuildValue("I", Message->m_ElapsedMs);
1115 						PyDict_SetItemString(pDataDict, "ElapsedMs", pObj);
1116 						Py_DECREF(pObj);
1117 					}
1118 
1119 					pObj = Py_BuildValue("b", pICMP->type());
1120 					PyDict_SetItemString(pIcmpDict, "Type", pObj);
1121 					Py_DECREF(pObj);
1122 
1123 					pObj = Py_BuildValue("b", pICMP->type());
1124 					PyDict_SetItemString(pDataDict, "Status", pObj);
1125 					Py_DECREF(pObj);
1126 
1127 					switch (pICMP->type())
1128 					{
1129 					case icmp_header::echo_reply:
1130 						pObj = Py_BuildValue("s", "echo_reply");
1131 						break;
1132 					case icmp_header::destination_unreachable:
1133 						pObj = Py_BuildValue("s", "destination_unreachable");
1134 						break;
1135 					case icmp_header::time_exceeded:
1136 						pObj = Py_BuildValue("s", "time_exceeded");
1137 						break;
1138 					default:
1139 						pObj = Py_BuildValue("s", "unknown");
1140 					}
1141 
1142 					PyDict_SetItemString(pDataDict, "Description", pObj);
1143 					Py_DECREF(pObj);
1144 
1145 					pObj = Py_BuildValue("b", pICMP->code());
1146 					PyDict_SetItemString(pIcmpDict, "Code", pObj);
1147 					Py_DECREF(pObj);
1148 
1149 					pObj = Py_BuildValue("h", pICMP->checksum());
1150 					PyDict_SetItemString(pIcmpDict, "Checksum", pObj);
1151 					Py_DECREF(pObj);
1152 
1153 					pObj = Py_BuildValue("h", pICMP->identifier());
1154 					PyDict_SetItemString(pIcmpDict, "Identifier", pObj);
1155 					Py_DECREF(pObj);
1156 
1157 					pObj = Py_BuildValue("h", pICMP->sequence_number());
1158 					PyDict_SetItemString(pIcmpDict, "SequenceNumber", pObj);
1159 					Py_DECREF(pObj);
1160 
1161 					iDataOffset += sizeof(icmp_header);
1162 					if (pICMP->type() == icmp_header::destination_unreachable)
1163 					{
1164 						ipv4_header* pIPv4 = (ipv4_header*)(pICMP + 1);
1165 						iDataOffset += pIPv4->header_length() + sizeof(icmp_header);
1166 					}
1167 				}
1168 				pIcmpDict = NULL;
1169 			}
1170 		}
1171 		else
1172 		{
1173 			pObj = Py_BuildValue("b", icmp_header::time_exceeded);
1174 			PyDict_SetItemString(pDataDict, "Status", pObj);
1175 			Py_DECREF(pObj);
1176 
1177 			pObj = Py_BuildValue("s", "time_exceeded");
1178 			PyDict_SetItemString(pDataDict, "Description", pObj);
1179 			Py_DECREF(pObj);
1180 		}
1181 
1182 		std::string		sData(Message->m_Buffer.begin(), Message->m_Buffer.end());
1183 		sData = sData.substr(iDataOffset, iTotalData - iDataOffset);
1184 		pObj = Py_BuildValue("y#", sData.c_str(), sData.length());
1185 		if (PyDict_SetItemString(pDataDict, "Data", pObj) == -1)
1186 			_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", "ICMP", "Data", sData.c_str());
1187 		Py_DECREF(pObj);
1188 
1189 		if (pDataDict)
1190 		{
1191 			Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, pDataDict));
1192 		}
1193 	}
1194 
1195 #define MQTT_CONNECT       1<<4
1196 #define MQTT_CONNACK       2<<4
1197 #define MQTT_PUBLISH       3<<4
1198 #define MQTT_PUBACK        4<<4
1199 #define MQTT_PUBREC        5<<4
1200 #define MQTT_PUBREL        6<<4
1201 #define MQTT_PUBCOMP       7<<4
1202 #define MQTT_SUBSCRIBE     8<<4
1203 #define MQTT_SUBACK        9<<4
1204 #define MQTT_UNSUBSCRIBE  10<<4
1205 #define MQTT_UNSUBACK     11<<4
1206 #define MQTT_PINGREQ      12<<4
1207 #define MQTT_PINGRESP     13<<4
1208 #define MQTT_DISCONNECT   14<<4
1209 
1210 #define MQTT_PROTOCOL	  4
1211 
MQTTPushBackNumber(int iNumber,std::vector<byte> & vVector)1212 	static void MQTTPushBackNumber(int iNumber, std::vector<byte>& vVector)
1213 	{
1214 		vVector.push_back(iNumber / 256);
1215 		vVector.push_back(iNumber % 256);
1216 	}
1217 
MQTTPushBackString(const std::string & sString,std::vector<byte> & vVector)1218 	static void MQTTPushBackString(const std::string& sString, std::vector<byte>& vVector)
1219 	{
1220 		vVector.insert(vVector.end(), sString.begin(), sString.end());
1221 	}
1222 
MQTTPushBackStringWLen(const std::string & sString,std::vector<byte> & vVector)1223 	static void MQTTPushBackStringWLen(const std::string& sString, std::vector<byte>& vVector)
1224 	{
1225 		MQTTPushBackNumber(sString.length(), vVector);
1226 		vVector.insert(vVector.end(), sString.begin(), sString.end());
1227 	}
1228 
ProcessInbound(const ReadEvent * Message)1229 	void CPluginProtocolMQTT::ProcessInbound(const ReadEvent* Message)
1230 	{
1231 		if (m_bErrored)
1232 		{
1233 			_log.Log(LOG_ERROR, "(%s) MQTT protocol errored, discarding additional data.", __func__);
1234 			return;
1235 		}
1236 
1237 		byte loop = 0;
1238 		m_sRetainedData.insert(m_sRetainedData.end(), Message->m_Buffer.begin(), Message->m_Buffer.end());
1239 
1240 		do {
1241 			std::vector<byte>::iterator it = m_sRetainedData.begin();
1242 
1243 			byte		header = *it++;
1244 			byte		bResponseType = header & 0xF0;
1245 			byte		flags = header & 0x0F;
1246 			PyObject* pMqttDict = PyDict_New();
1247 			PyObject* pObj = NULL;
1248 			uint16_t	iPacketIdentifier = 0;
1249 			long		iRemainingLength = 0;
1250 			long		multiplier = 1;
1251 			byte 		encodedByte;
1252 
1253 			do
1254 			{
1255 				encodedByte = *it++;
1256 				iRemainingLength += (encodedByte & 127) * multiplier;
1257 				multiplier *= 128;
1258 				if (multiplier > 128 * 128 * 128)
1259 				{
1260 					_log.Log(LOG_ERROR, "(%s) Malformed Remaining Length.", __func__);
1261 					return;
1262 				}
1263 			} while ((encodedByte & 128) != 0);
1264 
1265 			if (iRemainingLength > std::distance(it, m_sRetainedData.end()))
1266 			{
1267 				// Full packet has not arrived, wait for more data
1268 				_log.Debug(DEBUG_NORM, "(%s) Not enough data received (got %ld, expected %ld).", __func__, (long)std::distance(it, m_sRetainedData.end()), iRemainingLength);
1269 				return;
1270 			}
1271 
1272 			std::vector<byte>::iterator pktend = it + iRemainingLength;
1273 
1274 			switch (bResponseType)
1275 			{
1276 			case MQTT_CONNACK:
1277 			{
1278 				AddStringToDict(pMqttDict, "Verb", std::string("CONNACK"));
1279 				if (flags != 0)
1280 				{
1281 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message flags %u for packet type '%u'", __func__, flags, bResponseType >> 4);
1282 					m_bErrored = true;
1283 					break;
1284 				}
1285 				if (iRemainingLength == 2) // check length is correct
1286 				{
1287 					switch (*it++)
1288 					{
1289 					case 0:
1290 						AddStringToDict(pMqttDict, "Description", std::string("Connection Accepted"));
1291 						break;
1292 					case 1:
1293 						AddStringToDict(pMqttDict, "Description", std::string("Connection Refused, unacceptable protocol version"));
1294 						break;
1295 					case 2:
1296 						AddStringToDict(pMqttDict, "Description", std::string("Connection Refused, identifier rejected"));
1297 						break;
1298 					case 3:
1299 						AddStringToDict(pMqttDict, "Description", std::string("Connection Refused, Server unavailable"));
1300 						break;
1301 					case 4:
1302 						AddStringToDict(pMqttDict, "Description", std::string("Connection Refused, bad user name or password"));
1303 						break;
1304 					case 5:
1305 						AddStringToDict(pMqttDict, "Description", std::string("Connection Refused, not authorized"));
1306 						break;
1307 					default:
1308 						AddStringToDict(pMqttDict, "Description", std::string("Unknown status returned"));
1309 						break;
1310 					}
1311 					AddIntToDict(pMqttDict, "Status", *it++);
1312 				}
1313 				else
1314 				{
1315 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u'", __func__, iRemainingLength, bResponseType >> 4);
1316 					m_bErrored = true;
1317 				}
1318 				break;
1319 			}
1320 			case MQTT_SUBACK:
1321 			{
1322 				AddStringToDict(pMqttDict, "Verb", std::string("SUBACK"));
1323 				iPacketIdentifier = (*it++ << 8) + *it++;
1324 				AddIntToDict(pMqttDict, "PacketIdentifier", iPacketIdentifier);
1325 
1326 				if (flags != 0)
1327 				{
1328 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message flags %u for packet type '%u'", __func__, flags, bResponseType >> 4);
1329 					m_bErrored = true;
1330 					break;
1331 				}
1332 				if (iRemainingLength >= 3) // check length is acceptable
1333 				{
1334 					PyObject* pResponsesList = PyList_New(0);
1335 					if (PyDict_SetItemString(pMqttDict, "Topics", pResponsesList) == -1)
1336 					{
1337 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", __func__, "Topics");
1338 						break;
1339 					}
1340 					Py_DECREF(pResponsesList);
1341 
1342 					while (it != pktend)
1343 					{
1344 						PyObject* pResponseDict = PyDict_New();
1345 						byte Status = *it++;
1346 						AddIntToDict(pResponseDict, "Status", Status);
1347 						switch (Status)
1348 						{
1349 						case 0x00:
1350 							AddStringToDict(pResponseDict, "Description", std::string("Success - Maximum QoS 0"));
1351 							break;
1352 						case 0x01:
1353 							AddStringToDict(pResponseDict, "Description", std::string("Success - Maximum QoS 1"));
1354 							break;
1355 						case 0x02:
1356 							AddStringToDict(pResponseDict, "Description", std::string("Success - Maximum QoS 2"));
1357 							break;
1358 						case 0x80:
1359 							AddStringToDict(pResponseDict, "Description", std::string("Failure"));
1360 							break;
1361 						default:
1362 							AddStringToDict(pResponseDict, "Description", std::string("Unknown status returned"));
1363 							break;
1364 						}
1365 						PyList_Append(pResponsesList, pResponseDict);
1366 						Py_DECREF(pResponseDict);
1367 					}
1368 				}
1369 				else
1370 				{
1371 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u'", __func__, iRemainingLength, bResponseType >> 4);
1372 					m_bErrored = true;
1373 				}
1374 				break;
1375 			}
1376 			case MQTT_PUBACK:
1377 				AddStringToDict(pMqttDict, "Verb", std::string("PUBACK"));
1378 				if (flags != 0)
1379 				{
1380 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message flags %u for packet type '%u'", __func__, flags, bResponseType >> 4);
1381 					m_bErrored = true;
1382 					break;
1383 				}
1384 				if (iRemainingLength == 2) // check length is correct
1385 				{
1386 					iPacketIdentifier = (*it++ << 8) + *it++;
1387 					AddIntToDict(pMqttDict, "PacketIdentifier", iPacketIdentifier);
1388 				}
1389 				else
1390 				{
1391 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u'", __func__, iRemainingLength, bResponseType >> 4);
1392 					m_bErrored = true;
1393 				}
1394 				break;
1395 			case MQTT_PUBREC:
1396 				AddStringToDict(pMqttDict, "Verb", std::string("PUBREC"));
1397 				if (flags != 0)
1398 				{
1399 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message flags %u for packet type '%u'", __func__, flags, bResponseType >> 4);
1400 					m_bErrored = true;
1401 					break;
1402 				}
1403 				if (iRemainingLength == 2) // check length is correct
1404 				{
1405 					iPacketIdentifier = (*it++ << 8) + *it++;
1406 					AddIntToDict(pMqttDict, "PacketIdentifier", iPacketIdentifier);
1407 				}
1408 				else
1409 				{
1410 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u'", __func__, iRemainingLength, bResponseType >> 4);
1411 					m_bErrored = true;
1412 				}
1413 				break;
1414 			case MQTT_PUBCOMP:
1415 				AddStringToDict(pMqttDict, "Verb", std::string("PUBCOMP"));
1416 				if (flags != 0)
1417 				{
1418 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message flags %u for packet type '%u'", __func__, flags, bResponseType >> 4);
1419 					m_bErrored = true;
1420 					break;
1421 				}
1422 				if (iRemainingLength == 2) // check length is correct
1423 				{
1424 					iPacketIdentifier = (*it++ << 8) + *it++;
1425 					AddIntToDict(pMqttDict, "PacketIdentifier", iPacketIdentifier);
1426 				}
1427 				else
1428 				{
1429 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u'", __func__, iRemainingLength, bResponseType >> 4);
1430 					m_bErrored = true;
1431 				}
1432 				break;
1433 			case MQTT_PUBLISH:
1434 			{
1435 				// Fixed Header
1436 				AddStringToDict(pMqttDict, "Verb", std::string("PUBLISH"));
1437 				AddIntToDict(pMqttDict, "DUP", ((flags & 0x08) >> 3));
1438 				long	iQoS = (flags & 0x06) >> 1;
1439 				AddIntToDict(pMqttDict, "QoS", (int)iQoS);
1440 				PyDict_SetItemString(pMqttDict, "Retain", PyBool_FromLong(flags & 0x01));
1441 				// Variable Header
1442 				int		topicLen = (*it++ << 8) + *it++;
1443 				if (topicLen + 2 + (iQoS ? 2 : 0) > iRemainingLength)
1444 				{
1445 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u' (iQoS:%ld, topicLen:%d)", __func__, iRemainingLength, bResponseType >> 4, iQoS, topicLen);
1446 					m_bErrored = true;
1447 					break;
1448 				}
1449 				std::string	sTopic((char const*) & *it, topicLen);
1450 				AddStringToDict(pMqttDict, "Topic", sTopic);
1451 				it += topicLen;
1452 				if (iQoS)
1453 				{
1454 					iPacketIdentifier = (*it++ << 8) + *it++;
1455 					AddIntToDict(pMqttDict, "PacketIdentifier", iPacketIdentifier);
1456 				}
1457 				// Payload
1458 				const char* pPayload = (it == pktend) ? 0 : (const char*) & *it;
1459 				std::string	sPayload(pPayload, std::distance(it, pktend));
1460 				AddBytesToDict(pMqttDict, "Payload", sPayload);
1461 				break;
1462 			}
1463 			case MQTT_UNSUBACK:
1464 				AddStringToDict(pMqttDict, "Verb", std::string("UNSUBACK"));
1465 				if (flags != 0)
1466 				{
1467 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message flags %u for packet type '%u'", __func__, flags, bResponseType >> 4);
1468 					m_bErrored = true;
1469 					break;
1470 				}
1471 				if (iRemainingLength == 2) // check length is correct
1472 				{
1473 					iPacketIdentifier = (*it++ << 8) + *it++;
1474 					AddIntToDict(pMqttDict, "PacketIdentifier", iPacketIdentifier);
1475 				}
1476 				else
1477 				{
1478 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u'", __func__, iRemainingLength, bResponseType >> 4);
1479 					m_bErrored = true;
1480 				}
1481 				break;
1482 			case MQTT_PINGRESP:
1483 				AddStringToDict(pMqttDict, "Verb", std::string("PINGRESP"));
1484 				if (flags != 0)
1485 				{
1486 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message flags %u for packet type '%u'", __func__, flags, bResponseType >> 4);
1487 					m_bErrored = true;
1488 					break;
1489 				}
1490 				if (iRemainingLength != 0) // check length is correct
1491 				{
1492 					_log.Log(LOG_ERROR, "(%s) MQTT protocol violation: Invalid message length %ld for packet type '%u'", __func__, iRemainingLength, bResponseType >> 4);
1493 					m_bErrored = true;
1494 				}
1495 				break;
1496 			default:
1497 				_log.Log(LOG_ERROR, "(%s) MQTT data invalid: packet type '%d' is unknown.", __func__, bResponseType);
1498 				m_bErrored = true;
1499 			}
1500 
1501 			if (!m_bErrored) Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, pMqttDict));
1502 
1503 			m_sRetainedData.erase(m_sRetainedData.begin(), pktend);
1504 		} while (!m_bErrored && m_sRetainedData.size() > 0);
1505 
1506 		if (m_bErrored)
1507 		{
1508 			_log.Log(LOG_ERROR, "(%s) MQTT protocol violation, sending DisconnectedEvent to Connection.", __func__);
1509 			Message->m_pPlugin->MessagePlugin(new DisconnectedEvent(Message->m_pPlugin, Message->m_pConnection));
1510 		}
1511 	}
1512 
ProcessOutbound(const WriteDirective * WriteMessage)1513 	std::vector<byte> CPluginProtocolMQTT::ProcessOutbound(const WriteDirective* WriteMessage)
1514 	{
1515 		std::vector<byte>	vVariableHeader;
1516 		std::vector<byte>	vPayload;
1517 
1518 		std::vector<byte>	retVal;
1519 
1520 		// Sanity check input
1521 		if (!WriteMessage->m_Object || !PyDict_Check(WriteMessage->m_Object))
1522 		{
1523 			_log.Log(LOG_ERROR, "(%s) MQTT Send parameter was not a dictionary, ignored. See Python Plugin wiki page for help.", __func__);
1524 			return retVal;
1525 		}
1526 
1527 		// Extract potential values.  Failures return NULL, success returns borrowed reference
1528 		PyObject* pVerb = PyDict_GetItemString(WriteMessage->m_Object, "Verb");
1529 		if (pVerb)
1530 		{
1531 			if (!PyUnicode_Check(pVerb))
1532 			{
1533 				_log.Log(LOG_ERROR, "(%s) MQTT 'Verb' dictionary entry not a string, ignored. See Python Plugin wiki page for help.", __func__);
1534 				return retVal;
1535 			}
1536 			std::string sVerb = PyUnicode_AsUTF8(pVerb);
1537 
1538 			if (sVerb == "CONNECT")
1539 			{
1540 				MQTTPushBackStringWLen("MQTT", vVariableHeader);
1541 				vVariableHeader.push_back(MQTT_PROTOCOL);
1542 
1543 				byte	bControlFlags = 0;
1544 
1545 				// Client Identifier
1546 				PyObject* pID = PyDict_GetItemString(WriteMessage->m_Object, "ID");
1547 				if (pID && PyUnicode_Check(pID))
1548 				{
1549 					MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pID)), vPayload);
1550 				}
1551 				else
1552 					MQTTPushBackStringWLen("Domoticz", vPayload); // TODO: default ID should be more unique, for example "Domoticz_<plugin_name>_<HwID>"
1553 
1554 				byte	bCleanSession = 1;
1555 				PyObject* pCleanSession = PyDict_GetItemString(WriteMessage->m_Object, "CleanSession");
1556 				if (pCleanSession && PyLong_Check(pCleanSession))
1557 				{
1558 					bCleanSession = (byte)PyLong_AsLong(pCleanSession);
1559 				}
1560 				bControlFlags |= (bCleanSession & 1) << 1;
1561 
1562 				// Will topic
1563 				PyObject* pTopic = PyDict_GetItemString(WriteMessage->m_Object, "WillTopic");
1564 				if (pTopic && PyUnicode_Check(pTopic))
1565 				{
1566 					MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vPayload);
1567 					bControlFlags |= 4;
1568 				}
1569 
1570 				// Will QoS, Retain and Message
1571 				if (bControlFlags & 4)
1572 				{
1573 					PyObject* pQoS = PyDict_GetItemString(WriteMessage->m_Object, "WillQoS");
1574 					if (pQoS && PyLong_Check(pQoS))
1575 					{
1576 						byte bQoS = (byte)PyLong_AsLong(pQoS);
1577 						bControlFlags |= (bQoS & 3) << 3; // Set QoS flag
1578 					}
1579 
1580 					PyObject* pRetain = PyDict_GetItemString(WriteMessage->m_Object, "WillRetain");
1581 					if (pRetain && PyLong_Check(pRetain))
1582 					{
1583 						byte bRetain = (byte)PyLong_AsLong(pRetain);
1584 						bControlFlags |= (bRetain & 1) << 5; // Set retain flag
1585 					}
1586 
1587 					std::string sPayload = "";
1588 					PyObject* pPayload = PyDict_GetItemString(WriteMessage->m_Object, "WillPayload");
1589 					// Support both string and bytes
1590 					//if (pPayload && PyByteArray_Check(pPayload)) // Gives linker error, why?
1591 					if (pPayload && pPayload->ob_type->tp_name == std::string("bytearray"))
1592 					{
1593 						sPayload = std::string(PyByteArray_AsString(pPayload), PyByteArray_Size(pPayload));
1594 					}
1595 					else if (pPayload && PyUnicode_Check(pPayload))
1596 					{
1597 						sPayload = std::string(PyUnicode_AsUTF8(pPayload));
1598 					}
1599 					MQTTPushBackStringWLen(sPayload, vPayload);
1600 				}
1601 
1602 				// Username / Password
1603 				std::string		User;
1604 				std::string		Pass;
1605 				PyObject* pModule = (PyObject*)WriteMessage->m_pPlugin->PythonModule();
1606 				PyObject* pDict = PyObject_GetAttrString(pModule, "Parameters");
1607 				if (pDict)
1608 				{
1609 					PyObject* pUser = PyDict_GetItemString(pDict, "Username");
1610 					if (pUser) User = PyUnicode_AsUTF8(pUser);
1611 					PyObject* pPass = PyDict_GetItemString(pDict, "Password");
1612 					if (pPass) Pass = PyUnicode_AsUTF8(pPass);
1613 					Py_DECREF(pDict);
1614 				}
1615 				if (User.length())
1616 				{
1617 					MQTTPushBackStringWLen(User, vPayload);
1618 					bControlFlags |= 128;
1619 				}
1620 
1621 				if (Pass.length())
1622 				{
1623 					MQTTPushBackStringWLen(Pass, vPayload);
1624 					bControlFlags |= 64;
1625 				}
1626 
1627 				// Control Flags
1628 				vVariableHeader.push_back(bControlFlags);
1629 
1630 				// Keep Alive
1631 				vVariableHeader.push_back(0);
1632 				vVariableHeader.push_back(60);
1633 
1634 				retVal.push_back(MQTT_CONNECT);
1635 			}
1636 			else if (sVerb == "PING")
1637 			{
1638 				retVal.push_back(MQTT_PINGREQ);
1639 			}
1640 			else if (sVerb == "SUBSCRIBE")
1641 			{
1642 				// Variable header - Packet Identifier.
1643 				// If supplied then use it otherwise create one
1644 				PyObject* pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
1645 				long	iPacketIdentifier = 0;
1646 				if (pID && PyLong_Check(pID))
1647 				{
1648 					iPacketIdentifier = PyLong_AsLong(pID);
1649 				}
1650 				else iPacketIdentifier = m_PacketID++;
1651 				MQTTPushBackNumber((int)iPacketIdentifier, vVariableHeader);
1652 
1653 				// Payload is list of topics and QoS numbers
1654 				PyObject* pTopicList = PyDict_GetItemString(WriteMessage->m_Object, "Topics");
1655 				if (!pTopicList || !PyList_Check(pTopicList))
1656 				{
1657 					_log.Log(LOG_ERROR, "(%s) MQTT Subscribe: No 'Topics' list present, nothing to subscribe to. See Python Plugin wiki page for help.", __func__);
1658 					return retVal;
1659 				}
1660 				for (Py_ssize_t i = 0; i < PyList_Size(pTopicList); i++)
1661 				{
1662 					PyObject* pTopicDict = PyList_GetItem(pTopicList, i);
1663 					if (!pTopicDict || !PyDict_Check(pTopicDict))
1664 					{
1665 						_log.Log(LOG_ERROR, "(%s) MQTT Subscribe: Topics list entry is not a dictionary (Topic, QoS), nothing to subscribe to. See Python Plugin wiki page for help.", __func__);
1666 						return retVal;
1667 					}
1668 					PyObject* pTopic = PyDict_GetItemString(pTopicDict, "Topic");
1669 					if (pTopic && PyUnicode_Check(pTopic))
1670 					{
1671 						MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vPayload);
1672 						PyObject* pQoS = PyDict_GetItemString(pTopicDict, "QoS");
1673 						if (pQoS && PyLong_Check(pQoS))
1674 						{
1675 							vPayload.push_back((byte)PyLong_AsLong(pQoS));
1676 						}
1677 						else vPayload.push_back(0);
1678 					}
1679 					else
1680 					{
1681 						_log.Log(LOG_ERROR, "(%s) MQTT Subscribe: 'Topic' not in dictionary (Topic, QoS), nothing to subscribe to, skipping. See Python Plugin wiki page for help.", __func__);
1682 					}
1683 				}
1684 				retVal.push_back(MQTT_SUBSCRIBE | 0x02);	// Add mandatory reserved flags
1685 			}
1686 			else if (sVerb == "UNSUBSCRIBE")
1687 			{
1688 				// Variable Header
1689 				PyObject* pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
1690 				long	iPacketIdentifier = 0;
1691 				if (pID && PyLong_Check(pID))
1692 				{
1693 					iPacketIdentifier = PyLong_AsLong(pID);
1694 				}
1695 				else iPacketIdentifier = m_PacketID++;
1696 				MQTTPushBackNumber((int)iPacketIdentifier, vVariableHeader);
1697 
1698 				// Payload is a Python list of topics
1699 				PyObject* pTopicList = PyDict_GetItemString(WriteMessage->m_Object, "Topics");
1700 				if (!pTopicList || !PyList_Check(pTopicList))
1701 				{
1702 					_log.Log(LOG_ERROR, "(%s) MQTT Subscribe: No 'Topics' list present, nothing to unsubscribe from. See Python Plugin wiki page for help.", __func__);
1703 					return retVal;
1704 				}
1705 				for (Py_ssize_t i = 0; i < PyList_Size(pTopicList); i++)
1706 				{
1707 					PyObject* pTopic = PyList_GetItem(pTopicList, i);
1708 					if (pTopic && PyUnicode_Check(pTopic))
1709 					{
1710 						MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vPayload);
1711 					}
1712 				}
1713 
1714 				retVal.push_back(MQTT_UNSUBSCRIBE | 0x02);
1715 			}
1716 			else if (sVerb == "PUBLISH")
1717 			{
1718 				byte	bByte0 = MQTT_PUBLISH;
1719 
1720 				// Fixed Header
1721 				PyObject* pDUP = PyDict_GetItemString(WriteMessage->m_Object, "Duplicate");
1722 				if (pDUP && PyLong_Check(pDUP))
1723 				{
1724 					long	bDUP = PyLong_AsLong(pDUP);
1725 					if (bDUP) bByte0 |= 0x08; // Set duplicate flag
1726 				}
1727 
1728 				PyObject* pQoS = PyDict_GetItemString(WriteMessage->m_Object, "QoS");
1729 				long	iQoS = 0;
1730 				if (pQoS && PyLong_Check(pQoS))
1731 				{
1732 					iQoS = PyLong_AsLong(pQoS);
1733 					bByte0 |= ((iQoS & 3) << 1); // Set QoS flag
1734 				}
1735 
1736 				PyObject* pRetain = PyDict_GetItemString(WriteMessage->m_Object, "Retain");
1737 				if (pRetain && PyLong_Check(pRetain))
1738 				{
1739 					long	bRetain = PyLong_AsLong(pRetain);
1740 					bByte0 |= (bRetain & 1); // Set retain flag
1741 				}
1742 
1743 				// Variable Header
1744 				PyObject* pTopic = PyDict_GetItemString(WriteMessage->m_Object, "Topic");
1745 				if (pTopic && PyUnicode_Check(pTopic))
1746 				{
1747 					MQTTPushBackStringWLen(std::string(PyUnicode_AsUTF8(pTopic)), vVariableHeader);
1748 				}
1749 				else
1750 				{
1751 					_log.Log(LOG_ERROR, "(%s) MQTT Publish: No 'Topic' specified, nothing to publish. See Python Plugin wiki page for help.", __func__);
1752 					return retVal;
1753 				}
1754 
1755 				PyObject* pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
1756 				if (iQoS)
1757 				{
1758 					long	iPacketIdentifier = 0;
1759 					if (pID && PyLong_Check(pID))
1760 					{
1761 						iPacketIdentifier = PyLong_AsLong(pID);
1762 					}
1763 					else iPacketIdentifier = m_PacketID++;
1764 					MQTTPushBackNumber((int)iPacketIdentifier, vVariableHeader);
1765 				}
1766 				else if (pID)
1767 				{
1768 					_log.Log(LOG_ERROR, "(%s) MQTT Publish: PacketIdentifier ignored, QoS not specified. See Python Plugin wiki page for help.", __func__);
1769 				}
1770 
1771 				// Payload
1772 				std::string sPayload = "";
1773 				PyObject* pPayload = PyDict_GetItemString(WriteMessage->m_Object, "Payload");
1774 				// Support both string and bytes
1775 				//if (pPayload && PyByteArray_Check(pPayload)) // Gives linker error, why?
1776 				if (pPayload) {
1777 					_log.Debug(DEBUG_NORM, "(%s) MQTT Publish: payload %p (%s)", __func__, pPayload, pPayload->ob_type->tp_name);
1778 				}
1779 				if (pPayload && pPayload->ob_type->tp_name == std::string("bytearray"))
1780 				{
1781 					sPayload = std::string(PyByteArray_AsString(pPayload), PyByteArray_Size(pPayload));
1782 				}
1783 				else if (pPayload && PyUnicode_Check(pPayload))
1784 				{
1785 					sPayload = std::string(PyUnicode_AsUTF8(pPayload));
1786 				}
1787 				MQTTPushBackString(sPayload, vPayload);
1788 
1789 				retVal.push_back(bByte0);
1790 			}
1791 			else if (sVerb == "PUBREL")
1792 			{
1793 				// Variable Header
1794 				PyObject* pID = PyDict_GetItemString(WriteMessage->m_Object, "PacketIdentifier");
1795 				long	iPacketIdentifier = 0;
1796 				if (pID && PyLong_Check(pID))
1797 				{
1798 					iPacketIdentifier = PyLong_AsLong(pID);
1799 					MQTTPushBackNumber((int)iPacketIdentifier, vVariableHeader);
1800 				}
1801 				else
1802 				{
1803 					_log.Log(LOG_ERROR, "(%s) MQTT Publish: No valid PacketIdentifier specified. See Python Plugin wiki page for help.", __func__);
1804 					return retVal;
1805 				}
1806 
1807 				retVal.push_back(MQTT_PUBREL & 0x02);
1808 			}
1809 			else if (sVerb == "DISCONNECT")
1810 			{
1811 				retVal.push_back(MQTT_DISCONNECT);
1812 				retVal.push_back(0);
1813 			}
1814 			else
1815 			{
1816 				_log.Log(LOG_ERROR, "(%s) MQTT 'Verb' invalid '%s' is unknown.", __func__, sVerb.c_str());
1817 				return retVal;
1818 			}
1819 		}
1820 
1821 		// Build final message
1822 		unsigned long	iRemainingLength = vVariableHeader.size() + vPayload.size();
1823 		do
1824 		{
1825 			byte	encodedByte = iRemainingLength % 128;
1826 			iRemainingLength = iRemainingLength / 128;
1827 
1828 			// if there are more data to encode, set the top bit of this byte
1829 			if (iRemainingLength > 0)
1830 				encodedByte |= 128;
1831 			retVal.push_back(encodedByte);
1832 
1833 		} while (iRemainingLength > 0);
1834 
1835 		retVal.insert(retVal.end(), vVariableHeader.begin(), vVariableHeader.end());
1836 		retVal.insert(retVal.end(), vPayload.begin(), vPayload.end());
1837 
1838 		return retVal;
1839 	}
1840 
1841 	/*
1842 
1843 	See: https://tools.ietf.org/html/rfc6455#section-5.2
1844 
1845 	  0                   1                   2                   3
1846 	  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1847 	 +-+-+-+-+-------+-+-------------+-------------------------------+
1848 	 |F|R|R|R| opcode|M| Payload len |    Extended payload length    |
1849 	 |I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
1850 	 |N|V|V|V|       |S|             |   (if payload len==126/127)   |
1851 	 | |1|2|3|       |K|             |                               |
1852 	 +-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
1853 	 |     Extended payload length continued, if payload len == 127  |
1854 	 + - - - - - - - - - - - - - - - +-------------------------------+
1855 	 |                               |Masking-key, if MASK set to 1  |
1856 	 +-------------------------------+-------------------------------+
1857 	 | Masking-key (continued)       |          Payload Data         |
1858 	 +-------------------------------- - - - - - - - - - - - - - - - +
1859 	 :                     Payload Data continued ...                :
1860 	 + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
1861 	 |                     Payload Data continued ...                |
1862 	 +---------------------------------------------------------------+
1863 
1864 	*/
1865 
ProcessWholeMessage(std::vector<byte> & vMessage,const ReadEvent * Message)1866 	bool CPluginProtocolWS::ProcessWholeMessage(std::vector<byte>& vMessage, const ReadEvent* Message)
1867 	{
1868 		while (vMessage.size())
1869 		{
1870 			// Look for a complete message
1871 			std::vector<byte>	vPayload;
1872 			size_t		iOffset = 0;
1873 			int			iOpCode = 0;
1874 			long		lMaskingKey = 0;
1875 			bool		bFinish = false;
1876 
1877 			bFinish = (vMessage[iOffset] & 0x80);				// Indicates that this is the final fragment in a message if true
1878 			if (vMessage[iOffset] & 0x0F)
1879 			{
1880 				iOpCode = (vMessage[iOffset] & 0x0F);			// %x0 denotes a continuation frame
1881 			}
1882 			// %x1 denotes a text frame
1883 			// %x2 denotes a binary frame
1884 			// %x8 denotes a connection close
1885 			// %x9 denotes a ping
1886 			// %xA denotes a pong
1887 			iOffset++;
1888 			bool	bMasked = (vMessage[iOffset] & 0x80);			// Is the payload masked?
1889 			long	lPayloadLength = (vMessage[iOffset] & 0x7F);	// if < 126 then this is the length
1890 			if (lPayloadLength == 126)
1891 			{
1892 				if (vMessage.size() < (iOffset + 2))
1893 					return false;
1894 				lPayloadLength = (vMessage[iOffset + 1] << 8) + vMessage[iOffset + 2];
1895 				iOffset += 2;
1896 			}
1897 			else if (lPayloadLength == 127)							// 64 bit lengths not supported
1898 			{
1899 				_log.Log(LOG_ERROR, "(%s) 64 bit WebSocket messages lengths not supported.", __func__);
1900 				vMessage.clear();
1901 				iOffset += 5;
1902 				return false;
1903 			}
1904 			iOffset++;
1905 
1906 			byte* pbMask = NULL;
1907 			if (bMasked)
1908 			{
1909 				if (vMessage.size() < iOffset)
1910 					return false;
1911 				lMaskingKey = (long)vMessage[iOffset];
1912 				pbMask = &vMessage[iOffset];
1913 				iOffset += 4;
1914 			}
1915 
1916 			if (vMessage.size() < (iOffset + lPayloadLength))
1917 				return false;
1918 
1919 			// Append the payload to the existing (maybe) payload
1920 			if (lPayloadLength)
1921 			{
1922 				vPayload.reserve(vPayload.size() + lPayloadLength);
1923 				for (size_t i = iOffset; i < iOffset + lPayloadLength; i++)
1924 				{
1925 					vPayload.push_back(vMessage[i]);
1926 				}
1927 				iOffset += lPayloadLength;
1928 			}
1929 
1930 			PyObject* pDataDict = (PyObject*)PyDict_New();
1931 			PyObject* pPayload = NULL;
1932 
1933 			// Handle full message
1934 			PyObject* pObj = Py_BuildValue("N", PyBool_FromLong(bFinish));
1935 			if (PyDict_SetItemString(pDataDict, "Finish", pObj) == -1)
1936 				_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "Finish", bFinish ? "True" : "False");
1937 			Py_DECREF(pObj);
1938 
1939 			// Masked data?
1940 			if (lMaskingKey)
1941 			{
1942 				// Unmask data
1943 				for (int i = 0; i < lPayloadLength; i++)
1944 				{
1945 					vPayload[i] ^= pbMask[i % 4];
1946 				}
1947 				PyObject* pObj = Py_BuildValue("i", lMaskingKey);
1948 				if (PyDict_SetItemString(pDataDict, "Mask", pObj) == -1)
1949 					_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%ld' to dictionary.", __func__, "Mask", lMaskingKey);
1950 				Py_DECREF(pObj);
1951 			}
1952 
1953 			switch (iOpCode)
1954 			{
1955 			case 0x01:	// Text message
1956 			{
1957 				std::string		sPayload(vPayload.begin(), vPayload.end());
1958 				pPayload = Py_BuildValue("s", sPayload.c_str());
1959 				break;
1960 			}
1961 			case 0x02:	// Binary message
1962 				break;
1963 			case 0x08:	// Connection Close
1964 			{
1965 				PyObject* pObj = Py_BuildValue("s", "Close");
1966 				if (PyDict_SetItemString(pDataDict, "Operation", pObj) == -1)
1967 					_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "Operation", "Close");
1968 				Py_DECREF(pObj);
1969 				if (vPayload.size() == 2)
1970 				{
1971 					int		iReasonCode = (vPayload[0] << 8) + vPayload[1];
1972 					pPayload = Py_BuildValue("i", iReasonCode);
1973 				}
1974 				break;
1975 			}
1976 			case 0x09:	// Ping
1977 			{
1978 				pDataDict = (PyObject*)PyDict_New();
1979 				PyObject* pObj = Py_BuildValue("s", "Ping");
1980 				if (PyDict_SetItemString(pDataDict, "Operation", pObj) == -1)
1981 					_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "Operation", "Ping");
1982 				Py_DECREF(pObj);
1983 				break;
1984 			}
1985 			case 0x0A:	// Pong
1986 			{
1987 				pDataDict = (PyObject*)PyDict_New();
1988 				PyObject* pObj = Py_BuildValue("s", "Pong");
1989 				if (PyDict_SetItemString(pDataDict, "Operation", pObj) == -1)
1990 					_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "Operation", "Pong");
1991 				Py_DECREF(pObj);
1992 				break;
1993 			}
1994 			default:
1995 				_log.Log(LOG_ERROR, "(%s) Unknown Operation Code (%d) encountered.", __func__, iOpCode);
1996 			}
1997 
1998 			// If there is a payload but not handled then map it as binary
1999 			if (vPayload.size() && !pPayload)
2000 			{
2001 				pPayload = Py_BuildValue("y#", &vPayload[0], vPayload.size());
2002 			}
2003 
2004 			// If there is a payload then add it
2005 			if (pPayload)
2006 			{
2007 				if (PyDict_SetItemString(pDataDict, "Payload", pPayload) == -1)
2008 					_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", __func__, "Payload");
2009 				Py_DECREF(pPayload);
2010 			}
2011 
2012 			Message->m_pPlugin->MessagePlugin(new onMessageCallback(Message->m_pPlugin, Message->m_pConnection, pDataDict));
2013 
2014 			// Remove the processed message from retained data
2015 			vMessage.erase(vMessage.begin(), vMessage.begin() + iOffset);
2016 
2017 			return true;
2018 		}
2019 
2020 		return false;
2021 	}
2022 
ProcessInbound(const ReadEvent * Message)2023 	void CPluginProtocolWS::ProcessInbound(const ReadEvent* Message)
2024 	{
2025 		//	Although messages can be fragmented, control messages can be inserted in between fragments
2026 		//	so try to process just the message first, then retained data and the message
2027 		std::vector<byte>	Buffer = Message->m_Buffer;
2028 		if (ProcessWholeMessage(Buffer, Message))
2029 		{
2030 			return;		// Message processed
2031 		}
2032 
2033 		// Add new message to retained data, process all messages if this one is the finish of a message
2034 		m_sRetainedData.insert(m_sRetainedData.end(), Message->m_Buffer.begin(), Message->m_Buffer.end());
2035 
2036 		// Always process the whole buffer because we can't know if we have whole, multiple or even complete messages unless we work through from the start
2037 		if (ProcessWholeMessage(m_sRetainedData, Message))
2038 		{
2039 			return;		// Message processed
2040 		}
2041 
2042 	}
2043 
ProcessOutbound(const WriteDirective * WriteMessage)2044 	std::vector<byte> CPluginProtocolWS::ProcessOutbound(const WriteDirective* WriteMessage)
2045 	{
2046 		std::vector<byte>	retVal;
2047 
2048 		//
2049 		//	Parameters need to be in a dictionary.
2050 		//	if a 'URL' key is found message is assumed to be HTTP otherwise WebSocket is assumed
2051 		//
2052 		if (!WriteMessage->m_Object || !PyDict_Check(WriteMessage->m_Object))
2053 		{
2054 			_log.Log(LOG_ERROR, "(%s) Dictionary parameter expected.", __func__);
2055 		}
2056 		else
2057 		{
2058 			PyObject* pURL = PyDict_GetItemString(WriteMessage->m_Object, "URL");
2059 			if (pURL)
2060 			{
2061 				// Is a verb specified?
2062 				PyObject* pVerb = PyDict_GetItemString(WriteMessage->m_Object, "Verb");
2063 				if (!pVerb)
2064 				{
2065 					PyObject* pObj = Py_BuildValue("s", "GET");
2066 					if (PyDict_SetItemString(WriteMessage->m_Object, "Verb", pObj) == -1)
2067 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "Verb", "GET");
2068 					Py_DECREF(pObj);
2069 				}
2070 
2071 				// Required headers specified?
2072 				PyObject* pHeaders = PyDict_GetItemString(WriteMessage->m_Object, "Headers");
2073 				if (!pHeaders)
2074 				{
2075 					pHeaders = (PyObject*)PyDict_New();
2076 					if (PyDict_SetItemString(WriteMessage->m_Object, "Headers", (PyObject*)pHeaders) == -1)
2077 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s' to dictionary.", "WS", "Headers");
2078 					Py_DECREF(pHeaders);
2079 				}
2080 				PyObject* pConnection = PyDict_GetItemString(pHeaders, "Connection");
2081 				if (!pConnection)
2082 				{
2083 					PyObject* pObj = Py_BuildValue("s", "keep-alive, Upgrade");
2084 					if (PyDict_SetItemString(pHeaders, "Connection", pObj) == -1)
2085 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "Connection", "Upgrade");
2086 					Py_DECREF(pObj);
2087 				}
2088 				PyObject* pUpgrade = PyDict_GetItemString(pHeaders, "Upgrade");
2089 				if (!pUpgrade)
2090 				{
2091 					PyObject* pObj = Py_BuildValue("s", "websocket");
2092 					if (PyDict_SetItemString(pHeaders, "Upgrade", pObj) == -1)
2093 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "Upgrade", "websocket");
2094 					Py_DECREF(pObj);
2095 				}
2096 				PyObject* pUserAgent = PyDict_GetItemString(pHeaders, "User-Agent");
2097 				if (!pUserAgent)
2098 				{
2099 					PyObject* pObj = Py_BuildValue("s", "Domoticz/1.0");
2100 					if (PyDict_SetItemString(pHeaders, "User-Agent", pObj) == -1)
2101 						_log.Log(LOG_ERROR, "(%s) failed to add key '%s', value '%s' to dictionary.", __func__, "User-Agent", "Domoticz/1.0");
2102 					Py_DECREF(pObj);
2103 				}
2104 
2105 				// Use parent HTTP protocol object to do the actual formatting
2106 				return CPluginProtocolHTTP::ProcessOutbound(WriteMessage);
2107 			}
2108 			else
2109 			{
2110 				int			iOpCode = 0;
2111 				long		lMaskingKey = 0;
2112 				long		lPayloadLength = 0;
2113 				byte		bMaskBit = 0x00;
2114 
2115 				PyObject* pOperation = PyDict_GetItemString(WriteMessage->m_Object, "Operation");
2116 				PyObject* pPayload = PyDict_GetItemString(WriteMessage->m_Object, "Payload");
2117 				PyObject* pMask = PyDict_GetItemString(WriteMessage->m_Object, "Mask");
2118 
2119 				if (pOperation)
2120 				{
2121 					if (!PyUnicode_Check(pOperation))
2122 					{
2123 						_log.Log(LOG_ERROR, "(%s) Expected dictionary 'Operation' key to have a string value.", __func__);
2124 						return retVal;
2125 					}
2126 
2127 					std::string	sOperation = PyUnicode_AsUTF8(pOperation);
2128 					if (sOperation == "Ping")
2129 					{
2130 						iOpCode = 0x09;
2131 					}
2132 					else if (sOperation == "Pong")
2133 					{
2134 						iOpCode = 0x0A;
2135 					}
2136 					else if (sOperation == "Close")
2137 					{
2138 						iOpCode = 0x08;
2139 					}
2140 				}
2141 
2142 				// If there is no specific OpCode then set it from the payload datatype
2143 				if (pPayload)
2144 				{
2145 					if (PyUnicode_Check(pPayload))
2146 					{
2147 						lPayloadLength = PyUnicode_GetLength(pPayload);
2148 						if (!iOpCode) iOpCode = 0x01;				// Text message
2149 					}
2150 					else if (PyBytes_Check(pPayload))
2151 					{
2152 						lPayloadLength = PyBytes_Size(pPayload);
2153 						if (!iOpCode) iOpCode = 0x02;				// Binary message
2154 					}
2155 					else if (pPayload->ob_type->tp_name == std::string("bytearray"))
2156 					{
2157 						lPayloadLength = PyByteArray_Size(pPayload);
2158 						if (!iOpCode) iOpCode = 0x02;				// Binary message
2159 					}
2160 				}
2161 
2162 				if (pMask)
2163 				{
2164 					if (PyLong_Check(pMask))
2165 					{
2166 						lMaskingKey = PyLong_AsLong(pMask);
2167 						bMaskBit = 0x80;							// Set mask bit in header
2168 					}
2169 					else if (PyUnicode_Check(pMask))
2170 					{
2171 						std::string	sMask = PyUnicode_AsUTF8(pMask);
2172 						lMaskingKey = atoi(sMask.c_str());
2173 						bMaskBit = 0x80;							// Set mask bit in header
2174 					}
2175 					else
2176 					{
2177 						_log.Log(LOG_ERROR, "(%s) Invalid mask, expected number (integer or string).", __func__);
2178 						return retVal;
2179 					}
2180 				}
2181 
2182 				// Assemble the actual message
2183 				retVal.reserve(lPayloadLength + 16);		// Masking relies on vector not reallocating during message assembly
2184 				retVal.push_back(0x80 | iOpCode);
2185 				if (lPayloadLength < 126)
2186 				{
2187 					retVal.push_back((bMaskBit | lPayloadLength) & 0xFF);	// Short length
2188 				}
2189 				else
2190 				{
2191 					retVal.push_back(bMaskBit | 126);
2192 					retVal.push_back(lPayloadLength >> 24);
2193 					retVal.push_back((lPayloadLength >> 16) & 0xFF);
2194 					retVal.push_back((lPayloadLength >> 8) & 0xFF);
2195 					retVal.push_back(lPayloadLength & 0xFF);				// Longer length
2196 				}
2197 
2198 				byte* pbMask = NULL;
2199 				if (bMaskBit)
2200 				{
2201 					retVal.push_back(lMaskingKey >> 24);
2202 					pbMask = &retVal[retVal.size() - 1];
2203 					retVal.push_back((lMaskingKey >> 16) & 0xFF);
2204 					retVal.push_back((lMaskingKey >> 8) & 0xFF);
2205 					retVal.push_back(lMaskingKey & 0xFF);					// Encode mask
2206 				}
2207 
2208 				if (pPayload)
2209 				{
2210 					if (PyUnicode_Check(pPayload))
2211 					{
2212 						std::string	sPayload = PyUnicode_AsUTF8(pPayload);
2213 						for (int i = 0; i < lPayloadLength; i++)
2214 						{
2215 							retVal.push_back(sPayload[i] ^ pbMask[i % 4]);
2216 						}
2217 					}
2218 					else if (PyBytes_Check(pPayload))
2219 					{
2220 						byte* pByte = (byte*)PyBytes_AsString(pPayload);
2221 						for (int i = 0; i < lPayloadLength; i++)
2222 						{
2223 							retVal.push_back(pByte[i] ^ pbMask[i % 4]);
2224 						}
2225 					}
2226 					else if (pPayload->ob_type->tp_name == std::string("bytearray"))
2227 					{
2228 						byte* pByte = (byte*)PyByteArray_AsString(pPayload);
2229 						for (int i = 0; i < lPayloadLength; i++)
2230 						{
2231 							retVal.push_back(pByte[i] ^ pbMask[i % 4]);
2232 						}
2233 					}
2234 				}
2235 
2236 			}
2237 		}
2238 
2239 		return retVal;
2240 	}
2241 }
2242 #endif
2243