Exemple #1
0
/*----------------------------------------------------------------------------*/
void
coap_separate_resume(void *response, coap_separate_t *separate_store,
                     uint8_t code)
{
    coap_init_message(response, separate_store->type, code,
                      separate_store->mid);
    if(separate_store->token_len) {
        coap_set_token(response, separate_store->token,
                       separate_store->token_len);
    }
    if(separate_store->block1_size) {
        coap_set_header_block1(response, separate_store->block1_num,
                               0, separate_store->block1_size);
    }
}
static void
res_put_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
  coap_packet_t *const coap_req = (coap_packet_t *)request;
  uint8_t *incoming = NULL;
  size_t len = 0;

  unsigned int ct = -1;

  if(!REST.get_header_content_type(request, &ct)) {
    REST.set_response_status(response, REST.status.BAD_REQUEST);
    const char *error_msg = "NoContentType";
    REST.set_response_payload(response, error_msg, strlen(error_msg));
    return;
  }

  if((len = REST.get_request_payload(request, (const uint8_t **)&incoming))) {
    if(coap_req->block1_num * coap_req->block1_size + len <= sizeof(large_update_store)) {
      memcpy(
        large_update_store + coap_req->block1_num * coap_req->block1_size,
        incoming, len);
      large_update_size = coap_req->block1_num * coap_req->block1_size + len;
      large_update_ct = ct;

      REST.set_response_status(response, REST.status.CHANGED);
      coap_set_header_block1(response, coap_req->block1_num, 0,
                             coap_req->block1_size);
    } else {
      REST.set_response_status(response,
                               REST.status.REQUEST_ENTITY_TOO_LARGE);
      REST.set_response_payload(
        response,
        buffer,
        snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "%uB max.",
                 sizeof(large_update_store)));
      return;
    }
  } else {
    REST.set_response_status(response, REST.status.BAD_REQUEST);
    const char *error_msg = "NoPayload";
    REST.set_response_payload(response, error_msg, strlen(error_msg));
    return;
  }
}
Exemple #3
0
/**
 * \brief Block 1 support within a coap-ressource
 *
 *        This function will help you to use block 1. If target is null
 *        error handling and response configuration is active. On return
 *        value 0, the last block was recived, while on return value 1
 *        more blocks will follow. With target, len and maxlen this
 *        function will assemble the blocks.
 *
 *        You can find an example in:
 *        examples/er-rest-example/resources/res-b1-sep-b2.c
 *
 * \param request   Request pointer from the handler
 * \param response  Response pointer from the handler
 * \param target    Pointer to the buffer where the request payload can be assembled
 * \param len       Pointer to the variable, where the function stores the actual length
 * \param max_len   Length of the "target"-Buffer
 *
 * \return 0 if initialisation was successful
 *         -1 if initialisation failed
 */
int
coap_block1_handler(void *request, void *response, uint8_t *target, size_t *len, size_t max_len)
{
  const uint8_t *payload = 0;
  int pay_len = REST.get_request_payload(request, &payload);

  if(!pay_len || !payload) {
    erbium_status_code = REST.status.BAD_REQUEST;
    coap_error_message = "NoPayload";
    return -1;
  }

  coap_packet_t *packet = (coap_packet_t *)request;

  if(packet->block1_offset + pay_len > max_len) {
    erbium_status_code = REST.status.REQUEST_ENTITY_TOO_LARGE;
    coap_error_message = "Message to big";
    return -1;
  }

  if(target && len) {
    memcpy(target + packet->block1_offset, payload, pay_len);
    *len = packet->block1_offset + pay_len;
  }

  if(IS_OPTION(packet, COAP_OPTION_BLOCK1)) {
    PRINTF("Blockwise: block 1 request: Num: %u, More: %u, Size: %u, Offset: %u\n",
           packet->block1_num,
           packet->block1_more,
           packet->block1_size,
           packet->block1_offset);

    coap_set_header_block1(response, packet->block1_num, packet->block1_more, packet->block1_size);
    if(packet->block1_more) {
      coap_set_status_code(response, CONTINUE_2_31);
      return 1;
    }
  }

  return 0;
}
void
large_create_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
  coap_packet_t *const coap_req = (coap_packet_t *) request;

  uint8_t *incoming = NULL;
  size_t len = 0;

  unsigned int ct = REST.get_header_content_type(request);
  if (ct==-1)
  {
    REST.set_response_status(response, REST.status.BAD_REQUEST);
    const char *error_msg = "NoContentType";
    REST.set_response_payload(response, error_msg, strlen(error_msg));
    return;
  }

  if ((len = REST.get_request_payload(request, &incoming)))
  {
    if (coap_req->block1_num*coap_req->block1_size+len <= 2048)
    {
      REST.set_response_status(response, REST.status.CREATED);
      REST.set_header_location(response, "/nirvana");
      coap_set_header_block1(response, coap_req->block1_num, 0, coap_req->block1_size);
    }
    else
    {
      REST.set_response_status(response, REST.status.REQUEST_ENTITY_TOO_LARGE);
      const char *error_msg = "2048B max.";
      REST.set_response_payload(response, error_msg, strlen(error_msg));
      return;
    }
  }
  else
  {
    REST.set_response_status(response, REST.status.BAD_REQUEST);
    const char *error_msg = "NoPayload";
    REST.set_response_payload(response, error_msg, strlen(error_msg));
    return;
  }
}
void
mirror_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
  /* The ETag and Token is copied to the header. */
  uint8_t opaque[] = {0x0A, 0xBC, 0xDE};

  /* Strings are not copied, so use static string buffers or strings in .text memory (char *str = "string in .text";). */
  static char location[] = {'/','f','/','a','?','k','&','e', 0};

  /* Getter for the header option Content-Type. If the option is not set, text/plain is returned by default. */
  unsigned int content_type = REST.get_header_content_type(request);

  /* The other getters copy the value (or string/array pointer) to the given pointers and return 1 for success or the length of strings/arrays. */
  uint32_t max_age_and_size = 0;
  const char *str = NULL;
  uint32_t observe = 0;
  const uint8_t *bytes = NULL;
  uint32_t block_num = 0;
  uint8_t block_more = 0;
  uint16_t block_size = 0;
  const char *query = "";
  int len = 0;

  /* Mirror the received header options in the response payload. Unsupported getters (e.g., rest_get_header_observe() with HTTP) will return 0. */

  int strpos = 0;
  /* snprintf() counts the terminating '\0' to the size parameter.
   * The additional byte is taken care of by allocating REST_MAX_CHUNK_SIZE+1 bytes in the REST framework.
   * Add +1 to fill the complete buffer, as the payload does not need a terminating '\0'. */
  if (content_type!=-1)
  {
    strpos += snprintf((char *)buffer, REST_MAX_CHUNK_SIZE+1, "CT %u\n", content_type);
  }
  
  /* Some getters such as for ETag or Location are omitted, as these options should not appear in a request.
   * Max-Age might appear in HTTP requests or used for special purposes in CoAP. */
  if (strpos<=REST_MAX_CHUNK_SIZE && REST.get_header_max_age(request, &max_age_and_size))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "MA %lu\n", max_age_and_size);
  }
  /* For HTTP this is the Length option, for CoAP it is the Size option. */
  if (strpos<=REST_MAX_CHUNK_SIZE && REST.get_header_length(request, &max_age_and_size))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "SZ %lu\n", max_age_and_size);
  }

  if (strpos<=REST_MAX_CHUNK_SIZE && (len = REST.get_header_host(request, &str)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "UH %.*s\n", len, str);
  }

/* CoAP-specific example: actions not required for normal RESTful Web service. */
#if WITH_COAP > 1
  if (strpos<=REST_MAX_CHUNK_SIZE && coap_get_header_observe(request, &observe))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Ob %lu\n", observe);
  }
  if (strpos<=REST_MAX_CHUNK_SIZE && (len = coap_get_header_token(request, &bytes)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "To 0x");
    int index = 0;
    for (index = 0; index<len; ++index) {
        strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%02X", bytes[index]);
    }
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "\n");
  }
  if (strpos<=REST_MAX_CHUNK_SIZE && (len = coap_get_header_etag(request, &bytes)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "ET 0x");
    int index = 0;
    for (index = 0; index<len; ++index) {
        strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%02X", bytes[index]);
    }
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "\n");
  }
  if (strpos<=REST_MAX_CHUNK_SIZE && (len = coap_get_header_uri_path(request, &str)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "UP ");
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%.*s\n", len, str);
  }
#if WITH_COAP == 3
  if (strpos<=REST_MAX_CHUNK_SIZE && (len = coap_get_header_location(request, &str)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Lo %.*s\n", len, str);
  }
  if (strpos<=REST_MAX_CHUNK_SIZE && coap_get_header_block(request, &block_num, &block_more, &block_size, NULL)) /* This getter allows NULL pointers to get only a subset of the block parameters. */
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Bl %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size);
  }
#else
  if (strpos<=REST_MAX_CHUNK_SIZE && (len = coap_get_header_location_path(request, &str)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "LP %.*s\n", len, str);
  }
  if (strpos<=REST_MAX_CHUNK_SIZE && (len = coap_get_header_location_query(request, &str)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "LQ %.*s\n", len, str);
  }
  if (strpos<=REST_MAX_CHUNK_SIZE && coap_get_header_block2(request, &block_num, &block_more, &block_size, NULL)) /* This getter allows NULL pointers to get only a subset of the block parameters. */
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "B2 %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size);
  }
  /*
   * Critical Block1 option is currently rejected by engine.
   *
  if (strpos<=REST_MAX_CHUNK_SIZE && coap_get_header_block1(request, &block_num, &block_more, &block_size, NULL))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "B1 %lu%s (%u)\n", block_num, block_more ? "+" : "", block_size);
  }
  */
#endif /* CoAP > 03 */
#endif /* CoAP-specific example */

  if (strpos<=REST_MAX_CHUNK_SIZE && (len = REST.get_query(request, &query)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "Qu %.*s\n", len, query);
  }
  if (strpos<=REST_MAX_CHUNK_SIZE && (len = REST.get_request_payload(request, &bytes)))
  {
    strpos += snprintf((char *)buffer+strpos, REST_MAX_CHUNK_SIZE-strpos+1, "%.*s", len, bytes);
  }

  if (strpos >= REST_MAX_CHUNK_SIZE)
  {
      buffer[REST_MAX_CHUNK_SIZE-1] = 0xBB; /* '»' to indicate truncation */
  }

  REST.set_response_payload(response, buffer, strpos);

  PRINTF("/mirror options received: %s\n", buffer);

  /* Set dummy header options for response. Like getters, some setters are not implemented for HTTP and have no effect. */
  REST.set_header_content_type(response, REST.type.TEXT_PLAIN);
  REST.set_header_max_age(response, 17); /* For HTTP, browsers will not re-request the page for 17 seconds. */
  REST.set_header_etag(response, opaque, 2);
  REST.set_header_location(response, location); /* Initial slash is omitted by framework */
  REST.set_header_length(response, strpos); /* For HTTP, browsers will not re-request the page for 10 seconds. CoAP action depends on the client. */

/* CoAP-specific example: actions not required for normal RESTful Web service. */
#if WITH_COAP > 1
  coap_set_header_uri_host(response, "tiki");
  coap_set_header_observe(response, 10);
#if WITH_COAP == 3
  coap_set_header_block(response, 42, 0, 64); /* The block option might be overwritten by the framework when blockwise transfer is requested. */
#else
  coap_set_header_proxy_uri(response, "ftp://x");
  coap_set_header_block2(response, 42, 0, 64); /* The block option might be overwritten by the framework when blockwise transfer is requested. */
  coap_set_header_block1(response, 23, 0, 16);
  coap_set_header_accept(response, TEXT_PLAIN);
  coap_set_header_if_none_match(response);
#endif /* CoAP > 03 */
#endif /* CoAP-specific example */
}
Exemple #6
0
/* This function is an adaptation of function coap_receive() from Erbium's er-coap-13-engine.c.
 * Erbium is Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich
 * All rights reserved.
 */
void lwm2m_handle_packet(lwm2m_context_t * contextP,
                         uint8_t * buffer,
                         int length,
                         void * fromSessionH)
{
    uint8_t coap_error_code = NO_ERROR;
    static coap_packet_t message[1];
    static coap_packet_t response[1];

    LOG("Entering");
    coap_error_code = coap_parse_message(message, buffer, (uint16_t)length);
    if (coap_error_code == NO_ERROR)
    {
        LOG_ARG("Parsed: ver %u, type %u, tkl %u, code %u.%.2u, mid %u, Content type: %d",
                message->version, message->type, message->token_len, message->code >> 5, message->code & 0x1F, message->mid, message->content_type);
        LOG_ARG("Payload: %.*s", message->payload_len, message->payload);
        if (message->code >= COAP_GET && message->code <= COAP_DELETE)
        {
            uint32_t block_num = 0;
            uint16_t block_size = REST_MAX_CHUNK_SIZE;
            uint32_t block_offset = 0;
            int64_t new_offset = 0;

            /* prepare response */
            if (message->type == COAP_TYPE_CON)
            {
                /* Reliable CON requests are answered with an ACK. */
                coap_init_message(response, COAP_TYPE_ACK, COAP_205_CONTENT, message->mid);
            }
            else
            {
                /* Unreliable NON requests are answered with a NON as well. */
                coap_init_message(response, COAP_TYPE_NON, COAP_205_CONTENT, contextP->nextMID++);
            }

            /* mirror token */
            if (message->token_len)
            {
                coap_set_header_token(response, message->token, message->token_len);
            }

            /* get offset for blockwise transfers */
            if (coap_get_header_block2(message, &block_num, NULL, &block_size, &block_offset))
            {
                LOG_ARG("Blockwise: block request %u (%u/%u) @ %u bytes", block_num, block_size, REST_MAX_CHUNK_SIZE, block_offset);
                block_size = MIN(block_size, REST_MAX_CHUNK_SIZE);
                new_offset = block_offset;
            }

            /* handle block1 option */
            if (IS_OPTION(message, COAP_OPTION_BLOCK1))
            {
#ifdef LWM2M_CLIENT_MODE
                // get server
                lwm2m_server_t * serverP;
                serverP = utils_findServer(contextP, fromSessionH);
#ifdef LWM2M_BOOTSTRAP
                if (serverP == NULL)
                {
                    serverP = utils_findBootstrapServer(contextP, fromSessionH);
                }
#endif
                if (serverP == NULL)
                {
                    coap_error_code = COAP_500_INTERNAL_SERVER_ERROR;
                }
                else
                {
                    uint32_t block1_num;
                    uint8_t  block1_more;
                    uint16_t block1_size;
                    uint8_t * complete_buffer = NULL;
                    size_t complete_buffer_size;

                    // parse block1 header
                    coap_get_header_block1(message, &block1_num, &block1_more, &block1_size, NULL);
                    LOG_ARG("Blockwise: block1 request NUM %u (SZX %u/ SZX Max%u) MORE %u", block1_num, block1_size, REST_MAX_CHUNK_SIZE, block1_more);

                    // handle block 1
                    coap_error_code = coap_block1_handler(&serverP->block1Data, message->mid, message->payload, message->payload_len, block1_size, block1_num, block1_more, &complete_buffer, &complete_buffer_size);

                    // if payload is complete, replace it in the coap message.
                    if (coap_error_code == NO_ERROR)
                    {
                        message->payload = complete_buffer;
                        message->payload_len = complete_buffer_size;
                    }
                    else if (coap_error_code == COAP_231_CONTINUE)
                    {
                        block1_size = MIN(block1_size, REST_MAX_CHUNK_SIZE);
                        coap_set_header_block1(response,block1_num, block1_more,block1_size);
                    }
                }
#else
                coap_error_code = COAP_501_NOT_IMPLEMENTED;
#endif
            }
            if (coap_error_code == NO_ERROR)
            {
                coap_error_code = handle_request(contextP, fromSessionH, message, response);
            }
            if (coap_error_code==NO_ERROR)
            {
                /* Save original payload pointer for later freeing. Payload in response may be updated. */
                uint8_t *payload = response->payload;
                if ( IS_OPTION(message, COAP_OPTION_BLOCK2) )
                {
                    /* unchanged new_offset indicates that resource is unaware of blockwise transfer */
                    if (new_offset==block_offset)
                    {
                        LOG_ARG("Blockwise: unaware resource with payload length %u/%u", response->payload_len, block_size);
                        if (block_offset >= response->payload_len)
                        {
                            LOG("handle_incoming_data(): block_offset >= response->payload_len");

                            response->code = COAP_402_BAD_OPTION;
                            coap_set_payload(response, "BlockOutOfScope", 15); /* a const char str[] and sizeof(str) produces larger code size */
                        }
                        else
                        {
                            coap_set_header_block2(response, block_num, response->payload_len - block_offset > block_size, block_size);
                            coap_set_payload(response, response->payload+block_offset, MIN(response->payload_len - block_offset, block_size));
                        } /* if (valid offset) */
                    }
                    else
                    {
                        /* resource provides chunk-wise data */
                        LOG_ARG("Blockwise: blockwise resource, new offset %d", (int) new_offset);
                        coap_set_header_block2(response, block_num, new_offset!=-1 || response->payload_len > block_size, block_size);
                        if (response->payload_len > block_size) coap_set_payload(response, response->payload, block_size);
                    } /* if (resource aware of blockwise) */
                }
                else if (new_offset!=0)
                {
                    LOG_ARG("Blockwise: no block option for blockwise resource, using block size %u", REST_MAX_CHUNK_SIZE);

                    coap_set_header_block2(response, 0, new_offset!=-1, REST_MAX_CHUNK_SIZE);
                    coap_set_payload(response, response->payload, MIN(response->payload_len, REST_MAX_CHUNK_SIZE));
                } /* if (blockwise request) */

                coap_error_code = message_send(contextP, response, fromSessionH);

                lwm2m_free(payload);
                response->payload = NULL;
                response->payload_len = 0;
            }
            else if (coap_error_code != COAP_IGNORE)
            {
                if (1 == coap_set_status_code(response, coap_error_code))
                {
                    coap_error_code = message_send(contextP, response, fromSessionH);
                }
            }
        }
        else
        {
            /* Responses */
            switch (message->type)
            {
            case COAP_TYPE_NON:
            case COAP_TYPE_CON:
                {
                    bool done = transaction_handleResponse(contextP, fromSessionH, message, response);

#ifdef LWM2M_SERVER_MODE
                    if (!done && IS_OPTION(message, COAP_OPTION_OBSERVE) &&
                        ((message->code == COAP_204_CHANGED) || (message->code == COAP_205_CONTENT)))
                    {
                        done = observe_handleNotify(contextP, fromSessionH, message, response);
                    }
#endif
                    if (!done && message->type == COAP_TYPE_CON )
                    {
                        coap_init_message(response, COAP_TYPE_ACK, 0, message->mid);
                        coap_error_code = message_send(contextP, response, fromSessionH);
                    }
                }
                break;

            case COAP_TYPE_RST:
                /* Cancel possible subscriptions. */
                handle_reset(contextP, fromSessionH, message);
                transaction_handleResponse(contextP, fromSessionH, message, NULL);
                break;

            case COAP_TYPE_ACK:
                transaction_handleResponse(contextP, fromSessionH, message, NULL);
                break;

            default:
                break;
            }
        } /* Request or Response */
        coap_free_header(message);
    } /* if (parsed correctly) */
void
large_update_handler(void* request, void* response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset)
{
  coap_packet_t *const coap_req = (coap_packet_t *) request;
  uint8_t method = REST.get_method_type(request);

  if (method & METHOD_GET)
  {
    /* Check the offset for boundaries of the resource data. */
    if (*offset>=large_update_size)
    {
      REST.set_response_status(response, REST.status.BAD_OPTION);
      /* A block error message should not exceed the minimum block size (16). */

      const char *error_msg = "BlockOutOfScope";
      REST.set_response_payload(response, error_msg, strlen(error_msg));
      return;
    }

    REST.set_response_payload(response, large_update_store+*offset, preferred_size);
    REST.set_header_content_type(response, large_update_ct);

    /* IMPORTANT for chunk-wise resources: Signal chunk awareness to REST engine. */
    *offset += preferred_size;

    /* Signal end of resource representation. */
    if (*offset>=large_update_size)
    {
      *offset = -1;
    }
  } else {
    uint8_t *incoming = NULL;
    size_t len = 0;

    unsigned int ct = REST.get_header_content_type(request);
    if (ct==-1)
    {
      REST.set_response_status(response, REST.status.BAD_REQUEST);
      const char *error_msg = "NoContentType";
      REST.set_response_payload(response, error_msg, strlen(error_msg));
      return;
    }

    if ((len = REST.get_request_payload(request, &incoming)))
    {
      if (coap_req->block1_num*coap_req->block1_size+len <= sizeof(large_update_store))
      {
        memcpy(large_update_store+coap_req->block1_num*coap_req->block1_size, incoming, len);
        large_update_size = coap_req->block1_num*coap_req->block1_size+len;
        large_update_ct = REST.get_header_content_type(request);

        REST.set_response_status(response, REST.status.CHANGED);
        coap_set_header_block1(response, coap_req->block1_num, 0, coap_req->block1_size);
      }
      else
      {
        REST.set_response_status(response, REST.status.REQUEST_ENTITY_TOO_LARGE);
        REST.set_response_payload(response, buffer, snprintf((char *)buffer, MAX_PLUGFEST_PAYLOAD, "%uB max.", sizeof(large_update_store)));
        return;
      }
    }
    else
    {
      REST.set_response_status(response, REST.status.BAD_REQUEST);
      const char *error_msg = "NoPayload";
      REST.set_response_payload(response, error_msg, strlen(error_msg));
      return;
    }
  }
}
/*----------------------------------------------------------------------------*/
void
pf_handler(void *request, void *response, uint8_t *buffer,
           uint16_t preferred_size, int32_t *offset, tres_res_t *task)
{
  rest_resource_flags_t method;
  uint32_t num = 0;
  uint8_t more = 0;
  uint16_t size = 0;
  uint32_t b1_offset = 0;
  uint16_t plen;
  const uint8_t *payload;

  method = REST.get_method_type(request);
  PRINTF("Method %d\n", method);
  switch (method) {
  case METHOD_GET:
    //TODO: retrieve python bytecode
    REST.set_response_status(response, REST.status.NOT_IMPLEMENTED);
    break;
  case METHOD_PUT:
    if(coap_get_header_block1(request, &num, &more, &size, &b1_offset)) {
      coap_set_header_block1(response, num, more, size);
    }

    PRINTF("Request on /pf: num = %"PRIu32", more = %"PRIu8", size = %"PRIu16
        ", offset = %"PRIu32"\n", num, more, size, b1_offset);
    // if it's the first packet, stop input resource monitoring
    if(b1_offset == 0) {
      tres_stop_monitoring(task);
      // if the user is actually updating the PF, clear it first
      if(task->pf_img) {
        tres_mem_pf_clear(task->pf_img);
        task_reset_state(task);
      }
    }
    plen = coap_get_payload(request, &payload);
    PRINTF("Payload len: %d\n", plen);
#if TRES_COPPER_WORKAROUND
    // if no block-wise transfer and payload len equal to maximum size, then
    // we probably need block wise transfer. FIXME: that may be not true
    if(size == 0 && plen == REST_MAX_CHUNK_SIZE) {
      more = 1;
      size = REST_MAX_CHUNK_SIZE;
      coap_set_header_block1(response, num, more, size);
    }
#endif
    // if it's the first packet, allocate a memory slot
    if(b1_offset == 0) {
      // ... but, if the first byte is not 0x0a than it is not python bytecode
      if(payload[0] != 0x0a) {
        REST.set_response_status(response, REST.status.BAD_REQUEST);
        return;
      }
      task->sid = tres_mem_pf_store_start();
      if(task->sid < 0) {
        PRINTF("No memory slot available\n");
        // TODO: we should return a server error code
        return;
      }
    }
    // store the current block
    if(tres_mem_pf_store_step(task->sid, payload, plen) < 0) {
      PRINTF("PF too big\n");
      return;
    }
    // if it's the final block, finalize the memory slot
    if(more == 0) {
      task->pf_img = tres_mem_pf_store_done(task->sid);
      PRINTF("task->pf_img: %p\n", task->pf_img);
      REST.set_response_status(response, REST.status.CHANGED);
    }
    break;
  case METHOD_POST:
    REST.set_response_status(response, REST.status.METHOD_NOT_ALLOWED);
    break;
  case METHOD_DELETE:
    if(task->pf_img) {
      tres_stop_monitoring(task);
      tres_mem_pf_clear(task->pf_img);
      task_reset_state(task);
      task->pf_img = NULL;
    }
    REST.set_response_status(response, REST.status.DELETED);
    break;
  default:
    break;
  }
}
/*----------------------------------------------------------------------------*/
void
tasks_handler(void *request, void *response, uint8_t *buffer,
              uint16_t preferred_size, int32_t *offset)
{
  const char *path;
  char name[TRES_TASK_NAME_MAX_LEN];
  char subres[SUBRES_LEN];
  int len;
  int retv;

#if TRES_COPPER_WORKAROUND
  uint32_t num = 0;
  uint8_t more = 0;
  uint16_t size = 0;
  uint32_t b1_offset = 0;
  int i;
  tres_res_t *task;
  int32_t strpos = 0;
  int16_t bufpos = 0;

  if(coap_get_header_block1(request, &num, &more, &size, &b1_offset)
     && more == 0) {
    coap_set_header_block1(response, num, more, size);
  }
#endif

  len = REST.get_url(request, &path);
  retv = parse_tasks_path(path, len, name, subres);
  if(retv < 0) {
    PRINTF("Invalid path: %d\n", retv);
    REST.set_response_status(response, REST.status.BAD_REQUEST);
    return;
  }
  if(*name) {
    PRINTF("name: %s\n", name);
    task_name_handler(request, response, buffer, preferred_size, offset, name,
                      subres);
    return;
  }

  for(i = 0; i < tasks_mem.num; i++) {
    task = &tasks_mem_memb_mem[i];
    if(task->name[0]) {
      /* </tasks/[task_name] */
      if(strpos == 0) {
        BLOCK_SPRINTF("</" TRES_BASE_PATH "/", strpos, bufpos, offset,
                      preferred_size, buffer);
      } else {
        BLOCK_SPRINTF(",</" TRES_BASE_PATH "/", strpos, bufpos, offset,
                      preferred_size, buffer);
      }
      BLOCK_SPRINTF(task->name, strpos, bufpos, offset, preferred_size, buffer);
      BLOCK_SPRINTF(">;ct=40", strpos, bufpos, offset, preferred_size, buffer);
    }
  }
  REST.set_response_payload(response, buffer, bufpos);
  REST.set_header_content_type(response, APPLICATION_LINK_FORMAT);

  if(bufpos >= preferred_size || *offset > 0) {
    if(i == tasks_mem.num) {
      *offset = -1;
    } else {
      *offset += preferred_size;
    }
  }
}