Add toolchain and mbtk source

Change-Id: Ie12546301367ea59240bf23d5e184ad7e36e40b3
diff --git a/mbtk/mbtk_lib/src/mbtk_coap_pdu.cpp b/mbtk/mbtk_lib/src/mbtk_coap_pdu.cpp
new file mode 100755
index 0000000..c9f046d
--- /dev/null
+++ b/mbtk/mbtk_lib/src/mbtk_coap_pdu.cpp
@@ -0,0 +1,2065 @@
+/*

+Copyright (c) 2013, Ashley Mills.

+All rights reserved.

+

+Redistribution and use in source and binary forms, with or without

+modification, are permitted provided that the following conditions are met:

+

+1. Redistributions of source code must retain the above copyright notice, this

+   list of conditions and the following disclaimer.

+2. Redistributions in binary form must reproduce the above copyright notice,

+   this list of conditions and the following disclaimer in the documentation

+   and/or other materials provided with the distribution.

+

+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND

+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED

+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE

+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR

+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES

+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;

+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND

+ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT

+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS

+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

+ */

+

+// version, 2 bits

+// type, 2 bits

+// 00 Confirmable

+// 01 Non-confirmable

+// 10 Acknowledgement

+// 11 Reset

+

+// token length, 4 bits

+// length of token in bytes (only 0 to 8 bytes allowed)

+

+#include <stdio.h>

+#include <stdlib.h>

+#include <stdint.h>

+#include <string.h>

+

+#include "mbtk_coap_pdu.h"

+

+//#include "mbtk_log.h"

+//#ifdef FEATURE_MBTK_ECOAP

+/// Memory-managed constructor. Buffer for PDU is dynamically sized and allocated by the object.

+/**

+ * When using this constructor, the CoapPDU class will allocate space for the PDU.

+ * Contrast this with the parameterized constructors, which allow the use of an external buffer.

+ *

+ * Note, the PDU container and space can be reused by issuing a CoapPDU::reset(). If the new PDU exceeds the

+ * space of the previously allocated memory, then further memory will be dynamically allocated.

+ *

+ * Deleting the object will free the Object container and all dynamically allocated memory.

+ *

+ * \note It would have been nice to use something like UDP_CORK or MSG_MORE, to allow separate buffers

+ * for token, options, and payload but these FLAGS aren't implemented for UDP in LwIP so stuck with one buffer for now.

+ *

+ * CoAP version defaults to 1.

+ *

+ * \sa CoapPDU::CoapPDU(uint8_t *pdu, int pduLength), CoapPDU::CoapPDU::(uint8_t *buffer, int bufferLength, int pduLength),

+ * CoapPDU:CoapPDU()~

+ *

+ */

+

+//#define printf(fmtstr) LOGD(fmtstr)

+//#define printf(fmtstr ,arg1) LOGD(fmtstr, arg1)

+//#define printf(fmtstr, arg1, arg2) LOGD(fmtstr , arg1, arg2)

+//#define printf(fmtstr, arg1, arg2, arg3) LOGD(fmtstr , arg1, arg2, arg3)

+

+/*

+void* operator new(unsigned int size, void* alloc, ds_appsrv_mem_e_type mem_type) throw()

+{

+// Do nothing

+    return alloc;

+}

+

+void operator delete(void* obj, void* alloc, ds_appsrv_mem_e_type mem_type) throw()

+{

+// Do nothing

+}

+

+*/

+

+CoapPDU::CoapPDU()

+{

+    // pdu

+    _pdu = (uint8_t*)calloc(4,sizeof(uint8_t));

+    _pduLength = 4;

+    _bufferLength = _pduLength;

+

+    //options

+    _numOptions = 0;

+    _maxAddedOptionNumber = 0;

+

+    // payload

+    _payloadPointer = NULL;

+    _payloadLength = 0;

+

+    _constructedFromBuffer = 0;

+

+    setVersion(1);

+}

+

+/// Construct object from external buffer that may be larger than actual PDU.

+/**

+ * This differs from CoapPDU::CoapPDU(uint8_t *pdu, int pduLength) in that the buffer may be larger

+ * than the actual CoAP PDU contained int the buffer. This is typically used when a large buffer is reused

+ * multiple times. Note that \b pduLength can be 0.

+ *

+ * If an actual CoAP PDU is passed in the buffer, \b pduLength should match its length. CoapPDU::validate() must

+ * be called to initiate the object before member functions can be used.

+ *

+ * A PDU constructed in this manner must be validated with CoapPDU::validate() before the member variables will be accessible.

+ *

+ * \warning The validation call parses the PDU structure to set some internal parameters. If you do

+ * not validate the PDU, then the behaviour of member access functions will be undefined.

+ *

+ * The buffer can be reused by issuing a CoapPDU::reset() but the class will not change the size of the buffer. If the

+ * newly constructed PDU exceeds the size of the buffer, the function called (for example CoapPDU::addOption) will fail.

+ *

+ * Deleting this object will only delete the Object container and will not delete the PDU buffer.

+ *

+ * \param buffer A buffer which either contains a CoAP PDU or is intended to be used to construct one.

+ * \param bufferLength The length of the buffer

+ * \param pduLength If the buffer contains a CoAP PDU, this specifies the length of the PDU within the buffer.

+ *

+ * \sa CoapPDU::CoapPDU(), CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)

+ */

+CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)

+{

+    	printf("CoapPDU(),bufferLength:%d, pduLength;%d\n",bufferLength,pduLength);

+    // sanity

+    if(pduLength<4&&pduLength!=0)

+    {

+        printf("PDU cannot have a length less than 4");

+    }

+

+    // pdu

+    _pdu = buffer;

+    _bufferLength = bufferLength;

+    if(pduLength==0)

+    {

+        // this is actually a fresh pdu, header always exists

+        _pduLength = 4;

+        // make sure header is zeroed

+        _pdu[0] = 0x00;

+        _pdu[1] = 0x00;

+        _pdu[2] = 0x00;

+        _pdu[3] = 0x00;

+        setVersion(1);

+    }

+    else

+    {

+        _pduLength = pduLength;

+    }

+

+    _constructedFromBuffer = 1;

+

+    // options

+    _numOptions = 0;

+    _maxAddedOptionNumber = 0;

+

+    // payload

+    _payloadPointer = NULL;

+    _payloadLength = 0;

+}

+

+/// Reset CoapPDU container so it can be reused to build a new PDU.

+/**

+ * This resets the CoapPDU container, setting the pdu length, option count, etc back to zero. The

+ * PDU can then be populated as if it were newly constructed.

+ *

+ * Note that the space available will depend on how the CoapPDU was originally constructed:

+ * -# CoapPDU::CoapPDU()

+ *

+ *  Available space initially be \b _pduLength. But further space will be allocated as needed on demand,

+ *    limited only by the OS/environment.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)

+ *

+ *      Space is limited by the variable \b pduLength. The PDU cannot exceed \b pduLength bytes.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)

+ *

+ *      Space is limited by the variable \b bufferLength. The PDU cannot exceed \b bufferLength bytes.

+ *

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::reset()

+{

+    // pdu

+    memset(_pdu,0x00,_bufferLength);

+    // packet always has at least a header

+    _pduLength = 4;

+

+    // options

+    _numOptions = 0;

+    _maxAddedOptionNumber = 0;

+    // payload

+    _payloadPointer = NULL;

+    _payloadLength = 0;

+    content.clear();

+    return 0;

+}

+

+/// Validates a PDU constructed using an external buffer.

+/**

+ * When a CoapPDU is constructed using an external buffer, the programmer must call this function to

+ * check that the received PDU is a valid CoAP PDU.

+ *

+ * \warning The validation call parses the PDU structure to set some internal parameters. If you do

+ * not validate the PDU, then the behaviour of member access functions will be undefined.

+ *

+ * \return 1 if the PDU validates correctly, 0 if not. XXX maybe add some error codes

+ */

+int CoapPDU::validate()

+{

+    if(_pduLength<4)

+    {

+        printf("PDU has to be a minimum of 4 bytes. This: %d bytes",_pduLength);

+        return 0;

+    }

+

+    // check header

+    //   0                   1                   2                   3

+    //  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |Ver| T |  TKL  |      Code     |          Message ID           |

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |   Token (if any, TKL bytes) ...

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |   Options (if any) ...

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+    // |1 1 1 1 1 1 1 1|    Payload (if any) ...

+    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

+

+    // version must be 1

+    int version = getVersion();

+    if (version != 1)

+    {

+        printf("Invalid version: %d", version);

+        return 0;

+    }

+    printf("Version: %d", version);

+    printf("Type: %d", getType());

+

+    // token length must be between 0 and 8

+    int tokenLength = getTokenLength();

+    if(tokenLength<0||tokenLength>8)

+    {

+        printf("Invalid token length: %d",tokenLength);

+        return 0;

+    }

+    printf("Token length: %d",tokenLength);

+    // check total length

+    if((COAP_HDR_SIZE+tokenLength)>_pduLength)

+    {

+        printf("Token length would make pdu longer than actual length.");

+        return 0;

+    }

+

+    // check that code is valid

+    int code = getCode();

+    if(code<COAP_EMPTY ||

+       (code>COAP_LASTMETHOD&&code<COAP_CREATED) ||

+       (code>COAP_CONTENT&&code<COAP_CONTINUE) ||

+       (code>COAP_CONTINUE&&code<COAP_BAD_REQUEST) ||

+       (code>COAP_NOT_ACCEPTABLE&&code<COAP_PRECONDITION_FAILED) ||

+       (code==0x8E) ||

+       (code>COAP_UNSUPPORTED_CONTENT_FORMAT&&code<COAP_INTERNAL_SERVER_ERROR) ||

+       (code>COAP_PROXYING_NOT_SUPPORTED) )

+    {

+        printf("Invalid CoAP code: %d",code);

+        return 0;

+    }

+    printf("CoAP code: %d",code);

+

+    // token can be anything so nothing to check

+

+    // check that options all make sense

+    uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;

+    int totalLength = 0;

+

+    // first option occurs after token

+    int optionPos = COAP_HDR_SIZE + getTokenLength();

+

+    // may be 0 options

+    if(optionPos==_pduLength)

+    {

+        printf("No options. No payload.");

+        _numOptions = 0;

+        _payloadLength = 0;

+        return 1;

+    }

+

+    int bytesRemaining = _pduLength-optionPos;

+    int numOptions = 0;

+    uint8_t upperNibble = 0x00, lowerNibble = 0x00;

+

+    // walk over options and record information

+    while(1)

+    {

+        // check for payload marker

+        if(bytesRemaining>0)

+        {

+            uint8_t optionHeader = _pdu[optionPos];

+            if(optionHeader==0xFF)

+            {

+                // payload

+                if(bytesRemaining>1)

+                {

+                    _payloadPointer = &_pdu[optionPos+1];

+                    _payloadLength = (bytesRemaining-1);

+                    _numOptions = numOptions;

+                    printf("Payload found, length: %d",_payloadLength);

+                    return 1;

+                }

+                // payload marker but no payload

+                _payloadPointer = NULL;

+                _payloadLength = 0;

+                printf("Payload marker but no payload.");

+                return 0;

+            }

+

+            // check that option delta and option length are valid values

+            upperNibble = (optionHeader & 0xF0) >> 4;

+            lowerNibble = (optionHeader & 0x0F);

+            if(upperNibble==0x0F||lowerNibble==0x0F)

+            {

+                printf("Expected option header or payload marker, got: 0x%x%x",upperNibble,lowerNibble);

+                return 0;

+            }

+            printf("Option header byte appears sane: 0x%x%x",upperNibble,lowerNibble);

+        }

+        else

+        {

+            printf("No more data. No payload.");

+            _payloadPointer = NULL;

+            _payloadLength = 0;

+            _numOptions = numOptions;

+            return 1;

+        }

+

+        // skip over option header byte

+        bytesRemaining--;

+

+        // check that there is enough space for the extended delta and length bytes (if any)

+        int headerBytesNeeded = computeExtraBytes(upperNibble);

+        printf("%d extra bytes needed for extended delta",headerBytesNeeded);

+        if(headerBytesNeeded>bytesRemaining)

+        {

+            printf("Not enough space for extended option delta, needed %d, have %d.",headerBytesNeeded,bytesRemaining);

+            return 0;

+        }

+        headerBytesNeeded += computeExtraBytes(lowerNibble);

+        if(headerBytesNeeded>bytesRemaining)

+        {

+            printf("Not enough space for extended option length, needed %d, have %d.",

+                   (headerBytesNeeded-computeExtraBytes(upperNibble)),bytesRemaining);

+            return 0;

+        }

+        printf("Enough space for extended delta and length: %d, continuing.",headerBytesNeeded);

+

+        // extract option details

+        optionDelta = getOptionDelta(&_pdu[optionPos]);

+        optionNumber += optionDelta;

+        optionValueLength = getOptionValueLength(&_pdu[optionPos]);

+        printf("Got option: %d with length %d",optionNumber,optionValueLength);

+        // compute total length

+        totalLength = 1; // mandatory header

+        totalLength += computeExtraBytes(optionDelta);

+        totalLength += computeExtraBytes(optionValueLength);

+        totalLength += optionValueLength;

+        // check there is enough space

+        if(optionPos+totalLength>_pduLength)

+        {

+            printf("Not enough space for option payload, needed %d, have %d.",(totalLength-headerBytesNeeded-1),_pduLength-optionPos);

+            return 0;

+        }

+        printf("Enough space for option payload: %d %d",optionValueLength,(totalLength-headerBytesNeeded-1));

+

+        // recompute bytesRemaining

+        bytesRemaining -= totalLength;

+        bytesRemaining++; // correct for previous --

+

+        // move to next option

+        optionPos += totalLength;

+

+        // inc number of options XXX

+        numOptions++;

+    }

+

+    return 1;

+}

+

+/// Destructor. Does not free buffer if constructor passed an external buffer.

+/**

+ * The destructor acts differently, depending on how the object was initially constructed (from buffer or not):

+ *

+ * -# CoapPDU::CoapPDU()

+ *

+ *  Complete object is destroyed.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *pdu, int pduLength)

+ *

+ *      Only object container is destroyed. \b pdu is left intact.

+ *

+ * -# CoapPDU::CoapPDU(uint8_t *buffer, int bufferLength, int pduLength)

+ *

+ *      Only object container is destroyed. \b pdu is left intact.

+ *

+ */

+CoapPDU::~CoapPDU()

+{

+    if(!_constructedFromBuffer)

+    {

+        free(_pdu);

+    }

+}

+

+/// Returns a pointer to the internal buffer.

+uint8_t* CoapPDU::getPDUPointer()

+{

+    return _pdu;

+}

+

+/// Set the PDU length to the length specified.

+/**

+ * This is used when re-using a PDU container before calling CoapPDU::validate() as it

+ * is not possible to deduce the length of a PDU since the payload has no length marker.

+ * \param len The length of the PDU

+ */

+void CoapPDU::setPDULength(int len)

+{

+    _pduLength = len;

+}

+

+/// Shorthand function for setting a resource URI.

+/**

+ * Calls CoapPDU::setURI(uri,strlen(uri).

+ */

+int CoapPDU::setURI(char *uri)

+{

+    return setURI(uri,strlen(uri));

+}

+

+/// Shorthand function for setting a resource URI.

+/**

+ * This will parse the supplied \b uri and construct enough URI_PATH and URI_QUERY options to encode it.

+ * The options are added to the PDU.

+ *

+ * At present only simple URI formatting is handled, only '/','?', and '&' separators, and no port or protocol specificaiton.

+ *

+ * The function will split on '/' and create URI_PATH elements until it either reaches the end of the string

+ * in which case it will stop or if it reaches '?' it will start splitting on '&' and create URI_QUERY elements

+ * until it reaches the end of the string.

+ *

+ * Here is an example:

+ *

+ * /a/b/c/d?x=1&y=2&z=3

+ *

+ * Will be broken into four URI_PATH elements "a", "b", "c", "d", and three URI_QUERY elements "x=1", "y=2", "z=3"

+ *

+ * TODO: Add protocol extraction, port extraction, and some malformity checking.

+ *

+ * \param uri The uri to parse.

+ * \param urilen The length of the uri to parse.

+ *

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setURI(char *uri, int urilen)

+{

+    // only '/', '?', '&' and ascii chars allowed

+

+    // sanitation

+    if(urilen<=0||uri==NULL)

+    {

+        printf("Null or zero-length uri passed.");

+        return 1;

+    }

+

+    // single character URI path (including '/' case)

+    if(urilen==1)

+    {

+        addOption(COAP_OPTION_URI_PATH,1,(uint8_t*)uri);

+        return 0;

+    }

+

+    // TODO, queries

+    // extract ? to mark where to stop processing path components

+    // and then process the query params

+

+    // local vars

+    char *startP=uri,*endP=NULL;

+    int oLen = 0;

+    char splitChar = '/';

+    int queryStageTriggered = 0;

+    uint16_t optionType = COAP_OPTION_URI_PATH;

+    while(1)

+    {

+        // stop at end of string or query

+        if(*startP==0x00||*(startP+1)==0x00)

+        {

+            break;

+        }

+

+        // ignore leading slash

+        if(*startP==splitChar)

+        {

+            printf("Skipping leading slash");

+            startP++;

+        }

+

+        // find next split point

+        endP = strchr(startP,splitChar);

+

+        // might not be another slash

+        if(endP==NULL)

+        {

+            printf("Ending out of slash");

+            // check if there is a ?

+            endP = strchr(startP,'?');

+            // done if no queries

+            if(endP==NULL)

+            {

+                endP = uri+urilen;

+            }

+            else

+            {

+                queryStageTriggered = 1;

+            }

+        }

+

+        // get length of segment

+        oLen = endP-startP;

+

+#ifdef DEBUG

+        char *b = (char*)malloc(oLen+1);

+        memcpy(b,startP,oLen);

+        b[oLen] = 0x00;

+        printf("Adding URI_PATH %s",b);

+        free(b);

+#endif

+

+        // add option

+        if(addOption(optionType,oLen,(uint8_t*)startP)!=0)

+        {

+            printf("Error adding option");

+            return 1;

+        }

+        startP = endP;

+

+        if(queryStageTriggered)

+        {

+            splitChar = '&';

+            optionType = COAP_OPTION_URI_QUERY;

+            startP++;

+            queryStageTriggered = false;

+        }

+    }

+

+    return 0;

+}

+

+/// Shorthand for adding a URI QUERY to the option list.

+/**

+ * Adds a new option to the CoAP PDU that encodes a URI_QUERY.

+ *

+ * \param query The uri query to encode.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::addURIQuery(char *query)

+{

+    return addOption(COAP_OPTION_URI_QUERY,strlen(query),(uint8_t*)query);

+}

+

+/// Concatenates any URI_PATH elements and URI_QUERY elements into a single string.

+/**

+ * Parses the PDU options and extracts all URI_PATH and URI_QUERY elements,

+ * concatenating them into a single string with slash and amphersand separators accordingly.

+ *

+ * The produced string will be NULL terminated.

+ *

+ * \param dst Buffer into which to copy the concatenated path elements.

+ * \param dstlen Length of buffer.

+ * \param outLen Pointer to integer, into which URI length will be placed.

+ *

+ * \return 0 on success, 1 on failure. \b outLen will contain the length of the concatenated elements.

+ */

+int CoapPDU::getURI(char *dst, int dstlen, int *outLen)

+{

+    if(outLen==NULL)

+    {

+        printf("Output length pointer is NULL");

+        return 1;

+    }

+

+    if(dst==NULL)

+    {

+        printf("NULL destination buffer");

+        *outLen = 0;

+        return 1;

+    }

+

+    // check destination space

+    if(dstlen<=0)

+    {

+        *dst = 0x00;

+        *outLen = 0;

+        printf("Destination buffer too small (0)!");

+        return 1;

+    }

+    // check option count

+    if(_numOptions==0)

+    {

+        *dst = 0x00;

+        *outLen = 0;

+        return 0;

+    }

+    // get options

+    CoapPDU::CoapOption *options = getOptions();

+    if(options==NULL)

+    {

+        *dst = 0x00;

+        *outLen = 0;

+        return 0;

+    }

+    // iterate over options to construct URI

+    CoapOption *o = NULL;

+    int bytesLeft = dstlen-1; // space for 0x00

+    int oLen = 0;

+    // add slash at beggining

+    if(bytesLeft>=1)

+    {

+        *dst = '/';

+        dst++;

+        bytesLeft--;

+    }

+    else

+    {

+        printf("No space for initial slash needed 1, got %d",bytesLeft);

+        free(options);

+        return 1;

+    }

+

+    char separator = '/';

+    int firstQuery = 1;

+

+    for(int i=0; i<_numOptions; i++)

+    {

+        o = &options[i];

+        oLen = o->optionValueLength;

+        if(o->optionNumber==COAP_OPTION_URI_PATH||o->optionNumber==COAP_OPTION_URI_QUERY)

+        {

+            // if the option is a query, change the separator to &

+            if(o->optionNumber==COAP_OPTION_URI_QUERY)

+            {

+                if(firstQuery)

+                {

+                    // change previous '/' to a '?'

+                    *(dst-1) = '?';

+                    firstQuery = 0;

+                }

+                separator = '&';

+            }

+

+            // check space

+            if(oLen>bytesLeft)

+            {

+                printf("Destination buffer too small, needed %d, got %d",oLen,bytesLeft);

+                free(options);

+                return 1;

+            }

+

+            // case where single '/' exists

+            if(oLen==1&&o->optionValuePointer[0]=='/')

+            {

+                *dst = 0x00;

+                *outLen = 1;

+                free(options);

+                return 0;

+            }

+

+            // copy URI path or query component

+            memcpy(dst,o->optionValuePointer,oLen);

+

+            // adjust counters

+            dst += oLen;

+            bytesLeft -= oLen;

+

+            // add separator following (don't know at this point if another option is coming)

+            if(bytesLeft>=1)

+            {

+                *dst = separator;

+                dst++;

+                bytesLeft--;

+            }

+            else

+            {

+                printf("Ran out of space after processing option");

+                free(options);

+                return 1;

+            }

+        }

+    }

+

+    // remove terminating separator

+    dst--;

+    bytesLeft++;

+    // add null terminating byte (always space since reserved)

+    *dst = 0x00;

+    *outLen = (dstlen-1)-bytesLeft;

+    free(options);

+    return 0;

+}

+

+/// Sets the CoAP version.

+/**

+ * \param version CoAP version between 0 and 3.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setVersion(uint8_t version)

+{

+    if(version>3)

+    {

+        return 0;

+    }

+

+    _pdu[0] &= 0x3F;

+    _pdu[0] |= (version << 6);

+    return 1;

+}

+

+/**

+ * Gets the CoAP Version.

+ * @return The CoAP version between 0 and 3.

+ */

+uint8_t CoapPDU::getVersion()

+{

+    return (_pdu[0]&0xC0)>>6;

+}

+

+/**

+ * Sets the type of this CoAP PDU.

+ * \param mt The type, one of:

+ * - COAP_CONFIRMABLE

+ * - COAP_NON_CONFIRMABLE

+ * - COAP_ACKNOWLEDGEMENT

+ * - COAP_RESET.

+ */

+void CoapPDU::setType(int mt)

+{

+    _pdu[0] &= 0xCF;

+    _pdu[0] |= mt;

+}

+

+/// Returns the type of the PDU.

+int CoapPDU::getType()

+{

+    return (int)(_pdu[0]&0x30);

+}

+

+

+/// Set the token length.

+/**

+ * \param tokenLength The length of the token in bytes, between 0 and 8.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setTokenLength(uint8_t tokenLength)

+{

+    if(tokenLength>8)

+        return 1;

+

+    _pdu[0] &= 0xF0;

+    _pdu[0] |= tokenLength;

+    return 0;

+}

+

+/// Returns the token length.

+int CoapPDU::getTokenLength()

+{

+    return _pdu[0] & 0x0F;

+}

+

+/// Returns a pointer to the PDU token.

+uint8_t* CoapPDU::getTokenPointer()

+{

+    if(getTokenLength()==0)

+    {

+        return NULL;

+    }

+    return &_pdu[4];

+}

+

+/// Set the PDU token to the supplied byte sequence.

+/**

+ * This sets the PDU token to \b token and sets the token length to \b tokenLength.

+ * \param token A sequence of bytes representing the token.

+ * \param tokenLength The length of the byte sequence.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setToken(uint8_t *token, uint8_t tokenLength)

+{

+    printf("Setting token");

+    if(token==NULL)

+    {

+        printf("NULL pointer passed as token reference");

+        return 1;

+    }

+

+    if(tokenLength==0)

+    {

+        printf("Token has zero length");

+        return 1;

+    }

+

+    // if tokenLength has not changed, just copy the new value

+    uint8_t oldTokenLength = getTokenLength();

+    if(tokenLength==oldTokenLength)

+    {

+        memcpy((void*)&_pdu[4],token,tokenLength);

+        return 0;

+    }

+

+    // otherwise compute new length of PDU

+    uint8_t oldPDULength = _pduLength;

+    _pduLength -= oldTokenLength;

+    _pduLength += tokenLength;

+

+    // now, have to shift old memory around, but shift direction depends

+    // whether pdu is now bigger or smaller

+    if(_pduLength>oldPDULength)

+    {

+        // new PDU is bigger, need to allocate space for new PDU

+        if(!_constructedFromBuffer)

+        {

+            uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+            if(newMemory==NULL)

+            {

+                // malloc failed

+                printf("Failed to allocate memory for token");

+                _pduLength = oldPDULength;

+                return 1;

+            }

+            _pdu = newMemory;

+            _bufferLength = _pduLength;

+        }

+        else

+        {

+            // constructed from buffer, check space

+            if(_pduLength>_bufferLength)

+            {

+                printf("Buffer too small to contain token, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);

+                _pduLength = oldPDULength;

+                return 1;

+            }

+        }

+

+        // and then shift everything after token up to end of new PDU

+        // memory overlaps so do this manually so to avoid additional mallocs

+        int shiftOffset = _pduLength-oldPDULength;

+        int shiftAmount = _pduLength-tokenLength-COAP_HDR_SIZE; // everything after token

+        shiftPDUUp(shiftOffset,shiftAmount);

+

+        // now copy the token into the new space and set official token length

+        memcpy((void*)&_pdu[4],token,tokenLength);

+        setTokenLength(tokenLength);

+

+        // and return success

+        return 0;

+    }

+

+    // new PDU is smaller, copy the new token value over the old one

+    memcpy((void*)&_pdu[4],token,tokenLength);

+    // and shift everything after the new token down

+    int startLocation = COAP_HDR_SIZE+tokenLength;

+    int shiftOffset = oldPDULength-_pduLength;

+    int shiftAmount = oldPDULength-oldTokenLength-COAP_HDR_SIZE;

+    shiftPDUDown(startLocation,shiftOffset,shiftAmount);

+    // then reduce size of buffer

+    if(!_constructedFromBuffer)

+    {

+        uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+        if(newMemory==NULL)

+        {

+            // malloc failed, PDU in inconsistent state

+            printf("Failed to shrink PDU for new token. PDU probably broken");

+            return 1;

+        }

+        _pdu = newMemory;

+        _bufferLength = _pduLength;

+    }

+

+    // and officially set the new tokenLength

+    setTokenLength(tokenLength);

+    return 0;

+}

+

+/// Sets the CoAP response code

+void CoapPDU::setCode(int code)

+{

+    _pdu[1] = code;

+    // there is a limited set of response codes

+}

+

+/// Gets the CoAP response code

+int CoapPDU::getCode()

+{

+    return (int)_pdu[1];

+}

+

+

+/// Converts a http status code as an integer, to a CoAP code.

+/**

+ * \param httpStatus the HTTP status code as an integer (e.g 200)

+ * \return The correct corresponding int on success,

+ * CoapPDU::COAP_UNDEFINED_CODE on failure.

+ */

+int CoapPDU::httpStatusToCode(int httpStatus)

+{

+    switch(httpStatus)

+    {

+        case 1:

+            return CoapPDU::COAP_GET;

+        case 2:

+            return CoapPDU::COAP_POST;

+        case 3:

+            return CoapPDU::COAP_PUT;

+        case 4:

+            return CoapPDU::COAP_DELETE;

+        case 201:

+            return CoapPDU::COAP_CREATED;

+        case 202:

+            return CoapPDU::COAP_DELETED;

+        case 203:

+            return CoapPDU::COAP_VALID;

+        case 204:

+            return CoapPDU::COAP_CHANGED;

+        case 205:

+            return CoapPDU::COAP_CONTENT;

+        case 400:

+            return CoapPDU::COAP_BAD_REQUEST;

+        case 401:

+            return CoapPDU::COAP_UNAUTHORIZED;

+        case 402:

+            return CoapPDU::COAP_BAD_OPTION;

+        case 403:

+            return CoapPDU::COAP_FORBIDDEN;

+        case 404:

+            return CoapPDU::COAP_NOT_FOUND;

+        case 405:

+            return CoapPDU::COAP_METHOD_NOT_ALLOWED;

+        case 406:

+            return CoapPDU::COAP_NOT_ACCEPTABLE;

+        case 412:

+            return CoapPDU::COAP_PRECONDITION_FAILED;

+        case 413:

+            return CoapPDU::COAP_REQUEST_ENTITY_TOO_LARGE;

+        case 415:

+            return CoapPDU::COAP_UNSUPPORTED_CONTENT_FORMAT;

+        case 500:

+            return CoapPDU::COAP_INTERNAL_SERVER_ERROR;

+        case 501:

+            return CoapPDU::COAP_NOT_IMPLEMENTED;

+        case 502:

+            return CoapPDU::COAP_BAD_GATEWAY;

+        case 503:

+            return CoapPDU::COAP_SERVICE_UNAVAILABLE;

+        case 504:

+            return CoapPDU::COAP_GATEWAY_TIMEOUT;

+        case 505:

+            return CoapPDU::COAP_PROXYING_NOT_SUPPORTED;

+        default:

+            return CoapPDU::COAP_UNDEFINED_CODE;

+    }

+}

+

+/// Set messageID to the supplied value.

+/**

+ * \param messageID A 16bit message id.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setMessageID(uint16_t messageID)

+{

+    // message ID is stored in network byte order

+    uint8_t *to = &_pdu[2];

+    endian_store16(to, messageID);

+    return 0;

+}

+

+/// Returns the 16 bit message ID of the PDU.

+uint16_t CoapPDU::getMessageID()

+{

+    // mesasge ID is stored in network byteorder

+    uint8_t *from = &_pdu[2];

+    uint16_t messageID = endian_load16(uint16_t, from);

+    return messageID;

+}

+

+/// Returns the length of the PDU.

+int CoapPDU::getPDULength()

+{

+    return _pduLength;

+}

+

+/// Return the number of options that the PDU has.

+int CoapPDU::getNumOptions()

+{

+    return _numOptions;

+}

+

+

+/**

+ * This returns the options as a sequence of structs.

+ */

+CoapPDU::CoapOption* CoapPDU::getOptions()

+{

+    printf("getOptions() called, %d options.",_numOptions);

+

+    uint16_t optionDelta =0, optionNumber = 0, optionValueLength = 0;

+    int totalLength = 0;

+

+    if(_numOptions==0)

+    {

+        return NULL;

+    }

+

+    // malloc space for options

+    CoapOption *options = (CoapOption*)malloc(_numOptions*sizeof(CoapOption));

+    if(options==NULL)

+    {

+        printf("Failed to allocate memory for options.");

+        return NULL;

+    }

+

+    // first option occurs after token

+    int optionPos = COAP_HDR_SIZE + getTokenLength();

+

+    // walk over options and record information

+    for(int i=0; i<_numOptions; i++)

+    {

+        // extract option details

+        optionDelta = getOptionDelta(&_pdu[optionPos]);

+        optionNumber += optionDelta;

+        optionValueLength = getOptionValueLength(&_pdu[optionPos]);

+        // compute total length

+        totalLength = 1; // mandatory header

+        totalLength += computeExtraBytes(optionDelta);

+        totalLength += computeExtraBytes(optionValueLength);

+        totalLength += optionValueLength;

+        // record option details

+        options[i].optionNumber = optionNumber;

+        options[i].optionDelta = optionDelta;

+        options[i].optionValueLength = optionValueLength;

+        options[i].totalLength = totalLength;

+        options[i].optionPointer = &_pdu[optionPos];

+        options[i].optionValuePointer = &_pdu[optionPos+totalLength-optionValueLength];

+        // move to next option

+        optionPos += totalLength;

+    }

+

+    return options;

+}

+

+/// Add an option to the PDU.

+/**

+ * Unlike other implementations, options can be added in any order, and in-memory manipulation will be

+ * performed to ensure the correct ordering of options (they use a delta encoding of option numbers).

+ * Re-ordering memory like this incurs a small performance cost, so if you care about this, then you

+ * might want to add options in ascending order of option number.

+ * \param optionNumber The number of the option, see the enum CoapPDU::Option for shorthand notations.

+ * \param optionLength The length of the option payload in bytes.

+ * \param optionValue A pointer to the byte sequence that is the option payload (bytes will be copied).

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::addOption(uint16_t insertedOptionNumber, uint16_t optionValueLength, uint8_t *optionValue)

+{

+    // this inserts the option in memory, and re-computes the deltas accordingly

+    // prevOption <-- insertionPosition

+    // nextOption

+

+    // find insertion location and previous option number

+    uint16_t prevOptionNumber = 0; // option number of option before insertion point

+    int insertionPosition = findInsertionPosition(insertedOptionNumber,&prevOptionNumber);

+    printf("inserting option at position %d, after option with number: %hu",insertionPosition,prevOptionNumber);

+

+    // compute option delta length

+    uint16_t optionDelta = insertedOptionNumber-prevOptionNumber;

+    uint8_t extraDeltaBytes = computeExtraBytes(optionDelta);

+

+    // compute option length length

+    uint16_t extraLengthBytes = computeExtraBytes(optionValueLength);

+

+    // compute total length of option

+    uint16_t optionLength = COAP_OPTION_HDR_BYTE + extraDeltaBytes + extraLengthBytes + optionValueLength;

+

+    // if this is at the end of the PDU, job is done, just malloc and insert

+    if(insertionPosition==_pduLength)

+    {

+        printf("Inserting at end of PDU");

+        // optionNumber must be biggest added

+        _maxAddedOptionNumber = insertedOptionNumber;

+

+        // set new PDU length and allocate space for extra option

+        int oldPDULength = _pduLength;

+        _pduLength += optionLength;

+        if(!_constructedFromBuffer)

+        {

+            uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+            if(newMemory==NULL)

+            {

+                printf("Failed to allocate memory for option.");

+                _pduLength = oldPDULength;

+                // malloc failed

+                return 1;

+            }

+            _pdu = newMemory;

+            _bufferLength = _pduLength;

+        }

+        else

+        {

+            // constructed from buffer, check space

+            if(_pduLength>_bufferLength)

+            {

+                printf("Buffer too small for new option: needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);

+                _pduLength = oldPDULength;

+                return 1;

+            }

+        }

+

+        // insert option at position

+        insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);

+        _numOptions++;

+        return 0;

+    }

+    // XXX could do 0xFF pdu payload case for changing of dynamically allocated application space SDUs < yeah, if you're insane

+

+    // the next option might (probably) needs it's delta changing

+    // I want to take this into account when allocating space for the new

+    // option, to avoid having to do two mallocs, first get info about this option

+    int nextOptionDelta = getOptionDelta(&_pdu[insertionPosition]);

+    int nextOptionNumber = prevOptionNumber + nextOptionDelta;

+    int nextOptionDeltaBytes = computeExtraBytes(nextOptionDelta);

+    printf("nextOptionDeltaBytes: %d",nextOptionDeltaBytes);

+    // recompute option delta, relative to inserted option

+    int newNextOptionDelta = nextOptionNumber-insertedOptionNumber;

+    int newNextOptionDeltaBytes = computeExtraBytes(newNextOptionDelta);

+    printf("newNextOptionDeltaBytes: %d",newNextOptionDeltaBytes);

+    // determine adjustment

+    int optionDeltaAdjustment = newNextOptionDeltaBytes-nextOptionDeltaBytes;

+

+    // create space for new option, including adjustment space for option delta

+    printf("Creating space");

+    int mallocLength = optionLength+optionDeltaAdjustment;

+    int oldPDULength = _pduLength;

+    _pduLength += mallocLength;

+

+    if(!_constructedFromBuffer)

+    {

+        uint8_t *newMemory = (uint8_t*)realloc(_pdu,_pduLength);

+        if(newMemory==NULL)

+        {

+            printf("Failed to allocate memory for option");

+            _pduLength = oldPDULength;

+            return 1;

+        }

+        _pdu = newMemory;

+        _bufferLength = _pduLength;

+    }

+    else

+    {

+        // constructed from buffer, check space

+        if(_pduLength>_bufferLength)

+        {

+            printf("Buffer too small to contain option, needed %d, got %d.",_pduLength-oldPDULength,_bufferLength-oldPDULength);

+            _pduLength = oldPDULength;

+            return 1;

+        }

+    }

+

+    // move remainder of PDU data up to create hole for new option

+    printf("Shifting PDU.");

+    shiftPDUUp(mallocLength,_pduLength-(insertionPosition+mallocLength));

+

+    // adjust option delta bytes of following option

+    // move the option header to the correct position

+    int nextHeaderPos = insertionPosition+mallocLength;

+    _pdu[nextHeaderPos-optionDeltaAdjustment] = _pdu[nextHeaderPos];

+    nextHeaderPos -= optionDeltaAdjustment;

+    // and set the new value

+    setOptionDelta(nextHeaderPos, newNextOptionDelta);

+

+    // new option shorter

+    // p p n n x x x x x

+    // p p n n x x x x x -

+    // p p - n n x x x x x

+    // p p - - n x x x x x

+    // p p o o n x x x x x

+

+    // new option longer

+    // p p n n x x x x x

+    // p p n n x x x x x - - -

+    // p p - - - n n x x x x x

+    // p p - - n n n x x x x x

+    // p p o o n n n x x x x x

+

+    // note, it can only ever be shorter or the same since if an option was inserted the delta got smaller

+    // but I'll leave that little comment in, just to show that it would work even if the delta got bigger

+

+    // now insert the new option into the gap

+    printf("Inserting new option...");

+    insertOption(insertionPosition,optionDelta,optionValueLength,optionValue);

+    printf("done\r\n");

+

+    // done, mark it with B!

+    _numOptions++;

+    return 0;

+}

+

+/// Allocate space for a payload.

+/**

+ * For dynamically constructed PDUs, this will allocate space for a payload in the object

+ * and return a pointer to it. If the PDU was constructed from a buffer, this doesn't

+ * malloc anything, it just changes the _pduLength and returns the payload pointer.

+ *

+ * \note The pointer returned points into the PDU buffer.

+ * \param len The length of the payload buffer to allocate.

+ * \return Either a pointer to the payload buffer, or NULL if there wasn't enough space / allocation failed.

+ */

+uint8_t* CoapPDU::mallocPayload(int len)

+{

+    printf("Entering mallocPayload");

+    // sanity checks

+    if(len==0)

+    {

+        printf("Cannot allocate a zero length payload");

+        return NULL;

+    }

+

+    // further sanity

+    if(len==_payloadLength)

+    {

+        printf("Space for payload of specified length already exists");

+        if(_payloadPointer==NULL)

+        {

+            printf("Garbage PDU. Payload length is %d, but existing _payloadPointer NULL",_payloadLength);

+            return NULL;

+        }

+        return _payloadPointer;

+    }

+

+    printf("_bufferLength: %d, _pduLength: %d, _payloadLength: %d",_bufferLength,_pduLength,_payloadLength);

+

+    // might be making payload bigger (including bigger than 0) or smaller

+    int markerSpace = 1;

+    int payloadSpace = len;

+    // is this a resizing?

+    if(_payloadLength!=0)

+    {

+        // marker already exists

+        markerSpace = 0;

+        // compute new payload length (can be negative if shrinking payload)

+        payloadSpace = len-_payloadLength;

+    }

+

+    // make space for payload (and payload marker if necessary)

+    int newLen = _pduLength+payloadSpace+markerSpace;

+    if(!_constructedFromBuffer)

+    {

+        uint8_t* newPDU = (uint8_t*)realloc(_pdu,newLen);

+        if(newPDU==NULL)

+        {

+            printf("Cannot allocate (or shrink) space for payload");

+            return NULL;

+        }

+        _pdu = newPDU;

+        _bufferLength = newLen;

+    }

+    else

+    {

+        // constructed from buffer, check space

+        printf("newLen: %d, _bufferLength: %d",newLen,_bufferLength);

+        if(newLen>_bufferLength)

+        {

+            printf("Buffer too small to contain desired payload, needed %d, got %d.",newLen-_pduLength,_bufferLength-_pduLength);

+            return NULL;

+        }

+    }

+

+    // deal with fresh allocation case separately

+    if(_payloadPointer==NULL)

+    {

+        // set payload marker

+        _pdu[_pduLength] = 0xFF;

+        // payload at end of old PDU

+        _payloadPointer = &_pdu[_pduLength+1];

+        _pduLength = newLen;

+        _payloadLength = len;

+        return _payloadPointer;

+    }

+

+    // otherwise, just adjust length of PDU

+    _pduLength = newLen;

+    _payloadLength = len;

+    printf("Leaving mallocPayload");

+    return _payloadPointer;

+}

+

+/// Set the payload to the byte sequence specified. Allocates memory in dynamic PDU if necessary.

+/**

+ * This will set the payload to \b payload. It will allocate memory in the case where the PDU was

+ * constructed without an external buffer.

+ *

+ * This will fail either if the fixed buffer isn't big enough, or if memory could not be allocated

+ * in the non-external-buffer case.

+ *

+ * \param payload Pointer to payload byte sequence.

+ * \param len Length of payload byte sequence.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setPayload(uint8_t *payload, int len)

+{

+    if(payload==NULL)

+    {

+        printf("NULL payload pointer.");

+        return 1;

+    }

+

+    uint8_t *payloadPointer = mallocPayload(len);

+    if(payloadPointer==NULL)

+    {

+        printf("Allocation of payload failed");

+        return 1;

+    }

+

+    // copy payload contents

+    memcpy(payloadPointer,payload,len);

+

+    return 0;

+}

+

+/// Returns a pointer to the payload buffer.

+uint8_t* CoapPDU::getPayloadPointer()

+{

+    return _payloadPointer;

+}

+

+/// Gets the length of the payload buffer.

+int CoapPDU::getPayloadLength()

+{

+    return _payloadLength;

+}

+

+/// Returns a pointer to a buffer which is a copy of the payload buffer (dynamically allocated).

+uint8_t* CoapPDU::getPayloadCopy()

+{

+    if(_payloadLength==0)

+    {

+        return NULL;

+    }

+

+    // malloc space for copy

+    uint8_t *payload = (uint8_t*)malloc(_payloadLength);

+    if(payload==NULL)

+    {

+        printf("Unable to allocate memory for payload");

+        return NULL;

+    }

+

+    // copy and return

+    memcpy(payload,_payloadPointer,_payloadLength);

+    return payload;

+}

+

+/// Shorthand for setting the content-format option.

+/**

+ * Sets the content-format to the specified value (adds an option).

+ * \param format The content format, one of:

+ *

+ * - COAP_CONTENT_FORMAT_TEXT_PLAIN

+ * - COAP_CONTENT_FORMAT_APP_LINK

+ * - COAP_CONTENT_FORMAT_APP_XML

+ * - COAP_CONTENT_FORMAT_APP_OCTET

+ * - COAP_CONTENT_FORMAT_APP_EXI

+ * - COAP_CONTENT_FORMAT_APP_JSON

+ *

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::setContentFormat(int format)

+{

+    if(format==0)

+    {

+        // minimal representation means null option value

+        if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,0,NULL)!=0)

+        {

+            printf("Error setting content format");

+            return 1;

+        }

+        return 0;

+    }

+

+    uint8_t c[2];

+

+    // just use 1 byte if can do it

+    //if((uint16_t)format <= 0xffu) {

+    if(1)

+    {

+        c[0] = format;

+        if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,1,c)!=0)

+        {

+            printf("Error setting content format");

+            return 1;

+        }

+        return 0;

+    }

+

+    uint8_t *to = c;

+    endian_store16(to, format);

+    if(addOption(CoapPDU::COAP_OPTION_CONTENT_FORMAT,2,c)!=0)

+    {

+        printf("Error setting content format");

+        return 1;

+    }

+    return 0;

+}

+

+// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE

+// PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE PRIVATE

+

+/// Moves a block of bytes to end of PDU from given offset.

+/**

+ * This moves the block of bytes _pdu[_pduLength-1-shiftOffset-shiftAmount] ... _pdu[_pduLength-1-shiftOffset]

+ * to the end of the PDU.

+ * \param shiftOffset End of block to move, relative to end of PDU (-1).

+ * \param shiftAmount Length of block to move.

+ */

+void CoapPDU::shiftPDUUp(int shiftOffset, int shiftAmount)

+{

+    printf("shiftOffset: %d, shiftAmount: %d",shiftOffset,shiftAmount);

+    int destPointer = _pduLength-1;

+    int srcPointer  = destPointer-shiftOffset;

+    while(shiftAmount--)

+    {

+        _pdu[destPointer] = _pdu[srcPointer];

+        destPointer--;

+        srcPointer--;

+    }

+}

+

+/// Moves a block of bytes down a specified number of steps.

+/**

+ * Moves the block of bytes _pdu[startLocation+shiftOffset] ... _pdu[startLocation+shiftOffset+shiftAmount]

+ * down to \b startLocation.

+ * \param startLocation Index where to shift the block to.

+ * \param shiftOffset Where the block starts, relative to start index.

+ * \param shiftAmount Length of block to shift.

+ */

+void CoapPDU::shiftPDUDown(int startLocation, int shiftOffset, int shiftAmount)

+{

+    printf("startLocation: %d, shiftOffset: %d, shiftAmount: %d",startLocation,shiftOffset,shiftAmount);

+    int srcPointer = startLocation+shiftOffset;

+    while(shiftAmount--)

+    {

+        _pdu[startLocation] = _pdu[srcPointer];

+        startLocation++;

+        srcPointer++;

+    }

+}

+

+/// Gets the payload length of an option.

+/**

+ * \param option Pointer to location of option in PDU.

+ * \return The 16 bit option-payload length.

+ */

+uint16_t CoapPDU::getOptionValueLength(uint8_t *option)

+{

+    uint16_t delta = (option[0] & 0xF0) >> 4;

+    uint16_t length = (option[0] & 0x0F);

+    // no extra bytes

+    if(length<13)

+    {

+        return length;

+    }

+

+    // extra bytes skip header

+    int offset = 1;

+    // skip extra option delta bytes

+    if(delta==13)

+    {

+        offset++;

+    }

+    else if(delta==14)

+    {

+        offset+=2;

+    }

+

+    // process length

+    if(length==13)

+    {

+        return (option[offset]+13);

+    }

+    else

+    {

+        uint8_t *from = &option[offset];

+        uint16_t value = endian_load16(uint16_t, from);

+        return value+269;

+    }

+

+}

+

+/// Gets the delta of an option.

+/**

+ * \param option Pointer to location of option in PDU.

+ * \return The 16 bit delta.

+ */

+uint16_t CoapPDU::getOptionDelta(uint8_t *option)

+{

+    uint16_t delta = (option[0] & 0xF0) >> 4;

+    if(delta<13)

+    {

+        return delta;

+    }

+    else if(delta==13)

+    {

+        // single byte option delta

+        return (option[1]+13);

+    }

+    else if(delta==14)

+    {

+        uint8_t *from = &option[1];

+        uint16_t value = endian_load16(uint16_t, from);

+        return value+269;

+    }

+    else

+    {

+        // should only ever occur in payload marker

+        return delta;

+    }

+}

+

+/// Finds the insertion position in the current list of options for the specified option.

+/**

+ * \param optionNumber The option's number.

+ * \param prevOptionNumber A pointer to a uint16_t which will store the option number of the option previous

+ * to the insertion point.

+ * \return 0 on success, 1 on failure. \b prevOptionNumber will contain the option number of the option

+ * before the insertion position (for example 0 if no options have been inserted).

+ */

+int CoapPDU::findInsertionPosition(uint16_t optionNumber, uint16_t *prevOptionNumber)

+{

+    // zero this for safety

+    *prevOptionNumber = 0x00;

+

+    printf("_pduLength: %d",_pduLength);

+

+    // if option is bigger than any currently stored, it goes at the end

+    // this includes the case that no option has yet been added

+    if( (optionNumber >= _maxAddedOptionNumber) || (_pduLength == (COAP_HDR_SIZE+getTokenLength())) )

+    {

+        *prevOptionNumber = _maxAddedOptionNumber;

+        return _pduLength;

+    }

+

+    // otherwise walk over the options

+    int optionPos = COAP_HDR_SIZE + getTokenLength();

+    uint16_t optionDelta = 0, optionValueLength = 0;

+    uint16_t currentOptionNumber = 0;

+    while(optionPos<_pduLength && _pdu[optionPos]!=0xFF)

+    {

+        optionDelta = getOptionDelta(&_pdu[optionPos]);

+        currentOptionNumber += optionDelta;

+        optionValueLength = getOptionValueLength(&_pdu[optionPos]);

+        // test if this is insertion position

+        if(currentOptionNumber>optionNumber)

+        {

+            return optionPos;

+        }

+        // keep track of the last valid option number

+        *prevOptionNumber = currentOptionNumber;

+        // move onto next option

+        optionPos += computeExtraBytes(optionDelta);

+        optionPos += computeExtraBytes(optionValueLength);

+        optionPos += optionValueLength;

+        optionPos++; // (for mandatory option header byte)

+    }

+    return optionPos;

+

+}

+

+/// CoAP uses a minimal-byte representation for length fields. This returns the number of bytes needed to represent a given length.

+int CoapPDU::computeExtraBytes(uint16_t n)

+{

+    if(n<13)

+    {

+        return 0;

+    }

+

+    if(n<269)

+    {

+        return 1;

+    }

+

+    return 2;

+}

+

+/// Set the option delta to the specified value.

+/**

+ * This assumes space has been made for the option delta.

+ * \param optionPosition The index of the option in the PDU.

+ * \param optionDelta The option delta value to set.

+ */

+void CoapPDU::setOptionDelta(int optionPosition, uint16_t optionDelta)

+{

+    int headerStart = optionPosition;

+    // clear the old option delta bytes

+    _pdu[headerStart] &= 0x0F;

+

+    // set the option delta bytes

+    if(optionDelta<13)

+    {

+        _pdu[headerStart] |= (optionDelta << 4);

+    }

+    else if(optionDelta<269)

+    {

+        // 1 extra byte

+        _pdu[headerStart] |= 0xD0; // 13 in first nibble

+        _pdu[++optionPosition] &= 0x00;

+        _pdu[optionPosition] |= (optionDelta-13);

+    }

+    else

+    {

+        // 2 extra bytes, network byte order uint16_t

+        _pdu[headerStart] |= 0xE0; // 14 in first nibble

+        optionDelta -= 269;

+        uint8_t *to = &_pdu[++optionPosition];

+        endian_store16(to, optionDelta);

+    }

+}

+

+/// Insert an option in-memory at the specified location.

+/**

+ * This assumes that there is enough space at the location specified.

+ * \param insertionPosition Position in the PDU where the option should be placed.

+ * \param optionDelta The delta value for the option.

+ * \param optionValueLength The length of the option value.

+ * \param optionValue A pointer to the sequence of bytes representing the option value.

+ * \return 0 on success, 1 on failure.

+ */

+int CoapPDU::insertOption(

+    int insertionPosition,

+    uint16_t optionDelta,

+    uint16_t optionValueLength,

+    uint8_t *optionValue)

+{

+

+    int headerStart = insertionPosition;

+

+    // clear old option header start

+    _pdu[headerStart] &= 0x00;

+

+    // set the option delta bytes

+    if(optionDelta<13)

+    {

+        _pdu[headerStart] |= (optionDelta << 4);

+    }

+    else if(optionDelta<269)

+    {

+        // 1 extra byte

+        _pdu[headerStart] |= 0xD0; // 13 in first nibble

+        _pdu[++insertionPosition] &= 0x00;

+        _pdu[insertionPosition] |= (optionDelta-13);

+    }

+    else

+    {

+        // 2 extra bytes, network byte order uint16_t

+        _pdu[headerStart] |= 0xE0; // 14 in first nibble

+        optionDelta -= 269;

+        uint8_t *to = &_pdu[++insertionPosition];

+        endian_store16(to, optionDelta);

+        insertionPosition += 1;

+    }

+

+    // set the option value length bytes

+    if(optionValueLength<13)

+    {

+        _pdu[headerStart] |= (optionValueLength & 0x000F);

+    }

+    else if(optionValueLength<269)

+    {

+        _pdu[headerStart] |= 0x0D; // 13 in second nibble

+        _pdu[++insertionPosition] &= 0x00;

+        _pdu[insertionPosition] |= (optionValueLength-13);

+    }

+    else

+    {

+        _pdu[headerStart] |= 0x0E; // 14 in second nibble

+        // this is in network byte order

+        printf("optionValueLength: %u",optionValueLength);

+        uint8_t *to = &_pdu[++insertionPosition];

+        optionValueLength -= 269;

+        endian_store16(to, optionValueLength);

+        insertionPosition += 1;

+    }

+

+    // and finally copy the option value itself

+    memcpy(&_pdu[++insertionPosition],optionValue,optionValueLength);

+

+    return 0;

+}

+

+void CoapPDU::getOptionValueById

+(

+    uint16_t optionNumber,

+    uint16_t *optionValueLength,

+    uint8_t *optionValuePointer

+)

+{

+    if(_numOptions == 0)

+        return;

+    CoapOption* options = getOptions();

+    int i = 0;

+    for(; i < _numOptions; i++)

+    {

+        if(options[i].optionNumber == optionNumber)

+        {

+            *optionValueLength = options[i].optionValueLength;

+            memcpy(optionValuePointer, options[i].optionValuePointer, options[i].optionValueLength);

+        }

+    }

+    free(options);

+}

+

+int CoapPDU::hasOption

+(

+    uint16_t optionNumber

+)

+{

+    int res = 0;

+    if(_numOptions == 0)

+        return res;

+    CoapOption* options = getOptions();

+    int i = 0;

+    for(; i < _numOptions; i++)

+    {

+        if(options[i].optionNumber == optionNumber)

+        {

+            res = 1;

+            break;

+        }

+    }

+    free(options);

+    return res;

+}

+

+const char *CoapPDU::printHuman()

+{

+    content.clear();

+    char temp1[128];

+

+    sprintf(temp1,"PDU is %d bytes long\r\n",_pduLength);

+    content.append(temp1);

+

+    sprintf(temp1,"CoAP Version: %d\r\n",getVersion());

+    content.append("Message Type: ");

+    switch(getType())

+    {

+        case COAP_CONFIRMABLE:

+            content.append("Confirmable");

+            break;

+

+        case COAP_NON_CONFIRMABLE:

+            content.append("Non-Confirmable");

+            break;

+

+        case COAP_ACKNOWLEDGEMENT:

+            content.append("Acknowledgement");

+            break;

+

+        case COAP_RESET:

+            content.append("Reset");

+            break;

+    }

+

+    sprintf(temp1,"\r\nToken length: %d\r\n",getTokenLength());

+    content.append(temp1);

+    content.append("Code: ");

+    switch(getCode())

+    {

+        case COAP_EMPTY:

+            content.append("0.00 Empty");

+            break;

+        case COAP_GET:

+            content.append("0.01 GET");

+            break;

+        case COAP_POST:

+            content.append("0.02 POST");

+            break;

+        case COAP_PUT:

+            content.append("0.03 PUT");

+            break;

+        case COAP_DELETE:

+            content.append("0.04 DELETE");

+            break;

+        case COAP_CREATED:

+            content.append("2.01 Created");

+            break;

+        case COAP_DELETED:

+            content.append("2.02 Deleted");

+            break;

+        case COAP_VALID:

+            content.append("2.03 Valid");

+            break;

+        case COAP_CHANGED:

+            content.append("2.04 Changed");

+            break;

+        case COAP_CONTENT:

+            content.append("2.05 Content");

+            break;

+        case COAP_CONTINUE:

+            content.append("2.31 Continue");

+            break;

+        case COAP_BAD_REQUEST:

+            content.append("4.00 Bad Request");

+            break;

+        case COAP_UNAUTHORIZED:

+            content.append("4.01 Unauthorized");

+            break;

+        case COAP_BAD_OPTION:

+            content.append("4.02 Bad Option");

+            break;

+        case COAP_FORBIDDEN:

+            content.append("4.03 Forbidden");

+            break;

+        case COAP_NOT_FOUND:

+            content.append("4.04 Not Found");

+            break;

+        case COAP_METHOD_NOT_ALLOWED:

+            content.append("4.05 Method Not Allowed");

+            break;

+        case COAP_NOT_ACCEPTABLE:

+            content.append("4.06 Not Acceptable");

+            break;

+        case COAP_PRECONDITION_FAILED:

+            content.append("4.12 Precondition Failed");

+            break;

+        case COAP_REQUEST_ENTITY_TOO_LARGE:

+            content.append("4.13 Request Entity Too Large");

+            break;

+        case COAP_UNSUPPORTED_CONTENT_FORMAT:

+            content.append("4.15 Unsupported Content-Format");

+            break;

+        case COAP_INTERNAL_SERVER_ERROR:

+            content.append("5.00 Internal Server Error");

+            break;

+        case COAP_NOT_IMPLEMENTED:

+            content.append("5.01 Not Implemented");

+            break;

+        case COAP_BAD_GATEWAY:

+            content.append("5.02 Bad Gateway");

+            break;

+        case COAP_SERVICE_UNAVAILABLE:

+            content.append("5.03 Service Unavailable");

+            break;

+        case COAP_GATEWAY_TIMEOUT:

+            content.append("5.04 Gateway Timeout");

+            break;

+        case COAP_PROXYING_NOT_SUPPORTED:

+            content.append("5.05 Proxying Not Supported");

+            break;

+        default:

+            sprintf(temp1, "Undefined Code %u",(unsigned)(getCode()));

+            content.append(temp1);

+

+    }

+

+    // print message ID

+    sprintf(temp1,"\r\nMessage ID: %u\r\n",getMessageID());

+    content.append(temp1);

+

+    // print token value

+    int tokenLength = getTokenLength();

+    uint8_t *tokenPointer = getPDUPointer()+COAP_HDR_SIZE;

+    if(tokenLength==0)

+    {

+        content.append("No token.\r\n");

+    }

+    else

+    {

+        //memset(temp1,0,50);

+        sprintf(temp1,"Token of %d bytes.\r\n",tokenLength);

+        content.append(temp1);

+        content.append("Value: 0x");

+        char temp2[5];

+        for(int j=0; j<tokenLength; j++)

+        {

+            sprintf(temp2,"%.2X",tokenPointer[j]);

+            content.append(temp2);

+        }

+    }

+

+    // print options

+    CoapPDU::CoapOption* options = getOptions();

+    if(options==NULL)

+    {

+        content.append("\r\nNO options");

+    }

+    else

+    {

+        //memset(temp1,0,50);

+        sprintf(temp1,"\r\n%d options:",_numOptions);

+        content.append(temp1);

+    }

+

+    for(int i=0; i<_numOptions; i++)

+    {

+        char optionTemp[128];

+        sprintf(optionTemp,"\r\nOPTION (%d/%d)\r\n"

+                "Option number (delta): %hu (%hu)\r\n"

+                "Name: "

+                ,i + 1,_numOptions,

+                options[i].optionNumber,options[i].optionDelta);

+        content.append(optionTemp);

+        boolean asString = FALSE;

+        switch(options[i].optionNumber)

+        {

+            case COAP_OPTION_IF_MATCH:

+                content.append("IF_MATCH");

+                break;

+            case COAP_OPTION_URI_HOST:

+                content.append("URI_HOST");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_ETAG:

+                content.append("ETAG");

+                break;

+            case COAP_OPTION_IF_NONE_MATCH:

+                content.append("IF_NONE_MATCH");

+                break;

+            case COAP_OPTION_OBSERVE:

+                content.append("OBSERVE");

+                break;

+            case COAP_OPTION_URI_PORT:

+                content.append("URI_PORT");

+                break;

+            case COAP_OPTION_LOCATION_PATH:

+                content.append("LOCATION_PATH");

+                break;

+            case COAP_OPTION_URI_PATH:

+                content.append("URI_PATH");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_CONTENT_FORMAT:

+                content.append("CONTENT_FORMAT");

+                break;

+            case COAP_OPTION_MAX_AGE:

+                content.append("MAX_AGE");

+                break;

+            case COAP_OPTION_URI_QUERY:

+                content.append("URI_QUERY");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_ACCEPT:

+                content.append("ACCEPT");

+                break;

+            case COAP_OPTION_LOCATION_QUERY:

+                content.append("LOCATION_QUERY");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_PROXY_URI:

+                content.append("PROXY_URI");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_PROXY_SCHEME:

+                content.append("PROXY_SCHEME");

+                asString = TRUE;

+                break;

+            case COAP_OPTION_BLOCK1:

+                content.append("BLOCK1");

+                break;

+            case COAP_OPTION_BLOCK2:

+                content.append("BLOCK2");

+                break;

+            case COAP_OPTION_SIZE1:

+                content.append("SIZE1");

+                break;

+            case COAP_OPTION_SIZE2:

+                content.append("SIZE2");

+                break;

+            default:

+                //memset(temp1,0,50);

+                sprintf(temp1,"Unknown option %u",(unsigned)options[i].optionNumber);

+                content.append(temp1);

+                break;

+        }

+        //memset(temp1,0,50);

+        sprintf(temp1,"\r\nValue length: %u\r\n",options[i].optionValueLength);

+        content.append(temp1);

+        if(asString)

+        {

+            content.append("Value: ");

+        }

+        else

+        {

+            content.append("Value: 0x");

+        }

+        char temp3[5];

+        for(int j=0; j<options[i].optionValueLength; j++)

+        {

+            if(asString)

+            {

+                char c = options[i].optionValuePointer[j];

+                if((c>='!'&&c<='~')||c==' ')

+                {

+                    sprintf(temp3,"%c", c);

+                }

+                else

+                {

+                    sprintf(temp3,"\\%.2d",c);

+                }

+                content.append(temp3);

+            }

+            else

+            {

+                sprintf(temp3,"%.2X",options[i].optionValuePointer[j]);

+                content.append(temp3);

+            }

+        }

+    }

+

+    // print payload

+    if(_payloadLength==0)

+    {

+        content.append("\r\nNo payload.\r\n");

+    }

+    else

+    {

+        //memset(temp1,0,50);

+        sprintf(temp1,"\r\nPayload of %d bytes\r\nValue: 0x",_payloadLength);

+        content.append(temp1);

+        char temp4[5];

+        for(int j=0; j<_payloadLength; j++)

+        {

+            sprintf(temp4,"%.2X",_payloadPointer[j]);

+            content.append(temp4);

+        }

+        content.append("\r\n");

+    }

+    if(options)

+        free(options);

+    return content.c_str();

+}

+

+const char * CoapPDU::printHex()

+{

+    content.clear();

+    char temp[5];

+    for(int i=0; i<_pduLength; i++)

+    {

+        sprintf(temp,"%.2X",_pdu[i]);

+        content.append(temp);

+    }

+    content.append("\r\n");

+    return content.c_str();

+}

+//#endif