/*
   Unix SMB/CIFS implementation.

   Winbind client API - SSSD version

   Copyright (C) Sumit Bose <sbose@redhat.com> 2014

   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 3 of the License, or (at your option) any later version.

   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Library General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/* Required Headers */
#include "config.h"

#include <stdio.h>

#include <errno.h>

#include "sss_client/idmap/sss_nss_idmap.h"

#include "libwbclient.h"
#include "wbc_sssd_internal.h"

#define MAX_NAME_LEN 1024

static int sss_id_type_to_wbcSidType(enum sss_id_type sss_type,
                                     enum wbcSidType *name_type)
{
    switch (sss_type) {
    case SSS_ID_TYPE_NOT_SPECIFIED:
        *name_type = WBC_SID_NAME_USE_NONE;
        break;
    case SSS_ID_TYPE_UID:
    case SSS_ID_TYPE_BOTH:
        *name_type = WBC_SID_NAME_USER;
        break;
    case SSS_ID_TYPE_GID:
        *name_type = WBC_SID_NAME_DOM_GRP;
        break;
    default:
        return EINVAL;
    }

    return 0;
};

/* Convert a domain and name to SID */
wbcErr wbcLookupName(const char *domain,
             const char *name,
             struct wbcDomainSid *sid,
             enum wbcSidType *name_type)
{
    char *fq_name = NULL;
    char *str_sid;
    enum sss_id_type type;
    int ret;
    wbcErr wbc_status;

    if (domain == NULL || name == NULL
            || strnlen(domain, MAX_NAME_LEN) == MAX_NAME_LEN
            || strnlen(name, MAX_NAME_LEN) == MAX_NAME_LEN) {
        return WBC_ERR_INVALID_PARAM;
    }
    ret = asprintf(&fq_name, "%s@%s", name, domain);
    if (ret == -1) {
        return WBC_ERR_NO_MEMORY;
    }

    ret = sss_nss_getsidbyname(fq_name, &str_sid, &type);
    free(fq_name);
    if (ret != 0) {
        return WBC_ERR_UNKNOWN_FAILURE;
    }

    ret = sss_id_type_to_wbcSidType(type, name_type);
    if (ret != 0) {
        return WBC_ERR_UNKNOWN_FAILURE;
    }

    wbc_status = wbcStringToSid(str_sid, sid);
    free(str_sid);
    if (!WBC_ERROR_IS_OK(wbc_status)) {
        return wbc_status;
    }

    return WBC_ERR_SUCCESS;
}


/* Convert a SID to a domain and name */
wbcErr wbcLookupSid(const struct wbcDomainSid *sid,
            char **pdomain,
            char **pname,
            enum wbcSidType *pname_type)
{
    char *str_sid;
    char *fq_name = NULL;
    enum sss_id_type type;
    int ret;
    char *p;
    wbcErr wbc_status;

    wbc_status = wbcSidToString(sid, &str_sid);
    if (!WBC_ERROR_IS_OK(wbc_status)) {
        return wbc_status;
    }

    ret = sss_nss_getnamebysid(str_sid, &fq_name, &type);
    wbcFreeMemory(str_sid);
    if (ret != 0) {
        return WBC_ERR_UNKNOWN_FAILURE;
    }

    ret = sss_id_type_to_wbcSidType(type, pname_type);
    if (ret != 0) {
        wbc_status = WBC_ERR_UNKNOWN_FAILURE;
        goto done;
    }

    /* TODO: it would be nice to have a sss_nss_getnamebysid() call which
    * return name and domain separately. */
    p = strchr(fq_name, '@');
    if (p == NULL) {
        wbc_status = WBC_ERR_UNKNOWN_FAILURE;
        goto done;
    }

    *p = '\0';
    *pname = wbcStrDup(fq_name);
    if (*pname == NULL) {
        wbc_status = WBC_ERR_NO_MEMORY;
        goto done;
    }

    *pdomain = wbcStrDup(p + 1);
    if (*pdomain == NULL) {
        wbcFreeMemory(*pname);
        wbc_status = WBC_ERR_NO_MEMORY;
        goto done;
    }

    wbc_status = WBC_ERR_SUCCESS;
done:
    free(fq_name);
    return wbc_status;
}

wbcErr wbcLookupSids(const struct wbcDomainSid *sids, int num_sids,
             struct wbcDomainInfo **pdomains, int *pnum_domains,
             struct wbcTranslatedName **pnames)
{
    WBC_SSSD_NOT_IMPLEMENTED;
}

/* Translate a collection of RIDs within a domain to names */

wbcErr wbcLookupRids(struct wbcDomainSid *dom_sid,
             int num_rids,
             uint32_t *rids,
             const char **pp_domain_name,
             const char ***pnames,
             enum wbcSidType **ptypes)
{
    struct wbcDomainSid obj_sid = {0};
    size_t c;
    wbcErr err;
    char *domain;
    char *name;
    enum wbcSidType type;
    const char **names = NULL;
    enum wbcSidType *types = NULL;

    obj_sid.sid_rev_num = dom_sid->sid_rev_num;
    obj_sid.num_auths = dom_sid->num_auths + 1;
    for (c = 0; c < 6; c++) {
        obj_sid.id_auth[c] = dom_sid->id_auth[c];
    }
    for (c = 0; c < WBC_MAXSUBAUTHS; c++) {
        obj_sid.sub_auths[c] = dom_sid->sub_auths[c];
    }

    names = wbcAllocateStringArray(num_rids + 1);
    if (names == NULL) {
        err = WBC_ERR_NO_MEMORY;
        goto done;
    }

    types = wbcAllocateMemory(num_rids + 1, sizeof(enum wbcSidType), NULL);
    if (types == NULL) {
        err = WBC_ERR_NO_MEMORY;
        goto done;
    }

    for (c = 0; c < num_rids; c++) {
        obj_sid.sub_auths[obj_sid.num_auths - 1] = rids[c];

        err = wbcLookupSid(&obj_sid, &domain, &name, &type);
        if (err != WBC_ERR_SUCCESS) {
            goto done;
        }

        names[c] = strdup(name);
        wbcFreeMemory(name);
        if (names[c] == NULL) {
            err = WBC_ERR_NO_MEMORY;
            goto done;
        }
        types[c] = type;

        if (c == 0) {
            *pp_domain_name = domain;
        } else {
            wbcFreeMemory(domain);
        }
    }

    *pnames = names;
    *ptypes = types;

    err = WBC_ERR_SUCCESS;

done:
    if (err != WBC_ERR_SUCCESS) {
        wbcFreeMemory(types);
        wbcFreeMemory(names);
    }

    return err;
}

/* Get the groups a user belongs to */
wbcErr wbcLookupUserSids(const struct wbcDomainSid *user_sid,
             bool domain_groups_only,
             uint32_t *num_sids,
             struct wbcDomainSid **_sids)
{
    WBC_SSSD_NOT_IMPLEMENTED;
}

/* Get alias membership for sids */
wbcErr wbcGetSidAliases(const struct wbcDomainSid *dom_sid,
            struct wbcDomainSid *sids,
            uint32_t num_sids,
            uint32_t **alias_rids,
            uint32_t *num_alias_rids)
{
    WBC_SSSD_NOT_IMPLEMENTED;
}


/* Lists Users */
wbcErr wbcListUsers(const char *domain_name,
            uint32_t *_num_users,
            const char ***_users)
{
    WBC_SSSD_NOT_IMPLEMENTED;
}

/* Lists Groups */
wbcErr wbcListGroups(const char *domain_name,
             uint32_t *_num_groups,
             const char ***_groups)
{
    WBC_SSSD_NOT_IMPLEMENTED;
}

wbcErr wbcGetDisplayName(const struct wbcDomainSid *sid,
             char **pdomain,
             char **pfullname,
             enum wbcSidType *pname_type)
{
    WBC_SSSD_NOT_IMPLEMENTED;
}
