/* Copyright (C) 2011 the GSS-PROXY contributors, see COPYING for license */

#include "gssapi_gpm.h"

__thread gssx_status *tls_last_status = NULL;

/* Thread local storage for return status.
 * FIXME: it's not the most portable construct, so may need fixing in future */
void gpm_save_status(gssx_status *status)
{
    int ret;

    if (tls_last_status) {
        xdr_free((xdrproc_t)xdr_gssx_status, (char *)tls_last_status);
        free(tls_last_status);
    }

    ret = gp_copy_gssx_status_alloc(status, &tls_last_status);
    if (ret) {
        /* make sure tls_last_status is zeored on error */
        tls_last_status = NULL;
    }
}

/* This funciton is used to record internal mech errors that are
 * generated by the proxy client code */
void gpm_save_internal_status(uint32_t err, char *err_str)
{
    gssx_status status;

    memset(&status, 0, sizeof(gssx_status));

#define STD_MAJ_ERROR_STR "Internal gssproxy error"
    status.major_status = GSS_S_FAILURE;
    status.major_status_string.utf8string_val = strdup(STD_MAJ_ERROR_STR);
    status.major_status_string.utf8string_len = sizeof(STD_MAJ_ERROR_STR);
    status.minor_status = err;
    status.minor_status_string.utf8string_val = err_str;
    status.minor_status_string.utf8string_len = strlen(err_str) + 1;
    gpm_save_status(&status);
}

OM_uint32 gpm_display_status(OM_uint32 *minor_status,
                             OM_uint32 status_value,
                             int status_type,
                             const gss_OID mech_type,
                             OM_uint32 *message_context,
                             gss_buffer_t status_string)
{
    utf8string tmp;
    int ret;

    switch(status_type) {
    case GSS_C_GSS_CODE:
        if (tls_last_status &&
            tls_last_status->major_status == status_value &&
            tls_last_status->major_status_string.utf8string_len) {
                ret = gp_copy_utf8string(&tls_last_status->major_status_string,
                                         &tmp);
                if (ret) {
                    *minor_status = ret;
                    return GSS_S_FAILURE;
                }
                status_string->value = tmp.utf8string_val;
                status_string->length = tmp.utf8string_len;
                *minor_status = 0;
                return GSS_S_COMPLETE;
        } else {
            /* if we do not have it, make it clear */
            return GSS_S_UNAVAILABLE;
        }
    case GSS_C_MECH_CODE:
        if (tls_last_status &&
            tls_last_status->minor_status == status_value &&
            tls_last_status->minor_status_string.utf8string_len) {

            if (*message_context) {
                /* we do not support multiple messages for now */
                *minor_status = EINVAL;
                return GSS_S_FAILURE;
            }

            ret = gp_copy_utf8string(&tls_last_status->minor_status_string,
                                     &tmp);
            if (ret) {
                *minor_status = ret;
                return GSS_S_FAILURE;
            }
            status_string->value = tmp.utf8string_val;
            status_string->length = tmp.utf8string_len;
        } else {
            /* if we do not have it, make it clear */
            return GSS_S_UNAVAILABLE;
        }
        *minor_status = 0;
        return GSS_S_COMPLETE;
    default:
        *minor_status = EINVAL;
        return GSS_S_BAD_STATUS;
    }
}
