Blame src/user_types/user_yang_types.c

Packit Service 311553
/**
Packit Service 311553
 * @file user_yang_types.c
Packit Service 311553
 * @author Michal Vasko <mvasko@cesnet.cz>
Packit Service 311553
 * @brief ietf-yang-types typedef validation and conversion to canonical format
Packit Service 311553
 *
Packit Service 311553
 * Copyright (c) 2018 CESNET, z.s.p.o.
Packit Service 311553
 *
Packit Service 311553
 * This source code is licensed under BSD 3-Clause License (the "License").
Packit Service 311553
 * You may not use this file except in compliance with the License.
Packit Service 311553
 * You may obtain a copy of the License at
Packit Service 311553
 *
Packit Service 311553
 *     https://opensource.org/licenses/BSD-3-Clause
Packit Service 311553
 */
Packit Service 311553
#define _GNU_SOURCE
Packit Service 311553
Packit Service 311553
#include <stdlib.h>
Packit Service 311553
#include <string.h>
Packit Service 311553
#include <stdint.h>
Packit Service 311553
#include <errno.h>
Packit Service 311553
#include <time.h>
Packit Service 311553
#include <ctype.h>
Packit Service 311553
Packit Service 311553
#include "compat.h"
Packit Service 311553
#include "../user_types.h"
Packit Service 311553
Packit Service 311553
/**
Packit Service 311553
 * @brief Storage for ID used to check plugin API version compatibility.
Packit Service 311553
 */
Packit Service 311553
LYTYPE_VERSION_CHECK
Packit Service 311553
Packit Service 311553
#ifdef __GNUC__
Packit Service 311553
#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
Packit Service 311553
#else
Packit Service 311553
#  define UNUSED(x) UNUSED_ ## x
Packit Service 311553
#endif
Packit Service 311553
Packit Service 311553
static int
Packit Service 311553
date_and_time_store_clb(struct ly_ctx *UNUSED(ctx), const char *UNUSED(type_name), const char **value_str,
Packit Service 311553
                        lyd_val *UNUSED(value), char **err_msg)
Packit Service 311553
{
Packit Service 311553
    struct tm tm, tm2;
Packit Service 311553
    uint32_t i, j;
Packit Service 311553
    const char *val_str = *value_str;
Packit Service 311553
    int ret;
Packit Service 311553
Packit Service 311553
    /* \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[\+\-]\d{2}:\d{2})
Packit Service 311553
     * 2018-03-21T09:11:05(.55785...)(Z|+02:00) */
Packit Service 311553
    memset(&tm, 0, sizeof tm);
Packit Service 311553
    i = 0;
Packit Service 311553
Packit Service 311553
    /* year */
Packit Service 311553
    tm.tm_year = atoi(val_str + i);
Packit Service 311553
    /* if there was some invalid number, it will either be discovered in the loop below or by mktime() */
Packit Service 311553
    tm.tm_year -= 1900;
Packit Service 311553
    for (j = i + 4; i < j; ++i) {
Packit Service 311553
        if (!isdigit(val_str[i])) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
    }
Packit Service 311553
    if (val_str[i] != '-') {
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '-' expected.", val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
    ++i;
Packit Service 311553
Packit Service 311553
    /* month */
Packit Service 311553
    tm.tm_mon = atoi(val_str + i);
Packit Service 311553
    tm.tm_mon -= 1;
Packit Service 311553
    for (j = i + 2; i < j; ++i) {
Packit Service 311553
        if (!isdigit(val_str[i])) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
    }
Packit Service 311553
    if (val_str[i] != '-') {
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '-' expected.", val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
    ++i;
Packit Service 311553
Packit Service 311553
    /* day */
Packit Service 311553
    tm.tm_mday = atoi(val_str + i);
Packit Service 311553
    for (j = i + 2; i < j; ++i) {
Packit Service 311553
        if (!isdigit(val_str[i])) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
    }
Packit Service 311553
    if (val_str[i] != 'T') {
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", 'T' expected.", val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
    ++i;
Packit Service 311553
Packit Service 311553
    /* hours */
Packit Service 311553
    tm.tm_hour = atoi(val_str + i);
Packit Service 311553
    for (j = i + 2; i < j; ++i) {
Packit Service 311553
        if (!isdigit(val_str[i])) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
    }
Packit Service 311553
    if (val_str[i] != ':') {
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", ':' expected.", val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
    ++i;
Packit Service 311553
Packit Service 311553
    /* minutes */
Packit Service 311553
    tm.tm_min = atoi(val_str + i);
Packit Service 311553
    for (j = i + 2; i < j; ++i) {
Packit Service 311553
        if (!isdigit(val_str[i])) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
    }
Packit Service 311553
    if (val_str[i] != ':') {
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", ':' expected.", val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
    ++i;
Packit Service 311553
Packit Service 311553
    /* seconds */
Packit Service 311553
    tm.tm_sec = atoi(val_str + i);
Packit Service 311553
    for (j = i + 2; i < j; ++i) {
Packit Service 311553
        if (!isdigit(val_str[i])) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
    }
Packit Service 311553
    if ((val_str[i] != '.') && (val_str[i] != 'Z') && (val_str[i] != '+') && (val_str[i] != '-')) {
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", '.', 'Z', '+', or '-' expected.",
Packit Service 311553
                       val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
Packit Service 311553
    /* validate using mktime() */
Packit Service 311553
    tm2 = tm;
Packit Service 311553
    errno = 0;
Packit Service 311553
    mktime(&tm;;
Packit Service 311553
    /* ENOENT is set when "/etc/localtime" is missing but the function suceeeds */
Packit Service 311553
    if (errno && (errno != ENOENT)) {
Packit Service 311553
        ret = asprintf(err_msg, "Checking date-and-time value \"%s\" failed (%s).", val_str, strerror(errno));
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
    /* we now have correctly filled the remaining values, use them */
Packit Service 311553
    memcpy(((char *)&tm2) + (6 * sizeof(int)), ((char *)&tm) + (6 * sizeof(int)), sizeof(struct tm) - (6 * sizeof(int)));
Packit Service 311553
    /* back it up again */
Packit Service 311553
    tm = tm2;
Packit Service 311553
    /* let mktime() correct date & time with having the other values correct now */
Packit Service 311553
    errno = 0;
Packit Service 311553
    mktime(&tm;;
Packit Service 311553
    if (errno && (errno != ENOENT)) {
Packit Service 311553
        ret = asprintf(err_msg, "Checking date-and-time value \"%s\" failed (%s).", val_str, strerror(errno));
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
    /* detect changes in the filled values */
Packit Service 311553
    if (memcmp(&tm, &tm2, 6 * sizeof(int))) {
Packit Service 311553
        ret = asprintf(err_msg, "Checking date-and-time value \"%s\" failed, canonical date and time is \"%04d-%02d-%02dT%02d:%02d:%02d\".",
Packit Service 311553
                       val_str, tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
Packit Service 311553
    /* tenth of a second */
Packit Service 311553
    if (val_str[i] == '.') {
Packit Service 311553
        ++i;
Packit Service 311553
        if (!isdigit(val_str[i])) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", a digit expected.", val_str[i], i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
        do {
Packit Service 311553
            ++i;
Packit Service 311553
        } while (isdigit(val_str[i]));
Packit Service 311553
    }
Packit Service 311553
Packit Service 311553
    switch (val_str[i]) {
Packit Service 311553
    case 'Z':
Packit Service 311553
        /* done */
Packit Service 311553
        break;
Packit Service 311553
    case '+':
Packit Service 311553
    case '-':
Packit Service 311553
        /* timezone shift */
Packit Service 311553
        if ((val_str[i + 1] < '0') || (val_str[i + 1] > '2')) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".", val_str + i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
        if ((val_str[i + 2] < '0') || ((val_str[i + 1] == '2') && (val_str[i + 2] > '3')) || (val_str[i + 2] > '9')) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".", val_str + i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
Packit Service 311553
        if (val_str[i + 3] != ':') {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".", val_str + i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
Packit Service 311553
        if ((val_str[i + 4] < '0') || (val_str[i + 4] > '5')) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".", val_str + i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
        if ((val_str[i + 5] < '0') || (val_str[i + 5] > '9')) {
Packit Service 311553
            ret = asprintf(err_msg, "Invalid timezone \"%.6s\" in date-and-time value \"%s\".", val_str + i, val_str);
Packit Service 311553
            goto error;
Packit Service 311553
        }
Packit Service 311553
Packit Service 311553
        i += 5;
Packit Service 311553
        break;
Packit Service 311553
    default:
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", 'Z', '+', or '-' expected.", val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
Packit Service 311553
    /* no other characters expected */
Packit Service 311553
    ++i;
Packit Service 311553
    if (val_str[i]) {
Packit Service 311553
        ret = asprintf(err_msg, "Invalid character '%c'[%d] in date-and-time value \"%s\", no characters expected.", val_str[i], i, val_str);
Packit Service 311553
        goto error;
Packit Service 311553
    }
Packit Service 311553
Packit Service 311553
    /* validation succeeded and we do not want to change how it is stored */
Packit Service 311553
    return 0;
Packit Service 311553
Packit Service 311553
error:
Packit Service 311553
    if (ret == -1) {
Packit Service 311553
        *err_msg = NULL;
Packit Service 311553
    }
Packit Service 311553
    return 1;
Packit Service 311553
}
Packit Service 311553
Packit Service 311553
static int
Packit Service 311553
hex_string_store_clb(struct ly_ctx *ctx, const char *UNUSED(type_name), const char **value_str, lyd_val *value, char **err_msg)
Packit Service 311553
{
Packit Service 311553
    char *str;
Packit Service 311553
    uint32_t i, len;
Packit Service 311553
Packit Service 311553
    str = strdup(*value_str);
Packit Service 311553
    if (!str) {
Packit Service 311553
        /* we can hardly allocate an error message */
Packit Service 311553
        *err_msg = NULL;
Packit Service 311553
        return 1;
Packit Service 311553
    }
Packit Service 311553
Packit Service 311553
    len = strlen(str);
Packit Service 311553
    for (i = 0; i < len; ++i) {
Packit Service 311553
        if ((str[i] >= 'A') && (str[i] <= 'Z')) {
Packit Service 311553
            /* make it lowercase (canonical format) */
Packit Service 311553
            str[i] += 32;
Packit Service 311553
        }
Packit Service 311553
    }
Packit Service 311553
Packit Service 311553
    /* update the value correctly */
Packit Service 311553
    lydict_remove(ctx, *value_str);
Packit Service 311553
    *value_str = lydict_insert_zc(ctx, str);
Packit Service 311553
    value->string = *value_str;
Packit Service 311553
    return 0;
Packit Service 311553
}
Packit Service 311553
Packit Service 311553
/* Name of this array must match the file name! */
Packit Service 311553
struct lytype_plugin_list user_yang_types[] = {
Packit Service 311553
    {"ietf-yang-types", "2013-07-15", "date-and-time", date_and_time_store_clb, NULL},
Packit Service 311553
    {"ietf-yang-types", "2013-07-15", "phys-address", hex_string_store_clb, NULL},
Packit Service 311553
    {"ietf-yang-types", "2013-07-15", "mac-address", hex_string_store_clb, NULL},
Packit Service 311553
    {"ietf-yang-types", "2013-07-15", "hex-string", hex_string_store_clb, NULL},
Packit Service 311553
    {"ietf-yang-types", "2013-07-15", "uuid", hex_string_store_clb, NULL},
Packit Service 311553
    {NULL, NULL, NULL, NULL, NULL} /* terminating item */
Packit Service 311553
};