/*======================================================================
 FILE: icaltypes.c
 CREATOR: eric 16 May 1999

 SPDX-FileCopyrightText: 2000, Eric Busboom <eric@civicknowledge.com>
 SPDX-License-Identifier: LGPL-2.1-only OR MPL-2.0
 ======================================================================*/

/**
 * @file icaltypes.c
 * @brief Implements the functions to manipulate internal types
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "icaltypes.h"
#include "icalerror_p.h"
#include "icalmemory.h"

/// @cond PRIVATE
#define TMP_BUF_SIZE 1024
/// @endcond

#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
#include <pthread.h>
static pthread_mutex_t unk_token_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
#include <ctype.h>
static ICAL_GLOBAL_VAR ical_unknown_token_handling unknownTokenHandling = ICAL_TREAT_AS_ERROR;

bool icaltriggertype_is_null_trigger(struct icaltriggertype tr)
{
    if (icaltime_is_null_time(tr.time) && icaldurationtype_is_null_duration(tr.duration)) {
        return true;
    }

    return false;
}

bool icaltriggertype_is_bad_trigger(struct icaltriggertype tr)
{
    if (icaldurationtype_is_bad_duration(tr.duration)) {
        return true;
    }

    return false;
}

struct icaltriggertype icaltriggertype_from_seconds(const int reltime)
{
    struct icaltriggertype tr;

    tr.time = icaltime_null_time();
    tr.duration = icaldurationtype_from_seconds(reltime);

    return tr;
}

struct icaltriggertype icaltriggertype_from_string(const char *str)
{
    struct icaltriggertype tr;
    icalerrorstate es;
    icalerrorenum e;

    tr.time = icaltime_null_time();
    tr.duration = icaldurationtype_from_seconds(0);

    /* Suppress errors so a failure in icaltime_from_string() does not cause an abort */
    es = icalerror_get_error_state(ICAL_MALFORMEDDATA_ERROR);
    if (str == 0) {
        goto error;
    }
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, ICAL_ERROR_NONFATAL);
    e = icalerrno;
    icalerror_set_errno(ICAL_NO_ERROR);

    tr.time = icaltime_from_string(str);

    if (icaltime_is_null_time(tr.time)) {
        tr.duration = icaldurationtype_from_string(str);

        if (icaldurationtype_is_bad_duration(tr.duration)) {
            goto error;
        }
    }

    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, es);
    icalerror_set_errno(e);
    return tr;

error:
    icalerror_set_error_state(ICAL_MALFORMEDDATA_ERROR, es);
    icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
    return tr;
}

struct icalreqstattype icalreqstattype_from_string(const char *str)
{
    const char *s, *p1, *p2;
    struct icalreqstattype stat;
    short major = 0, minor = 0;

    icalerror_check_arg((str != 0), "str");

    stat.code = ICAL_UNKNOWN_STATUS;
    stat.debug = 0;
    stat.desc = 0;

    // Don't allow (fuzzer) garbage chars anywhere in the reqstat string
    s = str;
    /* cppcheck-suppress nullPointerRedundantCheck */
    while (*s && isprint((unsigned char)*s)) {
        ++s;
    }
    if (*s != '\0') {
        // garbage encountered. return the empty stat
        return stat;
    }

    /* Get the status numbers */

    sscanf(str, "%hd.%hd", &major, &minor);

    if (major <= 0 || minor < 0) {
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
        return stat;
    }

    stat.code = icalenum_num_to_reqstat(major, minor);

    if (stat.code == ICAL_UNKNOWN_STATUS) {
        icalerror_set_errno(ICAL_MALFORMEDDATA_ERROR);
        return stat;
    }

    p1 = strchr(str, ';');

    if (p1 == 0) {
        /*    icalerror_set_errno(ICAL_BADARG_ERROR);*/
        return stat;
    }

    /* Just ignore the second clause; it will be taken from inside the library
     */

    p2 = strchr(p1 + 1, ';');
    if (p2 != 0 && *(p2 + 1) != 0) { // skipping empty debug strings
        stat.debug = icalmemory_tmp_copy(p2 + 1);
    }

    return stat;
}

const char *icalreqstattype_as_string(struct icalreqstattype stat)
{
    char *buf;

    buf = icalreqstattype_as_string_r(stat);
    icalmemory_add_tmp_buffer(buf);
    return buf;
}

char *icalreqstattype_as_string_r(struct icalreqstattype stat)
{
    char *temp;

    icalerror_check_arg_rz((stat.code != ICAL_UNKNOWN_STATUS), "Status");

    temp = (char *)icalmemory_new_buffer(TMP_BUF_SIZE);

    if (stat.desc == 0) {
        stat.desc = icalenum_reqstat_desc(stat.code);
    }

    if (stat.debug != 0) {
        snprintf(temp, TMP_BUF_SIZE, "%d.%d;%s;%s", icalenum_reqstat_major(stat.code),
                 icalenum_reqstat_minor(stat.code), stat.desc, stat.debug);
    } else {
        snprintf(temp, TMP_BUF_SIZE, "%d.%d;%s", icalenum_reqstat_major(stat.code),
                 icalenum_reqstat_minor(stat.code), stat.desc);
    }

    return temp;
}

ical_unknown_token_handling ical_get_unknown_token_handling_setting(void)
{
    ical_unknown_token_handling myHandling;

#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
    if (pthread_mutex_lock(&unk_token_mutex) != 0) {
        icalerror_set_errno(ICAL_THREADING_ERROR);
    }
#endif

    myHandling = unknownTokenHandling;

#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
    if (pthread_mutex_unlock(&unk_token_mutex) != 0) {
        icalerror_set_errno(ICAL_THREADING_ERROR);
    }
#endif

    return myHandling;
}

void ical_set_unknown_token_handling_setting(ical_unknown_token_handling newSetting)
{
#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
    if (pthread_mutex_lock(&unk_token_mutex) != 0) {
        icalerror_set_errno(ICAL_THREADING_ERROR);
    }
#endif

    unknownTokenHandling = newSetting;

#if ICAL_SYNC_MODE == ICAL_SYNC_MODE_PTHREAD
    if (pthread_mutex_unlock(&unk_token_mutex) != 0) {
        icalerror_set_errno(ICAL_THREADING_ERROR);
    }
#endif
}
