void handleDataMessage(MQTT::MessageData& md) { TEMBOO_TRACE("data: "); MQTT::Message& msg = md.message; char* c; unsigned int count; for (c = (char*)msg.payload, count = 0; *c != '\0' && isdigit(*c) && count < msg.payloadlen; c++) { count++; if (count > 5) { TEMBOO_TRACELN(" long"); return; } } if (count == 0) { TEMBOO_TRACELN(" short"); return; } if (TAG_VALUE_SEPARATOR != *c) { TEMBOO_TRACELN(" bad msg"); return; } // Replace the : with \0 so we can use strtoul. *c = '\0'; unsigned long requestId = strtoul((char*)msg.payload, NULL, 10); if (UINT16_MAX < requestId) { TEMBOO_TRACELN(" bad id"); return; } if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) { TEMBOO_TRACE(" stale id: "); TEMBOO_TRACELN(requestId); return; } g_currentChoreo->setResponseData(c + 1, msg.payloadlen - strlen((char*)msg.payload) - 1); TEMBOO_TRACELN(" ok"); }
void handleTimeMessage(MQTT::MessageData& md) { TEMBOO_TRACE("time: "); MQTT::Message& msg = md.message; // Time messages should be <= 10 characters long. if (msg.payloadlen > 10) { TEMBOO_TRACELN("long"); return; } if (msg.payloadlen == 0) { TEMBOO_TRACELN("short"); return; } // Payload should consist only of digits (0 - 9) for (unsigned int i = 0; i < msg.payloadlen; i++) { if (! isdigit(((char*)msg.payload)[i])) { TEMBOO_TRACELN("!digit"); return; } } char timeStr[11]; memcpy(timeStr, msg.payload, msg.payloadlen); timeStr[msg.payloadlen] = '\0'; TEMBOO_TRACE(timeStr); uint32_t t = strtoul(timeStr, NULL, 10); TembooMQTTSession::setTime(t); TEMBOO_TRACELN(" ok"); }
int TembooSession::executeChoreo( const char* accountName, const char* appKeyName, const char* appKeyValue, const char* path, const ChoreoInputSet& inputSet, const ChoreoOutputSet& outputSet, const ChoreoPreset& preset) { DataFormatter fmt(&inputSet, &outputSet, &preset); char auth[HMAC_HEX_SIZE_BYTES + 1]; char buffer[11]; // We use the current time-of-day as salt on the app key. // We keep track of time-of-day by getting the current time // from the server and applying an offset (the length of time // we've been running.) uint32toa((uint32_t)TembooSession::getTime(), buffer); uint16_t contentLength = getAuth(fmt, appKeyValue, buffer, auth); m_client.stop(); m_client.flush(); int connected = 0; TEMBOO_TRACE("Connecting: "); // reserve space for the "host" string sufficient to hold either the // (dotted-quad) IP address + port, or the default <account>.temboolive.com // host string. int hostLen = (m_addr == INADDR_NONE ? (strlen_P(TEMBOO_DOMAIN) + strlen(accountName) + 1):21); char host[hostLen]; // If no explicit IP address was specified (the normal case), construct // the "host" string from the account name and the temboo domain name. if (m_addr == INADDR_NONE) { strcpy(host, accountName); strcat_P(host, TEMBOO_DOMAIN); TEMBOO_TRACELN(host); connected = m_client.connect(host, m_port); } else { // If an IP address was explicitly specified (presumably for testing purposes), // convert it to a dotted-quad text string. host[0] = '\0'; for(int i = 0; i < 4; i++) { uint16toa(m_addr[i], &host[strlen(host)]); strcat(host, "."); } // replace the last '.' with ':' host[strlen(host)-1] = ':'; // append the port number uint16toa(m_port, &host[strlen(host)]); TEMBOO_TRACELN(host); connected = m_client.connect(m_addr, m_port); } if (connected) { TEMBOO_TRACELN("OK. req:"); qsendProgmem(POST); qsendProgmem(BASE_CHOREO_URI); qsend(path); qsendProgmem(SDK_ID); qsendlnProgmem(POSTAMBLE); // Send our custom authentication header // (app-key-name:hmac) qsendProgmem(HEADER_AUTH); qsend(appKeyName); qsend(":"); qsendln(auth); // send the standard host header qsendProgmem(HEADER_HOST); qsendln(host); // send the standard accept header qsendlnProgmem(HEADER_ACCEPT); // send our custom account name neader qsendProgmem(HEADER_ORG); qsend(accountName); qsendlnProgmem(HEADER_DOM); // send the standard content type header qsendlnProgmem(HEADER_CONTENT_TYPE); // send our custom client time header qsendProgmem(HEADER_TIME); qsendln(buffer); // send the standard content length header qsendProgmem(HEADER_CONTENT_LENGTH); qsendln(uint16toa(contentLength, buffer)); qsendProgmem(EOL); // Format and send the body of the request fmt.reset(); while(fmt.hasNext()) { qsend(fmt.next()); } qsendProgmem(EOL); qflush(); return 0; } else { TEMBOO_TRACELN("FAIL"); return 1; } }
void handleAckMessage(MQTT::MessageData& md) { TEMBOO_TRACE("ack: "); MQTT::Message& msg = md.message; // Expected max length is 11 (for 65535:65535) if (msg.payloadlen > 11) { TEMBOO_TRACELN("long"); return; } if (msg.payloadlen == 0) { TEMBOO_TRACELN("short"); return; } // Copy the payload and nul terminate it; char ackStr[12]; memcpy(ackStr, msg.payload, msg.payloadlen); ackStr[msg.payloadlen] = '\0'; TEMBOO_TRACE(ackStr); if (!validateUint16PairMessage(ackStr)) { TEMBOO_TRACELN(" bad msg"); return; } char* next; unsigned long ackCode = TEMBOO_ERROR_FAILURE; unsigned long requestId = strtoul(ackStr, &next, 10); // validate only checks that the request ID // has at least 1 but no more than 6 digits. // so we have to check the actual value here. if (UINT16_MAX < requestId) { TEMBOO_TRACELN(" bad id"); return; } // If the request ID in the message doesn't match the // current request ID, then it's a stale ack. if ((NULL == g_currentChoreo) || (g_currentChoreo->getRequestId() != (uint16_t)requestId)) { TEMBOO_TRACE(" stale id: "); TEMBOO_TRACELN(ackStr); return; } next++; ackCode = strtoul(next, NULL, 10); // Validate only checks that the ack code // has at least 1 but no more than 6 digits, // so we have to check the actual value here. if (UINT16_MAX < ackCode) { TEMBOO_TRACELN(" bad code"); return; } // FINALLY, everything's OK. // pass the value to the waiting choreo. g_currentChoreo->setAckCode(ackCode); TEMBOO_TRACELN(" ok"); }
int TembooSession::executeChoreo( const char* accountName, const char* appKeyName, const char* appKeyValue, const char* path, const ChoreoInputSet& inputSet, const ChoreoOutputSet& outputSet, const ChoreoPreset& preset) { DataFormatter fmt(&inputSet, &outputSet, &preset); char auth[HMAC_HEX_SIZE_BYTES + 1]; char buffer[11]; // We use the current time-of-day as salt on the app key. // We keep track of time-of-day by getting the current time // from the server and applying an offset (the length of time // we've been running.) uint32toa((uint32_t)TembooSession::getTime(), buffer); uint16_t contentLength = getAuth(fmt, appKeyValue, buffer, auth); m_client.stop(); m_client.flush(); int connected = 0; TEMBOO_TRACE("Connecting: "); // reserve space for the "host" string sufficient to hold either the // (dotted-quad) IP address + port, or the default <account>.temboolive.com // host string. int hostLen = strlen_P(TEMBOO_DOMAIN) + strlen(accountName) + 1; char host[hostLen]; // Construct the "host" string from the account name and the temboo domain name. strcpy(host, accountName); strcat_P(host, TEMBOO_DOMAIN); bool useProxy = false; if (m_addr == INADDR_NONE) { TEMBOO_TRACELN(host); connected = m_client.connect(host, m_port); } else { TEMBOO_TRACELN(host); connected = m_client.connect(m_addr, m_port); useProxy = true; } if (connected) { TEMBOO_TRACELN("OK. req:"); qsendProgmem(POST); if(useProxy) { qsendProgmem(HTTP); qsend(host); } qsendProgmem(BASE_CHOREO_URI); qsend(path); qsendProgmem(SDK_ID); qsendlnProgmem(POSTAMBLE); // Send our custom authentication header // (app-key-name:hmac) qsendProgmem(HEADER_AUTH); qsend(appKeyName); qsend(":"); qsendln(auth); // send the standard host header qsendProgmem(HEADER_HOST); qsendln(host); // send the standard accept header qsendlnProgmem(HEADER_ACCEPT); // send our custom account name neader qsendProgmem(HEADER_ORG); qsend(accountName); qsendlnProgmem(HEADER_DOM); // send the standard content type header qsendlnProgmem(HEADER_CONTENT_TYPE); // send our custom client time header qsendProgmem(HEADER_TIME); qsendln(buffer); // send the standard content length header qsendProgmem(HEADER_CONTENT_LENGTH); qsendln(uint16toa(contentLength, buffer)); qsendProgmem(EOL); // Format and send the body of the request fmt.reset(); while(fmt.hasNext()) { qsend(fmt.next()); } qsendProgmem(EOL); qflush(); return 0; } else { TEMBOO_TRACELN("FAIL"); return 1; } }