/*
 * parse_store_main.c
 *
 * Copyright (c) 2018-2022 Eric Vidal <eric@obarun.org>
 *
 * All rights reserved.
 *
 * This file is part of Obarun. It is subject to the license terms in
 * the LICENSE file found in the top-level directory of this
 * distribution.
 * This file may not be copied, modified, propagated, or distributed
 * except according to the terms contained in the LICENSE file./
 */

#include <string.h>
#include <stdint.h>
#include <stdlib.h> //free
#include <pwd.h>

#include <oblibs/string.h>
#include <oblibs/sastr.h>
#include <oblibs/log.h>
#include <oblibs/directory.h>
#include <oblibs/types.h>

#include <skalibs/stralloc.h>
#include <skalibs/types.h>
#include <skalibs/sig.h>

#include <66/parse.h>
#include <66/resolve.h>
#include <66/constants.h>
#include <66/service.h>
#include <66/enum.h>
#include <66/utils.h>

int parse_store_main(resolve_service_t *res, char *store, int idsec, int idkey)
{
    int r = 0, e = 0 ;
    size_t pos = 0 ;
    stralloc sa = STRALLOC_ZERO ;
    resolve_wrapper_t_ref wres = resolve_set_struct(DATA_SERVICE, res) ;

    switch(idkey) {

        case KEY_MAIN_DESCRIPTION:

            if (!parse_clean_quotes(store))
                parse_error_return(0, 8, idsec, idkey) ;

            res->description = resolve_add_string(wres, store) ;

            break ;

        case KEY_MAIN_VERSION:

            if (!parse_clean_line(store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (!auto_stra(&sa, store))
                goto err ;

            r = version_scan(&sa, store, SS_CONFIG_VERSION_NDOT) ;
            if (r == -1)
                goto err ;

            if (!r)
                parse_error_return(0, 0, idsec, idkey) ;

            res->version = resolve_add_string(wres, sa.s) ;

            break ;

        case KEY_MAIN_TYPE:

            if (res->name)
                /** already passed through here */
                break ;

            if (!parse_clean_line(store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (!strcmp(store, "longrun")) {
                log_1_warn("deprecated type longrun -- convert it automatically to classic type") ;
                res->type = 0 ;
                break ;
            }

            r = get_enum_by_key(store) ;
            if (r == -1)
                parse_error_return(0, 0, idsec, idkey) ;

            res->type = (uint32_t)r ;

            break ;

        case KEY_MAIN_NOTIFY:

            parse_error_type(res->type,ENUM_KEY_SECTION_MAIN, idkey) ;

            if (!uint320_scan(store, &res->notify))
                parse_error_return(0, 3, idsec, idkey) ;

            if (res->notify < 3)
                parse_error_return(0, 0, idsec, idkey) ;

            break ;

        case KEY_MAIN_DEATH:


            if (!uint320_scan(store, &res->maxdeath))
                parse_error_return(0, 3, idsec, idkey) ;

            if (res->maxdeath > 4096)
                parse_error_return(0, 0, idsec, idkey) ;

            break ;

        case KEY_MAIN_FLAGS:

            parse_error_type(res->type,ENUM_KEY_SECTION_MAIN, idkey) ;

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            {
                pos = 0 ;
                FOREACH_SASTR(&sa, pos) {

                    r = get_enum_by_key_one(sa.s + pos, ENUM_FLAGS) ;

                    if (r == -1)
                        parse_error_return(0, 0, idsec, idkey) ;

                    if (r == FLAGS_DOWN)
                        res->execute.down = 1 ;/**0 means not enabled*/

                    if (r == FLAGS_EARLIER)
                        res->earlier = 1 ;/**0 means not enabled*/
                }
            }

            break ;

        case KEY_MAIN_SIGNAL:

            parse_error_type(res->type,ENUM_KEY_SECTION_MAIN, idkey) ;

            if (!parse_clean_line(store))
                parse_error_return(0, 8, idsec, idkey) ;

            int t = 0 ;
            if (!sig0_scan(store, &t))
                parse_error_return(0, 3, idsec, idkey) ;

            res->execute.downsignal = (uint32_t)t ;

            break ;

        case KEY_MAIN_T_KILL:

            parse_error_type(res->type,ENUM_KEY_SECTION_MAIN, idkey) ;

            if (!uint320_scan(store, &res->execute.timeout.kill))
                parse_error_return(0, 3, idsec, idkey) ;

            break ;

        case KEY_MAIN_T_FINISH:

            parse_error_type(res->type,ENUM_KEY_SECTION_MAIN, idkey) ;

            if (!uint320_scan(store, &res->execute.timeout.finish))
                parse_error_return(0, 3, idsec, idkey) ;

            break ;

        case KEY_MAIN_T_UP:

            parse_error_type(res->type,ENUM_KEY_SECTION_MAIN, idkey) ;

            if (!uint320_scan(store, &res->execute.timeout.up))
                parse_error_return(0, 3, idsec, idkey) ;

            break ;

        case KEY_MAIN_T_DOWN:

            parse_error_type(res->type,ENUM_KEY_SECTION_MAIN, idkey) ;

            if (!uint320_scan(store, &res->execute.timeout.down))
                parse_error_return(0, 3, idsec, idkey) ;

            break ;

        case KEY_MAIN_HIERCOPY:

            if (res->type == TYPE_BUNDLE)
                log_warn_return(LOG_EXIT_ONE,"key: ", get_key_by_enum(ENUM_KEY_SECTION_MAIN, idkey), ": is not valid for type ", get_key_by_enum(ENUM_TYPE, res->type), " -- ignoring it") ;

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (sa.len) {

                size_t len = sa.len ;

                char t[len + 1] ;

                sastr_to_char(t, &sa) ;

                sa.len = 0 ;
                pos = 0 ;

                for (; pos < len ; pos += strlen(t + pos) + 1) {
                    if (!auto_stra(&sa, t + pos, " "))
                        goto err ;
                }

                sa.len-- ;
                if (!stralloc_0(&sa))
                    goto err ;

                res->hiercopy = resolve_add_string(wres, sa.s) ;
            }

            break ;

        case KEY_MAIN_OPTIONS:

            if (res->type == TYPE_BUNDLE)
                log_warn_return(LOG_EXIT_ONE,"key: ", get_key_by_enum(ENUM_KEY_SECTION_MAIN, idkey), ": is not valid for type ", get_key_by_enum(ENUM_TYPE, res->type), " -- ignoring it") ;

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (sa.len) {

                pos = 0 ;
                FOREACH_SASTR(&sa, pos) {

                    uint8_t reverse = sa.s[pos] == '!' ? 1 : 0 ;

                    r = get_enum_by_key(sa.s + pos + reverse) ;

                    if (r == -1)
                        parse_error_return(0, 0, idsec, idkey) ;

                    /** do no set a logger by default */
                    if (reverse && r == OPTS_LOGGER)
                        res->logger.want = 0 ;

                    if (r == OPTS_ENVIR)
                        log_warn("options: env is deprecated -- simply set an [environment] section") ;
                }
            }

            break ;

        case KEY_MAIN_USER:

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (sa.len) {

                uid_t user[256] ;
                memset(user, 0, 256*sizeof(uid_t)) ;

                uid_t owner = MYUID ;
                if (!owner) {

                    if (sastr_find(&sa, "root") == -1)
                        log_warnu_return(LOG_EXIT_ZERO, "use the service -- permission denied") ;
                }
                /** special case, we don't know which user want to use
                 * the service, we need a general name to allow the current owner
                 * of the process. The term "user" is took here to allow him */
                ssize_t p = sastr_cmp(&sa, "user") ;
                size_t len = sa.len ;
                pos = 0 ;

                char t[len + 1] ;

                sastr_to_char(t, &sa) ;

                sa.len = 0 ;

                for (; pos < len ; pos += strlen(t + pos) + 1) {

                    if (pos == (size_t)p) {

                        if (!owner)
                            /** avoid field e.g root root where originaly
                             * we want e.g. user root. The term user will be
                             * root at getpwuid() call */
                            continue ;

                        struct passwd *pw = getpwuid(owner);
                        if (!pw) {

                            if (!errno) errno = ESRCH ;
                            log_warnu_return(LOG_EXIT_ZERO,"get user name") ;
                        }

                        if (!scan_uidlist(pw->pw_name, user))
                            parse_error_return(0, 0, idsec, idkey) ;

                        if (!auto_stra(&sa, pw->pw_name, " "))
                            log_warnu_return(LOG_EXIT_ZERO, "stralloc") ;

                        continue ;
                    }

                    if (!scan_uidlist(t + pos, user))
                        parse_error_return(0, 0, idsec, idkey) ;

                    if (!auto_stra(&sa, t + pos, " "))
                        log_warnu_return(LOG_EXIT_ZERO, "stralloc") ;

                }
                uid_t nb = user[0] ;
                if (p == -1 && owner) {

                    int e = 0 ;
                    for (int i = 1; i < nb+1; i++) {
                        if (user[i] == owner) {
                            e = 1 ;
                            break ;
                        }
                    }
                    if (!e)
                        log_warnu_return(LOG_EXIT_ZERO,"use the service -- permission denied") ;
                }

                res->user = resolve_add_string(wres, sa.s) ;
            }

            break ;

        case KEY_MAIN_DEPENDS:

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (sa.len)
                res->dependencies.depends = parse_compute_list(wres, &sa, &res->dependencies.ndepends, 0) ;

            break ;

        case KEY_MAIN_REQUIREDBY:

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (sa.len)
                res->dependencies.requiredby = parse_compute_list(wres, &sa, &res->dependencies.nrequiredby, 0) ;

            break ;

        case KEY_MAIN_OPTSDEPS:

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (sa.len)
                res->dependencies.optsdeps = parse_compute_list(wres, &sa, &res->dependencies.noptsdeps, 1) ;

            break ;

        case KEY_MAIN_CONTENTS:

            if (!parse_clean_list(&sa, store))
                parse_error_return(0, 8, idsec, idkey) ;

            if (sa.len)
                res->dependencies.contents = parse_compute_list(wres, &sa, &res->dependencies.ncontents, 0) ;

            break ;

        case KEY_MAIN_INTREE:

            if (!parse_clean_line(store))
                parse_error_return(0, 8, idsec, idkey) ;

            res->intree = resolve_add_string(wres, store) ;

            break ;

        default:
            log_warn_return(LOG_EXIT_ZERO, "unknown key: ", get_key_by_key_all(idsec, idkey)) ;
    }

    e = 1 ;

    err :
        stralloc_free(&sa) ;
        free(wres) ;
        return e ;

}