terence zhang / wakaama
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers registration.c Source File

registration.c

00001 /*******************************************************************************
00002  *
00003  * Copyright (c) 2013, 2014 Intel Corporation and others.
00004  * All rights reserved. This program and the accompanying materials
00005  * are made available under the terms of the Eclipse Public License v1.0
00006  * and Eclipse Distribution License v1.0 which accompany this distribution.
00007  *
00008  * The Eclipse Public License is available at
00009  *    http://www.eclipse.org/legal/epl-v10.html
00010  * The Eclipse Distribution License is available at
00011  *    http://www.eclipse.org/org/documents/edl-v10.php.
00012  *
00013  * Contributors:
00014  *    David Navarro, Intel Corporation - initial API and implementation
00015  *    domedambrosio - Please refer to git log
00016  *    Fabien Fleutot - Please refer to git log
00017  *    Simon Bernard - Please refer to git log
00018  *    Toby Jaffey - Please refer to git log
00019  *    Manuel Sangoi - Please refer to git log
00020  *    Julien Vermillard - Please refer to git log
00021  *    Bosch Software Innovations GmbH - Please refer to git log
00022  *    Pascal Rieux - Please refer to git log
00023  *
00024  *******************************************************************************/
00025 
00026 /*
00027  Copyright (c) 2013, 2014 Intel Corporation
00028 
00029  Redistribution and use in source and binary forms, with or without modification,
00030  are permitted provided that the following conditions are met:
00031 
00032      * Redistributions of source code must retain the above copyright notice,
00033        this list of conditions and the following disclaimer.
00034      * Redistributions in binary form must reproduce the above copyright notice,
00035        this list of conditions and the following disclaimer in the documentation
00036        and/or other materials provided with the distribution.
00037      * Neither the name of Intel Corporation nor the names of its contributors
00038        may be used to endorse or promote products derived from this software
00039        without specific prior written permission.
00040 
00041  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
00042  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
00043  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
00044  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
00045  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00046  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
00047  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00048  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
00049  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
00050  THE POSSIBILITY OF SUCH DAMAGE.
00051 
00052  David Navarro <david.navarro@intel.com>
00053 
00054 */
00055 
00056 #include "internals.h"
00057 
00058 #include <stdlib.h>
00059 #include <string.h>
00060 #include <stdio.h>
00061 
00062 #define MAX_LOCATION_LENGTH 10      // strlen("/rd/65534") + 1
00063 
00064 #ifdef LWM2M_CLIENT_MODE
00065 
00066 static int prv_getRegistrationQuery(lwm2m_context_t * contextP,
00067                                     lwm2m_server_t * server,
00068                                     char * buffer,
00069                                     size_t length)
00070 {
00071     int index;
00072     int res;
00073 
00074     index = utils_stringCopy(buffer, length, QUERY_STARTER QUERY_VERSION_FULL QUERY_DELIMITER QUERY_NAME);
00075     if (index < 0) return 0;
00076     res = utils_stringCopy(buffer + index, length - index, contextP->endpointName);
00077     if (res < 0) return 0;
00078     index += res;
00079 
00080     if (NULL != contextP->msisdn)
00081     {
00082         res = utils_stringCopy(buffer + index, length - index, QUERY_DELIMITER QUERY_SMS);
00083         if (res < 0) return 0;
00084         index += res;
00085         res = utils_stringCopy(buffer + index, length - index, contextP->msisdn);
00086         if (res < 0) return 0;
00087         index += res;
00088     }
00089 
00090     switch (server->binding)
00091     {
00092     case BINDING_U:
00093         res = utils_stringCopy(buffer + index, length - index, "&b=U");
00094         break;
00095     case BINDING_UQ:
00096         res = utils_stringCopy(buffer + index, length - index, "&b=UQ");
00097         break;
00098     case BINDING_S:
00099         res = utils_stringCopy(buffer + index, length - index, "&b=S");
00100         break;
00101     case BINDING_SQ:
00102         res = utils_stringCopy(buffer + index, length - index, "&b=SQ");
00103         break;
00104     case BINDING_US:
00105         res = utils_stringCopy(buffer + index, length - index, "&b=US");
00106         break;
00107     case BINDING_UQS:
00108         res = utils_stringCopy(buffer + index, length - index, "&b=UQS");
00109         break;
00110     default:
00111         res = -1;
00112     }
00113     if (res < 0) return 0;
00114 
00115     return index + res;
00116 }
00117 
00118 static void prv_handleRegistrationReply(lwm2m_transaction_t * transacP,
00119                                         void * message)
00120 {
00121     coap_packet_t * packet = (coap_packet_t *)message;
00122     lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->userData);
00123 
00124     if (targetP->status == STATE_REG_PENDING)
00125     {
00126         time_t tv_sec = lwm2m_gettime();
00127         if (tv_sec >= 0)
00128         {
00129             targetP->registration = tv_sec;
00130         }
00131         if (packet != NULL && packet->code == COAP_201_CREATED)
00132         {
00133             targetP->status = STATE_REGISTERED;
00134             if (NULL != targetP->location)
00135             {
00136                 lwm2m_free(targetP->location);
00137             }
00138             targetP->location = coap_get_multi_option_as_string(packet->location_path);
00139 
00140             LOG("Registration successful");
00141         }
00142         else
00143         {
00144             targetP->status = STATE_REG_FAILED;
00145             LOG("Registration failed");
00146         }
00147     }
00148 }
00149 
00150 #define PRV_QUERY_BUFFER_LENGTH 200
00151 
00152 // send the registration for a single server
00153 static uint8_t prv_register(lwm2m_context_t * contextP,
00154                             lwm2m_server_t * server)
00155 {
00156     char query[200];
00157     int query_length;
00158     uint8_t payload[512];
00159     int payload_length;
00160     lwm2m_transaction_t * transaction;
00161 
00162     payload_length = object_getRegisterPayload(contextP, payload, sizeof(payload));
00163     if (payload_length == 0) return COAP_500_INTERNAL_SERVER_ERROR;
00164 
00165     query_length = prv_getRegistrationQuery(contextP, server, query, sizeof(query));
00166 
00167     if (query_length == 0) return COAP_500_INTERNAL_SERVER_ERROR;
00168 
00169     if (0 != server->lifetime)
00170     {
00171         int res;
00172 
00173         res = utils_stringCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, QUERY_DELIMITER QUERY_LIFETIME);
00174         if (res < 0) return COAP_500_INTERNAL_SERVER_ERROR;
00175         query_length += res;
00176         res = utils_intCopy(query + query_length, PRV_QUERY_BUFFER_LENGTH - query_length, server->lifetime);
00177         if (res < 0) return COAP_500_INTERNAL_SERVER_ERROR;
00178         query_length += res;
00179     }
00180 
00181     if (server->sessionH == NULL)
00182     {
00183         server->sessionH = lwm2m_connect_server(server->secObjInstID, contextP->userData);
00184     }
00185 
00186     if (NULL == server->sessionH) return COAP_503_SERVICE_UNAVAILABLE;
00187 
00188     transaction = transaction_new(server->sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL);
00189     if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
00190 
00191     coap_set_header_uri_path(transaction->message, "/"URI_REGISTRATION_SEGMENT);
00192     coap_set_header_uri_query(transaction->message, query);
00193     coap_set_header_content_type(transaction->message, LWM2M_CONTENT_LINK);
00194     coap_set_payload(transaction->message, payload, payload_length);
00195 
00196     transaction->callback = prv_handleRegistrationReply;
00197     transaction->userData = (void *) server;
00198 
00199     contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
00200     if (transaction_send(contextP, transaction) != 0) return COAP_500_INTERNAL_SERVER_ERROR;
00201 
00202     server->status = STATE_REG_PENDING;
00203 
00204     return COAP_NO_ERROR;
00205 }
00206 
00207 static void prv_handleRegistrationUpdateReply(lwm2m_transaction_t * transacP,
00208                                               void * message)
00209 {
00210     coap_packet_t * packet = (coap_packet_t *)message;
00211     lwm2m_server_t * targetP = (lwm2m_server_t *)(transacP->userData);
00212 
00213     if (targetP->status == STATE_REG_UPDATE_PENDING)
00214     {
00215         time_t tv_sec = lwm2m_gettime();
00216         if (tv_sec >= 0)
00217         {
00218             targetP->registration = tv_sec;
00219         }
00220         if (packet != NULL && packet->code == COAP_204_CHANGED)
00221         {
00222             targetP->status = STATE_REGISTERED;
00223             LOG("Registration update successful");
00224         }
00225         else
00226         {
00227             targetP->status = STATE_REG_FAILED;
00228             LOG("Registration update failed");
00229         }
00230     }
00231 }
00232 
00233 static int prv_updateRegistration(lwm2m_context_t * contextP,
00234                                   lwm2m_server_t * server,
00235                                   bool withObjects)
00236 {
00237     lwm2m_transaction_t * transaction;
00238     uint8_t payload[512];
00239     int payload_length;
00240 
00241     transaction = transaction_new(server->sessionH, COAP_POST, NULL, NULL, contextP->nextMID++, 4, NULL);
00242     if (transaction == NULL) return COAP_500_INTERNAL_SERVER_ERROR;
00243 
00244     coap_set_header_uri_path(transaction->message, server->location);
00245 
00246     if (withObjects == true)
00247     {
00248         payload_length = object_getRegisterPayload(contextP, payload, sizeof(payload));
00249         if (payload_length == 0)
00250         {
00251             transaction_free(transaction);
00252             return COAP_500_INTERNAL_SERVER_ERROR;
00253         }
00254         coap_set_payload(transaction->message, payload, payload_length);
00255     }
00256 
00257     transaction->callback = prv_handleRegistrationUpdateReply;
00258     transaction->userData = (void *) server;
00259 
00260     contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
00261 
00262     if (transaction_send(contextP, transaction) == 0)
00263     {
00264         server->status = STATE_REG_UPDATE_PENDING;
00265     }
00266 
00267     return COAP_NO_ERROR;
00268 }
00269 
00270 // update the registration of a given server
00271 int lwm2m_update_registration(lwm2m_context_t * contextP,
00272                               uint16_t shortServerID,
00273                               bool withObjects)
00274 {
00275     lwm2m_server_t * targetP;
00276     uint8_t result;
00277 
00278     LOG_ARG("State: %s, shortServerID: %d", STR_STATE(contextP->state), shortServerID);
00279 
00280     result = COAP_NO_ERROR;
00281 
00282     targetP = contextP->serverList;
00283     if (targetP == NULL)
00284     {
00285         if (object_getServers(contextP) == -1)
00286         {
00287             LOG("No server found");
00288             return COAP_404_NOT_FOUND;
00289         }
00290     }
00291     while (targetP != NULL && result == COAP_NO_ERROR)
00292     {
00293         if (shortServerID != 0)
00294         {
00295             if (targetP->shortID == shortServerID)
00296             {
00297                 // found the server, trigger the update transaction
00298                 if (targetP->status == STATE_REGISTERED
00299                  || targetP->status == STATE_REG_UPDATE_PENDING)
00300                 {
00301                     if (withObjects == true)
00302                     {
00303                         targetP->status = STATE_REG_FULL_UPDATE_NEEDED;
00304                     }
00305                     else
00306                     {
00307                         targetP->status = STATE_REG_UPDATE_NEEDED;
00308                     }
00309                     return COAP_NO_ERROR;
00310                 }
00311                 else if ((targetP->status == STATE_REG_FULL_UPDATE_NEEDED)
00312                       || (targetP->status == STATE_REG_UPDATE_NEEDED))
00313                 {
00314                     // if REG (FULL) UPDATE is already set, returns COAP_NO_ERROR
00315                     if (withObjects == true)
00316                     {
00317                         targetP->status = STATE_REG_FULL_UPDATE_NEEDED;
00318                     }
00319                     return COAP_NO_ERROR;
00320                 }
00321                 else
00322                 {
00323                     return COAP_400_BAD_REQUEST;
00324                 }
00325             }
00326         }
00327         else
00328         {
00329             if (targetP->status == STATE_REGISTERED
00330              || targetP->status == STATE_REG_UPDATE_PENDING)
00331             {
00332                 if (withObjects == true)
00333                 {
00334                     targetP->status = STATE_REG_FULL_UPDATE_NEEDED;
00335                 }
00336                 else
00337                 {
00338                     targetP->status = STATE_REG_UPDATE_NEEDED;
00339                 }
00340             }
00341         }
00342         targetP = targetP->next;
00343     }
00344 
00345     if (shortServerID != 0
00346      && targetP == NULL)
00347     {
00348         // no server found
00349         result = COAP_404_NOT_FOUND;
00350     }
00351 
00352     return result;
00353 }
00354 
00355 uint8_t registration_start(lwm2m_context_t * contextP)
00356 {
00357     lwm2m_server_t * targetP;
00358     uint8_t result;
00359 
00360     LOG_ARG("State: %s", STR_STATE(contextP->state));
00361     
00362     result = COAP_NO_ERROR;
00363 
00364     targetP = contextP->serverList;
00365     while (targetP != NULL && result == COAP_NO_ERROR)
00366     {
00367         if (targetP->status == STATE_DEREGISTERED
00368          || targetP->status == STATE_REG_FAILED)
00369         {
00370             result = prv_register(contextP, targetP);
00371         }
00372         targetP = targetP->next;
00373     }
00374 
00375     return result;
00376 }
00377 
00378 
00379 /*
00380  * Returns STATE_REG_PENDING if at least one registration is still pending
00381  * Returns STATE_REGISTERED if no registration is pending and there is at least one server the client is registered to
00382  * Returns STATE_REG_FAILED if all registration failed.
00383  */
00384 lwm2m_status_t registration_getStatus(lwm2m_context_t * contextP)
00385 {
00386     lwm2m_server_t * targetP;
00387     lwm2m_status_t reg_status;
00388 
00389     LOG_ARG("State: %s", STR_STATE(contextP->state));
00390 
00391     targetP = contextP->serverList;
00392     reg_status = STATE_REG_FAILED;
00393 
00394     while (targetP != NULL)
00395     {
00396         LOG_ARG("targetP->status: %s", STR_STATUS(targetP->status));
00397         switch (targetP->status)
00398         {
00399             case STATE_REGISTERED:
00400             case STATE_REG_UPDATE_NEEDED:
00401             case STATE_REG_FULL_UPDATE_NEEDED:
00402             case STATE_REG_UPDATE_PENDING:
00403                 if (reg_status == STATE_REG_FAILED)
00404                 {
00405                     reg_status = STATE_REGISTERED;
00406                 }
00407                 break;
00408 
00409             case STATE_REG_PENDING:
00410                 reg_status = STATE_REG_PENDING;
00411                 break;
00412 
00413             case STATE_REG_FAILED:
00414             case STATE_DEREG_PENDING:
00415             case STATE_DEREGISTERED:
00416             default:
00417                 break;
00418         }
00419         LOG_ARG("reg_status: %s", STR_STATUS(reg_status));
00420 
00421         targetP = targetP->next;
00422     }
00423 
00424     return reg_status;
00425 }
00426 
00427 static void prv_handleDeregistrationReply(lwm2m_transaction_t * transacP,
00428                                           void * message)
00429 {
00430     lwm2m_server_t * targetP;
00431 
00432     targetP = (lwm2m_server_t *)(transacP->userData);
00433     if (NULL != targetP)
00434     {
00435         if (targetP->status == STATE_DEREG_PENDING)
00436         {
00437             targetP->status = STATE_DEREGISTERED;
00438         }
00439     }
00440 }
00441 
00442 void registration_deregister(lwm2m_context_t * contextP,
00443                              lwm2m_server_t * serverP)
00444 {
00445     lwm2m_transaction_t * transaction;
00446 
00447     LOG_ARG("State: %s, serverP->status: %s", STR_STATE(contextP->state), STR_STATUS(serverP->status));
00448 
00449     if (serverP->status == STATE_DEREGISTERED
00450      || serverP->status == STATE_REG_PENDING
00451      || serverP->status == STATE_DEREG_PENDING
00452      || serverP->status == STATE_REG_FAILED
00453      || serverP->location == NULL)
00454     {
00455         return;
00456     }
00457 
00458     transaction = transaction_new(serverP->sessionH, COAP_DELETE, NULL, NULL, contextP->nextMID++, 4, NULL);
00459     if (transaction == NULL) return;
00460 
00461     coap_set_header_uri_path(transaction->message, serverP->location);
00462 
00463     transaction->callback = prv_handleDeregistrationReply;
00464     transaction->userData = (void *) contextP;
00465 
00466     contextP->transactionList = (lwm2m_transaction_t *)LWM2M_LIST_ADD(contextP->transactionList, transaction);
00467     if (transaction_send(contextP, transaction) == 0)
00468     {
00469         serverP->status = STATE_DEREG_PENDING;
00470     }
00471 }
00472 #endif
00473 
00474 #ifdef LWM2M_SERVER_MODE
00475 static void prv_freeClientObjectList(lwm2m_client_object_t * objects)
00476 {
00477     while (objects != NULL)
00478     {
00479         lwm2m_client_object_t * objP;
00480 
00481         while (objects->instanceList != NULL)
00482         {
00483             lwm2m_list_t * target;
00484 
00485             target = objects->instanceList;
00486             objects->instanceList = objects->instanceList->next;
00487             lwm2m_free(target);
00488         }
00489 
00490         objP = objects;
00491         objects = objects->next;
00492         lwm2m_free(objP);
00493     }
00494 }
00495 
00496 static int prv_getParameters(multi_option_t * query,
00497                              char ** nameP,
00498                              uint32_t * lifetimeP,
00499                              char ** msisdnP,
00500                              lwm2m_binding_t * bindingP,
00501                              char ** versionP)
00502 {
00503     *nameP = NULL;
00504     *lifetimeP = 0;
00505     *msisdnP = NULL;
00506     *bindingP = BINDING_UNKNOWN;
00507     *versionP = NULL;
00508 
00509     while (query != NULL)
00510     {
00511         if (lwm2m_strncmp((char *)query->data, QUERY_NAME, QUERY_NAME_LEN) == 0)
00512         {
00513             if (*nameP != NULL) goto error;
00514             if (query->len == QUERY_NAME_LEN) goto error;
00515 
00516             *nameP = (char *)lwm2m_malloc(query->len - QUERY_NAME_LEN + 1);
00517             if (*nameP != NULL)
00518             {
00519                 memcpy(*nameP, query->data + QUERY_NAME_LEN, query->len - QUERY_NAME_LEN);
00520                 (*nameP)[query->len - QUERY_NAME_LEN] = 0;
00521             }
00522         }
00523         else if (lwm2m_strncmp((char *)query->data, QUERY_SMS, QUERY_SMS_LEN) == 0)
00524         {
00525             if (*msisdnP != NULL) goto error;
00526             if (query->len == QUERY_SMS_LEN) goto error;
00527 
00528             *msisdnP = (char *)lwm2m_malloc(query->len - QUERY_SMS_LEN + 1);
00529             if (*msisdnP != NULL)
00530             {
00531                 memcpy(*msisdnP, query->data + QUERY_SMS_LEN, query->len - QUERY_SMS_LEN);
00532                 (*msisdnP)[query->len - QUERY_SMS_LEN] = 0;
00533             }
00534         }
00535         else if (lwm2m_strncmp((char *)query->data, QUERY_LIFETIME, QUERY_LIFETIME_LEN) == 0)
00536         {
00537             int i;
00538 
00539             if (*lifetimeP != 0) goto error;
00540             if (query->len == QUERY_LIFETIME_LEN) goto error;
00541 
00542             for (i = QUERY_LIFETIME_LEN ; i < query->len ; i++)
00543             {
00544                 if (query->data[i] < '0' || query->data[i] > '9') goto error;
00545                 *lifetimeP = (*lifetimeP * 10) + (query->data[i] - '0');
00546             }
00547         }
00548         else if (lwm2m_strncmp((char *)query->data, QUERY_VERSION, QUERY_VERSION_LEN) == 0)
00549         {
00550             if (*versionP != NULL) goto error;
00551             if (query->len == QUERY_VERSION_LEN) goto error;
00552 
00553             *versionP = (char *)lwm2m_malloc(query->len - QUERY_VERSION_LEN + 1);
00554             if (*versionP != NULL)
00555             {
00556                 memcpy(*versionP, query->data + QUERY_VERSION_LEN, query->len - QUERY_VERSION_LEN);
00557                 (*versionP)[query->len - QUERY_VERSION_LEN] = 0;
00558             }
00559         }
00560         else if (lwm2m_strncmp((char *)query->data, QUERY_BINDING, QUERY_BINDING_LEN) == 0)
00561         {
00562             if (*bindingP != BINDING_UNKNOWN) goto error;
00563             if (query->len == QUERY_BINDING_LEN) goto error;
00564 
00565             *bindingP = utils_stringToBinding(query->data + QUERY_BINDING_LEN, query->len - QUERY_BINDING_LEN);
00566         }
00567         query = query->next;
00568     }
00569 
00570     return 0;
00571 
00572 error:
00573     if (*nameP != NULL) lwm2m_free(*nameP);
00574     if (*msisdnP != NULL) lwm2m_free(*msisdnP);
00575     if (*versionP != NULL) lwm2m_free(*versionP);
00576 
00577     return -1;
00578 }
00579 
00580 static uint16_t prv_splitLinkAttribute(uint8_t * data,
00581                                        uint16_t length,
00582                                        uint16_t * keyStart,
00583                                        uint16_t * keyLength,
00584                                        uint16_t * valueStart,
00585                                        uint16_t * valueLength)
00586 {
00587     uint16_t index;
00588     uint16_t end;
00589 
00590     index = 0;
00591     while (index < length && data[index] == ' ') index++;
00592     if (index == length) return 0;
00593 
00594     if (data[index] == REG_ATTR_SEPARATOR)
00595     {
00596         index++;
00597     }
00598     if (index == length) return 0;
00599 
00600     *keyStart = index;
00601 
00602     while (index < length && data[index] != REG_ATTR_EQUALS) index++;
00603     if (index == *keyStart || index == length) return 0;
00604 
00605     *keyLength = index - *keyStart;
00606 
00607     index++;
00608     while (index < length && data[index] == ' ') index++;
00609     if (index == length) return 0;
00610 
00611     *valueStart = index;
00612 
00613     while (index < length && data[index] != REG_ATTR_SEPARATOR) index++;
00614     end = index;
00615 
00616     index--;
00617     while (index > *valueStart && data[index] == ' ') index--;
00618     if (index == *valueStart) return 0;
00619 
00620     *valueLength = index - *valueStart + 1;
00621 
00622     return end;
00623 }
00624 
00625 static int prv_parseLinkAttributes(uint8_t * data,
00626                                    uint16_t length,
00627                                    bool * supportJSON,
00628                                    char ** altPath)
00629 {
00630     uint16_t index;
00631     uint16_t pathStart;
00632     uint16_t pathLength;
00633     bool isValid;
00634 
00635     isValid = false;
00636 
00637     // Expecting application/link-format (RFC6690)
00638     // leading space were removed before. Remove trailing spaces.
00639     while (length > 0 && data[length-1] == ' ') length--;
00640 
00641     // strip open tag
00642     if (length >= 2 && data[0] == REG_URI_START)
00643     {
00644         data += 1;
00645         length -= 1;
00646     }
00647     else
00648     {
00649         return 0;
00650     }
00651 
00652     pathStart = 0;
00653     index = length - 1;
00654     while (index > 0 && data[index] != REG_URI_END) index--;
00655     // link attributes are required
00656     if (index == 0 || index == length - 1) return 0;
00657 
00658     // If there is a preceding /, remove it
00659     if (data[pathStart] == '/')
00660     {
00661         pathStart += 1;
00662     }
00663     pathLength = index - pathStart;
00664 
00665     index++;
00666     if (index >= length || data[index] != REG_ATTR_SEPARATOR) return 0;
00667     index++;
00668 
00669     while (index < length)
00670     {
00671         uint16_t result;
00672         uint16_t keyStart;
00673         uint16_t keyLength;
00674         uint16_t valueStart;
00675         uint16_t valueLength;
00676 
00677         result = prv_splitLinkAttribute(data + index, length - index, &keyStart, &keyLength, &valueStart, &valueLength);
00678         if (result == 0) return 0;
00679 
00680         if (keyLength == REG_ATTR_TYPE_KEY_LEN
00681          && 0 == lwm2m_strncmp(REG_ATTR_TYPE_KEY, data + index + keyStart, keyLength))
00682         {
00683             if (isValid == true) return 0; // declared twice
00684             if (valueLength != REG_ATTR_TYPE_VALUE_LEN
00685              || 0 != lwm2m_strncmp(REG_ATTR_TYPE_VALUE, data + index + valueStart, valueLength))
00686             {
00687                 return 0;
00688             }
00689             isValid = true;
00690         }
00691         else if (keyLength == REG_ATTR_CONTENT_KEY_LEN
00692               && 0 == lwm2m_strncmp(REG_ATTR_CONTENT_KEY, data + index + keyStart, keyLength))
00693         {
00694             if (*supportJSON == true) return 0; // declared twice
00695             if (valueLength == REG_ATTR_CONTENT_JSON_LEN
00696              && 0 == lwm2m_strncmp(REG_ATTR_CONTENT_JSON, data + index + valueStart, valueLength))
00697             {
00698                 *supportJSON = true;
00699             }
00700             else
00701             {
00702                 return 0;
00703             }
00704         }
00705         // else ignore this one
00706 
00707         index += result;
00708     }
00709 
00710     if (isValid == false) return 0;
00711 
00712     if (pathLength != 0)
00713     {
00714         *altPath = (char *)lwm2m_malloc(pathLength + 1);
00715         if (*altPath == NULL) return 0;
00716         memcpy(*altPath, data + pathStart, pathLength);
00717         (*altPath)[pathLength] = 0;
00718     }
00719 
00720     return 1;
00721 }
00722 
00723 static int prv_getId(uint8_t * data,
00724                      uint16_t length,
00725                      uint16_t * objId,
00726                      uint16_t * instanceId)
00727 {
00728     int value;
00729     uint16_t limit;
00730 
00731     // Expecting application/link-format (RFC6690)
00732     // leading space were removed before. Remove trailing spaces.
00733     while (length > 0 && data[length-1] == ' ') length--;
00734 
00735     // strip open and close tags
00736     if (length >= 1 && data[0] == REG_URI_START && data[length-1] == REG_URI_END)
00737     {
00738         data += 1;
00739         length -= 2;
00740     } 
00741     else
00742     {
00743         return 0;
00744     }
00745 
00746     // If there is a preceding /, remove it
00747     if (length >= 1 && data[0] == '/')
00748     {
00749         data += 1;
00750         length -= 1;
00751     }
00752 
00753     limit = 0;
00754     while (limit < length && data[limit] != '/') limit++;
00755     value = uri_getNumber(data, limit);
00756     if (value < 0 || value >= LWM2M_MAX_ID) return 0;
00757     *objId = value;
00758 
00759     if (limit < length)
00760     {
00761         limit += 1;
00762         data += limit;
00763         length -= limit;
00764 
00765         if (length > 0)
00766         {
00767             value = uri_getNumber(data, length);
00768             if (value >= 0 && value < LWM2M_MAX_ID)
00769             {
00770                 *instanceId = value;
00771                 return 2;
00772             }
00773             else
00774             {
00775                 return 0;
00776             }
00777         }
00778     }
00779 
00780     return 1;
00781 }
00782 
00783 static lwm2m_client_object_t * prv_decodeRegisterPayload(uint8_t * payload,
00784                                                          uint16_t payloadLength,
00785                                                          bool * supportJSON,
00786                                                          char ** altPath)
00787 {
00788     uint16_t index;
00789     lwm2m_client_object_t * objList;
00790     bool linkAttrFound;
00791 
00792     *altPath = NULL;
00793     *supportJSON = false;
00794     objList = NULL;
00795     linkAttrFound = false;
00796     index = 0;
00797 
00798     while (index <= payloadLength)
00799     {
00800         uint16_t start;
00801         uint16_t length;
00802         int result;
00803         uint16_t id;
00804         uint16_t instance;
00805 
00806         while (index < payloadLength && payload[index] == ' ') index++;
00807         if (index == payloadLength) break;
00808 
00809         start = index;
00810         while (index < payloadLength && payload[index] != REG_DELIMITER) index++;
00811         length = index - start;
00812 
00813         result = prv_getId(payload + start, length, &id, &instance);
00814         if (result != 0)
00815         {
00816             lwm2m_client_object_t * objectP;
00817 
00818             objectP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objList, id);
00819             if (objectP == NULL)
00820             {
00821                 objectP = (lwm2m_client_object_t *)lwm2m_malloc(sizeof(lwm2m_client_object_t));
00822                 memset(objectP, 0, sizeof(lwm2m_client_object_t));
00823                 if (objectP == NULL) goto error;
00824                 objectP->id = id;
00825                 objList = (lwm2m_client_object_t *)LWM2M_LIST_ADD(objList, objectP);
00826             }
00827             if (result == 2)
00828             {
00829                 lwm2m_list_t * instanceP;
00830 
00831                 instanceP = lwm2m_list_find(objectP->instanceList, instance);
00832                 if (instanceP == NULL)
00833                 {
00834                     instanceP = (lwm2m_list_t *)lwm2m_malloc(sizeof(lwm2m_list_t));
00835                     memset(instanceP, 0, sizeof(lwm2m_list_t));
00836                     instanceP->id = instance;
00837                     objectP->instanceList = LWM2M_LIST_ADD(objectP->instanceList, instanceP);
00838                 }
00839             }
00840         }
00841         else if (linkAttrFound == false)
00842         {
00843             result = prv_parseLinkAttributes(payload + start, length, supportJSON, altPath);
00844             if (result == 0) goto error;
00845 
00846             linkAttrFound = true;
00847         }
00848         else goto error;
00849 
00850         index++;
00851     }
00852 
00853     return objList;
00854 
00855 error:
00856     if (*altPath != NULL)
00857     {
00858         lwm2m_free(*altPath);
00859         *altPath = NULL;
00860     }
00861     prv_freeClientObjectList(objList);
00862 
00863     return NULL;
00864 }
00865 
00866 static lwm2m_client_t * prv_getClientByName(lwm2m_context_t * contextP,
00867                                             char * name)
00868 {
00869     lwm2m_client_t * targetP;
00870 
00871     targetP = contextP->clientList;
00872     while (targetP != NULL && strcmp(name, targetP->name) != 0)
00873     {
00874         targetP = targetP->next;
00875     }
00876 
00877     return targetP;
00878 }
00879 
00880 void registration_freeClient(lwm2m_client_t * clientP)
00881 {
00882     LOG("Entering");
00883     if (clientP->name != NULL) lwm2m_free(clientP->name);
00884     if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn);
00885     if (clientP->altPath != NULL) lwm2m_free(clientP->altPath);
00886     prv_freeClientObjectList(clientP->objectList);
00887     while(clientP->observationList != NULL)
00888     {
00889         lwm2m_observation_t * targetP;
00890 
00891         targetP = clientP->observationList;
00892         clientP->observationList = clientP->observationList->next;
00893         lwm2m_free(targetP);
00894     }
00895     lwm2m_free(clientP);
00896 }
00897 
00898 static int prv_getLocationString(uint16_t id,
00899                                  char location[MAX_LOCATION_LENGTH])
00900 {
00901     int index;
00902     int result;
00903 
00904     memset(location, 0, MAX_LOCATION_LENGTH);
00905 
00906     result = utils_stringCopy(location, MAX_LOCATION_LENGTH, "/"URI_REGISTRATION_SEGMENT"/");
00907     if (result < 0) return 0;
00908     index = result;
00909 
00910     result = utils_intCopy(location + index, MAX_LOCATION_LENGTH - index, id);
00911     if (result < 0) return 0;
00912 
00913     return index + result;
00914 }
00915 
00916 coap_status_t registration_handleRequest(lwm2m_context_t * contextP,
00917                                           lwm2m_uri_t * uriP,
00918                                           void * fromSessionH,
00919                                           coap_packet_t * message,
00920                                           coap_packet_t * response)
00921 {
00922     coap_status_t result;
00923     time_t tv_sec;
00924 
00925     LOG_URI(uriP);
00926     tv_sec = lwm2m_gettime();
00927     if (tv_sec < 0) return COAP_500_INTERNAL_SERVER_ERROR;
00928 
00929     switch(message->code)
00930     {
00931     case COAP_POST:
00932     {
00933         char * name = NULL;
00934         uint32_t lifetime;
00935         char * msisdn;
00936         char * altPath;
00937         char * version;
00938         lwm2m_binding_t binding;
00939         lwm2m_client_object_t * objects;
00940         bool supportJSON;
00941         lwm2m_client_t * clientP;
00942         char location[MAX_LOCATION_LENGTH];
00943 
00944         if (0 != prv_getParameters(message->uri_query, &name, &lifetime, &msisdn, &binding, &version))
00945         {
00946             return COAP_400_BAD_REQUEST;
00947         }
00948         if (message->content_type != LWM2M_CONTENT_LINK
00949          && message->content_type != LWM2M_CONTENT_TEXT)
00950         {
00951             return COAP_400_BAD_REQUEST;
00952         }
00953 
00954         objects = prv_decodeRegisterPayload(message->payload, message->payload_len, &supportJSON, &altPath);
00955 
00956         switch (uriP->flag & LWM2M_URI_MASK_ID)
00957         {
00958         case 0:
00959             // Register operation
00960             // Version is mandatory
00961             if (version == NULL)
00962             {
00963                 if (name != NULL) lwm2m_free(name);
00964                 if (msisdn != NULL) lwm2m_free(msisdn);
00965                 return COAP_400_BAD_REQUEST;
00966             }
00967             // Endpoint client name is mandatory
00968             if (name == NULL)
00969             {
00970                 lwm2m_free(version);
00971                 if (msisdn != NULL) lwm2m_free(msisdn);
00972                 return COAP_400_BAD_REQUEST;
00973             }
00974             // Object list is mandatory
00975             if (objects == NULL)
00976             {
00977                 lwm2m_free(version);
00978                 lwm2m_free(name);
00979                 if (msisdn != NULL) lwm2m_free(msisdn);
00980                 return COAP_400_BAD_REQUEST;
00981             }
00982             // version must be 1.0
00983             if (strlen(version) != LWM2M_VERSION_LEN
00984                 || lwm2m_strncmp(version, LWM2M_VERSION, LWM2M_VERSION_LEN))
00985             {
00986                 lwm2m_free(version);
00987                 lwm2m_free(name);
00988                 if (msisdn != NULL) lwm2m_free(msisdn);
00989                 return COAP_412_PRECONDITION_FAILED;
00990             }
00991 
00992             if (lifetime == 0)
00993             {
00994                 lifetime = LWM2M_DEFAULT_LIFETIME;
00995             }
00996 
00997             clientP = prv_getClientByName(contextP, name);
00998             if (clientP != NULL)
00999             {
01000                 // we reset this registration
01001                 lwm2m_free(clientP->name);
01002                 if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn);
01003                 if (clientP->altPath != NULL) lwm2m_free(clientP->altPath);
01004                 prv_freeClientObjectList(clientP->objectList);
01005                 clientP->objectList = NULL;
01006             }
01007             else
01008             {
01009                 clientP = (lwm2m_client_t *)lwm2m_malloc(sizeof(lwm2m_client_t));
01010                 if (clientP == NULL)
01011                 {
01012                     lwm2m_free(name);
01013                     lwm2m_free(altPath);
01014                     if (msisdn != NULL) lwm2m_free(msisdn);
01015                     prv_freeClientObjectList(objects);
01016                     return COAP_500_INTERNAL_SERVER_ERROR;
01017                 }
01018                 memset(clientP, 0, sizeof(lwm2m_client_t));
01019                 clientP->internalID = lwm2m_list_newId((lwm2m_list_t *)contextP->clientList);
01020                 contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_ADD(contextP->clientList, clientP);
01021             }
01022             clientP->name = name;
01023             clientP->binding = binding;
01024             clientP->msisdn = msisdn;
01025             clientP->altPath = altPath;
01026             clientP->supportJSON = supportJSON;
01027             clientP->lifetime = lifetime;
01028             clientP->endOfLife = tv_sec + lifetime;
01029             clientP->objectList = objects;
01030             clientP->sessionH = fromSessionH;
01031 
01032             if (prv_getLocationString(clientP->internalID, location) == 0)
01033             {
01034                 registration_freeClient(clientP);
01035                 return COAP_500_INTERNAL_SERVER_ERROR;
01036             }
01037             if (coap_set_header_location_path(response, location) == 0)
01038             {
01039                 registration_freeClient(clientP);
01040                 return COAP_500_INTERNAL_SERVER_ERROR;
01041             }
01042 
01043             if (contextP->monitorCallback != NULL)
01044             {
01045                 contextP->monitorCallback(clientP->internalID, NULL, COAP_201_CREATED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData);
01046             }
01047             result = COAP_201_CREATED;
01048             break;
01049 
01050         case LWM2M_URI_FLAG_OBJECT_ID:
01051             clientP = (lwm2m_client_t *)lwm2m_list_find((lwm2m_list_t *)contextP->clientList, uriP->objectId);
01052             if (clientP == NULL) return COAP_404_NOT_FOUND;
01053 
01054             // Endpoint client name MUST NOT be present
01055             if (name != NULL)
01056             {
01057                 lwm2m_free(name);
01058                 if (msisdn != NULL) lwm2m_free(msisdn);
01059                 return COAP_400_BAD_REQUEST;
01060             }
01061 
01062             if (binding != BINDING_UNKNOWN)
01063             {
01064                 clientP->binding = binding;
01065             }
01066             if (msisdn != NULL)
01067             {
01068                 if (clientP->msisdn != NULL) lwm2m_free(clientP->msisdn);
01069                 clientP->msisdn = msisdn;
01070             }
01071             if (lifetime != 0)
01072             {
01073                 clientP->lifetime = lifetime;
01074             }
01075             // client IP address, port or MSISDN may have changed
01076             clientP->sessionH = fromSessionH;
01077 
01078             if (objects != NULL)
01079             {
01080                 lwm2m_observation_t * observationP;
01081 
01082                 // remove observations on object/instance no longer existing
01083                 observationP = clientP->observationList;
01084                 while (observationP != NULL)
01085                 {
01086                     lwm2m_client_object_t * objP;
01087                     lwm2m_observation_t * nextP;
01088 
01089                     nextP = observationP->next;
01090 
01091                     objP = (lwm2m_client_object_t *)lwm2m_list_find((lwm2m_list_t *)objects, observationP->uri.objectId);
01092                     if (objP == NULL)
01093                     {
01094                         observationP->callback(clientP->internalID,
01095                                                &observationP->uri,
01096                                                COAP_202_DELETED,
01097                                                LWM2M_CONTENT_TEXT, NULL, 0,
01098                                                observationP->userData);
01099                         observe_remove(observationP);
01100                     }
01101                     else
01102                     {
01103                         if ((observationP->uri.flag & LWM2M_URI_FLAG_INSTANCE_ID) != 0)
01104                         {
01105                             if (lwm2m_list_find((lwm2m_list_t *)objP->instanceList, observationP->uri.instanceId) == NULL)
01106                             {
01107                                 observationP->callback(clientP->internalID,
01108                                                        &observationP->uri,
01109                                                        COAP_202_DELETED,
01110                                                        LWM2M_CONTENT_TEXT, NULL, 0,
01111                                                        observationP->userData);
01112                                 observe_remove(observationP);
01113                             }
01114                         }
01115                     }
01116 
01117                     observationP = nextP;
01118                 }
01119 
01120                 prv_freeClientObjectList(clientP->objectList);
01121                 clientP->objectList = objects;
01122             }
01123 
01124             clientP->endOfLife = tv_sec + clientP->lifetime;
01125 
01126             if (contextP->monitorCallback != NULL)
01127             {
01128                 contextP->monitorCallback(clientP->internalID, NULL, COAP_204_CHANGED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData);
01129             }
01130             result = COAP_204_CHANGED;
01131             break;
01132 
01133             default:
01134                 return COAP_400_BAD_REQUEST;
01135         }
01136     }
01137     break;
01138 
01139     case COAP_DELETE:
01140     {
01141         lwm2m_client_t * clientP;
01142 
01143         if ((uriP->flag & LWM2M_URI_MASK_ID) != LWM2M_URI_FLAG_OBJECT_ID) return COAP_400_BAD_REQUEST;
01144 
01145         contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, uriP->objectId, &clientP);
01146         if (clientP == NULL) return COAP_400_BAD_REQUEST;
01147         if (contextP->monitorCallback != NULL)
01148         {
01149             contextP->monitorCallback(clientP->internalID, NULL, COAP_202_DELETED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData);
01150         }
01151         registration_freeClient(clientP);
01152         result = COAP_202_DELETED;
01153     }
01154     break;
01155 
01156     default:
01157         return COAP_400_BAD_REQUEST;
01158     }
01159 
01160     return result;
01161 }
01162 
01163 void lwm2m_set_monitoring_callback(lwm2m_context_t * contextP,
01164                                    lwm2m_result_callback_t callback,
01165                                    void * userData)
01166 {
01167     LOG("Entering");
01168     contextP->monitorCallback = callback;
01169     contextP->monitorUserData = userData;
01170 }
01171 #endif
01172 
01173 // for each server update the registration if needed
01174 // for each client check if the registration expired
01175 void registration_step(lwm2m_context_t * contextP,
01176                        time_t currentTime,
01177                        time_t * timeoutP)
01178 {
01179 #ifdef LWM2M_CLIENT_MODE
01180     lwm2m_server_t * targetP = contextP->serverList;
01181 
01182     LOG_ARG("State: %s", STR_STATE(contextP->state));
01183 
01184     targetP = contextP->serverList;
01185     while (targetP != NULL)
01186     {
01187         switch (targetP->status)
01188         {
01189         case STATE_REGISTERED:
01190         {
01191             time_t nextUpdate;
01192             time_t interval;
01193 
01194             nextUpdate = targetP->lifetime;
01195             if (COAP_MAX_TRANSMIT_WAIT < nextUpdate)
01196             {
01197                 nextUpdate -= COAP_MAX_TRANSMIT_WAIT;
01198             }
01199             else
01200             {
01201                 nextUpdate = nextUpdate >> 1;
01202             }
01203 
01204             interval = targetP->registration + nextUpdate - currentTime;
01205             if (0 >= interval)
01206             {
01207                 LOG("Updating registration");
01208                 prv_updateRegistration(contextP, targetP, false);
01209             }
01210             else if (interval < *timeoutP)
01211             {
01212                 *timeoutP = interval;
01213             }
01214         }
01215         break;
01216 
01217         case STATE_REG_UPDATE_NEEDED:
01218             prv_updateRegistration(contextP, targetP, false);
01219             break;
01220 
01221         case STATE_REG_FULL_UPDATE_NEEDED:
01222             prv_updateRegistration(contextP, targetP, true);
01223             break;
01224 
01225         case STATE_REG_FAILED:
01226             if (targetP->sessionH != NULL)
01227             {
01228                 lwm2m_close_connection(targetP->sessionH, contextP->userData);
01229                 targetP->sessionH = NULL;
01230             }
01231             break;
01232 
01233         default:
01234             break;
01235         }
01236         targetP = targetP->next;
01237     }
01238 
01239 #endif
01240 #ifdef LWM2M_SERVER_MODE
01241     lwm2m_client_t * clientP;
01242 
01243     LOG("Entering");
01244     // monitor clients lifetime
01245     clientP = contextP->clientList;
01246     while (clientP != NULL)
01247     {
01248         lwm2m_client_t * nextP = clientP->next;
01249 
01250         if (clientP->endOfLife <= currentTime)
01251         {
01252             contextP->clientList = (lwm2m_client_t *)LWM2M_LIST_RM(contextP->clientList, clientP->internalID, NULL);
01253             if (contextP->monitorCallback != NULL)
01254             {
01255                 contextP->monitorCallback(clientP->internalID, NULL, COAP_202_DELETED, LWM2M_CONTENT_TEXT, NULL, 0, contextP->monitorUserData);
01256             }
01257             registration_freeClient(clientP);
01258         }
01259         else
01260         {
01261             time_t interval;
01262 
01263             interval = clientP->endOfLife - currentTime;
01264 
01265             if (*timeoutP > interval)
01266             {
01267                 *timeoutP = interval;
01268             }
01269         }
01270         clientP = nextP;
01271     }
01272 #endif
01273 
01274 }
01275