├── LICENSE ├── MQTTClient-C └── src │ ├── MQTTClient.c │ ├── MQTTClient.h │ └── linux │ ├── MQTTLinux.c │ └── MQTTLinux.h ├── MQTTPacket └── src │ ├── MQTTConnect.h │ ├── MQTTConnectClient.c │ ├── MQTTConnectServer.c │ ├── MQTTDeserializePublish.c │ ├── MQTTFormat.c │ ├── MQTTFormat.h │ ├── MQTTPacket.c │ ├── MQTTPacket.h │ ├── MQTTPublish.h │ ├── MQTTSerializePublish.c │ ├── MQTTSubscribe.h │ ├── MQTTSubscribeClient.c │ ├── MQTTSubscribeServer.c │ ├── MQTTUnsubscribe.h │ ├── MQTTUnsubscribeClient.c │ ├── MQTTUnsubscribeServer.c │ └── StackTrace.h ├── Makefile ├── README.md ├── ini.c ├── ini.h └── wink-handler.c /LICENSE: -------------------------------------------------------------------------------- 1 | wink-handler.c: 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are met: 5 | 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | 12 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 13 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 14 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 15 | ARE DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY 16 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 17 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 19 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 20 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 21 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 22 | 23 | Code under MQTTClient-C and MQTTPacket: 24 | 25 | The Eclipse Distribution License as found at http://www.eclipse.org/org/documents/edl-v10.php 26 | 27 | ini.c and ini.h: 28 | 29 | The "inih" library is distributed under the New BSD license: 30 | 31 | Copyright (c) 2009, Ben Hoyt 32 | All rights reserved. 33 | 34 | Redistribution and use in source and binary forms, with or without 35 | modification, are permitted provided that the following conditions are met: 36 | * Redistributions of source code must retain the above copyright 37 | notice, this list of conditions and the following disclaimer. 38 | * Redistributions in binary form must reproduce the above copyright 39 | notice, this list of conditions and the following disclaimer in the 40 | documentation and/or other materials provided with the distribution. 41 | * Neither the name of Ben Hoyt nor the names of its contributors 42 | may be used to endorse or promote products derived from this software 43 | without specific prior written permission. 44 | 45 | THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY 46 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 47 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 48 | DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY 49 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 50 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 51 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 52 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 53 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 54 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 55 | -------------------------------------------------------------------------------- /MQTTClient-C/src/MQTTClient.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, 2015 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | #include "MQTTClient.h" 17 | 18 | static void NewMessageData(MessageData* md, MQTTString* aTopicName, MQTTMessage* aMessage) { 19 | md->topicName = aTopicName; 20 | md->message = aMessage; 21 | } 22 | 23 | 24 | static int getNextPacketId(MQTTClient *c) { 25 | return c->next_packetid = (c->next_packetid == MAX_PACKET_ID) ? 1 : c->next_packetid + 1; 26 | } 27 | 28 | 29 | static int sendPacket(MQTTClient* c, int length, Timer* timer) 30 | { 31 | int rc = FAILURE, 32 | sent = 0; 33 | 34 | while (sent < length && !TimerIsExpired(timer)) 35 | { 36 | rc = c->ipstack->mqttwrite(c->ipstack, &c->buf[sent], length, TimerLeftMS(timer)); 37 | if (rc < 0) // there was an error writing the data 38 | break; 39 | sent += rc; 40 | } 41 | if (sent == length) 42 | { 43 | TimerCountdown(&c->ping_timer, c->keepAliveInterval); // record the fact that we have successfully sent the packet 44 | rc = SUCCESS; 45 | } 46 | else 47 | rc = FAILURE; 48 | return rc; 49 | } 50 | 51 | 52 | void MQTTClientInit(MQTTClient* c, Network* network, unsigned int command_timeout_ms, 53 | unsigned char* sendbuf, size_t sendbuf_size, unsigned char* readbuf, size_t readbuf_size) 54 | { 55 | int i; 56 | c->ipstack = network; 57 | 58 | for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) 59 | c->messageHandlers[i].topicFilter = 0; 60 | c->command_timeout_ms = command_timeout_ms; 61 | c->buf = sendbuf; 62 | c->buf_size = sendbuf_size; 63 | c->readbuf = readbuf; 64 | c->readbuf_size = readbuf_size; 65 | c->isconnected = 0; 66 | c->ping_outstanding = 0; 67 | c->defaultMessageHandler = NULL; 68 | c->next_packetid = 1; 69 | TimerInit(&c->ping_timer); 70 | #if defined(MQTT_TASK) 71 | MutexInit(&c->mutex); 72 | #endif 73 | } 74 | 75 | 76 | static int decodePacket(MQTTClient* c, int* value, int timeout) 77 | { 78 | unsigned char i; 79 | int multiplier = 1; 80 | int len = 0; 81 | const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4; 82 | 83 | *value = 0; 84 | do 85 | { 86 | int rc = MQTTPACKET_READ_ERROR; 87 | 88 | if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) 89 | { 90 | rc = MQTTPACKET_READ_ERROR; /* bad data */ 91 | goto exit; 92 | } 93 | rc = c->ipstack->mqttread(c->ipstack, &i, 1, timeout); 94 | if (rc != 1) 95 | goto exit; 96 | *value += (i & 127) * multiplier; 97 | multiplier *= 128; 98 | } while ((i & 128) != 0); 99 | exit: 100 | return len; 101 | } 102 | 103 | 104 | static int readPacket(MQTTClient* c, Timer* timer) 105 | { 106 | int rc = FAILURE; 107 | MQTTHeader header = {0}; 108 | int len = 0; 109 | int rem_len = 0; 110 | 111 | /* 1. read the header byte. This has the packet type in it */ 112 | if (c->ipstack->mqttread(c->ipstack, c->readbuf, 1, TimerLeftMS(timer)) != 1) 113 | goto exit; 114 | 115 | len = 1; 116 | /* 2. read the remaining length. This is variable in itself */ 117 | decodePacket(c, &rem_len, TimerLeftMS(timer)); 118 | len += MQTTPacket_encode(c->readbuf + 1, rem_len); /* put the original remaining length back into the buffer */ 119 | 120 | /* 3. read the rest of the buffer using a callback to supply the rest of the data */ 121 | if (rem_len > 0 && (c->ipstack->mqttread(c->ipstack, c->readbuf + len, rem_len, TimerLeftMS(timer)) != rem_len)) 122 | goto exit; 123 | 124 | header.byte = c->readbuf[0]; 125 | rc = header.bits.type; 126 | exit: 127 | return rc; 128 | } 129 | 130 | 131 | // assume topic filter and name is in correct format 132 | // # can only be at end 133 | // + and # can only be next to separator 134 | static char isTopicMatched(char* topicFilter, MQTTString* topicName) 135 | { 136 | char* curf = topicFilter; 137 | char* curn = topicName->lenstring.data; 138 | char* curn_end = curn + topicName->lenstring.len; 139 | 140 | while (*curf && curn < curn_end) 141 | { 142 | if (*curn == '/' && *curf != '/') 143 | break; 144 | if (*curf != '+' && *curf != '#' && *curf != *curn) 145 | break; 146 | if (*curf == '+') 147 | { // skip until we meet the next separator, or end of string 148 | char* nextpos = curn + 1; 149 | while (nextpos < curn_end && *nextpos != '/') 150 | nextpos = ++curn + 1; 151 | } 152 | else if (*curf == '#') 153 | curn = curn_end - 1; // skip until end of string 154 | curf++; 155 | curn++; 156 | }; 157 | 158 | return (curn == curn_end) && (*curf == '\0'); 159 | } 160 | 161 | 162 | int deliverMessage(MQTTClient* c, MQTTString* topicName, MQTTMessage* message) 163 | { 164 | int i; 165 | int rc = FAILURE; 166 | 167 | // we have to find the right message handler - indexed by topic 168 | for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) 169 | { 170 | if (c->messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(topicName, (char*)c->messageHandlers[i].topicFilter) || 171 | isTopicMatched((char*)c->messageHandlers[i].topicFilter, topicName))) 172 | { 173 | if (c->messageHandlers[i].fp != NULL) 174 | { 175 | MessageData md; 176 | NewMessageData(&md, topicName, message); 177 | c->messageHandlers[i].fp(&md); 178 | rc = SUCCESS; 179 | } 180 | } 181 | } 182 | 183 | if (rc == FAILURE && c->defaultMessageHandler != NULL) 184 | { 185 | MessageData md; 186 | NewMessageData(&md, topicName, message); 187 | c->defaultMessageHandler(&md); 188 | rc = SUCCESS; 189 | } 190 | 191 | return rc; 192 | } 193 | 194 | 195 | int keepalive(MQTTClient* c) 196 | { 197 | int rc = FAILURE; 198 | 199 | if (c->keepAliveInterval == 0) 200 | { 201 | rc = SUCCESS; 202 | goto exit; 203 | } 204 | 205 | if (TimerIsExpired(&c->ping_timer)) 206 | { 207 | if (!c->ping_outstanding) 208 | { 209 | Timer timer; 210 | TimerInit(&timer); 211 | TimerCountdownMS(&timer, 1000); 212 | int len = MQTTSerialize_pingreq(c->buf, c->buf_size); 213 | if (len > 0 && (rc = sendPacket(c, len, &timer)) == SUCCESS) // send the ping packet 214 | c->ping_outstanding = 1; 215 | } 216 | } 217 | 218 | exit: 219 | return rc; 220 | } 221 | 222 | 223 | int cycle(MQTTClient* c, Timer* timer) 224 | { 225 | // read the socket, see what work is due 226 | unsigned short packet_type = readPacket(c, timer); 227 | 228 | int len = 0, 229 | rc = SUCCESS; 230 | 231 | switch (packet_type) 232 | { 233 | case CONNACK: 234 | case PUBACK: 235 | case SUBACK: 236 | break; 237 | case PUBLISH: 238 | { 239 | MQTTString topicName; 240 | MQTTMessage msg; 241 | int intQoS; 242 | if (MQTTDeserialize_publish(&msg.dup, &intQoS, &msg.retained, &msg.id, &topicName, 243 | (unsigned char**)&msg.payload, (int*)&msg.payloadlen, c->readbuf, c->readbuf_size) != 1) 244 | goto exit; 245 | msg.qos = (enum QoS)intQoS; 246 | deliverMessage(c, &topicName, &msg); 247 | if (msg.qos != QOS0) 248 | { 249 | if (msg.qos == QOS1) 250 | len = MQTTSerialize_ack(c->buf, c->buf_size, PUBACK, 0, msg.id); 251 | else if (msg.qos == QOS2) 252 | len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREC, 0, msg.id); 253 | if (len <= 0) 254 | rc = FAILURE; 255 | else 256 | rc = sendPacket(c, len, timer); 257 | if (rc == FAILURE) 258 | goto exit; // there was a problem 259 | } 260 | break; 261 | } 262 | case PUBREC: 263 | { 264 | unsigned short mypacketid; 265 | unsigned char dup, type; 266 | if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1) 267 | rc = FAILURE; 268 | else if ((len = MQTTSerialize_ack(c->buf, c->buf_size, PUBREL, 0, mypacketid)) <= 0) 269 | rc = FAILURE; 270 | else if ((rc = sendPacket(c, len, timer)) != SUCCESS) // send the PUBREL packet 271 | rc = FAILURE; // there was a problem 272 | if (rc == FAILURE) 273 | goto exit; // there was a problem 274 | break; 275 | } 276 | case PUBCOMP: 277 | break; 278 | case PINGRESP: 279 | c->ping_outstanding = 0; 280 | break; 281 | } 282 | keepalive(c); 283 | exit: 284 | if (rc == SUCCESS) 285 | rc = packet_type; 286 | return rc; 287 | } 288 | 289 | 290 | int MQTTYield(MQTTClient* c, int timeout_ms) 291 | { 292 | int rc = SUCCESS; 293 | Timer timer; 294 | 295 | TimerInit(&timer); 296 | TimerCountdownMS(&timer, timeout_ms); 297 | 298 | do 299 | { 300 | if (cycle(c, &timer) == FAILURE) 301 | { 302 | rc = FAILURE; 303 | break; 304 | } 305 | } while (!TimerIsExpired(&timer)); 306 | 307 | return rc; 308 | } 309 | 310 | 311 | void MQTTRun(void* parm) 312 | { 313 | Timer timer; 314 | MQTTClient* c = (MQTTClient*)parm; 315 | 316 | TimerInit(&timer); 317 | 318 | while (1) 319 | { 320 | #if defined(MQTT_TASK) 321 | MutexLock(&c->mutex); 322 | #endif 323 | TimerCountdownMS(&timer, 500); /* Don't wait too long if no traffic is incoming */ 324 | cycle(c, &timer); 325 | #if defined(MQTT_TASK) 326 | MutexUnlock(&c->mutex); 327 | #endif 328 | } 329 | } 330 | 331 | 332 | #if defined(MQTT_TASK) 333 | int MQTTStartTask(MQTTClient* client) 334 | { 335 | return ThreadStart(&client->thread, &MQTTRun, client); 336 | } 337 | #endif 338 | 339 | 340 | int waitfor(MQTTClient* c, int packet_type, Timer* timer) 341 | { 342 | int rc = FAILURE; 343 | 344 | do 345 | { 346 | if (TimerIsExpired(timer)) 347 | break; // we timed out 348 | } 349 | while ((rc = cycle(c, timer)) != packet_type); 350 | 351 | return rc; 352 | } 353 | 354 | 355 | int MQTTConnect(MQTTClient* c, MQTTPacket_connectData* options) 356 | { 357 | Timer connect_timer; 358 | int rc = FAILURE; 359 | MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer; 360 | int len = 0; 361 | 362 | #if defined(MQTT_TASK) 363 | MutexLock(&c->mutex); 364 | #endif 365 | if (c->isconnected) /* don't send connect packet again if we are already connected */ 366 | goto exit; 367 | 368 | TimerInit(&connect_timer); 369 | TimerCountdownMS(&connect_timer, c->command_timeout_ms); 370 | 371 | if (options == 0) 372 | options = &default_options; /* set default options if none were supplied */ 373 | 374 | c->keepAliveInterval = options->keepAliveInterval; 375 | TimerCountdown(&c->ping_timer, c->keepAliveInterval); 376 | if ((len = MQTTSerialize_connect(c->buf, c->buf_size, options)) <= 0) 377 | goto exit; 378 | if ((rc = sendPacket(c, len, &connect_timer)) != SUCCESS) // send the connect packet 379 | goto exit; // there was a problem 380 | 381 | // this will be a blocking call, wait for the connack 382 | if (waitfor(c, CONNACK, &connect_timer) == CONNACK) 383 | { 384 | unsigned char connack_rc = 255; 385 | unsigned char sessionPresent = 0; 386 | if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, c->readbuf, c->readbuf_size) == 1) 387 | rc = connack_rc; 388 | else 389 | rc = FAILURE; 390 | } 391 | else 392 | rc = FAILURE; 393 | 394 | exit: 395 | if (rc == SUCCESS) 396 | c->isconnected = 1; 397 | 398 | #if defined(MQTT_TASK) 399 | MutexUnlock(&c->mutex); 400 | #endif 401 | 402 | return rc; 403 | } 404 | 405 | 406 | int MQTTSubscribe(MQTTClient* c, const char* topicFilter, enum QoS qos, messageHandler messageHandler) 407 | { 408 | int rc = FAILURE; 409 | Timer timer; 410 | int len = 0; 411 | MQTTString topic = MQTTString_initializer; 412 | topic.cstring = (char *)topicFilter; 413 | 414 | #if defined(MQTT_TASK) 415 | MutexLock(&c->mutex); 416 | #endif 417 | if (!c->isconnected) 418 | goto exit; 419 | 420 | TimerInit(&timer); 421 | TimerCountdownMS(&timer, c->command_timeout_ms); 422 | 423 | len = MQTTSerialize_subscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic, (int*)&qos); 424 | if (len <= 0) 425 | goto exit; 426 | if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet 427 | goto exit; // there was a problem 428 | 429 | if (waitfor(c, SUBACK, &timer) == SUBACK) // wait for suback 430 | { 431 | int count = 0, grantedQoS = -1; 432 | unsigned short mypacketid; 433 | if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, c->readbuf, c->readbuf_size) == 1) 434 | rc = grantedQoS; // 0, 1, 2 or 0x80 435 | if (rc != 0x80) 436 | { 437 | int i; 438 | for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i) 439 | { 440 | if (c->messageHandlers[i].topicFilter == 0) 441 | { 442 | c->messageHandlers[i].topicFilter = strdup(topicFilter); 443 | c->messageHandlers[i].fp = messageHandler; 444 | rc = 0; 445 | break; 446 | } 447 | } 448 | } 449 | } 450 | else 451 | rc = FAILURE; 452 | 453 | exit: 454 | #if defined(MQTT_TASK) 455 | MutexUnlock(&c->mutex); 456 | #endif 457 | return rc; 458 | } 459 | 460 | 461 | int MQTTUnsubscribe(MQTTClient* c, const char* topicFilter) 462 | { 463 | int rc = FAILURE; 464 | Timer timer; 465 | MQTTString topic = MQTTString_initializer; 466 | topic.cstring = (char *)topicFilter; 467 | int len = 0; 468 | 469 | #if defined(MQTT_TASK) 470 | MutexLock(&c->mutex); 471 | #endif 472 | if (!c->isconnected) 473 | goto exit; 474 | 475 | TimerInit(&timer); 476 | TimerCountdownMS(&timer, c->command_timeout_ms); 477 | 478 | if ((len = MQTTSerialize_unsubscribe(c->buf, c->buf_size, 0, getNextPacketId(c), 1, &topic)) <= 0) 479 | goto exit; 480 | if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet 481 | goto exit; // there was a problem 482 | 483 | if (waitfor(c, UNSUBACK, &timer) == UNSUBACK) 484 | { 485 | unsigned short mypacketid; // should be the same as the packetid above 486 | if (MQTTDeserialize_unsuback(&mypacketid, c->readbuf, c->readbuf_size) == 1) 487 | rc = 0; 488 | } 489 | else 490 | rc = FAILURE; 491 | 492 | exit: 493 | #if defined(MQTT_TASK) 494 | MutexUnlock(&c->mutex); 495 | #endif 496 | return rc; 497 | } 498 | 499 | 500 | int MQTTPublish(MQTTClient* c, const char* topicName, MQTTMessage* message) 501 | { 502 | int rc = FAILURE; 503 | Timer timer; 504 | MQTTString topic = MQTTString_initializer; 505 | topic.cstring = (char *)topicName; 506 | int len = 0; 507 | 508 | #if defined(MQTT_TASK) 509 | MutexLock(&c->mutex); 510 | #endif 511 | if (!c->isconnected) 512 | goto exit; 513 | 514 | TimerInit(&timer); 515 | TimerCountdownMS(&timer, c->command_timeout_ms); 516 | 517 | if (message->qos == QOS1 || message->qos == QOS2) 518 | message->id = getNextPacketId(c); 519 | 520 | len = MQTTSerialize_publish(c->buf, c->buf_size, 0, message->qos, message->retained, message->id, 521 | topic, (unsigned char*)message->payload, message->payloadlen); 522 | if (len <= 0) 523 | goto exit; 524 | if ((rc = sendPacket(c, len, &timer)) != SUCCESS) // send the subscribe packet 525 | goto exit; // there was a problem 526 | 527 | if (message->qos == QOS1) 528 | { 529 | if (waitfor(c, PUBACK, &timer) == PUBACK) 530 | { 531 | unsigned short mypacketid; 532 | unsigned char dup, type; 533 | if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1) 534 | rc = FAILURE; 535 | } 536 | else 537 | rc = FAILURE; 538 | } 539 | else if (message->qos == QOS2) 540 | { 541 | if (waitfor(c, PUBCOMP, &timer) == PUBCOMP) 542 | { 543 | unsigned short mypacketid; 544 | unsigned char dup, type; 545 | if (MQTTDeserialize_ack(&type, &dup, &mypacketid, c->readbuf, c->readbuf_size) != 1) 546 | rc = FAILURE; 547 | } 548 | else 549 | rc = FAILURE; 550 | } 551 | 552 | exit: 553 | #if defined(MQTT_TASK) 554 | MutexUnlock(&c->mutex); 555 | #endif 556 | return rc; 557 | } 558 | 559 | 560 | int MQTTDisconnect(MQTTClient* c) 561 | { 562 | int rc = FAILURE; 563 | Timer timer; // we might wait for incomplete incoming publishes to complete 564 | int len = 0; 565 | 566 | #if defined(MQTT_TASK) 567 | MutexLock(&c->mutex); 568 | #endif 569 | TimerInit(&timer); 570 | TimerCountdownMS(&timer, c->command_timeout_ms); 571 | 572 | len = MQTTSerialize_disconnect(c->buf, c->buf_size); 573 | if (len > 0) 574 | rc = sendPacket(c, len, &timer); // send the disconnect packet 575 | 576 | c->isconnected = 0; 577 | 578 | #if defined(MQTT_TASK) 579 | MutexUnlock(&c->mutex); 580 | #endif 581 | return rc; 582 | } 583 | 584 | -------------------------------------------------------------------------------- /MQTTClient-C/src/MQTTClient.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014, 2015 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Allan Stockdill-Mander/Ian Craggs - initial API and implementation and/or initial documentation 15 | * Ian Craggs - documentation and platform specific header 16 | *******************************************************************************/ 17 | 18 | #if !defined(__MQTT_CLIENT_C_) 19 | #define __MQTT_CLIENT_C_ 20 | 21 | #if defined(__cplusplus) 22 | extern "C" { 23 | #endif 24 | 25 | #if defined(WIN32_DLL) || defined(WIN64_DLL) 26 | #define DLLImport __declspec(dllimport) 27 | #define DLLExport __declspec(dllexport) 28 | #elif defined(LINUX_SO) 29 | #define DLLImport extern 30 | #define DLLExport __attribute__ ((visibility ("default"))) 31 | #else 32 | #define DLLImport 33 | #define DLLExport 34 | #endif 35 | 36 | #include 37 | #include 38 | #include "stdio.h" 39 | 40 | #if defined(MQTTCLIENT_PLATFORM_HEADER) 41 | /* The following sequence of macros converts the MQTTCLIENT_PLATFORM_HEADER value 42 | * into a string constant suitable for use with include. 43 | */ 44 | #define xstr(s) str(s) 45 | #define str(s) #s 46 | #include xstr(MQTTCLIENT_PLATFORM_HEADER) 47 | #endif 48 | 49 | #define MAX_PACKET_ID 65535 /* according to the MQTT specification - do not change! */ 50 | 51 | #if !defined(MAX_MESSAGE_HANDLERS) 52 | #define MAX_MESSAGE_HANDLERS 5 /* redefinable - how many subscriptions do you want? */ 53 | #endif 54 | 55 | enum QoS { QOS0, QOS1, QOS2 }; 56 | 57 | /* all failure return codes must be negative */ 58 | enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 }; 59 | 60 | /* The Platform specific header must define the Network and Timer structures and functions 61 | * which operate on them. 62 | * 63 | typedef struct Network 64 | { 65 | int (*mqttread)(Network*, unsigned char* read_buffer, int, int); 66 | int (*mqttwrite)(Network*, unsigned char* send_buffer, int, int); 67 | } Network;*/ 68 | 69 | /* The Timer structure must be defined in the platform specific header, 70 | * and have the following functions to operate on it. */ 71 | extern void TimerInit(Timer*); 72 | extern char TimerIsExpired(Timer*); 73 | extern void TimerCountdownMS(Timer*, unsigned int); 74 | extern void TimerCountdown(Timer*, unsigned int); 75 | extern int TimerLeftMS(Timer*); 76 | 77 | typedef struct MQTTMessage 78 | { 79 | enum QoS qos; 80 | unsigned char retained; 81 | unsigned char dup; 82 | unsigned short id; 83 | void *payload; 84 | size_t payloadlen; 85 | } MQTTMessage; 86 | 87 | typedef struct MessageData 88 | { 89 | MQTTMessage* message; 90 | MQTTString* topicName; 91 | } MessageData; 92 | 93 | typedef void (*messageHandler)(MessageData*); 94 | 95 | typedef struct MQTTClient 96 | { 97 | unsigned int next_packetid, 98 | command_timeout_ms; 99 | size_t buf_size, 100 | readbuf_size; 101 | unsigned char *buf, 102 | *readbuf; 103 | unsigned int keepAliveInterval; 104 | char ping_outstanding; 105 | int isconnected; 106 | 107 | struct MessageHandlers 108 | { 109 | const char* topicFilter; 110 | void (*fp) (MessageData*); 111 | } messageHandlers[MAX_MESSAGE_HANDLERS]; /* Message handlers are indexed by subscription topic */ 112 | 113 | void (*defaultMessageHandler) (MessageData*); 114 | 115 | Network* ipstack; 116 | Timer ping_timer; 117 | #if defined(MQTT_TASK) 118 | Mutex mutex; 119 | Thread thread; 120 | #endif 121 | } MQTTClient; 122 | 123 | #define DefaultClient {0, 0, 0, 0, NULL, NULL, 0, 0, 0} 124 | 125 | 126 | /** 127 | * Create an MQTT client object 128 | * @param client 129 | * @param network 130 | * @param command_timeout_ms 131 | * @param 132 | */ 133 | DLLExport void MQTTClientInit(MQTTClient* client, Network* network, unsigned int command_timeout_ms, 134 | unsigned char* sendbuf, size_t sendbuf_size, unsigned char* readbuf, size_t readbuf_size); 135 | 136 | /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack 137 | * The nework object must be connected to the network endpoint before calling this 138 | * @param options - connect options 139 | * @return success code 140 | */ 141 | DLLExport int MQTTConnect(MQTTClient* client, MQTTPacket_connectData* options); 142 | 143 | /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs 144 | * @param client - the client object to use 145 | * @param topic - the topic to publish to 146 | * @param message - the message to send 147 | * @return success code 148 | */ 149 | DLLExport int MQTTPublish(MQTTClient* client, const char*, MQTTMessage*); 150 | 151 | /** MQTT Subscribe - send an MQTT subscribe packet and wait for suback before returning. 152 | * @param client - the client object to use 153 | * @param topicFilter - the topic filter to subscribe to 154 | * @param message - the message to send 155 | * @return success code 156 | */ 157 | DLLExport int MQTTSubscribe(MQTTClient* client, const char* topicFilter, enum QoS, messageHandler); 158 | 159 | /** MQTT Subscribe - send an MQTT unsubscribe packet and wait for unsuback before returning. 160 | * @param client - the client object to use 161 | * @param topicFilter - the topic filter to unsubscribe from 162 | * @return success code 163 | */ 164 | DLLExport int MQTTUnsubscribe(MQTTClient* client, const char* topicFilter); 165 | 166 | /** MQTT Disconnect - send an MQTT disconnect packet and close the connection 167 | * @param client - the client object to use 168 | * @return success code 169 | */ 170 | DLLExport int MQTTDisconnect(MQTTClient* client); 171 | 172 | /** MQTT Yield - MQTT background 173 | * @param client - the client object to use 174 | * @param time - the time, in milliseconds, to yield for 175 | * @return success code 176 | */ 177 | DLLExport int MQTTYield(MQTTClient* client, int time); 178 | 179 | #if defined(MQTT_TASK) 180 | /** MQTT start background thread for a client. After this, MQTTYield should not be called. 181 | * @param client - the client object to use 182 | * @return success code 183 | */ 184 | DLLExport int MQTTStartTask(MQTTClient* client); 185 | #endif 186 | 187 | #if defined(__cplusplus) 188 | } 189 | #endif 190 | 191 | #endif 192 | -------------------------------------------------------------------------------- /MQTTClient-C/src/linux/MQTTLinux.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Allan Stockdill-Mander - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTLinux.h" 18 | 19 | void TimerInit(Timer* timer) 20 | { 21 | timer->end_time = (struct timeval){0, 0}; 22 | } 23 | 24 | char TimerIsExpired(Timer* timer) 25 | { 26 | struct timeval now, res; 27 | gettimeofday(&now, NULL); 28 | timersub(&timer->end_time, &now, &res); 29 | return res.tv_sec < 0 || (res.tv_sec == 0 && res.tv_usec <= 0); 30 | } 31 | 32 | 33 | void TimerCountdownMS(Timer* timer, unsigned int timeout) 34 | { 35 | struct timeval now; 36 | gettimeofday(&now, NULL); 37 | struct timeval interval = {timeout / 1000, (timeout % 1000) * 1000}; 38 | timeradd(&now, &interval, &timer->end_time); 39 | } 40 | 41 | 42 | void TimerCountdown(Timer* timer, unsigned int timeout) 43 | { 44 | struct timeval now; 45 | gettimeofday(&now, NULL); 46 | struct timeval interval = {timeout, 0}; 47 | timeradd(&now, &interval, &timer->end_time); 48 | } 49 | 50 | 51 | int TimerLeftMS(Timer* timer) 52 | { 53 | struct timeval now, res; 54 | gettimeofday(&now, NULL); 55 | timersub(&timer->end_time, &now, &res); 56 | //printf("left %d ms\n", (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000); 57 | return (res.tv_sec < 0) ? 0 : res.tv_sec * 1000 + res.tv_usec / 1000; 58 | } 59 | 60 | 61 | int linux_read(Network* n, unsigned char* buffer, int len, int timeout_ms) 62 | { 63 | struct timeval interval = {timeout_ms / 1000, (timeout_ms % 1000) * 1000}; 64 | if (interval.tv_sec < 0 || (interval.tv_sec == 0 && interval.tv_usec <= 0)) 65 | { 66 | interval.tv_sec = 0; 67 | interval.tv_usec = 100; 68 | } 69 | 70 | setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&interval, sizeof(struct timeval)); 71 | 72 | int bytes = 0; 73 | while (bytes < len) 74 | { 75 | int rc = recv(n->my_socket, &buffer[bytes], (size_t)(len - bytes), 0); 76 | if (rc == -1) 77 | { 78 | if (errno != ENOTCONN && errno != ECONNRESET) 79 | { 80 | bytes = -1; 81 | break; 82 | } 83 | } else if (rc == 0) { 84 | return -1; 85 | } else { 86 | bytes += rc; 87 | } 88 | } 89 | return bytes; 90 | } 91 | 92 | 93 | int linux_write(Network* n, unsigned char* buffer, int len, int timeout_ms) 94 | { 95 | struct timeval tv; 96 | 97 | tv.tv_sec = 0; /* 30 Secs Timeout */ 98 | tv.tv_usec = timeout_ms * 1000; // Not init'ing this can cause strange errors 99 | 100 | setsockopt(n->my_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,sizeof(struct timeval)); 101 | int rc = write(n->my_socket, buffer, len); 102 | return rc; 103 | } 104 | 105 | 106 | void NetworkInit(Network* n) 107 | { 108 | n->my_socket = 0; 109 | n->mqttread = linux_read; 110 | n->mqttwrite = linux_write; 111 | } 112 | 113 | 114 | int NetworkConnect(Network* n, char* addr, int port) 115 | { 116 | int type = SOCK_STREAM; 117 | struct sockaddr_in address; 118 | int rc = -1; 119 | sa_family_t family = AF_INET; 120 | struct addrinfo *result = NULL; 121 | struct addrinfo hints = {0, AF_UNSPEC, SOCK_STREAM, IPPROTO_TCP, 0, NULL, NULL, NULL}; 122 | 123 | if ((rc = getaddrinfo(addr, NULL, &hints, &result)) == 0) 124 | { 125 | struct addrinfo* res = result; 126 | 127 | /* prefer ip4 addresses */ 128 | while (res) 129 | { 130 | if (res->ai_family == AF_INET) 131 | { 132 | result = res; 133 | break; 134 | } 135 | res = res->ai_next; 136 | } 137 | 138 | if (result->ai_family == AF_INET) 139 | { 140 | address.sin_port = htons(port); 141 | address.sin_family = family = AF_INET; 142 | address.sin_addr = ((struct sockaddr_in*)(result->ai_addr))->sin_addr; 143 | } 144 | else 145 | rc = -1; 146 | 147 | freeaddrinfo(result); 148 | } 149 | 150 | if (rc == 0) 151 | { 152 | n->my_socket = socket(family, type, 0); 153 | if (n->my_socket != -1) 154 | rc = connect(n->my_socket, (struct sockaddr*)&address, sizeof(address)); 155 | } 156 | 157 | return rc; 158 | } 159 | 160 | 161 | void NetworkDisconnect(Network* n) 162 | { 163 | close(n->my_socket); 164 | } 165 | -------------------------------------------------------------------------------- /MQTTClient-C/src/linux/MQTTLinux.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Allan Stockdill-Mander - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #if !defined(__MQTT_LINUX_) 18 | #define __MQTT_LINUX_ 19 | 20 | #if defined(WIN32_DLL) || defined(WIN64_DLL) 21 | #define DLLImport __declspec(dllimport) 22 | #define DLLExport __declspec(dllexport) 23 | #elif defined(LINUX_SO) 24 | #define DLLImport extern 25 | #define DLLExport __attribute__ ((visibility ("default"))) 26 | #else 27 | #define DLLImport 28 | #define DLLExport 29 | #endif 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | #include 46 | #include 47 | #include 48 | 49 | typedef struct Timer 50 | { 51 | struct timeval end_time; 52 | } Timer; 53 | 54 | void TimerInit(Timer*); 55 | char TimerIsExpired(Timer*); 56 | void TimerCountdownMS(Timer*, unsigned int); 57 | void TimerCountdown(Timer*, unsigned int); 58 | int TimerLeftMS(Timer*); 59 | 60 | typedef struct Network 61 | { 62 | int my_socket; 63 | int (*mqttread) (struct Network*, unsigned char*, int, int); 64 | int (*mqttwrite) (struct Network*, unsigned char*, int, int); 65 | } Network; 66 | 67 | int linux_read(Network*, unsigned char*, int, int); 68 | int linux_write(Network*, unsigned char*, int, int); 69 | 70 | DLLExport void NetworkInit(Network*); 71 | DLLExport int NetworkConnect(Network*, char*, int); 72 | DLLExport void NetworkDisconnect(Network*); 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTConnect.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTCONNECT_H_ 19 | #define MQTTCONNECT_H_ 20 | 21 | #if !defined(DLLImport) 22 | #define DLLImport 23 | #endif 24 | #if !defined(DLLExport) 25 | #define DLLExport 26 | #endif 27 | 28 | 29 | typedef union 30 | { 31 | unsigned char all; /**< all connect flags */ 32 | #if defined(REVERSED) 33 | struct 34 | { 35 | unsigned int username : 1; /**< 3.1 user name */ 36 | unsigned int password : 1; /**< 3.1 password */ 37 | unsigned int willRetain : 1; /**< will retain setting */ 38 | unsigned int willQoS : 2; /**< will QoS value */ 39 | unsigned int will : 1; /**< will flag */ 40 | unsigned int cleansession : 1; /**< clean session flag */ 41 | unsigned int : 1; /**< unused */ 42 | } bits; 43 | #else 44 | struct 45 | { 46 | unsigned int : 1; /**< unused */ 47 | unsigned int cleansession : 1; /**< cleansession flag */ 48 | unsigned int will : 1; /**< will flag */ 49 | unsigned int willQoS : 2; /**< will QoS value */ 50 | unsigned int willRetain : 1; /**< will retain setting */ 51 | unsigned int password : 1; /**< 3.1 password */ 52 | unsigned int username : 1; /**< 3.1 user name */ 53 | } bits; 54 | #endif 55 | } MQTTConnectFlags; /**< connect flags byte */ 56 | 57 | 58 | 59 | /** 60 | * Defines the MQTT "Last Will and Testament" (LWT) settings for 61 | * the connect packet. 62 | */ 63 | typedef struct 64 | { 65 | /** The eyecatcher for this structure. must be MQTW. */ 66 | char struct_id[4]; 67 | /** The version number of this structure. Must be 0 */ 68 | int struct_version; 69 | /** The LWT topic to which the LWT message will be published. */ 70 | MQTTString topicName; 71 | /** The LWT payload. */ 72 | MQTTString message; 73 | /** 74 | * The retained flag for the LWT message (see MQTTAsync_message.retained). 75 | */ 76 | unsigned char retained; 77 | /** 78 | * The quality of service setting for the LWT message (see 79 | * MQTTAsync_message.qos and @ref qos). 80 | */ 81 | char qos; 82 | } MQTTPacket_willOptions; 83 | 84 | 85 | #define MQTTPacket_willOptions_initializer { {'M', 'Q', 'T', 'W'}, 0, {NULL, {0, NULL}}, {NULL, {0, NULL}}, 0, 0 } 86 | 87 | 88 | typedef struct 89 | { 90 | /** The eyecatcher for this structure. must be MQTC. */ 91 | char struct_id[4]; 92 | /** The version number of this structure. Must be 0 */ 93 | int struct_version; 94 | /** Version of MQTT to be used. 3 = 3.1 4 = 3.1.1 95 | */ 96 | unsigned char MQTTVersion; 97 | MQTTString clientID; 98 | unsigned short keepAliveInterval; 99 | unsigned char cleansession; 100 | unsigned char willFlag; 101 | MQTTPacket_willOptions will; 102 | MQTTString username; 103 | MQTTString password; 104 | } MQTTPacket_connectData; 105 | 106 | typedef union 107 | { 108 | unsigned char all; /**< all connack flags */ 109 | #if defined(REVERSED) 110 | struct 111 | { 112 | unsigned int sessionpresent : 1; /**< session present flag */ 113 | unsigned int : 7; /**< unused */ 114 | } bits; 115 | #else 116 | struct 117 | { 118 | unsigned int : 7; /**< unused */ 119 | unsigned int sessionpresent : 1; /**< session present flag */ 120 | } bits; 121 | #endif 122 | } MQTTConnackFlags; /**< connack flags byte */ 123 | 124 | #define MQTTPacket_connectData_initializer { {'M', 'Q', 'T', 'C'}, 0, 4, {NULL, {0, NULL}}, 60, 1, 0, \ 125 | MQTTPacket_willOptions_initializer, {NULL, {0, NULL}}, {NULL, {0, NULL}} } 126 | 127 | DLLExport int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options); 128 | DLLExport int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len); 129 | 130 | DLLExport int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent); 131 | DLLExport int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen); 132 | 133 | DLLExport int MQTTSerialize_disconnect(unsigned char* buf, int buflen); 134 | DLLExport int MQTTSerialize_pingreq(unsigned char* buf, int buflen); 135 | 136 | #endif /* MQTTCONNECT_H_ */ 137 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTConnectClient.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | /** 23 | * Determines the length of the MQTT connect packet that would be produced using the supplied connect options. 24 | * @param options the options to be used to build the connect packet 25 | * @return the length of buffer needed to contain the serialized version of the packet 26 | */ 27 | int MQTTSerialize_connectLength(MQTTPacket_connectData* options) 28 | { 29 | int len = 0; 30 | 31 | FUNC_ENTRY; 32 | 33 | if (options->MQTTVersion == 3) 34 | len = 12; /* variable depending on MQTT or MQIsdp */ 35 | else if (options->MQTTVersion == 4) 36 | len = 10; 37 | 38 | len += MQTTstrlen(options->clientID)+2; 39 | if (options->willFlag) 40 | len += MQTTstrlen(options->will.topicName)+2 + MQTTstrlen(options->will.message)+2; 41 | if (options->username.cstring || options->username.lenstring.data) 42 | len += MQTTstrlen(options->username)+2; 43 | if (options->password.cstring || options->password.lenstring.data) 44 | len += MQTTstrlen(options->password)+2; 45 | 46 | FUNC_EXIT_RC(len); 47 | return len; 48 | } 49 | 50 | 51 | /** 52 | * Serializes the connect options into the buffer. 53 | * @param buf the buffer into which the packet will be serialized 54 | * @param len the length in bytes of the supplied buffer 55 | * @param options the options to be used to build the connect packet 56 | * @return serialized length, or error if 0 57 | */ 58 | int MQTTSerialize_connect(unsigned char* buf, int buflen, MQTTPacket_connectData* options) 59 | { 60 | unsigned char *ptr = buf; 61 | MQTTHeader header = {0}; 62 | MQTTConnectFlags flags = {0}; 63 | int len = 0; 64 | int rc = -1; 65 | 66 | FUNC_ENTRY; 67 | if (MQTTPacket_len(len = MQTTSerialize_connectLength(options)) > buflen) 68 | { 69 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 70 | goto exit; 71 | } 72 | 73 | header.byte = 0; 74 | header.bits.type = CONNECT; 75 | writeChar(&ptr, header.byte); /* write header */ 76 | 77 | ptr += MQTTPacket_encode(ptr, len); /* write remaining length */ 78 | 79 | if (options->MQTTVersion == 4) 80 | { 81 | writeCString(&ptr, "MQTT"); 82 | writeChar(&ptr, (char) 4); 83 | } 84 | else 85 | { 86 | writeCString(&ptr, "MQIsdp"); 87 | writeChar(&ptr, (char) 3); 88 | } 89 | 90 | flags.all = 0; 91 | flags.bits.cleansession = options->cleansession; 92 | flags.bits.will = (options->willFlag) ? 1 : 0; 93 | if (flags.bits.will) 94 | { 95 | flags.bits.willQoS = options->will.qos; 96 | flags.bits.willRetain = options->will.retained; 97 | } 98 | 99 | if (options->username.cstring || options->username.lenstring.data) 100 | flags.bits.username = 1; 101 | if (options->password.cstring || options->password.lenstring.data) 102 | flags.bits.password = 1; 103 | 104 | writeChar(&ptr, flags.all); 105 | writeInt(&ptr, options->keepAliveInterval); 106 | writeMQTTString(&ptr, options->clientID); 107 | if (options->willFlag) 108 | { 109 | writeMQTTString(&ptr, options->will.topicName); 110 | writeMQTTString(&ptr, options->will.message); 111 | } 112 | if (flags.bits.username) 113 | writeMQTTString(&ptr, options->username); 114 | if (flags.bits.password) 115 | writeMQTTString(&ptr, options->password); 116 | 117 | rc = ptr - buf; 118 | 119 | exit: FUNC_EXIT_RC(rc); 120 | return rc; 121 | } 122 | 123 | 124 | /** 125 | * Deserializes the supplied (wire) buffer into connack data - return code 126 | * @param sessionPresent the session present flag returned (only for MQTT 3.1.1) 127 | * @param connack_rc returned integer value of the connack return code 128 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 129 | * @param len the length in bytes of the data in the supplied buffer 130 | * @return error code. 1 is success, 0 is failure 131 | */ 132 | int MQTTDeserialize_connack(unsigned char* sessionPresent, unsigned char* connack_rc, unsigned char* buf, int buflen) 133 | { 134 | MQTTHeader header = {0}; 135 | unsigned char* curdata = buf; 136 | unsigned char* enddata = NULL; 137 | int rc = 0; 138 | int mylen; 139 | MQTTConnackFlags flags = {0}; 140 | 141 | FUNC_ENTRY; 142 | header.byte = readChar(&curdata); 143 | if (header.bits.type != CONNACK) 144 | goto exit; 145 | 146 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 147 | enddata = curdata + mylen; 148 | if (enddata - curdata < 2) 149 | goto exit; 150 | 151 | flags.all = readChar(&curdata); 152 | *sessionPresent = flags.bits.sessionpresent; 153 | *connack_rc = readChar(&curdata); 154 | 155 | rc = 1; 156 | exit: 157 | FUNC_EXIT_RC(rc); 158 | return rc; 159 | } 160 | 161 | 162 | /** 163 | * Serializes a 0-length packet into the supplied buffer, ready for writing to a socket 164 | * @param buf the buffer into which the packet will be serialized 165 | * @param buflen the length in bytes of the supplied buffer, to avoid overruns 166 | * @param packettype the message type 167 | * @return serialized length, or error if 0 168 | */ 169 | int MQTTSerialize_zero(unsigned char* buf, int buflen, unsigned char packettype) 170 | { 171 | MQTTHeader header = {0}; 172 | int rc = -1; 173 | unsigned char *ptr = buf; 174 | 175 | FUNC_ENTRY; 176 | if (buflen < 2) 177 | { 178 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 179 | goto exit; 180 | } 181 | header.byte = 0; 182 | header.bits.type = packettype; 183 | writeChar(&ptr, header.byte); /* write header */ 184 | 185 | ptr += MQTTPacket_encode(ptr, 0); /* write remaining length */ 186 | rc = ptr - buf; 187 | exit: 188 | FUNC_EXIT_RC(rc); 189 | return rc; 190 | } 191 | 192 | 193 | /** 194 | * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket 195 | * @param buf the buffer into which the packet will be serialized 196 | * @param buflen the length in bytes of the supplied buffer, to avoid overruns 197 | * @return serialized length, or error if 0 198 | */ 199 | int MQTTSerialize_disconnect(unsigned char* buf, int buflen) 200 | { 201 | return MQTTSerialize_zero(buf, buflen, DISCONNECT); 202 | } 203 | 204 | 205 | /** 206 | * Serializes a disconnect packet into the supplied buffer, ready for writing to a socket 207 | * @param buf the buffer into which the packet will be serialized 208 | * @param buflen the length in bytes of the supplied buffer, to avoid overruns 209 | * @return serialized length, or error if 0 210 | */ 211 | int MQTTSerialize_pingreq(unsigned char* buf, int buflen) 212 | { 213 | return MQTTSerialize_zero(buf, buflen, PINGREQ); 214 | } 215 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTConnectServer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "StackTrace.h" 18 | #include "MQTTPacket.h" 19 | #include 20 | 21 | #define min(a, b) ((a < b) ? a : b) 22 | 23 | 24 | /** 25 | * Validates MQTT protocol name and version combinations 26 | * @param protocol the MQTT protocol name as an MQTTString 27 | * @param version the MQTT protocol version number, as in the connect packet 28 | * @return correct MQTT combination? 1 is true, 0 is false 29 | */ 30 | int MQTTPacket_checkVersion(MQTTString* protocol, int version) 31 | { 32 | int rc = 0; 33 | 34 | if (version == 3 && memcmp(protocol->lenstring.data, "MQIsdp", 35 | min(6, protocol->lenstring.len)) == 0) 36 | rc = 1; 37 | else if (version == 4 && memcmp(protocol->lenstring.data, "MQTT", 38 | min(4, protocol->lenstring.len)) == 0) 39 | rc = 1; 40 | return rc; 41 | } 42 | 43 | 44 | /** 45 | * Deserializes the supplied (wire) buffer into connect data structure 46 | * @param data the connect data structure to be filled out 47 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 48 | * @param len the length in bytes of the data in the supplied buffer 49 | * @return error code. 1 is success, 0 is failure 50 | */ 51 | int MQTTDeserialize_connect(MQTTPacket_connectData* data, unsigned char* buf, int len) 52 | { 53 | MQTTHeader header = {0}; 54 | MQTTConnectFlags flags = {0}; 55 | unsigned char* curdata = buf; 56 | unsigned char* enddata = &buf[len]; 57 | int rc = 0; 58 | MQTTString Protocol; 59 | int version; 60 | int mylen = 0; 61 | 62 | FUNC_ENTRY; 63 | header.byte = readChar(&curdata); 64 | if (header.bits.type != CONNECT) 65 | goto exit; 66 | 67 | curdata += MQTTPacket_decodeBuf(curdata, &mylen); /* read remaining length */ 68 | 69 | if (!readMQTTLenString(&Protocol, &curdata, enddata) || 70 | enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ 71 | goto exit; 72 | 73 | version = (int)readChar(&curdata); /* Protocol version */ 74 | /* If we don't recognize the protocol version, we don't parse the connect packet on the 75 | * basis that we don't know what the format will be. 76 | */ 77 | if (MQTTPacket_checkVersion(&Protocol, version)) 78 | { 79 | flags.all = readChar(&curdata); 80 | data->cleansession = flags.bits.cleansession; 81 | data->keepAliveInterval = readInt(&curdata); 82 | if (!readMQTTLenString(&data->clientID, &curdata, enddata)) 83 | goto exit; 84 | data->willFlag = flags.bits.will; 85 | if (flags.bits.will) 86 | { 87 | data->will.qos = flags.bits.willQoS; 88 | data->will.retained = flags.bits.willRetain; 89 | if (!readMQTTLenString(&data->will.topicName, &curdata, enddata) || 90 | !readMQTTLenString(&data->will.message, &curdata, enddata)) 91 | goto exit; 92 | } 93 | if (flags.bits.username) 94 | { 95 | if (enddata - curdata < 3 || !readMQTTLenString(&data->username, &curdata, enddata)) 96 | goto exit; /* username flag set, but no username supplied - invalid */ 97 | if (flags.bits.password && 98 | (enddata - curdata < 3 || !readMQTTLenString(&data->password, &curdata, enddata))) 99 | goto exit; /* password flag set, but no password supplied - invalid */ 100 | } 101 | else if (flags.bits.password) 102 | goto exit; /* password flag set without username - invalid */ 103 | rc = 1; 104 | } 105 | exit: 106 | FUNC_EXIT_RC(rc); 107 | return rc; 108 | } 109 | 110 | 111 | /** 112 | * Serializes the connack packet into the supplied buffer. 113 | * @param buf the buffer into which the packet will be serialized 114 | * @param buflen the length in bytes of the supplied buffer 115 | * @param connack_rc the integer connack return code to be used 116 | * @param sessionPresent the MQTT 3.1.1 sessionPresent flag 117 | * @return serialized length, or error if 0 118 | */ 119 | int MQTTSerialize_connack(unsigned char* buf, int buflen, unsigned char connack_rc, unsigned char sessionPresent) 120 | { 121 | MQTTHeader header = {0}; 122 | int rc = 0; 123 | unsigned char *ptr = buf; 124 | MQTTConnackFlags flags = {0}; 125 | 126 | FUNC_ENTRY; 127 | if (buflen < 2) 128 | { 129 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 130 | goto exit; 131 | } 132 | header.byte = 0; 133 | header.bits.type = CONNACK; 134 | writeChar(&ptr, header.byte); /* write header */ 135 | 136 | ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ 137 | 138 | flags.all = 0; 139 | flags.bits.sessionpresent = sessionPresent; 140 | writeChar(&ptr, flags.all); 141 | writeChar(&ptr, connack_rc); 142 | 143 | rc = ptr - buf; 144 | exit: 145 | FUNC_EXIT_RC(rc); 146 | return rc; 147 | } 148 | 149 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTDeserializePublish.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "StackTrace.h" 18 | #include "MQTTPacket.h" 19 | #include 20 | 21 | #define min(a, b) ((a < b) ? 1 : 0) 22 | 23 | /** 24 | * Deserializes the supplied (wire) buffer into publish data 25 | * @param dup returned integer - the MQTT dup flag 26 | * @param qos returned integer - the MQTT QoS value 27 | * @param retained returned integer - the MQTT retained flag 28 | * @param packetid returned integer - the MQTT packet identifier 29 | * @param topicName returned MQTTString - the MQTT topic in the publish 30 | * @param payload returned byte buffer - the MQTT publish payload 31 | * @param payloadlen returned integer - the length of the MQTT payload 32 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 33 | * @param buflen the length in bytes of the data in the supplied buffer 34 | * @return error code. 1 is success 35 | */ 36 | int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, 37 | unsigned char** payload, int* payloadlen, unsigned char* buf, int buflen) 38 | { 39 | MQTTHeader header = {0}; 40 | unsigned char* curdata = buf; 41 | unsigned char* enddata = NULL; 42 | int rc = 0; 43 | int mylen = 0; 44 | 45 | FUNC_ENTRY; 46 | header.byte = readChar(&curdata); 47 | if (header.bits.type != PUBLISH) 48 | goto exit; 49 | *dup = header.bits.dup; 50 | *qos = header.bits.qos; 51 | *retained = header.bits.retain; 52 | 53 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 54 | enddata = curdata + mylen; 55 | 56 | if (!readMQTTLenString(topicName, &curdata, enddata) || 57 | enddata - curdata < 0) /* do we have enough data to read the protocol version byte? */ 58 | goto exit; 59 | 60 | if (*qos > 0) 61 | *packetid = readInt(&curdata); 62 | 63 | *payloadlen = enddata - curdata; 64 | *payload = curdata; 65 | rc = 1; 66 | exit: 67 | FUNC_EXIT_RC(rc); 68 | return rc; 69 | } 70 | 71 | 72 | 73 | /** 74 | * Deserializes the supplied (wire) buffer into an ack 75 | * @param packettype returned integer - the MQTT packet type 76 | * @param dup returned integer - the MQTT dup flag 77 | * @param packetid returned integer - the MQTT packet identifier 78 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 79 | * @param buflen the length in bytes of the data in the supplied buffer 80 | * @return error code. 1 is success, 0 is failure 81 | */ 82 | int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen) 83 | { 84 | MQTTHeader header = {0}; 85 | unsigned char* curdata = buf; 86 | unsigned char* enddata = NULL; 87 | int rc = 0; 88 | int mylen; 89 | 90 | FUNC_ENTRY; 91 | header.byte = readChar(&curdata); 92 | *dup = header.bits.dup; 93 | *packettype = header.bits.type; 94 | 95 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 96 | enddata = curdata + mylen; 97 | 98 | if (enddata - curdata < 2) 99 | goto exit; 100 | *packetid = readInt(&curdata); 101 | 102 | rc = 1; 103 | exit: 104 | FUNC_EXIT_RC(rc); 105 | return rc; 106 | } 107 | 108 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTFormat.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "StackTrace.h" 18 | #include "MQTTPacket.h" 19 | 20 | #include 21 | 22 | 23 | const char* MQTTPacket_names[] = 24 | { 25 | "RESERVED", "CONNECT", "CONNACK", "PUBLISH", "PUBACK", "PUBREC", "PUBREL", 26 | "PUBCOMP", "SUBSCRIBE", "SUBACK", "UNSUBSCRIBE", "UNSUBACK", 27 | "PINGREQ", "PINGRESP", "DISCONNECT" 28 | }; 29 | 30 | 31 | const char* MQTTPacket_getName(unsigned short packetid) 32 | { 33 | return MQTTPacket_names[packetid]; 34 | } 35 | 36 | 37 | int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data) 38 | { 39 | int strindex = 0; 40 | 41 | strindex = snprintf(strbuf, strbuflen, 42 | "CONNECT MQTT version %d, client id %.*s, clean session %d, keep alive %d", 43 | (int)data->MQTTVersion, data->clientID.lenstring.len, data->clientID.lenstring.data, 44 | (int)data->cleansession, data->keepAliveInterval); 45 | if (data->willFlag) 46 | strindex += snprintf(&strbuf[strindex], strbuflen - strindex, 47 | ", will QoS %d, will retain %d, will topic %.*s, will message %.*s", 48 | data->will.qos, data->will.retained, 49 | data->will.topicName.lenstring.len, data->will.topicName.lenstring.data, 50 | data->will.message.lenstring.len, data->will.message.lenstring.data); 51 | if (data->username.lenstring.data && data->username.lenstring.len > 0) 52 | strindex += snprintf(&strbuf[strindex], strbuflen - strindex, 53 | ", user name %.*s", data->username.lenstring.len, data->username.lenstring.data); 54 | if (data->password.lenstring.data && data->password.lenstring.len > 0) 55 | strindex += snprintf(&strbuf[strindex], strbuflen - strindex, 56 | ", password %.*s", data->password.lenstring.len, data->password.lenstring.data); 57 | return strindex; 58 | } 59 | 60 | 61 | int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent) 62 | { 63 | int strindex = snprintf(strbuf, strbuflen, "CONNACK session present %d, rc %d", sessionPresent, connack_rc); 64 | return strindex; 65 | } 66 | 67 | 68 | int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, 69 | unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen) 70 | { 71 | int strindex = snprintf(strbuf, strbuflen, 72 | "PUBLISH dup %d, QoS %d, retained %d, packet id %d, topic %.*s, payload length %d, payload %.*s", 73 | dup, qos, retained, packetid, 74 | (topicName.lenstring.len < 20) ? topicName.lenstring.len : 20, topicName.lenstring.data, 75 | payloadlen, (payloadlen < 20) ? payloadlen : 20, payload); 76 | return strindex; 77 | } 78 | 79 | 80 | int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid) 81 | { 82 | int strindex = snprintf(strbuf, strbuflen, "%s, packet id %d", MQTTPacket_names[packettype], packetid); 83 | if (dup) 84 | strindex += snprintf(strbuf + strindex, strbuflen - strindex, ", dup %d", dup); 85 | return strindex; 86 | } 87 | 88 | 89 | int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, 90 | MQTTString topicFilters[], int requestedQoSs[]) 91 | { 92 | return snprintf(strbuf, strbuflen, 93 | "SUBSCRIBE dup %d, packet id %d count %d topic %.*s qos %d", 94 | dup, packetid, count, 95 | topicFilters[0].lenstring.len, topicFilters[0].lenstring.data, 96 | requestedQoSs[0]); 97 | } 98 | 99 | 100 | int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs) 101 | { 102 | return snprintf(strbuf, strbuflen, 103 | "SUBACK packet id %d count %d granted qos %d", packetid, count, grantedQoSs[0]); 104 | } 105 | 106 | 107 | int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, 108 | int count, MQTTString topicFilters[]) 109 | { 110 | return snprintf(strbuf, strbuflen, 111 | "UNSUBSCRIBE dup %d, packet id %d count %d topic %.*s", 112 | dup, packetid, count, 113 | topicFilters[0].lenstring.len, topicFilters[0].lenstring.data); 114 | } 115 | 116 | 117 | char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) 118 | { 119 | int index = 0; 120 | int rem_length = 0; 121 | MQTTHeader header = {0}; 122 | int strindex = 0; 123 | 124 | header.byte = buf[index++]; 125 | index += MQTTPacket_decodeBuf(&buf[index], &rem_length); 126 | 127 | switch (header.bits.type) 128 | { 129 | case CONNACK: 130 | { 131 | unsigned char sessionPresent, connack_rc; 132 | if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) == 1) 133 | strindex = MQTTStringFormat_connack(strbuf, strbuflen, connack_rc, sessionPresent); 134 | } 135 | break; 136 | case PUBLISH: 137 | { 138 | unsigned char dup, retained, *payload; 139 | unsigned short packetid; 140 | int qos, payloadlen; 141 | MQTTString topicName = MQTTString_initializer; 142 | if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, 143 | &payload, &payloadlen, buf, buflen) == 1) 144 | strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, 145 | topicName, payload, payloadlen); 146 | } 147 | break; 148 | case PUBACK: 149 | case PUBREC: 150 | case PUBREL: 151 | case PUBCOMP: 152 | { 153 | unsigned char packettype, dup; 154 | unsigned short packetid; 155 | if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) 156 | strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); 157 | } 158 | break; 159 | case SUBACK: 160 | { 161 | unsigned short packetid; 162 | int maxcount = 1, count = 0; 163 | int grantedQoSs[1]; 164 | if (MQTTDeserialize_suback(&packetid, maxcount, &count, grantedQoSs, buf, buflen) == 1) 165 | strindex = MQTTStringFormat_suback(strbuf, strbuflen, packetid, count, grantedQoSs); 166 | } 167 | break; 168 | case UNSUBACK: 169 | { 170 | unsigned short packetid; 171 | if (MQTTDeserialize_unsuback(&packetid, buf, buflen) == 1) 172 | strindex = MQTTStringFormat_ack(strbuf, strbuflen, UNSUBACK, 0, packetid); 173 | } 174 | break; 175 | case PINGREQ: 176 | case PINGRESP: 177 | case DISCONNECT: 178 | strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); 179 | break; 180 | } 181 | return strbuf; 182 | } 183 | 184 | 185 | char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen) 186 | { 187 | int index = 0; 188 | int rem_length = 0; 189 | MQTTHeader header = {0}; 190 | int strindex = 0; 191 | 192 | header.byte = buf[index++]; 193 | index += MQTTPacket_decodeBuf(&buf[index], &rem_length); 194 | 195 | switch (header.bits.type) 196 | { 197 | case CONNECT: 198 | { 199 | MQTTPacket_connectData data; 200 | int rc; 201 | if ((rc = MQTTDeserialize_connect(&data, buf, buflen)) == 1) 202 | strindex = MQTTStringFormat_connect(strbuf, strbuflen, &data); 203 | } 204 | break; 205 | case PUBLISH: 206 | { 207 | unsigned char dup, retained, *payload; 208 | unsigned short packetid; 209 | int qos, payloadlen; 210 | MQTTString topicName = MQTTString_initializer; 211 | if (MQTTDeserialize_publish(&dup, &qos, &retained, &packetid, &topicName, 212 | &payload, &payloadlen, buf, buflen) == 1) 213 | strindex = MQTTStringFormat_publish(strbuf, strbuflen, dup, qos, retained, packetid, 214 | topicName, payload, payloadlen); 215 | } 216 | break; 217 | case PUBACK: 218 | case PUBREC: 219 | case PUBREL: 220 | case PUBCOMP: 221 | { 222 | unsigned char packettype, dup; 223 | unsigned short packetid; 224 | if (MQTTDeserialize_ack(&packettype, &dup, &packetid, buf, buflen) == 1) 225 | strindex = MQTTStringFormat_ack(strbuf, strbuflen, packettype, dup, packetid); 226 | } 227 | break; 228 | case SUBSCRIBE: 229 | { 230 | unsigned char dup; 231 | unsigned short packetid; 232 | int maxcount = 1, count = 0; 233 | MQTTString topicFilters[1]; 234 | int requestedQoSs[1]; 235 | if (MQTTDeserialize_subscribe(&dup, &packetid, maxcount, &count, 236 | topicFilters, requestedQoSs, buf, buflen) == 1) 237 | strindex = MQTTStringFormat_subscribe(strbuf, strbuflen, dup, packetid, count, topicFilters, requestedQoSs);; 238 | } 239 | break; 240 | case UNSUBSCRIBE: 241 | { 242 | unsigned char dup; 243 | unsigned short packetid; 244 | int maxcount = 1, count = 0; 245 | MQTTString topicFilters[1]; 246 | if (MQTTDeserialize_unsubscribe(&dup, &packetid, maxcount, &count, topicFilters, buf, buflen) == 1) 247 | strindex = MQTTStringFormat_unsubscribe(strbuf, strbuflen, dup, packetid, count, topicFilters); 248 | } 249 | break; 250 | case PINGREQ: 251 | case PINGRESP: 252 | case DISCONNECT: 253 | strindex = snprintf(strbuf, strbuflen, "%s", MQTTPacket_names[header.bits.type]); 254 | break; 255 | } 256 | strbuf[strbuflen] = '\0'; 257 | return strbuf; 258 | } 259 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTFormat.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #if !defined(MQTTFORMAT_H) 18 | #define MQTTFORMAT_H 19 | 20 | #include "StackTrace.h" 21 | #include "MQTTPacket.h" 22 | 23 | const char* MQTTPacket_getName(unsigned short packetid); 24 | int MQTTStringFormat_connect(char* strbuf, int strbuflen, MQTTPacket_connectData* data); 25 | int MQTTStringFormat_connack(char* strbuf, int strbuflen, unsigned char connack_rc, unsigned char sessionPresent); 26 | int MQTTStringFormat_publish(char* strbuf, int strbuflen, unsigned char dup, int qos, unsigned char retained, 27 | unsigned short packetid, MQTTString topicName, unsigned char* payload, int payloadlen); 28 | int MQTTStringFormat_ack(char* strbuf, int strbuflen, unsigned char packettype, unsigned char dup, unsigned short packetid); 29 | int MQTTStringFormat_subscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, int count, 30 | MQTTString topicFilters[], int requestedQoSs[]); 31 | int MQTTStringFormat_suback(char* strbuf, int strbuflen, unsigned short packetid, int count, int* grantedQoSs); 32 | int MQTTStringFormat_unsubscribe(char* strbuf, int strbuflen, unsigned char dup, unsigned short packetid, 33 | int count, MQTTString topicFilters[]); 34 | char* MQTTFormat_toClientString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); 35 | char* MQTTFormat_toServerString(char* strbuf, int strbuflen, unsigned char* buf, int buflen); 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTPacket.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Sergio R. Caprile - non-blocking packet read functions for stream transport 16 | *******************************************************************************/ 17 | 18 | #include "StackTrace.h" 19 | #include "MQTTPacket.h" 20 | 21 | #include 22 | 23 | /** 24 | * Encodes the message length according to the MQTT algorithm 25 | * @param buf the buffer into which the encoded data is written 26 | * @param length the length to be encoded 27 | * @return the number of bytes written to buffer 28 | */ 29 | int MQTTPacket_encode(unsigned char* buf, int length) 30 | { 31 | int rc = 0; 32 | 33 | FUNC_ENTRY; 34 | do 35 | { 36 | char d = length % 128; 37 | length /= 128; 38 | /* if there are more digits to encode, set the top bit of this digit */ 39 | if (length > 0) 40 | d |= 0x80; 41 | buf[rc++] = d; 42 | } while (length > 0); 43 | FUNC_EXIT_RC(rc); 44 | return rc; 45 | } 46 | 47 | 48 | /** 49 | * Decodes the message length according to the MQTT algorithm 50 | * @param getcharfn pointer to function to read the next character from the data source 51 | * @param value the decoded length returned 52 | * @return the number of bytes read from the socket 53 | */ 54 | int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value) 55 | { 56 | unsigned char c; 57 | int multiplier = 1; 58 | int len = 0; 59 | #define MAX_NO_OF_REMAINING_LENGTH_BYTES 4 60 | 61 | FUNC_ENTRY; 62 | *value = 0; 63 | do 64 | { 65 | int rc = MQTTPACKET_READ_ERROR; 66 | 67 | if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES) 68 | { 69 | rc = MQTTPACKET_READ_ERROR; /* bad data */ 70 | goto exit; 71 | } 72 | rc = (*getcharfn)(&c, 1); 73 | if (rc != 1) 74 | goto exit; 75 | *value += (c & 127) * multiplier; 76 | multiplier *= 128; 77 | } while ((c & 128) != 0); 78 | exit: 79 | FUNC_EXIT_RC(len); 80 | return len; 81 | } 82 | 83 | 84 | int MQTTPacket_len(int rem_len) 85 | { 86 | rem_len += 1; /* header byte */ 87 | 88 | /* now remaining_length field */ 89 | if (rem_len < 128) 90 | rem_len += 1; 91 | else if (rem_len < 16384) 92 | rem_len += 2; 93 | else if (rem_len < 2097151) 94 | rem_len += 3; 95 | else 96 | rem_len += 4; 97 | return rem_len; 98 | } 99 | 100 | 101 | static unsigned char* bufptr; 102 | 103 | int bufchar(unsigned char* c, int count) 104 | { 105 | int i; 106 | 107 | for (i = 0; i < count; ++i) 108 | *c = *bufptr++; 109 | return count; 110 | } 111 | 112 | 113 | int MQTTPacket_decodeBuf(unsigned char* buf, int* value) 114 | { 115 | bufptr = buf; 116 | return MQTTPacket_decode(bufchar, value); 117 | } 118 | 119 | 120 | /** 121 | * Calculates an integer from two bytes read from the input buffer 122 | * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned 123 | * @return the integer value calculated 124 | */ 125 | int readInt(unsigned char** pptr) 126 | { 127 | unsigned char* ptr = *pptr; 128 | int len = 256*(*ptr) + (*(ptr+1)); 129 | *pptr += 2; 130 | return len; 131 | } 132 | 133 | 134 | /** 135 | * Reads one character from the input buffer. 136 | * @param pptr pointer to the input buffer - incremented by the number of bytes used & returned 137 | * @return the character read 138 | */ 139 | char readChar(unsigned char** pptr) 140 | { 141 | char c = **pptr; 142 | (*pptr)++; 143 | return c; 144 | } 145 | 146 | 147 | /** 148 | * Writes one character to an output buffer. 149 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 150 | * @param c the character to write 151 | */ 152 | void writeChar(unsigned char** pptr, char c) 153 | { 154 | **pptr = c; 155 | (*pptr)++; 156 | } 157 | 158 | 159 | /** 160 | * Writes an integer as 2 bytes to an output buffer. 161 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 162 | * @param anInt the integer to write 163 | */ 164 | void writeInt(unsigned char** pptr, int anInt) 165 | { 166 | **pptr = (unsigned char)(anInt / 256); 167 | (*pptr)++; 168 | **pptr = (unsigned char)(anInt % 256); 169 | (*pptr)++; 170 | } 171 | 172 | 173 | /** 174 | * Writes a "UTF" string to an output buffer. Converts C string to length-delimited. 175 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 176 | * @param string the C string to write 177 | */ 178 | void writeCString(unsigned char** pptr, const char* string) 179 | { 180 | int len = strlen(string); 181 | writeInt(pptr, len); 182 | memcpy(*pptr, string, len); 183 | *pptr += len; 184 | } 185 | 186 | 187 | int getLenStringLen(char* ptr) 188 | { 189 | int len = 256*((unsigned char)(*ptr)) + (unsigned char)(*(ptr+1)); 190 | return len; 191 | } 192 | 193 | 194 | void writeMQTTString(unsigned char** pptr, MQTTString mqttstring) 195 | { 196 | if (mqttstring.lenstring.len > 0) 197 | { 198 | writeInt(pptr, mqttstring.lenstring.len); 199 | memcpy(*pptr, mqttstring.lenstring.data, mqttstring.lenstring.len); 200 | *pptr += mqttstring.lenstring.len; 201 | } 202 | else if (mqttstring.cstring) 203 | writeCString(pptr, mqttstring.cstring); 204 | else 205 | writeInt(pptr, 0); 206 | } 207 | 208 | 209 | /** 210 | * @param mqttstring the MQTTString structure into which the data is to be read 211 | * @param pptr pointer to the output buffer - incremented by the number of bytes used & returned 212 | * @param enddata pointer to the end of the data: do not read beyond 213 | * @return 1 if successful, 0 if not 214 | */ 215 | int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata) 216 | { 217 | int rc = 0; 218 | 219 | FUNC_ENTRY; 220 | /* the first two bytes are the length of the string */ 221 | if (enddata - (*pptr) > 1) /* enough length to read the integer? */ 222 | { 223 | mqttstring->lenstring.len = readInt(pptr); /* increments pptr to point past length */ 224 | if (&(*pptr)[mqttstring->lenstring.len] <= enddata) 225 | { 226 | mqttstring->lenstring.data = (char*)*pptr; 227 | *pptr += mqttstring->lenstring.len; 228 | rc = 1; 229 | } 230 | } 231 | mqttstring->cstring = NULL; 232 | FUNC_EXIT_RC(rc); 233 | return rc; 234 | } 235 | 236 | 237 | /** 238 | * Return the length of the MQTTstring - C string if there is one, otherwise the length delimited string 239 | * @param mqttstring the string to return the length of 240 | * @return the length of the string 241 | */ 242 | int MQTTstrlen(MQTTString mqttstring) 243 | { 244 | int rc = 0; 245 | 246 | if (mqttstring.cstring) 247 | rc = strlen(mqttstring.cstring); 248 | else 249 | rc = mqttstring.lenstring.len; 250 | return rc; 251 | } 252 | 253 | 254 | /** 255 | * Compares an MQTTString to a C string 256 | * @param a the MQTTString to compare 257 | * @param bptr the C string to compare 258 | * @return boolean - equal or not 259 | */ 260 | int MQTTPacket_equals(MQTTString* a, char* bptr) 261 | { 262 | int alen = 0, 263 | blen = 0; 264 | char *aptr; 265 | 266 | if (a->cstring) 267 | { 268 | aptr = a->cstring; 269 | alen = strlen(a->cstring); 270 | } 271 | else 272 | { 273 | aptr = a->lenstring.data; 274 | alen = a->lenstring.len; 275 | } 276 | blen = strlen(bptr); 277 | 278 | return (alen == blen) && (strncmp(aptr, bptr, alen) == 0); 279 | } 280 | 281 | 282 | /** 283 | * Helper function to read packet data from some source into a buffer 284 | * @param buf the buffer into which the packet will be serialized 285 | * @param buflen the length in bytes of the supplied buffer 286 | * @param getfn pointer to a function which will read any number of bytes from the needed source 287 | * @return integer MQTT packet type, or -1 on error 288 | * @note the whole message must fit into the caller's buffer 289 | */ 290 | int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)) 291 | { 292 | int rc = -1; 293 | MQTTHeader header = {0}; 294 | int len = 0; 295 | int rem_len = 0; 296 | 297 | /* 1. read the header byte. This has the packet type in it */ 298 | if ((*getfn)(buf, 1) != 1) 299 | goto exit; 300 | 301 | len = 1; 302 | /* 2. read the remaining length. This is variable in itself */ 303 | MQTTPacket_decode(getfn, &rem_len); 304 | len += MQTTPacket_encode(buf + 1, rem_len); /* put the original remaining length back into the buffer */ 305 | 306 | /* 3. read the rest of the buffer using a callback to supply the rest of the data */ 307 | if((rem_len + len) > buflen) 308 | goto exit; 309 | if ((*getfn)(buf + len, rem_len) != rem_len) 310 | goto exit; 311 | 312 | header.byte = buf[0]; 313 | rc = header.bits.type; 314 | exit: 315 | return rc; 316 | } 317 | 318 | /** 319 | * Decodes the message length according to the MQTT algorithm, non-blocking 320 | * @param trp pointer to a transport structure holding what is needed to solve getting data from it 321 | * @param value the decoded length returned 322 | * @return integer the number of bytes read from the socket, 0 for call again, or -1 on error 323 | */ 324 | static int MQTTPacket_decodenb(MQTTTransport *trp) 325 | { 326 | unsigned char c; 327 | int rc = MQTTPACKET_READ_ERROR; 328 | 329 | FUNC_ENTRY; 330 | if(trp->len == 0){ /* initialize on first call */ 331 | trp->multiplier = 1; 332 | trp->rem_len = 0; 333 | } 334 | do { 335 | int frc; 336 | if (++(trp->len) > MAX_NO_OF_REMAINING_LENGTH_BYTES) 337 | goto exit; 338 | if ((frc=(*trp->getfn)(trp->sck, &c, 1)) == -1) 339 | goto exit; 340 | if (frc == 0){ 341 | rc = 0; 342 | goto exit; 343 | } 344 | trp->rem_len += (c & 127) * trp->multiplier; 345 | trp->multiplier *= 128; 346 | } while ((c & 128) != 0); 347 | rc = trp->len; 348 | exit: 349 | FUNC_EXIT_RC(rc); 350 | return rc; 351 | } 352 | 353 | /** 354 | * Helper function to read packet data from some source into a buffer, non-blocking 355 | * @param buf the buffer into which the packet will be serialized 356 | * @param buflen the length in bytes of the supplied buffer 357 | * @param trp pointer to a transport structure holding what is needed to solve getting data from it 358 | * @return integer MQTT packet type, 0 for call again, or -1 on error 359 | * @note the whole message must fit into the caller's buffer 360 | */ 361 | int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp) 362 | { 363 | int rc = -1, frc; 364 | MQTTHeader header = {0}; 365 | 366 | switch(trp->state){ 367 | default: 368 | trp->state = 0; 369 | /*FALLTHROUGH*/ 370 | case 0: 371 | /* read the header byte. This has the packet type in it */ 372 | if ((frc=(*trp->getfn)(trp->sck, buf, 1)) == -1) 373 | goto exit; 374 | if (frc == 0) 375 | return 0; 376 | trp->len = 0; 377 | ++trp->state; 378 | /*FALLTHROUGH*/ 379 | /* read the remaining length. This is variable in itself */ 380 | case 1: 381 | if((frc=MQTTPacket_decodenb(trp)) == MQTTPACKET_READ_ERROR) 382 | goto exit; 383 | if(frc == 0) 384 | return 0; 385 | trp->len = 1 + MQTTPacket_encode(buf + 1, trp->rem_len); /* put the original remaining length back into the buffer */ 386 | if((trp->rem_len + trp->len) > buflen) 387 | goto exit; 388 | ++trp->state; 389 | /*FALLTHROUGH*/ 390 | case 2: 391 | /* read the rest of the buffer using a callback to supply the rest of the data */ 392 | if ((frc=(*trp->getfn)(trp->sck, buf + trp->len, trp->rem_len)) == -1) 393 | goto exit; 394 | if (frc == 0) 395 | return 0; 396 | trp->rem_len -= frc; 397 | trp->len += frc; 398 | if(trp->rem_len) 399 | return 0; 400 | 401 | header.byte = buf[0]; 402 | rc = header.bits.type; 403 | break; 404 | } 405 | 406 | exit: 407 | trp->state = 0; 408 | return rc; 409 | } 410 | 411 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTPacket.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTPACKET_H_ 19 | #define MQTTPACKET_H_ 20 | 21 | #if defined(__cplusplus) /* If this is a C++ compiler, use C linkage */ 22 | extern "C" { 23 | #endif 24 | 25 | #if defined(WIN32_DLL) || defined(WIN64_DLL) 26 | #define DLLImport __declspec(dllimport) 27 | #define DLLExport __declspec(dllexport) 28 | #elif defined(LINUX_SO) 29 | #define DLLImport extern 30 | #define DLLExport __attribute__ ((visibility ("default"))) 31 | #else 32 | #define DLLImport 33 | #define DLLExport 34 | #endif 35 | 36 | enum errors 37 | { 38 | MQTTPACKET_BUFFER_TOO_SHORT = -2, 39 | MQTTPACKET_READ_ERROR = -1, 40 | MQTTPACKET_READ_COMPLETE 41 | }; 42 | 43 | enum msgTypes 44 | { 45 | CONNECT = 1, CONNACK, PUBLISH, PUBACK, PUBREC, PUBREL, 46 | PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, 47 | PINGREQ, PINGRESP, DISCONNECT 48 | }; 49 | 50 | /** 51 | * Bitfields for the MQTT header byte. 52 | */ 53 | typedef union 54 | { 55 | unsigned char byte; /**< the whole byte */ 56 | #if defined(REVERSED) 57 | struct 58 | { 59 | unsigned int type : 4; /**< message type nibble */ 60 | unsigned int dup : 1; /**< DUP flag bit */ 61 | unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ 62 | unsigned int retain : 1; /**< retained flag bit */ 63 | } bits; 64 | #else 65 | struct 66 | { 67 | unsigned int retain : 1; /**< retained flag bit */ 68 | unsigned int qos : 2; /**< QoS value, 0, 1 or 2 */ 69 | unsigned int dup : 1; /**< DUP flag bit */ 70 | unsigned int type : 4; /**< message type nibble */ 71 | } bits; 72 | #endif 73 | } MQTTHeader; 74 | 75 | typedef struct 76 | { 77 | int len; 78 | char* data; 79 | } MQTTLenString; 80 | 81 | typedef struct 82 | { 83 | char* cstring; 84 | MQTTLenString lenstring; 85 | } MQTTString; 86 | 87 | #define MQTTString_initializer {NULL, {0, NULL}} 88 | 89 | int MQTTstrlen(MQTTString mqttstring); 90 | 91 | #include "MQTTConnect.h" 92 | #include "MQTTPublish.h" 93 | #include "MQTTSubscribe.h" 94 | #include "MQTTUnsubscribe.h" 95 | #include "MQTTFormat.h" 96 | 97 | int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char type, unsigned char dup, unsigned short packetid); 98 | int MQTTDeserialize_ack(unsigned char* packettype, unsigned char* dup, unsigned short* packetid, unsigned char* buf, int buflen); 99 | 100 | int MQTTPacket_len(int rem_len); 101 | int MQTTPacket_equals(MQTTString* a, char* b); 102 | 103 | int MQTTPacket_encode(unsigned char* buf, int length); 104 | int MQTTPacket_decode(int (*getcharfn)(unsigned char*, int), int* value); 105 | int MQTTPacket_decodeBuf(unsigned char* buf, int* value); 106 | 107 | int readInt(unsigned char** pptr); 108 | char readChar(unsigned char** pptr); 109 | void writeChar(unsigned char** pptr, char c); 110 | void writeInt(unsigned char** pptr, int anInt); 111 | int readMQTTLenString(MQTTString* mqttstring, unsigned char** pptr, unsigned char* enddata); 112 | void writeCString(unsigned char** pptr, const char* string); 113 | void writeMQTTString(unsigned char** pptr, MQTTString mqttstring); 114 | 115 | DLLExport int MQTTPacket_read(unsigned char* buf, int buflen, int (*getfn)(unsigned char*, int)); 116 | 117 | typedef struct { 118 | int (*getfn)(void *, unsigned char*, int); /* must return -1 for error, 0 for call again, or the number of bytes read */ 119 | void *sck; /* pointer to whatever the system may use to identify the transport */ 120 | int multiplier; 121 | int rem_len; 122 | int len; 123 | char state; 124 | }MQTTTransport; 125 | 126 | int MQTTPacket_readnb(unsigned char* buf, int buflen, MQTTTransport *trp); 127 | 128 | #ifdef __cplusplus /* If this is a C++ compiler, use C linkage */ 129 | } 130 | #endif 131 | 132 | 133 | #endif /* MQTTPACKET_H_ */ 134 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTPublish.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTPUBLISH_H_ 19 | #define MQTTPUBLISH_H_ 20 | 21 | #if !defined(DLLImport) 22 | #define DLLImport 23 | #endif 24 | #if !defined(DLLExport) 25 | #define DLLExport 26 | #endif 27 | 28 | DLLExport int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, 29 | MQTTString topicName, unsigned char* payload, int payloadlen); 30 | 31 | DLLExport int MQTTDeserialize_publish(unsigned char* dup, int* qos, unsigned char* retained, unsigned short* packetid, MQTTString* topicName, 32 | unsigned char** payload, int* payloadlen, unsigned char* buf, int len); 33 | 34 | DLLExport int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid); 35 | DLLExport int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid); 36 | DLLExport int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid); 37 | 38 | #endif /* MQTTPUBLISH_H_ */ 39 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSerializePublish.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Ian Craggs - fix for https://bugs.eclipse.org/bugs/show_bug.cgi?id=453144 16 | *******************************************************************************/ 17 | 18 | #include "MQTTPacket.h" 19 | #include "StackTrace.h" 20 | 21 | #include 22 | 23 | 24 | /** 25 | * Determines the length of the MQTT publish packet that would be produced using the supplied parameters 26 | * @param qos the MQTT QoS of the publish (packetid is omitted for QoS 0) 27 | * @param topicName the topic name to be used in the publish 28 | * @param payloadlen the length of the payload to be sent 29 | * @return the length of buffer needed to contain the serialized version of the packet 30 | */ 31 | int MQTTSerialize_publishLength(int qos, MQTTString topicName, int payloadlen) 32 | { 33 | int len = 0; 34 | 35 | len += 2 + MQTTstrlen(topicName) + payloadlen; 36 | if (qos > 0) 37 | len += 2; /* packetid */ 38 | return len; 39 | } 40 | 41 | 42 | /** 43 | * Serializes the supplied publish data into the supplied buffer, ready for sending 44 | * @param buf the buffer into which the packet will be serialized 45 | * @param buflen the length in bytes of the supplied buffer 46 | * @param dup integer - the MQTT dup flag 47 | * @param qos integer - the MQTT QoS value 48 | * @param retained integer - the MQTT retained flag 49 | * @param packetid integer - the MQTT packet identifier 50 | * @param topicName MQTTString - the MQTT topic in the publish 51 | * @param payload byte buffer - the MQTT publish payload 52 | * @param payloadlen integer - the length of the MQTT payload 53 | * @return the length of the serialized data. <= 0 indicates error 54 | */ 55 | int MQTTSerialize_publish(unsigned char* buf, int buflen, unsigned char dup, int qos, unsigned char retained, unsigned short packetid, 56 | MQTTString topicName, unsigned char* payload, int payloadlen) 57 | { 58 | unsigned char *ptr = buf; 59 | MQTTHeader header = {0}; 60 | int rem_len = 0; 61 | int rc = 0; 62 | 63 | FUNC_ENTRY; 64 | if (MQTTPacket_len(rem_len = MQTTSerialize_publishLength(qos, topicName, payloadlen)) > buflen) 65 | { 66 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 67 | goto exit; 68 | } 69 | 70 | header.bits.type = PUBLISH; 71 | header.bits.dup = dup; 72 | header.bits.qos = qos; 73 | header.bits.retain = retained; 74 | writeChar(&ptr, header.byte); /* write header */ 75 | 76 | ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; 77 | 78 | writeMQTTString(&ptr, topicName); 79 | 80 | if (qos > 0) 81 | writeInt(&ptr, packetid); 82 | 83 | memcpy(ptr, payload, payloadlen); 84 | ptr += payloadlen; 85 | 86 | rc = ptr - buf; 87 | 88 | exit: 89 | FUNC_EXIT_RC(rc); 90 | return rc; 91 | } 92 | 93 | 94 | 95 | /** 96 | * Serializes the ack packet into the supplied buffer. 97 | * @param buf the buffer into which the packet will be serialized 98 | * @param buflen the length in bytes of the supplied buffer 99 | * @param type the MQTT packet type 100 | * @param dup the MQTT dup flag 101 | * @param packetid the MQTT packet identifier 102 | * @return serialized length, or error if 0 103 | */ 104 | int MQTTSerialize_ack(unsigned char* buf, int buflen, unsigned char packettype, unsigned char dup, unsigned short packetid) 105 | { 106 | MQTTHeader header = {0}; 107 | int rc = 0; 108 | unsigned char *ptr = buf; 109 | 110 | FUNC_ENTRY; 111 | if (buflen < 4) 112 | { 113 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 114 | goto exit; 115 | } 116 | header.bits.type = packettype; 117 | header.bits.dup = dup; 118 | header.bits.qos = (packettype == PUBREL) ? 1 : 0; 119 | writeChar(&ptr, header.byte); /* write header */ 120 | 121 | ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ 122 | writeInt(&ptr, packetid); 123 | rc = ptr - buf; 124 | exit: 125 | FUNC_EXIT_RC(rc); 126 | return rc; 127 | } 128 | 129 | 130 | /** 131 | * Serializes a puback packet into the supplied buffer. 132 | * @param buf the buffer into which the packet will be serialized 133 | * @param buflen the length in bytes of the supplied buffer 134 | * @param packetid integer - the MQTT packet identifier 135 | * @return serialized length, or error if 0 136 | */ 137 | int MQTTSerialize_puback(unsigned char* buf, int buflen, unsigned short packetid) 138 | { 139 | return MQTTSerialize_ack(buf, buflen, PUBACK, 0, packetid); 140 | } 141 | 142 | 143 | /** 144 | * Serializes a pubrel packet into the supplied buffer. 145 | * @param buf the buffer into which the packet will be serialized 146 | * @param buflen the length in bytes of the supplied buffer 147 | * @param dup integer - the MQTT dup flag 148 | * @param packetid integer - the MQTT packet identifier 149 | * @return serialized length, or error if 0 150 | */ 151 | int MQTTSerialize_pubrel(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid) 152 | { 153 | return MQTTSerialize_ack(buf, buflen, PUBREL, dup, packetid); 154 | } 155 | 156 | 157 | /** 158 | * Serializes a pubrel packet into the supplied buffer. 159 | * @param buf the buffer into which the packet will be serialized 160 | * @param buflen the length in bytes of the supplied buffer 161 | * @param packetid integer - the MQTT packet identifier 162 | * @return serialized length, or error if 0 163 | */ 164 | int MQTTSerialize_pubcomp(unsigned char* buf, int buflen, unsigned short packetid) 165 | { 166 | return MQTTSerialize_ack(buf, buflen, PUBCOMP, 0, packetid); 167 | } 168 | 169 | 170 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSubscribe.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTSUBSCRIBE_H_ 19 | #define MQTTSUBSCRIBE_H_ 20 | 21 | #if !defined(DLLImport) 22 | #define DLLImport 23 | #endif 24 | #if !defined(DLLExport) 25 | #define DLLExport 26 | #endif 27 | 28 | DLLExport int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, 29 | int count, MQTTString topicFilters[], int requestedQoSs[]); 30 | 31 | DLLExport int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, 32 | int maxcount, int* count, MQTTString topicFilters[], int requestedQoSs[], unsigned char* buf, int len); 33 | 34 | DLLExport int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs); 35 | 36 | DLLExport int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int len); 37 | 38 | 39 | #endif /* MQTTSUBSCRIBE_H_ */ 40 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSubscribeClient.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | /** 23 | * Determines the length of the MQTT subscribe packet that would be produced using the supplied parameters 24 | * @param count the number of topic filter strings in topicFilters 25 | * @param topicFilters the array of topic filter strings to be used in the publish 26 | * @return the length of buffer needed to contain the serialized version of the packet 27 | */ 28 | int MQTTSerialize_subscribeLength(int count, MQTTString topicFilters[]) 29 | { 30 | int i; 31 | int len = 2; /* packetid */ 32 | 33 | for (i = 0; i < count; ++i) 34 | len += 2 + MQTTstrlen(topicFilters[i]) + 1; /* length + topic + req_qos */ 35 | return len; 36 | } 37 | 38 | 39 | /** 40 | * Serializes the supplied subscribe data into the supplied buffer, ready for sending 41 | * @param buf the buffer into which the packet will be serialized 42 | * @param buflen the length in bytes of the supplied bufferr 43 | * @param dup integer - the MQTT dup flag 44 | * @param packetid integer - the MQTT packet identifier 45 | * @param count - number of members in the topicFilters and reqQos arrays 46 | * @param topicFilters - array of topic filter names 47 | * @param requestedQoSs - array of requested QoS 48 | * @return the length of the serialized data. <= 0 indicates error 49 | */ 50 | int MQTTSerialize_subscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, int count, 51 | MQTTString topicFilters[], int requestedQoSs[]) 52 | { 53 | unsigned char *ptr = buf; 54 | MQTTHeader header = {0}; 55 | int rem_len = 0; 56 | int rc = 0; 57 | int i = 0; 58 | 59 | FUNC_ENTRY; 60 | if (MQTTPacket_len(rem_len = MQTTSerialize_subscribeLength(count, topicFilters)) > buflen) 61 | { 62 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 63 | goto exit; 64 | } 65 | 66 | header.byte = 0; 67 | header.bits.type = SUBSCRIBE; 68 | header.bits.dup = dup; 69 | header.bits.qos = 1; 70 | writeChar(&ptr, header.byte); /* write header */ 71 | 72 | ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; 73 | 74 | writeInt(&ptr, packetid); 75 | 76 | for (i = 0; i < count; ++i) 77 | { 78 | writeMQTTString(&ptr, topicFilters[i]); 79 | writeChar(&ptr, requestedQoSs[i]); 80 | } 81 | 82 | rc = ptr - buf; 83 | exit: 84 | FUNC_EXIT_RC(rc); 85 | return rc; 86 | } 87 | 88 | 89 | 90 | /** 91 | * Deserializes the supplied (wire) buffer into suback data 92 | * @param packetid returned integer - the MQTT packet identifier 93 | * @param maxcount - the maximum number of members allowed in the grantedQoSs array 94 | * @param count returned integer - number of members in the grantedQoSs array 95 | * @param grantedQoSs returned array of integers - the granted qualities of service 96 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 97 | * @param buflen the length in bytes of the data in the supplied buffer 98 | * @return error code. 1 is success, 0 is failure 99 | */ 100 | int MQTTDeserialize_suback(unsigned short* packetid, int maxcount, int* count, int grantedQoSs[], unsigned char* buf, int buflen) 101 | { 102 | MQTTHeader header = {0}; 103 | unsigned char* curdata = buf; 104 | unsigned char* enddata = NULL; 105 | int rc = 0; 106 | int mylen; 107 | 108 | FUNC_ENTRY; 109 | header.byte = readChar(&curdata); 110 | if (header.bits.type != SUBACK) 111 | goto exit; 112 | 113 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 114 | enddata = curdata + mylen; 115 | if (enddata - curdata < 2) 116 | goto exit; 117 | 118 | *packetid = readInt(&curdata); 119 | 120 | *count = 0; 121 | while (curdata < enddata) 122 | { 123 | if (*count > maxcount) 124 | { 125 | rc = -1; 126 | goto exit; 127 | } 128 | grantedQoSs[(*count)++] = readChar(&curdata); 129 | } 130 | 131 | rc = 1; 132 | exit: 133 | FUNC_EXIT_RC(rc); 134 | return rc; 135 | } 136 | 137 | 138 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTSubscribeServer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | 23 | /** 24 | * Deserializes the supplied (wire) buffer into subscribe data 25 | * @param dup integer returned - the MQTT dup flag 26 | * @param packetid integer returned - the MQTT packet identifier 27 | * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays 28 | * @param count - number of members in the topicFilters and requestedQoSs arrays 29 | * @param topicFilters - array of topic filter names 30 | * @param requestedQoSs - array of requested QoS 31 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 32 | * @param buflen the length in bytes of the data in the supplied buffer 33 | * @return the length of the serialized data. <= 0 indicates error 34 | */ 35 | int MQTTDeserialize_subscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], 36 | int requestedQoSs[], unsigned char* buf, int buflen) 37 | { 38 | MQTTHeader header = {0}; 39 | unsigned char* curdata = buf; 40 | unsigned char* enddata = NULL; 41 | int rc = -1; 42 | int mylen = 0; 43 | 44 | FUNC_ENTRY; 45 | header.byte = readChar(&curdata); 46 | if (header.bits.type != SUBSCRIBE) 47 | goto exit; 48 | *dup = header.bits.dup; 49 | 50 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 51 | enddata = curdata + mylen; 52 | 53 | *packetid = readInt(&curdata); 54 | 55 | *count = 0; 56 | while (curdata < enddata) 57 | { 58 | if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) 59 | goto exit; 60 | if (curdata >= enddata) /* do we have enough data to read the req_qos version byte? */ 61 | goto exit; 62 | requestedQoSs[*count] = readChar(&curdata); 63 | (*count)++; 64 | } 65 | 66 | rc = 1; 67 | exit: 68 | FUNC_EXIT_RC(rc); 69 | return rc; 70 | } 71 | 72 | 73 | /** 74 | * Serializes the supplied suback data into the supplied buffer, ready for sending 75 | * @param buf the buffer into which the packet will be serialized 76 | * @param buflen the length in bytes of the supplied buffer 77 | * @param packetid integer - the MQTT packet identifier 78 | * @param count - number of members in the grantedQoSs array 79 | * @param grantedQoSs - array of granted QoS 80 | * @return the length of the serialized data. <= 0 indicates error 81 | */ 82 | int MQTTSerialize_suback(unsigned char* buf, int buflen, unsigned short packetid, int count, int* grantedQoSs) 83 | { 84 | MQTTHeader header = {0}; 85 | int rc = -1; 86 | unsigned char *ptr = buf; 87 | int i; 88 | 89 | FUNC_ENTRY; 90 | if (buflen < 2 + count) 91 | { 92 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 93 | goto exit; 94 | } 95 | header.byte = 0; 96 | header.bits.type = SUBACK; 97 | writeChar(&ptr, header.byte); /* write header */ 98 | 99 | ptr += MQTTPacket_encode(ptr, 2 + count); /* write remaining length */ 100 | 101 | writeInt(&ptr, packetid); 102 | 103 | for (i = 0; i < count; ++i) 104 | writeChar(&ptr, grantedQoSs[i]); 105 | 106 | rc = ptr - buf; 107 | exit: 108 | FUNC_EXIT_RC(rc); 109 | return rc; 110 | } 111 | 112 | 113 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTUnsubscribe.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Xiang Rong - 442039 Add makefile to Embedded C client 16 | *******************************************************************************/ 17 | 18 | #ifndef MQTTUNSUBSCRIBE_H_ 19 | #define MQTTUNSUBSCRIBE_H_ 20 | 21 | #if !defined(DLLImport) 22 | #define DLLImport 23 | #endif 24 | #if !defined(DLLExport) 25 | #define DLLExport 26 | #endif 27 | 28 | DLLExport int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, 29 | int count, MQTTString topicFilters[]); 30 | 31 | DLLExport int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int max_count, int* count, MQTTString topicFilters[], 32 | unsigned char* buf, int len); 33 | 34 | DLLExport int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid); 35 | 36 | DLLExport int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int len); 37 | 38 | #endif /* MQTTUNSUBSCRIBE_H_ */ 39 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTUnsubscribeClient.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | /** 23 | * Determines the length of the MQTT unsubscribe packet that would be produced using the supplied parameters 24 | * @param count the number of topic filter strings in topicFilters 25 | * @param topicFilters the array of topic filter strings to be used in the publish 26 | * @return the length of buffer needed to contain the serialized version of the packet 27 | */ 28 | int MQTTSerialize_unsubscribeLength(int count, MQTTString topicFilters[]) 29 | { 30 | int i; 31 | int len = 2; /* packetid */ 32 | 33 | for (i = 0; i < count; ++i) 34 | len += 2 + MQTTstrlen(topicFilters[i]); /* length + topic*/ 35 | return len; 36 | } 37 | 38 | 39 | /** 40 | * Serializes the supplied unsubscribe data into the supplied buffer, ready for sending 41 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 42 | * @param buflen the length in bytes of the data in the supplied buffer 43 | * @param dup integer - the MQTT dup flag 44 | * @param packetid integer - the MQTT packet identifier 45 | * @param count - number of members in the topicFilters array 46 | * @param topicFilters - array of topic filter names 47 | * @return the length of the serialized data. <= 0 indicates error 48 | */ 49 | int MQTTSerialize_unsubscribe(unsigned char* buf, int buflen, unsigned char dup, unsigned short packetid, 50 | int count, MQTTString topicFilters[]) 51 | { 52 | unsigned char *ptr = buf; 53 | MQTTHeader header = {0}; 54 | int rem_len = 0; 55 | int rc = -1; 56 | int i = 0; 57 | 58 | FUNC_ENTRY; 59 | if (MQTTPacket_len(rem_len = MQTTSerialize_unsubscribeLength(count, topicFilters)) > buflen) 60 | { 61 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 62 | goto exit; 63 | } 64 | 65 | header.byte = 0; 66 | header.bits.type = UNSUBSCRIBE; 67 | header.bits.dup = dup; 68 | header.bits.qos = 1; 69 | writeChar(&ptr, header.byte); /* write header */ 70 | 71 | ptr += MQTTPacket_encode(ptr, rem_len); /* write remaining length */; 72 | 73 | writeInt(&ptr, packetid); 74 | 75 | for (i = 0; i < count; ++i) 76 | writeMQTTString(&ptr, topicFilters[i]); 77 | 78 | rc = ptr - buf; 79 | exit: 80 | FUNC_EXIT_RC(rc); 81 | return rc; 82 | } 83 | 84 | 85 | /** 86 | * Deserializes the supplied (wire) buffer into unsuback data 87 | * @param packetid returned integer - the MQTT packet identifier 88 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 89 | * @param buflen the length in bytes of the data in the supplied buffer 90 | * @return error code. 1 is success, 0 is failure 91 | */ 92 | int MQTTDeserialize_unsuback(unsigned short* packetid, unsigned char* buf, int buflen) 93 | { 94 | unsigned char type = 0; 95 | unsigned char dup = 0; 96 | int rc = 0; 97 | 98 | FUNC_ENTRY; 99 | rc = MQTTDeserialize_ack(&type, &dup, packetid, buf, buflen); 100 | if (type == UNSUBACK) 101 | rc = 1; 102 | FUNC_EXIT_RC(rc); 103 | return rc; 104 | } 105 | 106 | 107 | -------------------------------------------------------------------------------- /MQTTPacket/src/MQTTUnsubscribeServer.c: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | *******************************************************************************/ 16 | 17 | #include "MQTTPacket.h" 18 | #include "StackTrace.h" 19 | 20 | #include 21 | 22 | 23 | /** 24 | * Deserializes the supplied (wire) buffer into unsubscribe data 25 | * @param dup integer returned - the MQTT dup flag 26 | * @param packetid integer returned - the MQTT packet identifier 27 | * @param maxcount - the maximum number of members allowed in the topicFilters and requestedQoSs arrays 28 | * @param count - number of members in the topicFilters and requestedQoSs arrays 29 | * @param topicFilters - array of topic filter names 30 | * @param buf the raw buffer data, of the correct length determined by the remaining length field 31 | * @param buflen the length in bytes of the data in the supplied buffer 32 | * @return the length of the serialized data. <= 0 indicates error 33 | */ 34 | int MQTTDeserialize_unsubscribe(unsigned char* dup, unsigned short* packetid, int maxcount, int* count, MQTTString topicFilters[], 35 | unsigned char* buf, int len) 36 | { 37 | MQTTHeader header = {0}; 38 | unsigned char* curdata = buf; 39 | unsigned char* enddata = NULL; 40 | int rc = 0; 41 | int mylen = 0; 42 | 43 | FUNC_ENTRY; 44 | header.byte = readChar(&curdata); 45 | if (header.bits.type != UNSUBSCRIBE) 46 | goto exit; 47 | *dup = header.bits.dup; 48 | 49 | curdata += (rc = MQTTPacket_decodeBuf(curdata, &mylen)); /* read remaining length */ 50 | enddata = curdata + mylen; 51 | 52 | *packetid = readInt(&curdata); 53 | 54 | *count = 0; 55 | while (curdata < enddata) 56 | { 57 | if (!readMQTTLenString(&topicFilters[*count], &curdata, enddata)) 58 | goto exit; 59 | (*count)++; 60 | } 61 | 62 | rc = 1; 63 | exit: 64 | FUNC_EXIT_RC(rc); 65 | return rc; 66 | } 67 | 68 | 69 | /** 70 | * Serializes the supplied unsuback data into the supplied buffer, ready for sending 71 | * @param buf the buffer into which the packet will be serialized 72 | * @param buflen the length in bytes of the supplied buffer 73 | * @param packetid integer - the MQTT packet identifier 74 | * @return the length of the serialized data. <= 0 indicates error 75 | */ 76 | int MQTTSerialize_unsuback(unsigned char* buf, int buflen, unsigned short packetid) 77 | { 78 | MQTTHeader header = {0}; 79 | int rc = 0; 80 | unsigned char *ptr = buf; 81 | 82 | FUNC_ENTRY; 83 | if (buflen < 2) 84 | { 85 | rc = MQTTPACKET_BUFFER_TOO_SHORT; 86 | goto exit; 87 | } 88 | header.byte = 0; 89 | header.bits.type = UNSUBACK; 90 | writeChar(&ptr, header.byte); /* write header */ 91 | 92 | ptr += MQTTPacket_encode(ptr, 2); /* write remaining length */ 93 | 94 | writeInt(&ptr, packetid); 95 | 96 | rc = ptr - buf; 97 | exit: 98 | FUNC_EXIT_RC(rc); 99 | return rc; 100 | } 101 | 102 | 103 | -------------------------------------------------------------------------------- /MQTTPacket/src/StackTrace.h: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | * 13 | * Contributors: 14 | * Ian Craggs - initial API and implementation and/or initial documentation 15 | * Ian Craggs - fix for bug #434081 16 | *******************************************************************************/ 17 | 18 | #ifndef STACKTRACE_H_ 19 | #define STACKTRACE_H_ 20 | 21 | #include 22 | #define NOSTACKTRACE 1 23 | 24 | #if defined(NOSTACKTRACE) 25 | #define FUNC_ENTRY 26 | #define FUNC_ENTRY_NOLOG 27 | #define FUNC_ENTRY_MED 28 | #define FUNC_ENTRY_MAX 29 | #define FUNC_EXIT 30 | #define FUNC_EXIT_NOLOG 31 | #define FUNC_EXIT_MED 32 | #define FUNC_EXIT_MAX 33 | #define FUNC_EXIT_RC(x) 34 | #define FUNC_EXIT_MED_RC(x) 35 | #define FUNC_EXIT_MAX_RC(x) 36 | 37 | #else 38 | 39 | #if defined(WIN32) 40 | #define inline __inline 41 | #define FUNC_ENTRY StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MINIMUM) 42 | #define FUNC_ENTRY_NOLOG StackTrace_entry(__FUNCTION__, __LINE__, -1) 43 | #define FUNC_ENTRY_MED StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MEDIUM) 44 | #define FUNC_ENTRY_MAX StackTrace_entry(__FUNCTION__, __LINE__, TRACE_MAXIMUM) 45 | #define FUNC_EXIT StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MINIMUM) 46 | #define FUNC_EXIT_NOLOG StackTrace_exit(__FUNCTION__, __LINE__, -1) 47 | #define FUNC_EXIT_MED StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MEDIUM) 48 | #define FUNC_EXIT_MAX StackTrace_exit(__FUNCTION__, __LINE__, NULL, TRACE_MAXIMUM) 49 | #define FUNC_EXIT_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MINIMUM) 50 | #define FUNC_EXIT_MED_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MEDIUM) 51 | #define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__FUNCTION__, __LINE__, &x, TRACE_MAXIMUM) 52 | #else 53 | #define FUNC_ENTRY StackTrace_entry(__func__, __LINE__, TRACE_MINIMUM) 54 | #define FUNC_ENTRY_NOLOG StackTrace_entry(__func__, __LINE__, -1) 55 | #define FUNC_ENTRY_MED StackTrace_entry(__func__, __LINE__, TRACE_MEDIUM) 56 | #define FUNC_ENTRY_MAX StackTrace_entry(__func__, __LINE__, TRACE_MAXIMUM) 57 | #define FUNC_EXIT StackTrace_exit(__func__, __LINE__, NULL, TRACE_MINIMUM) 58 | #define FUNC_EXIT_NOLOG StackTrace_exit(__func__, __LINE__, NULL, -1) 59 | #define FUNC_EXIT_MED StackTrace_exit(__func__, __LINE__, NULL, TRACE_MEDIUM) 60 | #define FUNC_EXIT_MAX StackTrace_exit(__func__, __LINE__, NULL, TRACE_MAXIMUM) 61 | #define FUNC_EXIT_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MINIMUM) 62 | #define FUNC_EXIT_MED_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MEDIUM) 63 | #define FUNC_EXIT_MAX_RC(x) StackTrace_exit(__func__, __LINE__, &x, TRACE_MAXIMUM) 64 | 65 | void StackTrace_entry(const char* name, int line, int trace); 66 | void StackTrace_exit(const char* name, int line, void* return_value, int trace); 67 | 68 | void StackTrace_printStack(FILE* dest); 69 | char* StackTrace_get(unsigned long); 70 | 71 | #endif 72 | 73 | #endif 74 | 75 | 76 | 77 | 78 | #endif /* STACKTRACE_H_ */ 79 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=${ANDROID_NDK}/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc 2 | CFLAGS=-IMQTTPacket/src -IMQTTClient-C/src -IMQTTClient-C/src/linux --sysroot ${ANDROID_NDK}/platforms/android-18/arch-arm/ -g 3 | 4 | MQTTPACKET=MQTTPacket/src/MQTTFormat.c MQTTPacket/src/MQTTPacket.c MQTTPacket/src/MQTTDeserializePublish.c MQTTPacket/src/MQTTConnectClient.c MQTTPacket/src/MQTTSubscribeClient.c MQTTPacket/src/MQTTSerializePublish.c MQTTPacket/src/MQTTConnectServer.c MQTTPacket/src/MQTTSubscribeServer.c MQTTPacket/src/MQTTUnsubscribeServer.c MQTTPacket/src/MQTTUnsubscribeClient.c 5 | 6 | MQTTCLIENT=MQTTClient-C/src/MQTTClient.c MQTTClient-C/src/linux/MQTTLinux.c 7 | 8 | wink-handler: wink-handler.c ini.c ${MQTTPACKET} ${MQTTCLIENT} 9 | 10 | clean: 11 | rm -f wink-handler 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | wink-handler 2 | ============ 3 | 4 | **This project is deprecated** 5 | 6 | **Use [wink-relay-manager](https://github.com/jimpastos/wink-relay-manager) instead** 7 | 8 | **Original README follows** 9 | 10 | This is a simple app that can be run on a Wink Relay to turn it into a generic MQTT device. It will send button pushes, sensor data, and motion (proximity sensor) detection to the configured MQTT server, and will accept commands to turn on and off the built-in relays. 11 | 12 | Download 13 | -------- 14 | 15 | Grab the zip file from https://github.com/mjg59/wink-relay-handler/releases and extract it. 16 | 17 | Building 18 | -------- 19 | 20 | Note: you don't need to do this if you've downloaded the binary - just use the wink-handler file from the zip archive. 21 | 22 | You'll need the Android NDK installed. Run ANDROID_NDK=/path/to/android/Ndk make 23 | 24 | Installing 25 | ---------- 26 | 27 | You'll need adb access to a rooted Wink Relay. Disable the existing Wink control software by running 28 | 29 | ``` 30 | pm disable http://com.quirky.android.wink.projectone 31 | ``` 32 | 33 | as root. Remount /system read-write: 34 | 35 | ``` 36 | mount -o rw,remount /system 37 | ``` 38 | 39 | Delete /system/bin/edisonwink: 40 | 41 | ``` 42 | rm /system/bin/edisonwink 43 | ``` 44 | 45 | adb push wink-handler to /sdcard and then copy it over edisonwink and fix permissions: 46 | 47 | ``` 48 | cp /sdcard/wink-handler /system/bin/edisonwink 49 | chmod 755 /system/bin/edisonwink 50 | ``` 51 | 52 | Now write a config file that looks something like: 53 | 54 | ``` 55 | host=192.168.1.5 56 | port=1883 57 | user=username 58 | password=password 59 | clientid=Wink_Relay1 60 | topic_prefix=Relay1 61 | screen_timeout=20 62 | motion_timeout=60 63 | switch_toggle=false 64 | send_switch=true 65 | ``` 66 | and put that in /sdcard/mqtt.ini on the Wink Relay. 67 | 68 | host: Hostname or IP address of the MQTT broker 69 | port: Port of the MQTT broker 70 | user: Username used to authenticate to the MQTT broker (optional) 71 | password: Password used to authenticate to the MQTT broker (optional) 72 | clientid: Client ID passed to the broker (optional - Wink_Relay if not provided) 73 | topic_prefix: Prefix to the topics presented by the device (optional - Relay if not provided) 74 | screen_timeout: Time in seconds until the screen turns off after a touch or proximity detection (optional - 10s if not provided) 75 | switch_toggle: Whether pressing the switch should toggle the relay directly (optional - false if not provided) 76 | send_switch: Whether pressing the switch should generate an MQTT message (optional - true if not provided) 77 | motion_timeout: Time in seconds until the motion detection resets (i.e. time it takes for an "off" message to follow an "on" message) 78 | prox_delta: The percentage change in proximity reading required before triggering the screen to turn on and an mqtt motion detection message (optional - 0.75 if not provided). 79 | 80 | Finally, reset your Relay. 81 | 82 | ``` 83 | reboot 84 | ``` 85 | 86 | It will then reboot - the relays will default to on post-boot. 87 | 88 | Sensors 89 | ------- 90 | 91 | Temperature information will be posted to the 92 | 93 | ``` 94 | Relay/sensors/temperature 95 | ``` 96 | 97 | state topic. Humidity information will be posted to the 98 | 99 | ``` 100 | Relay/sensors/humidity 101 | ``` 102 | 103 | state topic. 104 | 105 | Motion 106 | ------ 107 | 108 | Motion detection (via the proximity sensor on the front of the Wink Relay) will send an "on" message to 109 | 110 | ``` 111 | Relay/motion 112 | ``` 113 | 114 | state topic. An "off" message will follow after 30 seconds, by default. This duration can be configured via the `motion_timeout` parameter in mqtt.ini. 115 | 116 | Buttons 117 | ------- 118 | 119 | Button presses will be posted to 120 | 121 | ``` 122 | Relay/switches/upper 123 | ``` 124 | 125 | and 126 | 127 | ``` 128 | Relay/switches/lower 129 | ``` 130 | 131 | state topics. 132 | 133 | 134 | Relays 135 | ------ 136 | 137 | Relays can be controlled via the 138 | 139 | ``` 140 | Relay/relays/upper 141 | ``` 142 | 143 | and 144 | 145 | ``` 146 | Relay/relays/lower 147 | ``` 148 | 149 | command topics, and will report their state to the 150 | 151 | ``` 152 | Relay/relays/upper_state 153 | ``` 154 | 155 | and 156 | 157 | ``` 158 | Relay/relays/lower_state 159 | ``` 160 | 161 | state topics. 162 | 163 | Screen 164 | ------ 165 | 166 | The screen will turn on if the screen is touched or if the proximity sensor is triggered. It can also be controlled via the 167 | 168 | ``` 169 | Relay/screen 170 | ``` 171 | 172 | command topic: "ON" turns the screen on, "OFF" turns the screen off. 173 | 174 | The screen will turn off 10 seconds after the last touch, proximity, or mqtt "ON" command by default. This duration can be configured via the `screen_timeout` parameter in mqtt.ini. 175 | 176 | Proximity Sensor Sensitivity 177 | ---------------------------- 178 | 179 | The proximity sensor on the front of the Wink Relay triggers both the screen to turn on and an MQTT motion message to be sent. But, the sensitivity of these proximity sensors across different Wink Relay units is not uniform. 180 | 181 | Proximity sensor sensitivity can be controlled via the `prox_delta` parameter in mqtt.ini. The default sensivity is 0.75 if `prox_delta` is not specified. This parameter represents the percentage change in proximity sensor reading required before triggering the screen to turn on and an MQTT motion detection message to be sent. You can make the proximity sensor more "sensitive" by reducing the `prox_delta` value (e.g. to 0.6 or 0.5). If you find that the screen turns on by itself (or won't turn off), increase the `prox_delta` value in small increments (e.g. to 0.8, 0.9, 1.0, etc.) 182 | -------------------------------------------------------------------------------- /ini.c: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | https://github.com/benhoyt/inih 7 | 8 | */ 9 | 10 | #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) 11 | #define _CRT_SECURE_NO_WARNINGS 12 | #endif 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "ini.h" 19 | 20 | #if !INI_USE_STACK 21 | #include 22 | #endif 23 | 24 | #define MAX_SECTION 50 25 | #define MAX_NAME 50 26 | 27 | /* Strip whitespace chars off end of given string, in place. Return s. */ 28 | static char* rstrip(char* s) 29 | { 30 | char* p = s + strlen(s); 31 | while (p > s && isspace((unsigned char)(*--p))) 32 | *p = '\0'; 33 | return s; 34 | } 35 | 36 | /* Return pointer to first non-whitespace char in given string. */ 37 | static char* lskip(const char* s) 38 | { 39 | while (*s && isspace((unsigned char)(*s))) 40 | s++; 41 | return (char*)s; 42 | } 43 | 44 | /* Return pointer to first char (of chars) or inline comment in given string, 45 | or pointer to null at end of string if neither found. Inline comment must 46 | be prefixed by a whitespace character to register as a comment. */ 47 | static char* find_chars_or_comment(const char* s, const char* chars) 48 | { 49 | #if INI_ALLOW_INLINE_COMMENTS 50 | int was_space = 0; 51 | while (*s && (!chars || !strchr(chars, *s)) && 52 | !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { 53 | was_space = isspace((unsigned char)(*s)); 54 | s++; 55 | } 56 | #else 57 | while (*s && (!chars || !strchr(chars, *s))) { 58 | s++; 59 | } 60 | #endif 61 | return (char*)s; 62 | } 63 | 64 | /* Version of strncpy that ensures dest (size bytes) is null-terminated. */ 65 | static char* strncpy0(char* dest, const char* src, size_t size) 66 | { 67 | strncpy(dest, src, size); 68 | dest[size - 1] = '\0'; 69 | return dest; 70 | } 71 | 72 | /* See documentation in header file. */ 73 | int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, 74 | void* user) 75 | { 76 | /* Uses a fair bit of stack (use heap instead if you need to) */ 77 | #if INI_USE_STACK 78 | char line[INI_MAX_LINE]; 79 | #else 80 | char* line; 81 | #endif 82 | char section[MAX_SECTION] = ""; 83 | char prev_name[MAX_NAME] = ""; 84 | 85 | char* start; 86 | char* end; 87 | char* name; 88 | char* value; 89 | int lineno = 0; 90 | int error = 0; 91 | 92 | #if !INI_USE_STACK 93 | line = (char*)malloc(INI_MAX_LINE); 94 | if (!line) { 95 | return -2; 96 | } 97 | #endif 98 | 99 | #if INI_HANDLER_LINENO 100 | #define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) 101 | #else 102 | #define HANDLER(u, s, n, v) handler(u, s, n, v) 103 | #endif 104 | 105 | /* Scan through stream line by line */ 106 | while (reader(line, INI_MAX_LINE, stream) != NULL) { 107 | lineno++; 108 | 109 | start = line; 110 | #if INI_ALLOW_BOM 111 | if (lineno == 1 && (unsigned char)start[0] == 0xEF && 112 | (unsigned char)start[1] == 0xBB && 113 | (unsigned char)start[2] == 0xBF) { 114 | start += 3; 115 | } 116 | #endif 117 | start = lskip(rstrip(start)); 118 | 119 | if (*start == ';' || *start == '#') { 120 | /* Per Python configparser, allow both ; and # comments at the 121 | start of a line */ 122 | } 123 | #if INI_ALLOW_MULTILINE 124 | else if (*prev_name && *start && start > line) { 125 | /* Non-blank line with leading whitespace, treat as continuation 126 | of previous name's value (as per Python configparser). */ 127 | if (!HANDLER(user, section, prev_name, start) && !error) 128 | error = lineno; 129 | } 130 | #endif 131 | else if (*start == '[') { 132 | /* A "[section]" line */ 133 | end = find_chars_or_comment(start + 1, "]"); 134 | if (*end == ']') { 135 | *end = '\0'; 136 | strncpy0(section, start + 1, sizeof(section)); 137 | *prev_name = '\0'; 138 | } 139 | else if (!error) { 140 | /* No ']' found on section line */ 141 | error = lineno; 142 | } 143 | } 144 | else if (*start) { 145 | /* Not a comment, must be a name[=:]value pair */ 146 | end = find_chars_or_comment(start, "=:"); 147 | if (*end == '=' || *end == ':') { 148 | *end = '\0'; 149 | name = rstrip(start); 150 | value = end + 1; 151 | #if INI_ALLOW_INLINE_COMMENTS 152 | end = find_chars_or_comment(value, NULL); 153 | if (*end) 154 | *end = '\0'; 155 | #endif 156 | value = lskip(value); 157 | rstrip(value); 158 | 159 | /* Valid name[=:]value pair found, call handler */ 160 | strncpy0(prev_name, name, sizeof(prev_name)); 161 | if (!HANDLER(user, section, name, value) && !error) 162 | error = lineno; 163 | } 164 | else if (!error) { 165 | /* No '=' or ':' found on name[=:]value line */ 166 | error = lineno; 167 | } 168 | } 169 | 170 | #if INI_STOP_ON_FIRST_ERROR 171 | if (error) 172 | break; 173 | #endif 174 | } 175 | 176 | #if !INI_USE_STACK 177 | free(line); 178 | #endif 179 | 180 | return error; 181 | } 182 | 183 | /* See documentation in header file. */ 184 | int ini_parse_file(FILE* file, ini_handler handler, void* user) 185 | { 186 | return ini_parse_stream((ini_reader)fgets, file, handler, user); 187 | } 188 | 189 | /* See documentation in header file. */ 190 | int ini_parse(const char* filename, ini_handler handler, void* user) 191 | { 192 | FILE* file; 193 | int error; 194 | 195 | file = fopen(filename, "r"); 196 | if (!file) 197 | return -1; 198 | error = ini_parse_file(file, handler, user); 199 | fclose(file); 200 | return error; 201 | } 202 | -------------------------------------------------------------------------------- /ini.h: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 4 | home page for more info: 5 | 6 | https://github.com/benhoyt/inih 7 | 8 | */ 9 | 10 | #ifndef __INI_H__ 11 | #define __INI_H__ 12 | 13 | /* Make this header file easier to include in C++ code */ 14 | #ifdef __cplusplus 15 | extern "C" { 16 | #endif 17 | 18 | #include 19 | 20 | /* Nonzero if ini_handler callback should accept lineno parameter. */ 21 | #ifndef INI_HANDLER_LINENO 22 | #define INI_HANDLER_LINENO 0 23 | #endif 24 | 25 | /* Typedef for prototype of handler function. */ 26 | #if INI_HANDLER_LINENO 27 | typedef int (*ini_handler)(void* user, const char* section, 28 | const char* name, const char* value, 29 | int lineno); 30 | #else 31 | typedef int (*ini_handler)(void* user, const char* section, 32 | const char* name, const char* value); 33 | #endif 34 | 35 | /* Typedef for prototype of fgets-style reader function. */ 36 | typedef char* (*ini_reader)(char* str, int num, void* stream); 37 | 38 | /* Parse given INI-style file. May have [section]s, name=value pairs 39 | (whitespace stripped), and comments starting with ';' (semicolon). Section 40 | is "" if name=value pair parsed before any section heading. name:value 41 | pairs are also supported as a concession to Python's configparser. 42 | 43 | For each name=value pair parsed, call handler function with given user 44 | pointer as well as section, name, and value (data only valid for duration 45 | of handler call). Handler should return nonzero on success, zero on error. 46 | 47 | Returns 0 on success, line number of first error on parse error (doesn't 48 | stop on first error), -1 on file open error, or -2 on memory allocation 49 | error (only when INI_USE_STACK is zero). 50 | */ 51 | int ini_parse(const char* filename, ini_handler handler, void* user); 52 | 53 | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't 54 | close the file when it's finished -- the caller must do that. */ 55 | int ini_parse_file(FILE* file, ini_handler handler, void* user); 56 | 57 | /* Same as ini_parse(), but takes an ini_reader function pointer instead of 58 | filename. Used for implementing custom or string-based I/O. */ 59 | int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, 60 | void* user); 61 | 62 | /* Nonzero to allow multi-line value parsing, in the style of Python's 63 | configparser. If allowed, ini_parse() will call the handler with the same 64 | name for each subsequent line parsed. */ 65 | #ifndef INI_ALLOW_MULTILINE 66 | #define INI_ALLOW_MULTILINE 1 67 | #endif 68 | 69 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of 70 | the file. See http://code.google.com/p/inih/issues/detail?id=21 */ 71 | #ifndef INI_ALLOW_BOM 72 | #define INI_ALLOW_BOM 1 73 | #endif 74 | 75 | /* Nonzero to allow inline comments (with valid inline comment characters 76 | specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match 77 | Python 3.2+ configparser behaviour. */ 78 | #ifndef INI_ALLOW_INLINE_COMMENTS 79 | #define INI_ALLOW_INLINE_COMMENTS 1 80 | #endif 81 | #ifndef INI_INLINE_COMMENT_PREFIXES 82 | #define INI_INLINE_COMMENT_PREFIXES ";" 83 | #endif 84 | 85 | /* Nonzero to use stack, zero to use heap (malloc/free). */ 86 | #ifndef INI_USE_STACK 87 | #define INI_USE_STACK 1 88 | #endif 89 | 90 | /* Stop parsing on first error (default is to keep parsing). */ 91 | #ifndef INI_STOP_ON_FIRST_ERROR 92 | #define INI_STOP_ON_FIRST_ERROR 0 93 | #endif 94 | 95 | /* Maximum line length for any line in INI file. */ 96 | #ifndef INI_MAX_LINE 97 | #define INI_MAX_LINE 200 98 | #endif 99 | 100 | #ifdef __cplusplus 101 | } 102 | #endif 103 | 104 | #endif /* __INI_H__ */ 105 | -------------------------------------------------------------------------------- /wink-handler.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | 14 | #include "ini.h" 15 | 16 | struct configuration { 17 | char *username; 18 | char *password; 19 | char *host; 20 | char *clientid; 21 | char *topic_prefix; 22 | int port; 23 | int screen_timeout; 24 | int motion_timeout; 25 | bool switch_toggle; 26 | bool send_switch; 27 | float prox_delta; 28 | }; 29 | 30 | static struct configuration config; 31 | 32 | int last_input; 33 | 34 | #define UPPER_RELAY "/sys/class/gpio/gpio203/value" 35 | #define LOWER_RELAY "/sys/class/gpio/gpio204/value" 36 | #define SCREEN "/sys/class/gpio/gpio30/value" 37 | 38 | void toggle_relay(char *path) 39 | { 40 | int fd; 41 | char state; 42 | 43 | fd = open(path, O_RDWR); 44 | read(fd, &state, sizeof(state)); 45 | lseek(fd, 0, SEEK_SET); 46 | if (state == '0') { 47 | state = '1'; 48 | } else { 49 | state = '0'; 50 | } 51 | write(fd, &state, 1); 52 | close(fd); 53 | } 54 | 55 | void handle_relay(char *path, char *payload, int len) 56 | { 57 | int fd; 58 | char power; 59 | 60 | signal(SIGPIPE, SIG_IGN); 61 | 62 | fd = open(path, O_RDWR); 63 | if (strncmp(payload, "ON", len) == 0) { 64 | power = '1'; 65 | write(fd, &power, 1); 66 | } else if (strncmp(payload, "OFF", len) == 0) { 67 | power = '0'; 68 | write(fd, &power, 1); 69 | } 70 | close(fd); 71 | } 72 | 73 | void handle_relay1(MessageData *md) 74 | { 75 | MQTTMessage *message = md->message; 76 | 77 | handle_relay(UPPER_RELAY, message->payload, message->payloadlen); 78 | } 79 | 80 | void handle_relay2(MessageData *md) 81 | { 82 | MQTTMessage *message = md->message; 83 | 84 | handle_relay(LOWER_RELAY, message->payload, message->payloadlen); 85 | } 86 | 87 | void handle_screen(MessageData *md) 88 | { 89 | int fd; 90 | char power; 91 | MQTTMessage *message = md->message; 92 | 93 | fd = open(SCREEN, O_RDWR); 94 | lseek(fd, 0, SEEK_SET); 95 | read(fd, &power, sizeof(power)); 96 | 97 | if (strncmp(message->payload, "ON", message->payloadlen) == 0) { 98 | last_input = time(NULL); 99 | power = '1'; 100 | write(fd, &power, 1); 101 | } else if (strncmp(message->payload, "OFF", message->payloadlen) == 0) { 102 | power = '0'; 103 | write(fd, &power, 1); 104 | } 105 | close(fd); 106 | } 107 | 108 | int mqtt_connect(Network *n, MQTTClient *c, char *buf, char *readbuf) { 109 | int ret; 110 | MQTTPacket_connectData data = MQTTPacket_connectData_initializer; 111 | 112 | NetworkInit(n); 113 | 114 | if (config.host == NULL || config.port == 0) { 115 | fprintf(stderr, "No valid configuration\n"); 116 | return 1; 117 | } 118 | 119 | ret = NetworkConnect(n, config.host, config.port); 120 | if (ret < 0) 121 | return ret; 122 | 123 | MQTTClientInit(c, n, 1000, buf, 100, readbuf, 100); 124 | 125 | data.willFlag = 0; 126 | data.MQTTVersion = 4; 127 | if (config.clientid != NULL) { 128 | data.clientID.cstring = config.clientid; 129 | } else { 130 | data.clientID.cstring = "Wink_Relay"; 131 | } 132 | data.keepAliveInterval = 10; 133 | data.cleansession = 1; 134 | if (config.username != NULL) { 135 | data.username.cstring = config.username; 136 | } 137 | if (config.password != NULL) { 138 | data.password.cstring = config.password; 139 | } 140 | ret = MQTTConnect(c, &data); 141 | if (ret < 0) 142 | return ret; 143 | 144 | return 0; 145 | } 146 | 147 | static int config_handler(void* data, const char* section, const char* name, 148 | const char* value) 149 | { 150 | if (strcmp(name, "user") == 0) { 151 | config.username = strdup(value); 152 | } else if (strcmp(name, "password") == 0) { 153 | config.password = strdup(value); 154 | } else if (strcmp(name, "clientid") == 0) { 155 | config.clientid = strdup(value); 156 | } else if (strcmp(name, "topic_prefix") == 0) { 157 | config.topic_prefix = strdup(value); 158 | } else if (strcmp(name, "host") == 0) { 159 | config.host = strdup(value); 160 | } else if (strcmp(name, "port") == 0) { 161 | config.port = atoi(value); 162 | } else if (strcmp(name, "screen_timeout") == 0) { 163 | config.screen_timeout = atoi(value); 164 | } else if (strcmp(name, "motion_timeout") == 0) { 165 | config.motion_timeout = atoi(value); 166 | } else if (strcmp(name, "switch_toggle") == 0) { 167 | if (strcmp(value, "true") == 0) { 168 | config.switch_toggle = true; 169 | } else if (strcmp(value, "false") == 0) { 170 | config.switch_toggle = false; 171 | } 172 | } else if (strcmp(name, "send_switch") == 0) { 173 | if (strcmp(value, "true") == 0) { 174 | config.send_switch = true; 175 | } else if (strcmp(value, "false") == 0) { 176 | config.send_switch = false; 177 | } 178 | } else if (strcmp(name, "prox_delta") == 0) { 179 | config.prox_delta = atof(value); 180 | } 181 | return 1; 182 | } 183 | 184 | int main() { 185 | int ret; 186 | int uswitch, lswitch, input, screen, relay1, relay2, temp, humid, prox; 187 | int uswitchstate=1; 188 | int lswitchstate=1; 189 | bool motion = 0; 190 | char urelaystate=' '; 191 | char lrelaystate=' '; 192 | int last_input = time(NULL); 193 | int last_motion = time(NULL); 194 | struct input_event event; 195 | unsigned char buf[100], readbuf[100]; 196 | char buffer[30]; 197 | char power = '1'; 198 | char payload[30]; 199 | char proxdata[100]; 200 | int temperature, humidity, last_temperature = -1, last_humidity = -1; 201 | long proximity; 202 | Network n; 203 | MQTTClient c; 204 | MQTTMessage message; 205 | struct rlimit limits; 206 | char *prefix, topic[1024]; 207 | int s_timeout; 208 | int m_timeout; 209 | int last_proximity; 210 | float delta; 211 | 212 | last_input = time(NULL); 213 | config.send_switch = true; 214 | 215 | if (ini_parse("/sdcard/mqtt.ini", config_handler, NULL) < 0) { 216 | printf("Can't load /sdcard/mqtt.ini\n"); 217 | return 1; 218 | } 219 | 220 | if (config.topic_prefix != NULL) { 221 | prefix = config.topic_prefix; 222 | } else { 223 | prefix = strdup("Relay"); 224 | } 225 | 226 | if (config.screen_timeout == 0) { 227 | s_timeout = 10; 228 | } else { 229 | s_timeout = config.screen_timeout; 230 | } 231 | 232 | if (config.motion_timeout == 0) { 233 | m_timeout = 30; 234 | } else { 235 | m_timeout = config.motion_timeout; 236 | } 237 | 238 | if (config.prox_delta == 0) { 239 | delta = 0.75; 240 | } else { 241 | delta = config.prox_delta; 242 | } 243 | 244 | uswitch = open("/sys/class/gpio/gpio8/value", O_RDONLY); 245 | lswitch = open("/sys/class/gpio/gpio7/value", O_RDONLY); 246 | screen = open("/sys/class/gpio/gpio30/value", O_RDWR); 247 | relay1 = open("/sys/class/gpio/gpio203/value", O_RDWR); 248 | relay2 = open("/sys/class/gpio/gpio204/value", O_RDWR); 249 | input = open("/dev/input/event0", O_RDONLY | O_NONBLOCK); 250 | temp = open("/sys/bus/i2c/devices/2-0040/temp1_input", O_RDONLY); 251 | humid = open("/sys/bus/i2c/devices/2-0040/humidity1_input", O_RDONLY); 252 | prox = open("/sys/devices/platform/imx-i2c.2/i2c-2/2-005a/input/input3/ps_input_data", O_RDONLY); 253 | 254 | write(relay1, &power, 1); 255 | write(relay2, &power, 1); 256 | 257 | limits.rlim_cur = RLIM_INFINITY; 258 | limits.rlim_max = RLIM_INFINITY; 259 | setrlimit(RLIMIT_CORE, &limits); 260 | while (mqtt_connect(&n, &c, buf, readbuf) < 0) 261 | sleep(10); 262 | 263 | sprintf(topic, "%s/relays/upper", prefix); 264 | MQTTSubscribe(&c, topic, 0, handle_relay1); 265 | sprintf(topic, "%s/relays/lower", prefix); 266 | MQTTSubscribe(&c, topic, 0, handle_relay2); 267 | sprintf(topic, "%s/screen", prefix); 268 | MQTTSubscribe(&c, topic, 0, handle_screen); 269 | 270 | 271 | while (1) { 272 | lseek(relay1, 0, SEEK_SET); 273 | read(relay1, buffer, sizeof(buffer)); 274 | if (buffer[0] != urelaystate) { 275 | message.qos = 1; 276 | message.payload = payload; 277 | if (buffer[0] == '0') { 278 | sprintf(payload, "OFF"); 279 | } else { 280 | sprintf(payload, "ON"); 281 | } 282 | message.payloadlen = strlen(payload); 283 | sprintf(topic, "%s/relays/upper_state", prefix); 284 | MQTTPublish(&c, topic, &message); 285 | urelaystate = buffer[0]; 286 | } 287 | 288 | lseek(relay2, 0, SEEK_SET); 289 | read(relay2, buffer, sizeof(buffer)); 290 | if (buffer[0] != lrelaystate) { 291 | message.qos = 1; 292 | message.payload = payload; 293 | if (buffer[0] == '0') { 294 | sprintf(payload, "OFF"); 295 | } else { 296 | sprintf(payload, "ON"); 297 | } 298 | message.payloadlen = strlen(payload); 299 | sprintf(topic, "%s/relays/lower_state", prefix); 300 | MQTTPublish(&c, topic, &message); 301 | lrelaystate = buffer[0]; 302 | } 303 | 304 | lseek(uswitch, 0, SEEK_SET); 305 | read(uswitch, buffer, sizeof(buffer)); 306 | if (buffer[0] == '0' && uswitchstate == 1) { 307 | uswitchstate = 0; 308 | if (config.send_switch) { 309 | message.qos = 1; 310 | message.payload = payload; 311 | sprintf(payload, "on"); 312 | message.payloadlen = strlen(payload); 313 | message.retained = 0; 314 | sprintf(topic, "%s/switches/upper", prefix); 315 | MQTTPublish(&c, topic, &message); 316 | } 317 | if (config.switch_toggle) 318 | toggle_relay(UPPER_RELAY); 319 | } else if (buffer[0] == '1' && uswitchstate == 0) { 320 | uswitchstate = 1; 321 | } 322 | lseek(lswitch, 0, SEEK_SET); 323 | read(lswitch, buffer, sizeof(buffer)); 324 | if (buffer[0] == '0' && lswitchstate == 1) { 325 | lswitchstate = 0; 326 | if (config.send_switch) { 327 | message.qos = 1; 328 | message.payload = payload; 329 | sprintf(payload, "on"); 330 | message.payloadlen = strlen(payload); 331 | message.retained = 0; 332 | sprintf(topic, "%s/switches/lower", prefix); 333 | MQTTPublish(&c, topic, &message); 334 | } 335 | if (config.switch_toggle) 336 | toggle_relay(LOWER_RELAY); 337 | } else if (buffer[0] == '1' && lswitchstate == 0) { 338 | lswitchstate = 1; 339 | } 340 | 341 | lseek(temp, 0, SEEK_SET); 342 | read(temp, buffer, sizeof(buffer)); 343 | temperature = atoi(buffer); 344 | if (abs(temperature - last_temperature) > 100) { 345 | message.qos = 1; 346 | message.payload = payload; 347 | sprintf(payload, "%f", temperature/1000.0); 348 | message.payloadlen = strlen(payload); 349 | sprintf(topic, "%s/sensors/temperature", prefix); 350 | MQTTPublish(&c, topic, &message); 351 | last_temperature = temperature; 352 | } 353 | 354 | lseek(humid, 0, SEEK_SET); 355 | read(humid, buffer, sizeof(buffer)); 356 | humidity = atoi(buffer); 357 | if (abs(humidity - last_humidity) > 100) { 358 | message.qos = 1; 359 | message.payload = payload; 360 | sprintf(payload, "%f", humidity/1000.0); 361 | message.payloadlen = strlen(payload); 362 | sprintf(topic, "%s/sensors/humidity", prefix); 363 | MQTTPublish(&c, topic, &message); 364 | last_humidity = humidity; 365 | } 366 | 367 | lseek(prox, 0, SEEK_SET); 368 | read(prox, proxdata, sizeof(proxdata)); 369 | proximity = strtol(proxdata, NULL, 10); 370 | lseek(screen, 0, SEEK_SET); 371 | read(screen, &power, sizeof(power)); 372 | 373 | if (proximity > (last_proximity * (1 + delta / 100))) { 374 | last_input = time(NULL); 375 | if (power != '1') { 376 | power = '1'; 377 | write(screen, &power, sizeof(power)); 378 | } 379 | last_motion = time(NULL); 380 | if (!motion) { 381 | motion = 1; 382 | message.qos = 1; 383 | message.payload = payload; 384 | sprintf(payload, "on"); 385 | message.payloadlen = strlen(payload); 386 | message.retained = 1; 387 | sprintf(topic, "%s/motion", prefix); 388 | MQTTPublish(&c, topic, &message); 389 | } 390 | } 391 | last_proximity = proximity; 392 | 393 | while (read(input, &event, sizeof(event)) > 0) { 394 | last_input = time(NULL); 395 | if (power != '1') { 396 | power = '1'; 397 | write(screen, &power, sizeof(power)); 398 | } 399 | } 400 | 401 | if ((time(NULL) - last_input > s_timeout) && power == '1') { 402 | power = '0'; 403 | write(screen, &power, sizeof(power)); 404 | } 405 | 406 | if ((time(NULL) - last_motion > m_timeout) && motion) { 407 | motion = 0; 408 | message.qos = 1; 409 | message.payload = payload; 410 | sprintf(payload, "off"); 411 | message.payloadlen = strlen(payload); 412 | message.retained = 1; 413 | sprintf(topic, "%s/motion", prefix); 414 | MQTTPublish(&c, topic, &message); 415 | } 416 | 417 | if (MQTTYield(&c, 100) < 0) 418 | mqtt_connect(&n, &c, buf, readbuf); 419 | } 420 | } 421 | --------------------------------------------------------------------------------