예제 #1
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_OpenSession(CK_SLOT_ID slotID,
	      CK_FLAGS flags,
	      CK_VOID_PTR pApplication,
	      CK_NOTIFY Notify,
	      CK_SESSION_HANDLE_PTR phSession)
{
    size_t i;
    INIT_CONTEXT();
    st_logf("OpenSession: slot: %d\n", (int)slotID);

    if (soft_token.open_sessions == MAX_NUM_SESSION)
	return CKR_SESSION_COUNT;

    soft_token.application = pApplication;
    soft_token.notify = Notify;

    for (i = 0; i < MAX_NUM_SESSION; i++)
	if (soft_token.state[i].session_handle == CK_INVALID_HANDLE)
	    break;
    if (i == MAX_NUM_SESSION)
	abort();

    soft_token.open_sessions++;

    soft_token.state[i].session_handle =
	(CK_SESSION_HANDLE)(random() & 0xfffff);
    *phSession = soft_token.state[i].session_handle;

    return CKR_OK;
}
예제 #2
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_VerifyInit(CK_SESSION_HANDLE hSession,
	     CK_MECHANISM_PTR pMechanism,
	     CK_OBJECT_HANDLE hKey)
{
    struct session_state *state;
    CK_MECHANISM_TYPE mechs[] = { CKM_RSA_PKCS };
    CK_BBOOL bool_true = CK_TRUE;
    CK_ATTRIBUTE attr[] = {
	{ CKA_VERIFY, &bool_true, sizeof(bool_true) }
    };
    struct st_object *o;
    CK_RV ret;

    INIT_CONTEXT();
    st_logf("VerifyInit\n");
    VERIFY_SESSION_HANDLE(hSession, &state);

    ret = commonInit(attr, sizeof(attr)/sizeof(attr[0]),
		     mechs, sizeof(mechs)/sizeof(mechs[0]),
		     pMechanism, hKey, &o);
    if (ret)
	return ret;

    ret = dup_mechanism(&state->verify_mechanism, pMechanism);
    if (ret == CKR_OK)
	state->verify_object = OBJECT_ID(o);

    return ret;
}
예제 #3
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetSlotInfo(CK_SLOT_ID slotID,
	      CK_SLOT_INFO_PTR pInfo)
{
    INIT_CONTEXT();
    st_logf("GetSlotInfo: slot: %d : %s\n", (int)slotID, has_session());

    memset(pInfo, 18, sizeof(*pInfo));

    if (slotID != 1)
	return CKR_ARGUMENTS_BAD;

    snprintf_fill((char *)pInfo->slotDescription,
		  sizeof(pInfo->slotDescription),
		  ' ',
		  "Heimdal hx509 SoftToken (slot)");
    snprintf_fill((char *)pInfo->manufacturerID,
		  sizeof(pInfo->manufacturerID),
		  ' ',
		  "Heimdal hx509 SoftToken (slot)");
    pInfo->flags = CKF_TOKEN_PRESENT;
    if (soft_token.flags.hardware_slot)
	pInfo->flags |= CKF_HW_SLOT;
    pInfo->hardwareVersion.major = 1;
    pInfo->hardwareVersion.minor = 0;
    pInfo->firmwareVersion.major = 1;
    pInfo->firmwareVersion.minor = 0;

    return CKR_OK;
}
예제 #4
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetFunctionList(CK_FUNCTION_LIST_PTR_PTR ppFunctionList)
{
    INIT_CONTEXT();

    *ppFunctionList = &funcs;
    return CKR_OK;
}
예제 #5
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV CK_SPEC
C_Initialize(CK_VOID_PTR a)
{
    CK_C_INITIALIZE_ARGS_PTR args = a;
    CK_RV ret;
    size_t i;

    st_logf("Initialize\n");

    INIT_CONTEXT();

    OpenSSL_add_all_algorithms();

    srandom(getpid() ^ (int) time(NULL));

    for (i = 0; i < MAX_NUM_SESSION; i++) {
	soft_token.state[i].session_handle = CK_INVALID_HANDLE;
	soft_token.state[i].find.attributes = NULL;
	soft_token.state[i].find.num_attributes = 0;
	soft_token.state[i].find.next_object = -1;
	reset_crypto_state(&soft_token.state[i]);
    }

    soft_token.flags.hardware_slot = 1;
    soft_token.flags.app_error_fatal = 0;
    soft_token.flags.login_done = 0;

    soft_token.object.objs = NULL;
    soft_token.object.num_objs = 0;

    soft_token.logfile = NULL;
#if 0
    soft_token.logfile = stdout;
#endif
#if 0
    soft_token.logfile = fopen("/tmp/log-pkcs11.txt", "a");
#endif

    if (a != NULL_PTR) {
	st_logf("\tCreateMutex:\t%p\n", args->CreateMutex);
	st_logf("\tDestroyMutext\t%p\n", args->DestroyMutex);
	st_logf("\tLockMutext\t%p\n", args->LockMutex);
	st_logf("\tUnlockMutext\t%p\n", args->UnlockMutex);
	st_logf("\tFlags\t%04x\n", (unsigned int)args->flags);
    }

    soft_token.config_file = get_config_file_for_user();

    /*
     * This operations doesn't return CKR_OK if any of the
     * certificates failes to be unparsed (ie password protected).
     */
    ret = read_conf_file(soft_token.config_file, CKU_USER, NULL);
    if (ret == CKR_OK)
	soft_token.flags.login_done = 1;

    return CKR_OK;
}
예제 #6
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_DigestInit(CK_SESSION_HANDLE hSession,
	     CK_MECHANISM_PTR pMechanism)
{
    st_logf("DigestInit\n");
    INIT_CONTEXT();
    VERIFY_SESSION_HANDLE(hSession, NULL);
    return CKR_FUNCTION_NOT_SUPPORTED;
}
예제 #7
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_Logout(CK_SESSION_HANDLE hSession)
{
    st_logf("Logout\n");
    INIT_CONTEXT();

    VERIFY_SESSION_HANDLE(hSession, NULL);
    return CKR_FUNCTION_NOT_SUPPORTED;
}
예제 #8
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_VerifyFinal(CK_SESSION_HANDLE hSession,
	      CK_BYTE_PTR pSignature,
	      CK_ULONG ulSignatureLen)
{
    INIT_CONTEXT();
    st_logf("VerifyFinal\n");
    VERIFY_SESSION_HANDLE(hSession, NULL);
    return CKR_FUNCTION_NOT_SUPPORTED;
}
예제 #9
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_VerifyUpdate(CK_SESSION_HANDLE hSession,
	       CK_BYTE_PTR pPart,
	       CK_ULONG ulPartLen)
{
    INIT_CONTEXT();
    st_logf("VerifyUpdate\n");
    VERIFY_SESSION_HANDLE(hSession, NULL);
    return CKR_FUNCTION_NOT_SUPPORTED;
}
예제 #10
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GenerateRandom(CK_SESSION_HANDLE hSession,
		 CK_BYTE_PTR RandomData,
		 CK_ULONG ulRandomLen)
{
    INIT_CONTEXT();
    st_logf("GenerateRandom\n");
    VERIFY_SESSION_HANDLE(hSession, NULL);
    return CKR_FUNCTION_NOT_SUPPORTED;
}
예제 #11
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_InitToken(CK_SLOT_ID slotID,
	    CK_UTF8CHAR_PTR pPin,
	    CK_ULONG ulPinLen,
	    CK_UTF8CHAR_PTR pLabel)
{
    INIT_CONTEXT();
    st_logf("InitToken: slot %d\n", (int)slotID);
    return CKR_FUNCTION_NOT_SUPPORTED;
}
예제 #12
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetObjectSize(CK_SESSION_HANDLE hSession,
		CK_OBJECT_HANDLE hObject,
		CK_ULONG_PTR pulSize)
{
    st_logf("GetObjectSize\n");
    INIT_CONTEXT();

    VERIFY_SESSION_HANDLE(hSession, NULL);
    return CKR_FUNCTION_NOT_SUPPORTED;
}
예제 #13
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_FindObjectsFinal(CK_SESSION_HANDLE hSession)
{
    struct session_state *state;

    INIT_CONTEXT();

    st_logf("FindObjectsFinal\n");
    VERIFY_SESSION_HANDLE(hSession, &state);
    find_object_final(state);
    return CKR_OK;
}
예제 #14
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetMechanismInfo(CK_SLOT_ID slotID,
		   CK_MECHANISM_TYPE type,
		   CK_MECHANISM_INFO_PTR pInfo)
{
    INIT_CONTEXT();
    st_logf("GetMechanismInfo: slot %d type: %d\n",
	    (int)slotID, (int)type);
    memset(pInfo, 0, sizeof(*pInfo));

    return CKR_OK;
}
예제 #15
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetTokenInfo(CK_SLOT_ID slotID,
	       CK_TOKEN_INFO_PTR pInfo)
{
    INIT_CONTEXT();
    st_logf("GetTokenInfo: %s\n", has_session());

    memset(pInfo, 19, sizeof(*pInfo));

    snprintf_fill((char *)pInfo->label,
		  sizeof(pInfo->label),
		  ' ',
		  "Heimdal hx509 SoftToken (token)");
    snprintf_fill((char *)pInfo->manufacturerID,
		  sizeof(pInfo->manufacturerID),
		  ' ',
		  "Heimdal hx509 SoftToken (token)");
    snprintf_fill((char *)pInfo->model,
		  sizeof(pInfo->model),
		  ' ',
		  "Heimdal hx509 SoftToken (token)");
    snprintf_fill((char *)pInfo->serialNumber,
		  sizeof(pInfo->serialNumber),
		  ' ',
		  "4711");
    pInfo->flags =
	CKF_TOKEN_INITIALIZED |
	CKF_USER_PIN_INITIALIZED;

    if (soft_token.flags.login_done == 0)
	pInfo->flags |= CKF_LOGIN_REQUIRED;

    /* CFK_RNG |
       CKF_RESTORE_KEY_NOT_NEEDED |
    */
    pInfo->ulMaxSessionCount = MAX_NUM_SESSION;
    pInfo->ulSessionCount = soft_token.open_sessions;
    pInfo->ulMaxRwSessionCount = MAX_NUM_SESSION;
    pInfo->ulRwSessionCount = soft_token.open_sessions;
    pInfo->ulMaxPinLen = 1024;
    pInfo->ulMinPinLen = 0;
    pInfo->ulTotalPublicMemory = 4711;
    pInfo->ulFreePublicMemory = 4712;
    pInfo->ulTotalPrivateMemory = 4713;
    pInfo->ulFreePrivateMemory = 4714;
    pInfo->hardwareVersion.major = 2;
    pInfo->hardwareVersion.minor = 0;
    pInfo->firmwareVersion.major = 2;
    pInfo->firmwareVersion.minor = 0;

    return CKR_OK;
}
예제 #16
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetSlotList(CK_BBOOL tokenPresent,
	      CK_SLOT_ID_PTR pSlotList,
	      CK_ULONG_PTR   pulCount)
{
    INIT_CONTEXT();
    st_logf("GetSlotList: %s\n",
	    tokenPresent ? "tokenPresent" : "token not Present");
    if (pSlotList)
	pSlotList[0] = 1;
    *pulCount = 1;
    return CKR_OK;
}
예제 #17
0
파일: va_glx.c 프로젝트: bjsnider/vaapi
// Destroy a VA/GLX surface
VAStatus vaDestroySurfaceGLX(
    VADisplay dpy,
    void     *gl_surface
)
{
    VADriverContextP ctx;
    VAStatus status;

    INIT_CONTEXT(ctx, dpy);

    INVOKE(ctx, DestroySurface, (ctx, gl_surface));
    return status;
}
예제 #18
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_Verify(CK_SESSION_HANDLE hSession,
	 CK_BYTE_PTR pData,
	 CK_ULONG ulDataLen,
	 CK_BYTE_PTR pSignature,
	 CK_ULONG ulSignatureLen)
{
    struct session_state *state;
    struct st_object *o;
    const AlgorithmIdentifier *alg;
    CK_RV ret;
    int hret;
    heim_octet_string data, sig;

    INIT_CONTEXT();
    st_logf("Verify\n");
    VERIFY_SESSION_HANDLE(hSession, &state);

    if (state->verify_object == -1)
	return CKR_ARGUMENTS_BAD;

    o = soft_token.object.objs[state->verify_object];

    switch(state->verify_mechanism->mechanism) {
    case CKM_RSA_PKCS:
	alg = hx509_signature_rsa_pkcs1_x509();
	break;
    default:
	ret = CKR_FUNCTION_NOT_SUPPORTED;
	goto out;
    }

    sig.data = pData;
    sig.length = ulDataLen;
    data.data = pSignature;
    data.length = ulSignatureLen;

    hret = _hx509_verify_signature(context,
				   o->cert,
				   alg,
				   &data,
				   &sig);
    if (hret) {
	ret = CKR_GENERAL_ERROR;
	goto out;
    }
    ret = CKR_OK;

 out:
    return ret;
}
예제 #19
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_CloseAllSessions(CK_SLOT_ID slotID)
{
    size_t i;
    INIT_CONTEXT();

    st_logf("CloseAllSessions\n");

    for (i = 0; i < MAX_NUM_SESSION; i++)
	if (soft_token.state[i].session_handle != CK_INVALID_HANDLE)
	    close_session(&soft_token.state[i]);

    return CKR_OK;
}
예제 #20
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_CloseSession(CK_SESSION_HANDLE hSession)
{
    struct session_state *state;
    INIT_CONTEXT();
    st_logf("CloseSession\n");

    if (verify_session_handle(hSession, &state) != CKR_OK)
	application_error("closed session not open");
    else
	close_session(state);

    return CKR_OK;
}
예제 #21
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetAttributeValue(CK_SESSION_HANDLE hSession,
		    CK_OBJECT_HANDLE hObject,
		    CK_ATTRIBUTE_PTR pTemplate,
		    CK_ULONG ulCount)
{
    struct session_state *state;
    struct st_object *obj;
    CK_ULONG i;
    CK_RV ret;
    int j;

    INIT_CONTEXT();

    st_logf("GetAttributeValue: %lx\n",
	    (unsigned long)HANDLE_OBJECT_ID(hObject));
    VERIFY_SESSION_HANDLE(hSession, &state);

    if ((ret = object_handle_to_object(hObject, &obj)) != CKR_OK) {
	st_logf("object not found: %lx\n",
		(unsigned long)HANDLE_OBJECT_ID(hObject));
	return ret;
    }

    for (i = 0; i < ulCount; i++) {
	st_logf("	getting 0x%08lx\n", (unsigned long)pTemplate[i].type);
	for (j = 0; j < obj->num_attributes; j++) {
	    if (obj->attrs[j].secret) {
		pTemplate[i].ulValueLen = (CK_ULONG)-1;
		break;
	    }
	    if (pTemplate[i].type == obj->attrs[j].attribute.type) {
		if (pTemplate[i].pValue != NULL_PTR && obj->attrs[j].secret == 0) {
		    if (pTemplate[i].ulValueLen >= obj->attrs[j].attribute.ulValueLen)
			memcpy(pTemplate[i].pValue, obj->attrs[j].attribute.pValue,
			       obj->attrs[j].attribute.ulValueLen);
		}
		pTemplate[i].ulValueLen = obj->attrs[j].attribute.ulValueLen;
		break;
	    }
	}
	if (j == obj->num_attributes) {
	    st_logf("key type: 0x%08lx not found\n", (unsigned long)pTemplate[i].type);
	    pTemplate[i].ulValueLen = (CK_ULONG)-1;
	}

    }
    return CKR_OK;
}
예제 #22
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_FindObjectsInit(CK_SESSION_HANDLE hSession,
		  CK_ATTRIBUTE_PTR pTemplate,
		  CK_ULONG ulCount)
{
    struct session_state *state;

    st_logf("FindObjectsInit\n");

    INIT_CONTEXT();

    VERIFY_SESSION_HANDLE(hSession, &state);

    if (state->find.next_object != -1) {
	application_error("application didn't do C_FindObjectsFinal\n");
	find_object_final(state);
    }
    if (ulCount) {
	CK_ULONG i;

	print_attributes(pTemplate, ulCount);

	state->find.attributes =
	    calloc(1, ulCount * sizeof(state->find.attributes[0]));
	if (state->find.attributes == NULL)
	    return CKR_DEVICE_MEMORY;
	for (i = 0; i < ulCount; i++) {
	    state->find.attributes[i].pValue =
		malloc(pTemplate[i].ulValueLen);
	    if (state->find.attributes[i].pValue == NULL) {
		find_object_final(state);
		return CKR_DEVICE_MEMORY;
	    }
	    memcpy(state->find.attributes[i].pValue,
		   pTemplate[i].pValue, pTemplate[i].ulValueLen);
	    state->find.attributes[i].type = pTemplate[i].type;
	    state->find.attributes[i].ulValueLen = pTemplate[i].ulValueLen;
	}
	state->find.num_attributes = ulCount;
	state->find.next_object = 0;
    } else {
	st_logf("find all objects\n");
	state->find.attributes = NULL;
	state->find.num_attributes = 0;
	state->find.next_object = 0;
    }

    return CKR_OK;
}
예제 #23
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetMechanismList(CK_SLOT_ID slotID,
		   CK_MECHANISM_TYPE_PTR pMechanismList,
		   CK_ULONG_PTR pulCount)
{
    INIT_CONTEXT();
    st_logf("GetMechanismList\n");

    *pulCount = 1;
    if (pMechanismList == NULL_PTR)
	return CKR_OK;
    pMechanismList[0] = CKM_RSA_PKCS;

    return CKR_OK;
}
예제 #24
0
파일: va_glx.c 프로젝트: bjsnider/vaapi
// Copy a VA surface to a VA/GLX surface
VAStatus vaCopySurfaceGLX(
    VADisplay    dpy,
    void        *gl_surface,
    VASurfaceID  surface,
    unsigned int flags
)
{
    VADriverContextP ctx;
    VAStatus status;

    INIT_CONTEXT(ctx, dpy);

    INVOKE(ctx, CopySurface, (ctx, gl_surface, surface, flags));
    return status;
}
예제 #25
0
파일: va_glx.c 프로젝트: bjsnider/vaapi
// Create a surface used for display to OpenGL
VAStatus vaCreateSurfaceGLX(
    VADisplay dpy,
    GLenum    target,
    GLuint    texture,
    void    **gl_surface
)
{
    VADriverContextP ctx;
    VAStatus status;

    /* Make sure it is a valid GL texture object */
    if (!glIsTexture(texture))
        return VA_STATUS_ERROR_INVALID_PARAMETER;

    INIT_CONTEXT(ctx, dpy);

    INVOKE(ctx, CreateSurface, (ctx, target, texture, gl_surface));
    return status;
}
예제 #26
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_Finalize(CK_VOID_PTR args)
{
    size_t i;

    INIT_CONTEXT();

    st_logf("Finalize\n");

    for (i = 0; i < MAX_NUM_SESSION; i++) {
	if (soft_token.state[i].session_handle != CK_INVALID_HANDLE) {
	    application_error("application finalized without "
			      "closing session\n");
	    close_session(&soft_token.state[i]);
	}
    }

    return CKR_OK;
}
예제 #27
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetSessionInfo(CK_SESSION_HANDLE hSession,
		 CK_SESSION_INFO_PTR pInfo)
{
    st_logf("GetSessionInfo\n");
    INIT_CONTEXT();

    VERIFY_SESSION_HANDLE(hSession, NULL);

    memset(pInfo, 20, sizeof(*pInfo));

    pInfo->slotID = 1;
    if (soft_token.flags.login_done)
	pInfo->state = CKS_RO_USER_FUNCTIONS;
    else
	pInfo->state = CKS_RO_PUBLIC_SESSION;
    pInfo->flags = CKF_SERIAL_SESSION;
    pInfo->ulDeviceError = 0;

    return CKR_OK;
}
예제 #28
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_GetInfo(CK_INFO_PTR args)
{
    INIT_CONTEXT();

    st_logf("GetInfo\n");

    memset(args, 17, sizeof(*args));
    args->cryptokiVersion.major = 2;
    args->cryptokiVersion.minor = 10;
    snprintf_fill((char *)args->manufacturerID,
		  sizeof(args->manufacturerID),
		  ' ',
		  "Heimdal hx509 SoftToken");
    snprintf_fill((char *)args->libraryDescription,
		  sizeof(args->libraryDescription), ' ',
		  "Heimdal hx509 SoftToken");
    args->libraryVersion.major = 2;
    args->libraryVersion.minor = 0;

    return CKR_OK;
}
예제 #29
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_FindObjects(CK_SESSION_HANDLE hSession,
	      CK_OBJECT_HANDLE_PTR phObject,
	      CK_ULONG ulMaxObjectCount,
	      CK_ULONG_PTR pulObjectCount)
{
    struct session_state *state;
    int i;

    INIT_CONTEXT();

    st_logf("FindObjects\n");

    VERIFY_SESSION_HANDLE(hSession, &state);

    if (state->find.next_object == -1) {
	application_error("application didn't do C_FindObjectsInit\n");
	return CKR_ARGUMENTS_BAD;
    }
    if (ulMaxObjectCount == 0) {
	application_error("application asked for 0 objects\n");
	return CKR_ARGUMENTS_BAD;
    }
    *pulObjectCount = 0;
    for (i = state->find.next_object; i < soft_token.object.num_objs; i++) {
	st_logf("FindObjects: %d\n", i);
	state->find.next_object = i + 1;
	if (attributes_match(soft_token.object.objs[i],
			     state->find.attributes,
			     state->find.num_attributes)) {
	    *phObject++ = soft_token.object.objs[i]->object_handle;
	    ulMaxObjectCount--;
	    (*pulObjectCount)++;
	    if (ulMaxObjectCount == 0)
		break;
	}
    }
    return CKR_OK;
}
예제 #30
0
파일: softp11.c 프로젝트: heimdal/heimdal
CK_RV
C_Login(CK_SESSION_HANDLE hSession,
	CK_USER_TYPE userType,
	CK_UTF8CHAR_PTR pPin,
	CK_ULONG ulPinLen)
{
    char *pin = NULL;
    CK_RV ret;
    INIT_CONTEXT();

    st_logf("Login\n");

    VERIFY_SESSION_HANDLE(hSession, NULL);

    if (pPin != NULL_PTR) {
	int aret;

	aret = asprintf(&pin, "%.*s", (int)ulPinLen, pPin);
	if (aret != -1 && pin)
		st_logf("type: %d password: %s\n", (int)userType, pin);
	else
		st_logf("memory error: asprintf failed\n");
    }

    /*
     * Login
     */

    ret = read_conf_file(soft_token.config_file, userType, pin);
    if (ret == CKR_OK)
	soft_token.flags.login_done = 1;

    free(pin);

    return soft_token.flags.login_done ? CKR_OK : CKR_PIN_INCORRECT;
}