提交 6ddbb4cd 编写于 作者: R Rich Salz

X509_STORE_CTX accessors.

Add some functions that were missing when a number of X509
objects became opaque (thanks, Roumen!)
Reviewed-by: NRichard Levitte <levitte@openssl.org>
上级 aa6bb135
......@@ -201,8 +201,8 @@ int crl_main(int argc, char **argv)
goto end;
}
xobj = X509_STORE_get_X509_by_subject(ctx, X509_LU_X509,
X509_CRL_get_issuer(x));
xobj = X509_STORE_CTX_get_obj_by_subject(ctx, X509_LU_X509,
X509_CRL_get_issuer(x));
if (xobj == NULL) {
BIO_printf(bio_err, "Error getting CRL issuer certificate\n");
goto end;
......
......@@ -576,8 +576,8 @@ static int cert_status_cb(SSL *s, void *arg)
SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s)),
NULL, NULL))
goto err;
obj = X509_STORE_get_X509_by_subject(inctx, X509_LU_X509,
X509_get_issuer_name(x));
obj = X509_STORE_CTX_get_obj_by_subject(inctx, X509_LU_X509,
X509_get_issuer_name(x));
if (obj == NULL) {
BIO_puts(bio_err, "cert_status: Can't retrieve issuer certificate.\n");
goto done;
......
/*
* Generated by util/mkerr.pl DO NOT EDIT
* Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the OpenSSL license (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NOTE: this file was auto generated by the mkerr.pl script: any changes
* made to it will be overwritten when the script next updates this file,
* only reason strings will be preserved.
*/
#include <stdio.h>
......@@ -64,6 +59,7 @@ static ERR_STRING_DATA X509_str_functs[] = {
"X509_NAME_ENTRY_set_object"},
{ERR_FUNC(X509_F_X509_NAME_ONELINE), "X509_NAME_oneline"},
{ERR_FUNC(X509_F_X509_NAME_PRINT), "X509_NAME_print"},
{ERR_FUNC(X509_F_X509_OBJECT_NEW), "X509_OBJECT_new"},
{ERR_FUNC(X509_F_X509_PRINT_EX_FP), "X509_print_ex_fp"},
{ERR_FUNC(X509_F_X509_PUBKEY_DECODE), "x509_pubkey_decode"},
{ERR_FUNC(X509_F_X509_PUBKEY_GET0), "X509_PUBKEY_get0"},
......@@ -81,8 +77,6 @@ static ERR_STRING_DATA X509_str_functs[] = {
{ERR_FUNC(X509_F_X509_STORE_CTX_NEW), "X509_STORE_CTX_new"},
{ERR_FUNC(X509_F_X509_STORE_CTX_PURPOSE_INHERIT),
"X509_STORE_CTX_purpose_inherit"},
{ERR_FUNC(X509_F_X509_STORE_GET_X509_BY_SUBJECT),
"X509_STORE_get_X509_by_subject"},
{ERR_FUNC(X509_F_X509_TO_X509_REQ), "X509_to_X509_REQ"},
{ERR_FUNC(X509_F_X509_TRUST_ADD), "X509_TRUST_add"},
{ERR_FUNC(X509_F_X509_TRUST_SET), "X509_TRUST_set"},
......
......@@ -246,25 +246,22 @@ X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m)
}
}
X509_OBJECT *X509_STORE_get_X509_by_subject(X509_STORE_CTX *vs, int type,
X509_NAME *name)
X509_OBJECT *X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX *vs, int type,
X509_NAME *name)
{
X509_OBJECT *ret;
X509_OBJECT *ret = X509_OBJECT_new();
ret = OPENSSL_malloc(sizeof (*ret));
if (ret == NULL) {
X509err(X509_F_X509_STORE_GET_X509_BY_SUBJECT, ERR_R_MALLOC_FAILURE);
if (ret == NULL)
return NULL;
}
if (!X509_STORE_get_by_subject(vs, type, name, ret)) {
OPENSSL_free(ret);
if (!X509_STORE_CTX_get_by_subject(vs, type, name, ret)) {
X509_OBJECT_free(ret);
return NULL;
}
return ret;
}
int X509_STORE_get_by_subject(X509_STORE_CTX *vs, X509_LOOKUP_TYPE type,
X509_NAME *name, X509_OBJECT *ret)
int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, X509_LOOKUP_TYPE type,
X509_NAME *name, X509_OBJECT *ret)
{
X509_STORE *ctx = vs->ctx;
X509_LOOKUP *lu;
......@@ -293,9 +290,6 @@ int X509_STORE_get_by_subject(X509_STORE_CTX *vs, X509_LOOKUP_TYPE type,
return 0;
}
/*- if (ret->data.ptr != NULL)
X509_OBJECT_free_contents(ret); */
ret->type = tmp->type;
ret->data.ptr = tmp->data.ptr;
......@@ -311,11 +305,9 @@ int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
if (x == NULL)
return 0;
obj = OPENSSL_malloc(sizeof(*obj));
if (obj == NULL) {
X509err(X509_F_X509_STORE_ADD_CERT, ERR_R_MALLOC_FAILURE);
obj = X509_OBJECT_new();
if (obj == NULL)
return 0;
}
obj->type = X509_LU_X509;
obj->data.x509 = x;
......@@ -324,8 +316,7 @@ int X509_STORE_add_cert(X509_STORE *ctx, X509 *x)
X509_OBJECT_up_ref_count(obj);
if (X509_OBJECT_retrieve_match(ctx->objs, obj)) {
X509_OBJECT_free_contents(obj);
OPENSSL_free(obj);
X509_OBJECT_free(obj);
X509err(X509_F_X509_STORE_ADD_CERT,
X509_R_CERT_ALREADY_IN_HASH_TABLE);
ret = 0;
......@@ -344,11 +335,9 @@ int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x)
if (x == NULL)
return 0;
obj = OPENSSL_malloc(sizeof(*obj));
if (obj == NULL) {
X509err(X509_F_X509_STORE_ADD_CRL, ERR_R_MALLOC_FAILURE);
obj = X509_OBJECT_new();
if (obj == NULL)
return 0;
}
obj->type = X509_LU_CRL;
obj->data.crl = x;
......@@ -357,8 +346,7 @@ int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x)
X509_OBJECT_up_ref_count(obj);
if (X509_OBJECT_retrieve_match(ctx->objs, obj)) {
X509_OBJECT_free_contents(obj);
OPENSSL_free(obj);
X509_OBJECT_free(obj);
X509err(X509_F_X509_STORE_ADD_CRL, X509_R_CERT_ALREADY_IN_HASH_TABLE);
ret = 0;
} else
......@@ -384,23 +372,37 @@ int X509_OBJECT_up_ref_count(X509_OBJECT *a)
X509 *X509_OBJECT_get0_X509(X509_OBJECT *a)
{
if (a == NULL || a->type != X509_LU_X509)
return NULL;
return a->data.x509;
}
X509_CRL *X509_OBJECT_get0_X509_CRL(X509_OBJECT *a)
{
if (a == NULL || a->type != X509_LU_CRL)
return NULL;
return a->data.crl;
}
int X509_OBJECT_get_type(X509_OBJECT *a)
{
return a->type;
}
void X509_OBJECT_free(X509_OBJECT *a)
X509_OBJECT *X509_OBJECT_new()
{
if (a == NULL)
return;
X509_OBJECT_free_contents(a);
OPENSSL_free(a);
X509_OBJECT *ret = OPENSSL_zalloc(sizeof(*ret));
if (ret == NULL) {
X509err(X509_F_X509_OBJECT_NEW, ERR_R_MALLOC_FAILURE);
return NULL;
}
ret->type = X509_LU_FAIL;
return ret;
}
void X509_OBJECT_free_contents(X509_OBJECT *a)
void X509_OBJECT_free(X509_OBJECT *a)
{
if (a == NULL)
return;
......@@ -414,6 +416,7 @@ void X509_OBJECT_free_contents(X509_OBJECT *a)
X509_CRL_free(a->data.crl);
break;
}
OPENSSL_free(a);
}
static int x509_object_idx_cnt(STACK_OF(X509_OBJECT) *h, int type,
......@@ -476,13 +479,13 @@ STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *v)
return v->objs;
}
STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm)
STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm)
{
int i, idx, cnt;
STACK_OF(X509) *sk;
STACK_OF(X509) *sk = NULL;
X509 *x;
X509_OBJECT *obj;
sk = sk_X509_new_null();
CRYPTO_THREAD_write_lock(ctx->ctx->lock);
idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_X509, nm, &cnt);
if (idx < 0) {
......@@ -490,21 +493,25 @@ STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm)
* Nothing found in cache: do lookup to possibly add new objects to
* cache
*/
X509_OBJECT xobj;
X509_OBJECT *xobj = X509_OBJECT_new();
CRYPTO_THREAD_unlock(ctx->ctx->lock);
if (!X509_STORE_get_by_subject(ctx, X509_LU_X509, nm, &xobj)) {
sk_X509_free(sk);
if (xobj == NULL)
return NULL;
if (!X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, nm, xobj)) {
X509_OBJECT_free(xobj);
return NULL;
}
X509_OBJECT_free_contents(&xobj);
X509_OBJECT_free(xobj);
CRYPTO_THREAD_write_lock(ctx->ctx->lock);
idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_X509, nm, &cnt);
if (idx < 0) {
CRYPTO_THREAD_unlock(ctx->ctx->lock);
sk_X509_free(sk);
return NULL;
}
}
sk = sk_X509_new_null();
for (i = 0; i < cnt; i++, idx++) {
obj = sk_X509_OBJECT_value(ctx->ctx->objs, idx);
x = obj->data.x509;
......@@ -518,25 +525,23 @@ STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *ctx, X509_NAME *nm)
}
CRYPTO_THREAD_unlock(ctx->ctx->lock);
return sk;
}
STACK_OF(X509_CRL) *X509_STORE_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm)
STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *ctx, X509_NAME *nm)
{
int i, idx, cnt;
STACK_OF(X509_CRL) *sk;
STACK_OF(X509_CRL) *sk = sk_X509_CRL_new_null();
X509_CRL *x;
X509_OBJECT *obj, xobj;
sk = sk_X509_CRL_new_null();
X509_OBJECT *obj, *xobj = X509_OBJECT_new();
/*
* Always do lookup to possibly add new CRLs to cache
*/
if (!X509_STORE_get_by_subject(ctx, X509_LU_CRL, nm, &xobj)) {
/* Always do lookup to possibly add new CRLs to cache */
if (sk == NULL || xobj == NULL ||
!X509_STORE_CTX_get_by_subject(ctx, X509_LU_CRL, nm, xobj)) {
X509_OBJECT_free(xobj);
sk_X509_CRL_free(sk);
return NULL;
}
X509_OBJECT_free_contents(&xobj);
X509_OBJECT_free(xobj);
CRYPTO_THREAD_write_lock(ctx->ctx->lock);
idx = x509_object_idx_cnt(ctx->ctx->objs, X509_LU_CRL, nm, &cnt);
if (idx < 0) {
......@@ -602,32 +607,36 @@ X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h,
int X509_STORE_CTX_get1_issuer(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
{
X509_NAME *xn;
X509_OBJECT obj, *pobj;
X509_OBJECT *obj = X509_OBJECT_new(), *pobj = NULL;
int i, ok, idx, ret;
if (obj == NULL)
return -1;
*issuer = NULL;
xn = X509_get_issuer_name(x);
ok = X509_STORE_get_by_subject(ctx, X509_LU_X509, xn, &obj);
ok = X509_STORE_CTX_get_by_subject(ctx, X509_LU_X509, xn, obj);
if (ok != X509_LU_X509) {
X509_OBJECT_free(obj);
if (ok == X509_LU_RETRY) {
X509_OBJECT_free_contents(&obj);
X509err(X509_F_X509_STORE_CTX_GET1_ISSUER, X509_R_SHOULD_RETRY);
return -1;
} else if (ok != X509_LU_FAIL) {
X509_OBJECT_free_contents(&obj);
}
if (ok != X509_LU_FAIL) {
/* not good :-(, break anyway */
return -1;
}
return 0;
}
/* If certificate matches all OK */
if (ctx->check_issued(ctx, x, obj.data.x509)) {
if (x509_check_cert_time(ctx, obj.data.x509, -1)) {
*issuer = obj.data.x509;
if (ctx->check_issued(ctx, x, obj->data.x509)) {
if (x509_check_cert_time(ctx, obj->data.x509, -1)) {
*issuer = obj->data.x509;
X509_up_ref(*issuer);
X509_OBJECT_free(obj);
return 1;
}
}
X509_OBJECT_free_contents(&obj);
X509_OBJECT_free(obj);
/* Else find index of first cert accepted by 'check_issued' */
ret = 0;
......
......@@ -2172,12 +2172,12 @@ int X509_STORE_CTX_init(X509_STORE_CTX *ctx, X509_STORE *store, X509 *x509,
if (store && store->lookup_certs)
ctx->lookup_certs = store->lookup_certs;
else
ctx->lookup_certs = X509_STORE_get1_certs;
ctx->lookup_certs = X509_STORE_CTX_get1_certs;
if (store && store->lookup_crls)
ctx->lookup_crls = store->lookup_crls;
else
ctx->lookup_crls = X509_STORE_get1_crls;
ctx->lookup_crls = X509_STORE_CTX_get1_crls;
ctx->check_policy = check_policy;
......
......@@ -995,12 +995,12 @@ char *X509_TRUST_get0_name(X509_TRUST *xp);
int X509_TRUST_get_trust(X509_TRUST *xp);
/* BEGIN ERROR CODES */
/*
* The following lines are auto generated by the script mkerr.pl. Any changes
* made after this point may be overwritten when the script is next run.
* Content after this point is generated by util/mkerr.pl
* DO NOT EDIT!
*/
void ERR_load_X509_strings(void);
/* Error codes for the X509 functions. */
/* Function codes. */
......@@ -1035,6 +1035,7 @@ void ERR_load_X509_strings(void);
# define X509_F_X509_NAME_ENTRY_SET_OBJECT 115
# define X509_F_X509_NAME_ONELINE 116
# define X509_F_X509_NAME_PRINT 117
# define X509_F_X509_OBJECT_NEW 150
# define X509_F_X509_PRINT_EX_FP 118
# define X509_F_X509_PUBKEY_DECODE 148
# define X509_F_X509_PUBKEY_GET0 119
......
......@@ -230,17 +230,18 @@ X509_OBJECT *X509_OBJECT_retrieve_by_subject(STACK_OF(X509_OBJECT) *h,
X509_OBJECT *X509_OBJECT_retrieve_match(STACK_OF(X509_OBJECT) *h,
X509_OBJECT *x);
int X509_OBJECT_up_ref_count(X509_OBJECT *a);
X509_OBJECT *X509_OBJECT_new(void);
void X509_OBJECT_free(X509_OBJECT *a);
int X509_OBJECT_get_type(X509_OBJECT *a);
X509 *X509_OBJECT_get0_X509(X509_OBJECT *a);
void X509_OBJECT_free_contents(X509_OBJECT *a);
X509_CRL *X509_OBJECT_get0_X509_CRL(X509_OBJECT *a);
X509_STORE *X509_STORE_new(void);
void X509_STORE_free(X509_STORE *v);
int X509_STORE_up_ref(X509_STORE *v);
STACK_OF(X509_OBJECT) *X509_STORE_get0_objects(X509_STORE *v);
STACK_OF(X509) *X509_STORE_get1_certs(X509_STORE_CTX *st, X509_NAME *nm);
STACK_OF(X509_CRL) *X509_STORE_get1_crls(X509_STORE_CTX *st, X509_NAME *nm);
STACK_OF(X509) *X509_STORE_CTX_get1_certs(X509_STORE_CTX *st, X509_NAME *nm);
STACK_OF(X509_CRL) *X509_STORE_CTX_get1_crls(X509_STORE_CTX *st, X509_NAME *nm);
int X509_STORE_set_flags(X509_STORE *ctx, unsigned long flags);
int X509_STORE_set_purpose(X509_STORE *ctx, int purpose);
int X509_STORE_set_trust(X509_STORE *ctx, int trust);
......@@ -284,6 +285,9 @@ X509_STORE_CTX_verify X509_STORE_CTX_get_verify(X509_STORE_CTX *ctx);
# define X509_STORE_CTX_get_chain X509_STORE_CTX_get0_chain
# define X509_STORE_CTX_set_chain X509_STORE_CTX_set0_untrusted
# define X509_STORE_CTX_trusted_stack X509_STORE_CTX_set0_trusted_stack
# define X509_STORE_get_by_subject X509_STORE_CTX_get_by_subject
# define X509_STORE_get1_cert X509_STORE_CTX_get1_certs
# define X509_STORE_get1_crl X509_STORE_CTX_get1_crls
#endif
X509_LOOKUP *X509_STORE_add_lookup(X509_STORE *v, X509_LOOKUP_METHOD *m);
......@@ -293,10 +297,10 @@ X509_LOOKUP_METHOD *X509_LOOKUP_file(void);
int X509_STORE_add_cert(X509_STORE *ctx, X509 *x);
int X509_STORE_add_crl(X509_STORE *ctx, X509_CRL *x);
int X509_STORE_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
X509_OBJECT *ret);
X509_OBJECT *X509_STORE_get_X509_by_subject(X509_STORE_CTX *vs, int type,
X509_NAME *name);
int X509_STORE_CTX_get_by_subject(X509_STORE_CTX *vs, int type, X509_NAME *name,
X509_OBJECT *ret);
X509_OBJECT *X509_STORE_CTX_get_obj_by_subject(X509_STORE_CTX *vs, int type,
X509_NAME *name);
int X509_LOOKUP_ctrl(X509_LOOKUP *ctx, int cmd, const char *argc,
long argl, char **ret);
......
......@@ -419,7 +419,7 @@ EVP_CipherFinal_ex 411 1_1_0 EXIST::FUNCTION:
d2i_DSA_PUBKEY 412 1_1_0 EXIST::FUNCTION:DSA
BN_CTX_get 413 1_1_0 EXIST::FUNCTION:
BN_to_montgomery 414 1_1_0 EXIST::FUNCTION:
X509_OBJECT_free_contents 415 1_1_0 EXIST::FUNCTION:
X509_OBJECT_get0_X509_CRL 415 1_1_0 EXIST::FUNCTION:
EVP_camellia_128_cfb8 416 1_1_0 EXIST::FUNCTION:CAMELLIA
EC_KEY_METHOD_free 417 1_1_0 EXIST::FUNCTION:EC
TS_TST_INFO_set_policy_id 418 1_1_0 EXIST::FUNCTION:TS
......@@ -1187,7 +1187,7 @@ X509_STORE_CTX_set0_param 1152 1_1_0 EXIST::FUNCTION:
DES_check_key_parity 1153 1_1_0 EXIST::FUNCTION:DES
EVP_aes_256_ocb 1154 1_1_0 EXIST::FUNCTION:OCB
X509_VAL_free 1155 1_1_0 EXIST::FUNCTION:
X509_STORE_get1_certs 1156 1_1_0 EXIST::FUNCTION:
X509_STORE_CTX_get1_certs 1156 1_1_0 EXIST::FUNCTION:
PEM_write_RSA_PUBKEY 1157 1_1_0 EXIST::FUNCTION:RSA
PKCS12_SAFEBAG_get0_p8inf 1158 1_1_0 EXIST::FUNCTION:
X509_CRL_set_issuer_name 1159 1_1_0 EXIST::FUNCTION:
......@@ -2323,7 +2323,7 @@ OCSP_request_add1_cert 2250 1_1_0 EXIST::FUNCTION:OCSP
ERR_load_X509_strings 2251 1_1_0 EXIST::FUNCTION:
SHA1_Transform 2252 1_1_0 EXIST::FUNCTION:
CMS_signed_get_attr_by_NID 2253 1_1_0 EXIST::FUNCTION:CMS
X509_STORE_get_by_subject 2254 1_1_0 EXIST::FUNCTION:
X509_STORE_CTX_get_by_subject 2254 1_1_0 EXIST::FUNCTION:
ASN1_OCTET_STRING_it 2255 1_1_0 EXIST:!EXPORT_VAR_AS_FUNCTION:VARIABLE:
ASN1_OCTET_STRING_it 2255 1_1_0 EXIST:EXPORT_VAR_AS_FUNCTION:FUNCTION:
sk_set_cmp_func 2256 1_1_0 EXIST::FUNCTION:
......@@ -2645,7 +2645,7 @@ i2d_X509_EXTENSION 2563 1_1_0 EXIST::FUNCTION:
PEM_read_bio_X509 2564 1_1_0 EXIST::FUNCTION:
DES_key_sched 2565 1_1_0 EXIST::FUNCTION:DES
GENERAL_NAME_dup 2566 1_1_0 EXIST::FUNCTION:
X509_STORE_get1_crls 2567 1_1_0 EXIST::FUNCTION:
X509_STORE_CTX_get1_crls 2567 1_1_0 EXIST::FUNCTION:
EVP_PKEY_meth_set_verify 2568 1_1_0 EXIST::FUNCTION:
EVP_sha256 2569 1_1_0 EXIST::FUNCTION:
CMS_unsigned_delete_attr 2570 1_1_0 EXIST::FUNCTION:CMS
......@@ -4193,7 +4193,7 @@ DH_meth_set_generate_key 4067 1_1_0 EXIST::FUNCTION:DH
DH_meth_free 4068 1_1_0 EXIST::FUNCTION:DH
DH_meth_get_generate_key 4069 1_1_0 EXIST::FUNCTION:DH
DH_set_flags 4070 1_1_0 EXIST::FUNCTION:DH
X509_STORE_get_X509_by_subject 4071 1_1_0 EXIST::FUNCTION:
X509_STORE_CTX_get_obj_by_subject 4071 1_1_0 EXIST::FUNCTION:
X509_OBJECT_free 4072 1_1_0 EXIST::FUNCTION:
X509_OBJECT_get0_X509 4073 1_1_0 EXIST::FUNCTION:
X509_STORE_CTX_get0_untrusted 4074 1_1_0 EXIST::FUNCTION:
......@@ -4209,6 +4209,7 @@ OPENSSL_hexchar2int 4083 1_1_0 EXIST::FUNCTION:
X509_STORE_set_ex_data 4084 1_1_0 EXIST::FUNCTION:
X509_STORE_get_ex_data 4085 1_1_0 EXIST::FUNCTION:
X509_STORE_get0_objects 4086 1_1_0 EXIST::FUNCTION:
X509_STORE_get0_param 4087 1_1_0 EXIST::FUNCTION:
X509_OBJECT_get_type 4088 1_1_0 EXIST::FUNCTION:
X509_STORE_set_verify 4089 1_1_0 EXIST::FUNCTION:
X509_OBJECT_get_type 4087 1_1_0 EXIST::FUNCTION:
X509_STORE_set_verify 4088 1_1_0 EXIST::FUNCTION:
X509_OBJECT_new 4089 1_1_0 EXIST::FUNCTION:
X509_STORE_get0_param 4090 1_1_0 EXIST::FUNCTION:
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册