GSSAPI="BASE"  # This ensures that a full module is generated by Cython

from gssapi.raw.cython_types cimport *
from gssapi.raw.sec_contexts cimport SecurityContext
from gssapi.raw.ext_dce cimport IOV, gss_iov_buffer_desc

from gssapi.raw.misc import GSSError
from gssapi.raw.ext_dce import IOVBufferType


cdef extern from "python_gssapi_ext.h":
    OM_uint32 gss_get_mic_iov(OM_uint32 *min_stat, gss_ctx_id_t context_handle,
                              gss_qop_t qop_req, gss_iov_buffer_desc *iov,
                              int iov_count) nogil

    OM_uint32 gss_get_mic_iov_length(OM_uint32 *min_stat,
                                     gss_ctx_id_t context_handle,
                                     gss_qop_t qop_req,
                                     gss_iov_buffer_desc *iov,
                                     int iov_count) nogil

    OM_uint32 gss_verify_mic_iov(OM_uint32 *min_stat,
                                 gss_ctx_id_t context_handle,
                                 gss_qop_t *qop_state,
                                 gss_iov_buffer_desc *iov,
                                 int iov_count) nogil

    # more in the enum extension file


IOV.AUTO_ALLOC_BUFFERS.add(IOVBufferType.mic_token)


def get_mic_iov(SecurityContext context not None, IOV message not None,
                qop=None):
    """
    get_mic_iov(context, message, qop=None)
    Generate MIC tokens for the given IOV message.

    This method generates a MIC token for the given IOV message, and places it
    in the :attr:`IOVBufferType.mic_token` buffer in the IOV.  This method
    operates entirely in-place, and returns nothing.

    Warning:
        This modifies the input :class:`IOV`.

    Args:
        context (SecurityContext): the current security context
        message (IOV): the :class:`IOV` containing the message
        qop (int): the desired Quality of Protection
            (or None for the default QoP)

    Raises:
        GSSError
    """

    cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT

    cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()

    cdef OM_uint32 maj_stat, min_stat

    with nogil:
        maj_stat = gss_get_mic_iov(&min_stat, context.raw_ctx, qop_req,
                                   res_arr, message.iov_len)

    if maj_stat == GSS_S_COMPLETE:
        message.c_changed = True
        return
    else:
        raise GSSError(maj_stat, min_stat)


def get_mic_iov_length(SecurityContext context not None, IOV message not None,
                       qop=None):
    """
    get_mic_iov_length(context, message, qop=None)
    Allocate space for the MIC buffer in the given IOV message.

    This method allocates space for the MIC token buffer
    (:attr:`IOVBufferType.mic_token`) in the given IOV message.

    Warning:
        This modifies the input :class:`IOV`.

    Args:
        context (SecurityContext): the current security context
        message (IOV): the :class:`IOV` containing the message
        qop (int): the desired Quality of Protection
            (or None for the default QoP)

    Raises:
        GSSError
    """

    cdef gss_qop_t qop_req = qop if qop is not None else GSS_C_QOP_DEFAULT

    cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()

    cdef OM_uint32 maj_stat, min_stat

    with nogil:
        maj_stat = gss_get_mic_iov_length(&min_stat, context.raw_ctx, qop_req,
                                          res_arr, message.iov_len)

    if maj_stat == GSS_S_COMPLETE:
        message.c_changed = True
        return
    else:
        raise GSSError(maj_stat, min_stat)


def verify_mic_iov(SecurityContext context not None, IOV message not None,
                   qop=None):
    """
    verify_mic_iov(context, message, qop=None)
    Verify that the MIC matches the data in the given IOV message.

    This method verifies that the MIC token in the MIC buffer
    (:attr:`IOVBufferType.mic_token`) match the data buffer(s)
    in the given IOV method.

    Args:
        context (SecurityContext): the current security context
        message (IOV): the :class:`IOV` containing the message

    Returns:
        int: the QoP used to generate the MIC token

    Raises:
        GSSError
    """

    cdef gss_iov_buffer_desc *res_arr = message.__cvalue__()

    cdef gss_qop_t qop_state

    cdef OM_uint32 maj_stat, min_stat

    with nogil:
        maj_stat = gss_verify_mic_iov(&min_stat, context.raw_ctx, &qop_state,
                                      res_arr, message.iov_len)

    if maj_stat == GSS_S_COMPLETE:
        return qop_state
    else:
        raise GSSError(maj_stat, min_stat)
