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¶m2=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