mirror of
https://git.openldap.org/openldap/openldap.git
synced 2026-01-06 15:10:22 -05:00
Rework URI parser to provide true scheme not proto/properties.
Plus more pthread rwlock code
This commit is contained in:
parent
f192014c1a
commit
e5ebc553ac
11 changed files with 142 additions and 69 deletions
|
|
@ -831,7 +831,7 @@ search_ldap_url(
|
|||
rc = ldap_url_parse( url, &ludp );
|
||||
if ( rc ) {
|
||||
switch ( rc ) {
|
||||
case LDAP_URL_ERR_NOTLDAP:
|
||||
case LDAP_URL_ERR_BADSCHEME:
|
||||
syslog( LOG_ALERT,
|
||||
"Not an LDAP URL: %s", url );
|
||||
break;
|
||||
|
|
|
|||
4
configure
vendored
4
configure
vendored
|
|
@ -1,6 +1,6 @@
|
|||
#! /bin/sh
|
||||
# $OpenLDAP$
|
||||
# from OpenLDAP: pkg/ldap/configure.in,v 1.307 2000/06/05 21:56:28 kurt Exp
|
||||
# from OpenLDAP: pkg/ldap/configure.in,v 1.308 2000/06/05 23:23:19 kurt Exp
|
||||
|
||||
# Copyright 1998-2000 The OpenLDAP Foundation. All Rights Reserved.
|
||||
#
|
||||
|
|
@ -9979,7 +9979,7 @@ done
|
|||
echo "configure: warning: could not locate sched_yield() or pthread_yield()" 1>&2
|
||||
fi
|
||||
|
||||
for ac_func in pthread_kill
|
||||
for ac_func in pthread_kill pthread_rwlock_destroy
|
||||
do
|
||||
echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
|
||||
echo "configure:9986: checking for $ac_func" >&5
|
||||
|
|
|
|||
|
|
@ -1215,7 +1215,7 @@ dnl [ol_cv_pthread_lpthread_lexc])
|
|||
fi
|
||||
|
||||
dnl Check functions for compatibility
|
||||
AC_CHECK_FUNCS(pthread_kill)
|
||||
AC_CHECK_FUNCS(pthread_kill pthread_rwlock_destroy)
|
||||
|
||||
dnl Check for pthread_detach with <pthread.h> inclusion
|
||||
dnl as it's symbol may have been mangled.
|
||||
|
|
|
|||
|
|
@ -173,13 +173,11 @@ typedef struct ldapcontrol {
|
|||
#define LDAP_CONTROL_MANAGEDSAIT "2.16.840.1.113730.3.4.2"
|
||||
|
||||
/* Experimental Controls */
|
||||
#define LDAP_CONTROL_X_MODIFY_PASSWD "1.3.6.1.4.1.4203.666.5.1"
|
||||
|
||||
/* LDAP Unsolicited Notifications */
|
||||
#define LDAP_NOTICE_OF_DISCONNECTION "1.3.6.1.4.1.1466.20036"
|
||||
#define LDAP_NOTICE_DISCONNECT LDAP_NOTICE_OF_DISCONNECTION
|
||||
|
||||
|
||||
/* LDAP Extended Operations */
|
||||
#define LDAP_EXOP_START_TLS "1.3.6.1.4.1.1466.20037"
|
||||
|
||||
|
|
@ -493,8 +491,7 @@ typedef struct ldap_friendly {
|
|||
*/
|
||||
typedef struct ldap_url_desc {
|
||||
struct ldap_url_desc *lud_next;
|
||||
unsigned long lud_properties;
|
||||
int lud_protocol;
|
||||
char *lud_scheme;
|
||||
char *lud_host;
|
||||
int lud_port;
|
||||
char *lud_dn;
|
||||
|
|
@ -504,19 +501,11 @@ typedef struct ldap_url_desc {
|
|||
char **lud_exts;
|
||||
} LDAPURLDesc;
|
||||
|
||||
/* lud_properties */
|
||||
#define LDAP_URL_USE_SSL 0x00000001
|
||||
|
||||
/* lud_protocol */
|
||||
#define LDAP_PROTO_TCP 0x00
|
||||
#define LDAP_PROTO_UDP 0x01
|
||||
#define LDAP_PROTO_LOCAL 0x02
|
||||
|
||||
#define LDAP_URL_SUCCESS 0x00 /* Success */
|
||||
#define LDAP_URL_ERR_MEM 0x01 /* can't allocate memory space */
|
||||
#define LDAP_URL_ERR_PARAM 0x02 /* parameter is bad */
|
||||
|
||||
#define LDAP_URL_ERR_NOTLDAP 0x03 /* URL doesn't begin with "ldap[s]://" */
|
||||
#define LDAP_URL_ERR_BADSCHEME 0x03 /* URL doesn't begin with "ldap[si]://" */
|
||||
#define LDAP_URL_ERR_BADENCLOSURE 0x04 /* URL is missing trailing ">" */
|
||||
#define LDAP_URL_ERR_BADURL 0x05 /* URL is bad */
|
||||
#define LDAP_URL_ERR_BADHOST 0x06 /* host port is bad */
|
||||
|
|
|
|||
|
|
@ -21,6 +21,18 @@
|
|||
|
||||
LDAP_BEGIN_DECL
|
||||
|
||||
#define LDAP_PROTO_TCP 1
|
||||
#define LDAP_PROTO_UDP 2
|
||||
#define LDAP_PROTO_IPC 3
|
||||
|
||||
LIBLDAP_F ( int )
|
||||
ldap_pvt_url_scheme2proto LDAP_P((
|
||||
const char * ));
|
||||
LIBLDAP_F ( int )
|
||||
ldap_pvt_url_scheme2tls LDAP_P((
|
||||
const char * ));
|
||||
|
||||
|
||||
LIBLDAP_F ( int )
|
||||
ldap_pvt_domain2dn LDAP_P((
|
||||
LDAP_CONST char *domain,
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ open_ldap_connection( LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv,
|
|||
if ( srv->lud_host == NULL || *srv->lud_host == 0 )
|
||||
addr = htonl( INADDR_LOOPBACK );
|
||||
|
||||
switch ( srv->lud_protocol ) {
|
||||
switch ( ldap_pvt_url_scheme2proto( srv->lud_scheme ) ) {
|
||||
case LDAP_PROTO_TCP:
|
||||
rc = ldap_connect_to_host( ld, sb, srv->lud_host,
|
||||
addr, port, async );
|
||||
|
|
@ -321,7 +321,8 @@ open_ldap_connection( LDAP *ld, Sockbuf *sb, LDAPURLDesc *srv,
|
|||
|
||||
#ifdef HAVE_TLS
|
||||
if (ld->ld_options.ldo_tls_mode == LDAP_OPT_X_TLS_HARD ||
|
||||
(srv->lud_properties & LDAP_URL_USE_SSL)) {
|
||||
strcmp( srv->lud_scheme, "ldaps" ) == 0 )
|
||||
{
|
||||
rc = ldap_pvt_tls_start( ld, sb, ld->ld_options.ldo_tls_ctx );
|
||||
if (rc != LDAP_SUCCESS)
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -827,6 +827,8 @@ ldap_chase_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp, int *hadrefp )
|
|||
ldap_pvt_hex_unescape( ref );
|
||||
len = strlen( ref );
|
||||
|
||||
/* FIXME: we should use the URL Parser */
|
||||
|
||||
if ( len > LDAP_LDAP_REF_STR_LEN && strncasecmp( ref,
|
||||
LDAP_LDAP_REF_STR, LDAP_LDAP_REF_STR_LEN ) == 0 ) {
|
||||
Debug( LDAP_DEBUG_TRACE,
|
||||
|
|
@ -865,6 +867,13 @@ ldap_chase_referrals( LDAP *ld, LDAPRequest *lr, char **errstrp, int *hadrefp )
|
|||
return( -1 );
|
||||
}
|
||||
|
||||
if (( srv->lud_scheme = LDAP_STRDUP("ldap")) == NULL ) {
|
||||
LDAP_FREE( (char *)srv );
|
||||
ber_free( ber, 1 );
|
||||
ld->ld_errno = LDAP_NO_MEMORY;
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if (( srv->lud_host = LDAP_STRDUP( tmpref )) == NULL ) {
|
||||
LDAP_FREE( (char *)srv );
|
||||
ber_free( ber, 1 );
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
* LIBLDAP url.c -- LDAP URL (RFC 2255) related routines
|
||||
*
|
||||
* LDAP URLs look like this:
|
||||
* ldap[s]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
|
||||
* ldap[is]://host:port[/[dn[?[attributes][?[scope][?[filter][?exts]]]]]]
|
||||
*
|
||||
* where:
|
||||
* attributes is a comma separated list
|
||||
|
|
@ -40,51 +40,98 @@
|
|||
static const char* skip_url_prefix LDAP_P((
|
||||
const char *url,
|
||||
int *enclosedp,
|
||||
unsigned long *properties,
|
||||
int *protocol));
|
||||
const char **scheme ));
|
||||
|
||||
int ldap_pvt_url_scheme2proto( const char *scheme )
|
||||
{
|
||||
assert( scheme );
|
||||
|
||||
if( scheme == NULL ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( strcmp("ldap", scheme) == 0 ) {
|
||||
return LDAP_PROTO_TCP;
|
||||
}
|
||||
|
||||
if( strcmp("ldapi", scheme) == 0 ) {
|
||||
return LDAP_PROTO_IPC;
|
||||
}
|
||||
|
||||
if( strcmp("ldaps", scheme) == 0 ) {
|
||||
return LDAP_PROTO_TCP;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ldap_pvt_url_scheme2tls( const char *scheme )
|
||||
{
|
||||
assert( scheme );
|
||||
|
||||
if( scheme == NULL ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return strcmp("ldaps", scheme) == 0;
|
||||
}
|
||||
|
||||
int
|
||||
ldap_is_ldap_url( LDAP_CONST char *url )
|
||||
{
|
||||
int enclosed, protocol;
|
||||
unsigned long properties;
|
||||
int enclosed;
|
||||
const char * scheme;
|
||||
|
||||
if( url == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( skip_url_prefix( url, &enclosed, &properties, &protocol) == NULL ) {
|
||||
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return !(properties & LDAP_URL_USE_SSL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int
|
||||
ldap_is_ldaps_url( LDAP_CONST char *url )
|
||||
{
|
||||
int enclosed, protocol;
|
||||
unsigned long properties;
|
||||
int enclosed;
|
||||
const char * scheme;
|
||||
|
||||
if( url == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( skip_url_prefix( url, &enclosed, &properties, &protocol) == NULL ) {
|
||||
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (properties & LDAP_URL_USE_SSL);
|
||||
return strcmp(scheme, "ldaps") == 0;
|
||||
}
|
||||
|
||||
int
|
||||
ldap_is_ldapi_url( LDAP_CONST char *url )
|
||||
{
|
||||
int enclosed;
|
||||
const char * scheme;
|
||||
|
||||
if( url == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if( skip_url_prefix( url, &enclosed, &scheme ) == NULL ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strcmp(scheme, "ldapi") == 0;
|
||||
}
|
||||
|
||||
static const char*
|
||||
skip_url_prefix(
|
||||
const char *url,
|
||||
int *enclosedp,
|
||||
unsigned long *properties,
|
||||
int *protocol
|
||||
)
|
||||
const char **scheme )
|
||||
{
|
||||
/*
|
||||
* return non-zero if this looks like a LDAP URL; zero if not
|
||||
|
|
@ -112,13 +159,11 @@ skip_url_prefix(
|
|||
p += LDAP_URL_URLCOLON_LEN;
|
||||
}
|
||||
|
||||
*properties = 0;
|
||||
|
||||
/* check for "ldap://" prefix */
|
||||
if ( strncasecmp( p, LDAP_URL_PREFIX, LDAP_URL_PREFIX_LEN ) == 0 ) {
|
||||
/* skip over "ldap://" prefix and return success */
|
||||
p += LDAP_URL_PREFIX_LEN;
|
||||
*protocol = LDAP_PROTO_TCP;
|
||||
*scheme = "ldap";
|
||||
return( p );
|
||||
}
|
||||
|
||||
|
|
@ -126,8 +171,7 @@ skip_url_prefix(
|
|||
if ( strncasecmp( p, LDAPS_URL_PREFIX, LDAPS_URL_PREFIX_LEN ) == 0 ) {
|
||||
/* skip over "ldaps://" prefix and return success */
|
||||
p += LDAPS_URL_PREFIX_LEN;
|
||||
*protocol = LDAP_PROTO_TCP;
|
||||
*properties |= LDAP_URL_USE_SSL;
|
||||
*scheme = "ldaps";
|
||||
return( p );
|
||||
}
|
||||
|
||||
|
|
@ -135,16 +179,7 @@ skip_url_prefix(
|
|||
if ( strncasecmp( p, LDAPI_URL_PREFIX, LDAPI_URL_PREFIX_LEN ) == 0 ) {
|
||||
/* skip over "ldapi://" prefix and return success */
|
||||
p += LDAPI_URL_PREFIX_LEN;
|
||||
*protocol = LDAP_PROTO_LOCAL;
|
||||
return( p );
|
||||
}
|
||||
|
||||
/* check for "ldapis://" prefix: should this be legal? */
|
||||
if ( strncasecmp( p, LDAPIS_URL_PREFIX, LDAPIS_URL_PREFIX_LEN ) == 0 ) {
|
||||
/* skip over "ldapis://" prefix and return success */
|
||||
p += LDAPIS_URL_PREFIX_LEN;
|
||||
*protocol = LDAP_PROTO_LOCAL;
|
||||
*properties |= LDAP_URL_USE_SSL;
|
||||
*scheme = "ldapi";
|
||||
return( p );
|
||||
}
|
||||
|
||||
|
|
@ -183,8 +218,8 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
|
|||
|
||||
LDAPURLDesc *ludp;
|
||||
char *p, *q;
|
||||
int i, enclosed, protocol;
|
||||
unsigned long properties;
|
||||
int i, enclosed;
|
||||
const char *scheme = NULL;
|
||||
const char *url_tmp;
|
||||
char *url;
|
||||
|
||||
|
|
@ -196,12 +231,14 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
|
|||
|
||||
*ludpp = NULL; /* pessimistic */
|
||||
|
||||
url_tmp = skip_url_prefix( url_in, &enclosed, &properties, &protocol );
|
||||
url_tmp = skip_url_prefix( url_in, &enclosed, &scheme );
|
||||
|
||||
if ( url_tmp == NULL ) {
|
||||
return LDAP_URL_ERR_NOTLDAP;
|
||||
return LDAP_URL_ERR_BADSCHEME;
|
||||
}
|
||||
|
||||
assert( scheme );
|
||||
|
||||
/* make working copy of the remainder of the URL */
|
||||
if (( url = LDAP_STRDUP( url_tmp )) == NULL ) {
|
||||
return( LDAP_URL_ERR_MEM );
|
||||
|
|
@ -232,13 +269,12 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
|
|||
ludp->lud_dn = NULL;
|
||||
ludp->lud_attrs = NULL;
|
||||
ludp->lud_filter = NULL;
|
||||
ludp->lud_properties = properties;
|
||||
ludp->lud_protocol = protocol;
|
||||
ludp->lud_scope = LDAP_SCOPE_BASE;
|
||||
ludp->lud_filter = NULL;
|
||||
|
||||
ludp->lud_filter = LDAP_STRDUP("(objectClass=*)");
|
||||
ludp->lud_scheme = LDAP_STRDUP( scheme );
|
||||
|
||||
if( ludp->lud_filter == NULL ) {
|
||||
if ( ludp->lud_scheme == NULL ) {
|
||||
LDAP_FREE( url );
|
||||
ldap_free_urldesc( ludp );
|
||||
return LDAP_URL_ERR_MEM;
|
||||
|
|
@ -275,7 +311,7 @@ ldap_url_parse( LDAP_CONST char *url_in, LDAPURLDesc **ludpp )
|
|||
}
|
||||
|
||||
/*
|
||||
* Kluge. ldap://111.222.333.444:389??cn=abc,o=company
|
||||
* Kludge. ldap://111.222.333.444:389??cn=abc,o=company
|
||||
*
|
||||
* On early Novell releases, search references/referrals were returned
|
||||
* in this format, i.e., the dn was kind of in the scope position,
|
||||
|
|
@ -484,8 +520,22 @@ ldap_url_dup ( LDAPURLDesc *ludp )
|
|||
return NULL;
|
||||
|
||||
*dest = *ludp;
|
||||
dest->lud_scheme = NULL;
|
||||
dest->lud_host = NULL;
|
||||
dest->lud_dn = NULL;
|
||||
dest->lud_filter = NULL;
|
||||
dest->lud_attrs = NULL;
|
||||
dest->lud_exts = NULL;
|
||||
dest->lud_next = NULL;
|
||||
|
||||
if ( ludp->lud_scheme != NULL ) {
|
||||
dest->lud_scheme = LDAP_STRDUP( ludp->lud_scheme );
|
||||
if (dest->lud_scheme == NULL) {
|
||||
ldap_free_urldesc(dest);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ludp->lud_host != NULL ) {
|
||||
dest->lud_host = LDAP_STRDUP( ludp->lud_host );
|
||||
if (dest->lud_host == NULL) {
|
||||
|
|
@ -602,7 +652,8 @@ ldap_url_parsehosts (LDAPURLDesc **ludlist, const char *hosts )
|
|||
return LDAP_NO_MEMORY;
|
||||
|
||||
/* count the URLs... */
|
||||
for (i = 0; specs[i] != NULL; i++) ;
|
||||
for (i = 0; specs[i] != NULL; i++) /* EMPTY */;
|
||||
|
||||
/* ...and put them in the "stack" backward */
|
||||
while (--i >= 0) {
|
||||
ludp = LDAP_CALLOC( 1, sizeof(LDAPURLDesc) );
|
||||
|
|
@ -621,8 +672,7 @@ ldap_url_parsehosts (LDAPURLDesc **ludlist, const char *hosts )
|
|||
ludp->lud_port = atoi(p);
|
||||
}
|
||||
ldap_pvt_hex_unescape(ludp->lud_host);
|
||||
ludp->lud_protocol = LDAP_PROTO_TCP;
|
||||
ludp->lud_properties = 0;
|
||||
ludp->lud_scheme = LDAP_STRDUP("ldap");
|
||||
ludp->lud_next = *ludlist;
|
||||
*ludlist = ludp;
|
||||
}
|
||||
|
|
@ -668,7 +718,8 @@ ldap_url_list2hosts (LDAPURLDesc *ludlist)
|
|||
}
|
||||
|
||||
char *
|
||||
ldap_url_list2urls (LDAPURLDesc *ludlist)
|
||||
ldap_url_list2urls(
|
||||
LDAPURLDesc *ludlist )
|
||||
{
|
||||
LDAPURLDesc *ludp;
|
||||
int size;
|
||||
|
|
@ -680,17 +731,22 @@ ldap_url_list2urls (LDAPURLDesc *ludlist)
|
|||
/* figure out how big the string is */
|
||||
size = 1; /* nul-term */
|
||||
for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
|
||||
size += strlen(ludp->lud_host) + 1 + sizeof("ldapis:///"); /* prefix, host, /, and space */
|
||||
if (ludp->lud_port != 0)
|
||||
size += strlen(ludp->lud_scheme) + strlen(ludp->lud_host);
|
||||
size += sizeof(":/// ");
|
||||
|
||||
if (ludp->lud_port != 0) {
|
||||
size += sprintf(buf, ":%d", ludp->lud_port);
|
||||
}
|
||||
}
|
||||
|
||||
s = LDAP_MALLOC(size);
|
||||
if (s == NULL)
|
||||
if (s == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = s;
|
||||
for (ludp = ludlist; ludp != NULL; ludp = ludp->lud_next) {
|
||||
p += sprintf(p, "ldap%s://%s", (ludp->lud_properties & LDAP_URL_USE_SSL) ? "s" : "", ludp->lud_host);
|
||||
p += sprintf(p, "%s://%s", ludp->lud_scheme, ludp->lud_host);
|
||||
if (ludp->lud_port != 0)
|
||||
p += sprintf(p, ":%d", ludp->lud_port);
|
||||
*p++ = '/';
|
||||
|
|
@ -720,6 +776,10 @@ ldap_free_urldesc( LDAPURLDesc *ludp )
|
|||
return;
|
||||
}
|
||||
|
||||
if ( ludp->lud_scheme != NULL ) {
|
||||
LDAP_FREE( ludp->lud_scheme );
|
||||
}
|
||||
|
||||
if ( ludp->lud_host != NULL ) {
|
||||
LDAP_FREE( ludp->lud_host );
|
||||
}
|
||||
|
|
|
|||
|
|
@ -685,7 +685,9 @@ cache_release_all( Cache *cache )
|
|||
Debug( LDAP_DEBUG_TRACE, "====> cache_release_all\n", 0, 0, 0 );
|
||||
|
||||
while ( (e = cache->c_lrutail) != NULL && LEI(e)->lei_refcnt == 0 ) {
|
||||
#ifdef LDAP_RDWR_DEBUG
|
||||
assert(!ldap_pvt_thread_rdwr_active(&LEI(e)->lei_rdwr));
|
||||
#endif
|
||||
|
||||
/* delete from cache and lru q */
|
||||
/* XXX do we need rc ? */
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ id2entry_delete( Backend *be, Entry *e )
|
|||
e->e_dn, 0 );
|
||||
|
||||
#ifdef notdef
|
||||
#ifdef LDAP_DEBUG
|
||||
#ifdef LDAP_RDWR_DEBUG
|
||||
/* check for writer lock */
|
||||
assert(ldap_pvt_thread_rdwr_writers(&e->e_rdwr) == 1);
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -218,7 +218,7 @@ static Listener * open_listener( const char* url )
|
|||
}
|
||||
|
||||
#ifndef HAVE_TLS
|
||||
if( lud->lud_properties & LDAP_URL_USE_SSL ) {
|
||||
if( ldap_is_ldaps_url( lud ) ) {
|
||||
Debug( LDAP_DEBUG_ANY,
|
||||
"daemon: TLS not supported (%s)\n",
|
||||
url, 0, 0 );
|
||||
|
|
@ -231,10 +231,10 @@ static Listener * open_listener( const char* url )
|
|||
}
|
||||
|
||||
#else
|
||||
l.sl_is_tls = (lud->lud_properties & LDAP_URL_USE_SSL);
|
||||
l.sl_is_tls = ldap_is_ldaps_url( lud );
|
||||
|
||||
if(! lud->lud_port ) {
|
||||
lud->lud_port = (lud->lud_properties & LDAP_URL_USE_SSL) ? LDAPS_PORT : LDAP_PORT;
|
||||
lud->lud_port = l.sl_is_tls ? LDAPS_PORT : LDAP_PORT;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue