smcp_status_t plugtest_separate_handler( smcp_node_t node ) { struct smcp_async_response_s* async_response = NULL; smcp_transaction_t transaction = NULL; smcp_status_t ret = SMCP_STATUS_NOT_ALLOWED; smcp_method_t method = smcp_inbound_get_code(); if(method==COAP_METHOD_GET) { if(smcp_inbound_is_dupe()) { smcp_outbound_begin_response(COAP_CODE_EMPTY); smcp_outbound_send(); ret = SMCP_STATUS_OK; goto bail; } async_response = calloc(sizeof(struct smcp_async_response_s),1); if(!async_response) { ret = SMCP_STATUS_MALLOC_FAILURE; goto bail; } #if !SMCP_AVOID_PRINTF || VERBOSE_DEBUG printf("This request needs an async response. %p\n",async_response); #endif transaction = smcp_transaction_init( NULL, SMCP_TRANSACTION_DELAY_START|SMCP_TRANSACTION_ALWAYS_INVALIDATE, &plugtest_separate_async_resend_response, &plugtest_separate_async_ack_handler, (void*)async_response ); if(!transaction) { free(async_response); // TODO: Consider dropping instead...? ret = SMCP_STATUS_MALLOC_FAILURE; goto bail; } ret = smcp_transaction_begin( smcp_get_current_instance(), transaction, (smcp_inbound_get_packet()->tt==COAP_TRANS_TYPE_CONFIRMABLE)?(smcp_cms_t)(COAP_MAX_TRANSMIT_WAIT*MSEC_PER_SEC):1 ); if(SMCP_STATUS_OK != ret) { smcp_transaction_end(smcp_get_current_instance(),transaction); goto bail; } ret = smcp_start_async_response(async_response,0); if(ret) { goto bail; } async_response = NULL; } bail: return ret; }
static smcp_status_t response_test_handler(int statuscode, void* context) { test_data_s * const test_data = context; smcp_t interface = smcp_get_current_instance(); bool has_observe_option = false; smcp_status_t status = 0; if(!test_data->finished) { if (statuscode < 0) { test_data->finished = true; if (statuscode != SMCP_STATUS_TRANSACTION_INVALIDATED) { test_data->error = statuscode; } } else { const uint8_t* value; coap_size_t value_len; coap_option_key_t key; test_data->msg_id = smcp_inbound_get_packet()->msg_id; if (smcp_inbound_is_dupe()) { test_data->inbound_dupe_packets++; } else { test_data->inbound_packets++; } while ((key=smcp_inbound_next_option(&value, &value_len))!=COAP_OPTION_INVALID) { if (key == COAP_OPTION_BLOCK1) { test_data->block1_option = 0; test_data->has_block1_option = 1; test_data->block1_option = coap_decode_uint32(value,(uint8_t)value_len); if ( statuscode == COAP_RESULT_231_CONTINUE) { smcp_transaction_new_msg_id(interface, test_data->transaction, smcp_get_next_msg_id(interface)); smcp_transaction_tickle(interface, test_data->transaction); } } if (key == COAP_OPTION_BLOCK2) { test_data->block2_option = 0; test_data->has_block2_option = 1; test_data->block2_option = coap_decode_uint32(value,(uint8_t)value_len); if (!(test_data->block2_option & (1<<3))) { test_data->finished = true; } } if (key == COAP_OPTION_OBSERVE) { if (test_data->inbound_packets > 2) { test_data->finished = true; } has_observe_option = true; printf("\tobs-response: \"%s\"\n", smcp_inbound_get_content_ptr()); } } if ( !has_observe_option && !test_data->has_block2_option && statuscode != COAP_RESULT_231_CONTINUE ) { test_data->finished = true; } test_data->inbound_content_len += smcp_inbound_get_content_len(); test_data->inbound_code = (coap_code_t)statuscode; if (test_data->has_block2_option) { strncat(test_data->response, smcp_inbound_get_content_ptr(), smcp_inbound_get_content_len()); } else { strncpy(test_data->response, smcp_inbound_get_content_ptr(), smcp_inbound_get_content_len()); } } } return status; }
smcp_status_t smcp_variable_node_request_handler( smcp_variable_node_t node ) { // TODO: Make this function use less stack space! smcp_method_t method = smcp_inbound_get_code(); smcp_status_t ret = SMCP_STATUS_NOT_FOUND; SMCP_NON_RECURSIVE coap_content_type_t content_type; SMCP_NON_RECURSIVE char* content_ptr; SMCP_NON_RECURSIVE size_t content_len; SMCP_NON_RECURSIVE char buffer[SMCP_VARIABLE_MAX_VALUE_LENGTH+1]; SMCP_NON_RECURSIVE coap_content_type_t reply_content_type; uint8_t key_index = BAD_KEY_INDEX; size_t value_len; bool needs_prefix = true; char* prefix_name = ""; content_type = smcp_inbound_get_content_type(); content_ptr = (char*)smcp_inbound_get_content_ptr(); content_len = smcp_inbound_get_content_len(); reply_content_type = SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED; require(node, bail); // Look up the key index. if(smcp_inbound_peek_option(NULL,&value_len)==COAP_OPTION_URI_PATH) { if(!value_len) { needs_prefix = false; smcp_inbound_next_option(NULL,NULL); } else for(key_index=0;key_index<BAD_KEY_INDEX;key_index++) { ret = node->func(node,SMCP_VAR_GET_KEY,key_index,buffer); require_action(ret==0,bail,ret=SMCP_STATUS_NOT_FOUND); if(smcp_inbound_option_strequal(COAP_OPTION_URI_PATH, buffer)) { smcp_inbound_next_option(NULL,NULL); break; } } } { coap_option_key_t key; const uint8_t* value; while((key=smcp_inbound_next_option(&value, &value_len))!=COAP_OPTION_INVALID) { require_action(key!=COAP_OPTION_URI_PATH,bail,ret=SMCP_STATUS_NOT_FOUND); if(key==COAP_OPTION_URI_QUERY) { if( method == COAP_METHOD_POST && value_len>=2 && strhasprefix_const((const char*)value,"v=") ) { DEBUG_PRINTF("variable-node: value is in the query."); content_type = COAP_CONTENT_TYPE_TEXT_PLAIN; content_ptr = (char*)value+2; content_len = value_len-2; } // } else if(key==COAP_OPTION_ETAG) { // } else if(key==COAP_OPTION_IF_MATCH) { // } else if(key==COAP_OPTION_IF_NONE_MATCH) { } else if(key==COAP_OPTION_ACCEPT) { reply_content_type = 0; if(value_len==1) reply_content_type = value[0]; else { // Unsupported type. } } else if(COAP_OPTION_IS_CRITICAL(key)) { ret=SMCP_STATUS_BAD_OPTION; assert_printf("Unrecognized option %d, \"%s\"", key, coap_option_key_to_cstr(key, false) ); goto bail; } } } // TODO: Implement me! if(method == COAP_METHOD_PUT) method = COAP_METHOD_POST; if(method == COAP_METHOD_POST) { require_action(!smcp_inbound_is_dupe(),bail,ret=0); require_action( key_index!=BAD_KEY_INDEX, bail, ret=SMCP_STATUS_NOT_ALLOWED ); if(content_type==SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED) { char* key = NULL; char* value = NULL; content_len = 0; while( url_form_next_value( (char**)&content_ptr, &key, &value ) && key && value ) { if(strequal_const(key, "v")) { content_ptr = value; content_len = strlen(value); break; } } } // Make sure our content is zero terminated. ((char*)content_ptr)[content_len] = 0; ret = node->func(node,SMCP_VAR_SET_VALUE,key_index,(char*)content_ptr); require_noerr(ret,bail); ret = smcp_outbound_begin_response(COAP_RESULT_204_CHANGED); require_noerr(ret,bail); ret = smcp_outbound_send(); require_noerr(ret,bail); } else if(method == COAP_METHOD_GET) { if(key_index==BAD_KEY_INDEX) { char* content_end_ptr; ret = smcp_outbound_begin_response(COAP_RESULT_205_CONTENT); require_noerr(ret,bail); smcp_outbound_add_option_uint(COAP_OPTION_CONTENT_TYPE, COAP_CONTENT_TYPE_APPLICATION_LINK_FORMAT); ret = smcp_observable_update(&node->observable, SMCP_OBSERVABLE_BROADCAST_KEY); check_string(ret==0,smcp_status_to_cstr(ret)); content_ptr = smcp_outbound_get_content_ptr(&content_len); content_end_ptr = content_ptr+content_len; for(key_index=0;key_index<BAD_KEY_INDEX;key_index++) { ret = node->func(node,SMCP_VAR_GET_KEY,key_index,buffer); if(ret) break; if(content_ptr+2>=content_end_ptr) { // No more room for content. break; } *content_ptr++ = '<'; if(needs_prefix) { content_ptr += url_encode_cstr(content_ptr, prefix_name, (content_end_ptr-content_ptr)-1); content_ptr = stpncpy(content_ptr,"/",MIN(1,(content_end_ptr-content_ptr)-1)); } content_ptr += url_encode_cstr(content_ptr, buffer, (content_end_ptr-content_ptr)-1); *content_ptr++ = '>'; ret = node->func(node,SMCP_VAR_GET_VALUE,key_index,buffer); if(content_ptr+4>=content_end_ptr) { // No more room for content. break; } if(!ret) { strcpy(content_ptr,";v="); content_ptr += 3; content_ptr += quoted_cstr(content_ptr, buffer, (content_end_ptr-content_ptr)-1); } ret = node->func(node,SMCP_VAR_GET_LF_TITLE,key_index,buffer); if(content_ptr+8>=content_end_ptr) { // No more room for content. break; } if(!ret) { strcpy(content_ptr,";title="); content_ptr += 7; content_ptr += quoted_cstr(content_ptr, buffer, (content_end_ptr-content_ptr)-1); } // Observation flag if(0==node->func(node,SMCP_VAR_GET_OBSERVABLE,key_index,NULL)) { content_ptr = stpncpy(content_ptr,";obs",MIN(4,(content_end_ptr-content_ptr)-1)); } *content_ptr++ = ','; } ret = smcp_outbound_set_content_len(content_len-(content_end_ptr-content_ptr)); require_noerr(ret,bail); ret = smcp_outbound_send(); } else { size_t replyContentLength = 0; char *replyContent; ret = smcp_outbound_begin_response(COAP_RESULT_205_CONTENT); require_noerr(ret,bail); if(0==node->func(node,SMCP_VAR_GET_OBSERVABLE,key_index,buffer)) { ret = smcp_observable_update(&node->observable, key_index); check_string(ret==0,smcp_status_to_cstr(ret)); } if(reply_content_type == SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED) { uint32_t etag; if(0==node->func(node,SMCP_VAR_GET_MAX_AGE,key_index,buffer)) { #if HAVE_STRTOL uint32_t max_age = strtol(buffer,NULL,0)&0xFFFFFF; #else uint32_t max_age = atoi(buffer)&0xFFFFFF; #endif smcp_outbound_add_option_uint(COAP_OPTION_MAX_AGE, max_age); } ret = node->func(node,SMCP_VAR_GET_VALUE,key_index,buffer); require_noerr(ret,bail); fasthash_start(0); fasthash_feed((const uint8_t*)buffer,strlen(buffer)); etag = fasthash_finish_uint32(); smcp_outbound_add_option_uint(COAP_OPTION_CONTENT_TYPE, SMCP_CONTENT_TYPE_APPLICATION_FORM_URLENCODED); smcp_outbound_add_option_uint(COAP_OPTION_ETAG, etag); replyContent = smcp_outbound_get_content_ptr(&replyContentLength); *replyContent++ = 'v'; *replyContent++ = '='; replyContentLength -= 2; replyContentLength = url_encode_cstr( replyContent, buffer, replyContentLength ); ret = smcp_outbound_set_content_len(replyContentLength+2); } else { ret = node->func(node,SMCP_VAR_GET_VALUE,key_index,buffer); require_noerr(ret,bail); ret = smcp_outbound_append_content(buffer, SMCP_CSTR_LEN); } require_noerr(ret,bail); ret = smcp_outbound_send(); } } else { ret = smcp_default_request_handler( (void*)node ); check_string(ret == SMCP_STATUS_OK, smcp_status_to_cstr(ret)); } bail: check_string(ret == SMCP_STATUS_OK, smcp_status_to_cstr(ret)); return ret; }