SSL patch from Magnus
This commit is contained in:
parent
3498ea8308
commit
7bc654bb16
|
@ -11,7 +11,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.163 2000/08/29 16:40:19 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/postmaster/postmaster.c,v 1.164 2000/08/30 14:54:22 momjian Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
*
|
*
|
||||||
|
@ -195,10 +195,7 @@ static int SendStop = false;
|
||||||
bool NetServer = false; /* listen on TCP/IP */
|
bool NetServer = false; /* listen on TCP/IP */
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
static bool SecureNetServer = false; /* if not zero, postmaster listens
|
static bool DisableSSL = false; /* Completely disable SSL, even if compiled in */
|
||||||
* for only SSL non-local
|
|
||||||
* connections */
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static pid_t StartupPID = 0,
|
static pid_t StartupPID = 0,
|
||||||
|
@ -455,7 +452,7 @@ PostmasterMain(int argc, char *argv[])
|
||||||
break;
|
break;
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
case 'l':
|
case 'l':
|
||||||
SecureNetServer = true;
|
DisableSSL = true;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case 'm':
|
case 'm':
|
||||||
|
@ -566,12 +563,13 @@ PostmasterMain(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
if (!NetServer && SecureNetServer)
|
if (!NetServer && !DisableSSL)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s: For SSL, you must enable TCP/IP connections.\n",
|
fprintf(stderr, "%s: For SSL, you must enable TCP/IP connections. Use -l to disable SSL\n",
|
||||||
progname);
|
progname);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (!DisableSSL)
|
||||||
InitSSL();
|
InitSSL();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -754,7 +752,7 @@ usage(const char *progname)
|
||||||
printf(" -F turn fsync off\n");
|
printf(" -F turn fsync off\n");
|
||||||
printf(" -i listen on TCP/IP sockets\n");
|
printf(" -i listen on TCP/IP sockets\n");
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
printf(" -l listen only on SSL connections (EXPERIMENTAL)\n");
|
printf(" -l disable SSL\n");
|
||||||
#endif
|
#endif
|
||||||
printf(" -N <number> maximum number of allowed connections (1..%d, default %d)\n",
|
printf(" -N <number> maximum number of allowed connections (1..%d, default %d)\n",
|
||||||
MAXBACKENDS, DEF_MAXBACKENDS);
|
MAXBACKENDS, DEF_MAXBACKENDS);
|
||||||
|
@ -1062,6 +1060,10 @@ readStartupPacket(void *arg, PacketLen len, void *pkt)
|
||||||
char SSLok;
|
char SSLok;
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
|
if (DisableSSL || port->laddr.sa.sa_family != AF_INET)
|
||||||
|
/* No SSL when disabled or on Unix sockets */
|
||||||
|
SSLok = 'N';
|
||||||
|
else
|
||||||
SSLok = 'S'; /* Support for SSL */
|
SSLok = 'S'; /* Support for SSL */
|
||||||
#else
|
#else
|
||||||
SSLok = 'N'; /* No support for SSL */
|
SSLok = 'N'; /* No support for SSL */
|
||||||
|
@ -1073,6 +1075,7 @@ readStartupPacket(void *arg, PacketLen len, void *pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
|
if (SSLok == 'S') {
|
||||||
if (!(port->ssl = SSL_new(SSL_context)) ||
|
if (!(port->ssl = SSL_new(SSL_context)) ||
|
||||||
!SSL_set_fd(port->ssl, port->sock) ||
|
!SSL_set_fd(port->ssl, port->sock) ||
|
||||||
SSL_accept(port->ssl) <= 0)
|
SSL_accept(port->ssl) <= 0)
|
||||||
|
@ -1081,6 +1084,7 @@ readStartupPacket(void *arg, PacketLen len, void *pkt)
|
||||||
ERR_reason_error_string(ERR_get_error()), errno, strerror(errno));
|
ERR_reason_error_string(ERR_get_error()), errno, strerror(errno));
|
||||||
return STATUS_ERROR;
|
return STATUS_ERROR;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
/* ready for the normal startup packet */
|
/* ready for the normal startup packet */
|
||||||
PacketReceiveSetup(&port->pktInfo,
|
PacketReceiveSetup(&port->pktInfo,
|
||||||
|
@ -1091,18 +1095,6 @@ readStartupPacket(void *arg, PacketLen len, void *pkt)
|
||||||
|
|
||||||
/* Could add additional special packet types here */
|
/* Could add additional special packet types here */
|
||||||
|
|
||||||
#ifdef USE_SSL
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Any SSL negotiation must have taken place here, so drop the
|
|
||||||
* connection ASAP if we require SSL
|
|
||||||
*/
|
|
||||||
if (SecureNetServer && !port->ssl)
|
|
||||||
{
|
|
||||||
PacketSendError(&port->pktInfo, "Backend requires secure connection.");
|
|
||||||
return STATUS_OK;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* Check we can handle the protocol the frontend is using. */
|
/* Check we can handle the protocol the frontend is using. */
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright 2000 by PostgreSQL Global Development Group
|
* Copyright 2000 by PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.34 2000/07/02 15:21:17 petere Exp $
|
* $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.35 2000/08/30 14:54:23 momjian Exp $
|
||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
@ -81,6 +81,10 @@ static void
|
||||||
static void
|
static void
|
||||||
showVersion(void);
|
showVersion(void);
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
static void
|
||||||
|
printSSLInfo(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -263,7 +267,9 @@ main(int argc, char *argv[])
|
||||||
" \\g or terminate with semicolon to execute query\n"
|
" \\g or terminate with semicolon to execute query\n"
|
||||||
" \\q to quit\n\n", pset.progname);
|
" \\q to quit\n\n", pset.progname);
|
||||||
}
|
}
|
||||||
|
#ifdef USE_SSL
|
||||||
|
printSSLInfo();
|
||||||
|
#endif
|
||||||
SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1);
|
SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1);
|
||||||
SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
|
SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
|
||||||
SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
|
SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
|
||||||
|
@ -639,3 +645,27 @@ showVersion(void)
|
||||||
puts("Read the file COPYRIGHT or use the command \\copyright to see the");
|
puts("Read the file COPYRIGHT or use the command \\copyright to see the");
|
||||||
puts("usage and distribution terms.");
|
puts("usage and distribution terms.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* printSSLInfo
|
||||||
|
*
|
||||||
|
* Prints information about the current SSL connection, if SSL is in use
|
||||||
|
*/
|
||||||
|
#ifdef USE_SSL
|
||||||
|
static void
|
||||||
|
printSSLInfo(void)
|
||||||
|
{
|
||||||
|
int sslbits = -1;
|
||||||
|
SSL *ssl;
|
||||||
|
|
||||||
|
ssl = PQgetssl(pset.db);
|
||||||
|
if (!ssl)
|
||||||
|
return; /* no SSL */
|
||||||
|
|
||||||
|
SSL_get_cipher_bits(ssl, &sslbits);
|
||||||
|
printf("SSL enabled connection. Chiper: %s, bits: %i\n\n",
|
||||||
|
SSL_get_cipher(ssl),sslbits);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.132 2000/08/20 10:55:35 petere Exp $
|
* $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.133 2000/08/30 14:54:23 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -63,7 +63,6 @@ inet_aton(const char *cp, struct in_addr * inp)
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
static SSL_CTX *SSL_context = NULL;
|
static SSL_CTX *SSL_context = NULL;
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define NOTIFYLIST_INITIAL_SIZE 10
|
#define NOTIFYLIST_INITIAL_SIZE 10
|
||||||
|
@ -131,6 +130,11 @@ static const PQconninfoOption PQconninfoOptions[] = {
|
||||||
{"options", "PGOPTIONS", DefaultOption, NULL,
|
{"options", "PGOPTIONS", DefaultOption, NULL,
|
||||||
"Backend-Debug-Options", "D", 40},
|
"Backend-Debug-Options", "D", 40},
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
{"requiressl", "PGREQUIRESSL", "0", NULL,
|
||||||
|
"Require-SSL", "", 1 },
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Terminating entry --- MUST BE LAST */
|
/* Terminating entry --- MUST BE LAST */
|
||||||
{NULL, NULL, NULL, NULL,
|
{NULL, NULL, NULL, NULL,
|
||||||
NULL, NULL, 0}
|
NULL, NULL, 0}
|
||||||
|
@ -303,6 +307,10 @@ PQconnectStart(const char *conninfo)
|
||||||
conn->pguser = tmp ? strdup(tmp) : NULL;
|
conn->pguser = tmp ? strdup(tmp) : NULL;
|
||||||
tmp = conninfo_getval(connOptions, "password");
|
tmp = conninfo_getval(connOptions, "password");
|
||||||
conn->pgpass = tmp ? strdup(tmp) : NULL;
|
conn->pgpass = tmp ? strdup(tmp) : NULL;
|
||||||
|
#ifdef USE_SSL
|
||||||
|
tmp = conninfo_getval(connOptions, "requiressl");
|
||||||
|
conn->require_ssl = tmp ? (tmp[0]=='1'?true:false) : false;
|
||||||
|
#endif
|
||||||
|
|
||||||
/* ----------
|
/* ----------
|
||||||
* Free the option info - all is in conn now
|
* Free the option info - all is in conn now
|
||||||
|
@ -475,6 +483,14 @@ PQsetdbLogin(const char *pghost, const char *pgport, const char *pgoptions,
|
||||||
else
|
else
|
||||||
conn->dbName = strdup(dbName);
|
conn->dbName = strdup(dbName);
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
if ((tmp = getenv("PGREQUIRESSL")) != NULL)
|
||||||
|
conn->require_ssl = (tmp[0]=='1')?true:false;
|
||||||
|
else
|
||||||
|
conn->require_ssl = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (error)
|
if (error)
|
||||||
conn->status = CONNECTION_BAD;
|
conn->status = CONNECTION_BAD;
|
||||||
else
|
else
|
||||||
|
@ -781,13 +797,55 @@ connectDBStart(PGconn *conn)
|
||||||
goto connect_errReturn;
|
goto connect_errReturn;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef USE_SSL
|
/* ----------
|
||||||
|
* Start / make connection. We are hopefully in non-blocking mode
|
||||||
|
* now, but it is possible that:
|
||||||
|
* 1. Older systems will still block on connect, despite the
|
||||||
|
* non-blocking flag. (Anyone know if this is true?)
|
||||||
|
* 2. We are running under Windows, and aren't even trying
|
||||||
|
* to be non-blocking (see above).
|
||||||
|
* 3. We are using SSL.
|
||||||
|
* Thus, we have make arrangements for all eventualities.
|
||||||
|
* ----------
|
||||||
|
*/
|
||||||
|
if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
|
||||||
|
{
|
||||||
|
#ifndef WIN32
|
||||||
|
if (errno == EINPROGRESS || errno == 0)
|
||||||
|
#else
|
||||||
|
if (WSAGetLastError() == WSAEINPROGRESS)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This needs to be done before we set into nonblocking, since SSL
|
* This is fine - we're in non-blocking mode, and the
|
||||||
* negotiation does not like that mode
|
* connection is in progress.
|
||||||
*/
|
*/
|
||||||
|
conn->status = CONNECTION_STARTED;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* Something's gone wrong */
|
||||||
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
|
"connectDBStart() -- connect() failed: %s\n"
|
||||||
|
"\tIs the postmaster running%s at '%s'\n"
|
||||||
|
"\tand accepting connections on %s '%s'?\n",
|
||||||
|
strerror(errno),
|
||||||
|
(family == AF_INET) ? " (with -i)" : "",
|
||||||
|
conn->pghost ? conn->pghost : "localhost",
|
||||||
|
(family == AF_INET) ?
|
||||||
|
"TCP/IP port" : "Unix socket",
|
||||||
|
conn->pgport);
|
||||||
|
goto connect_errReturn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* We're connected already */
|
||||||
|
conn->status = CONNECTION_MADE;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
/* Attempt to negotiate SSL usage */
|
/* Attempt to negotiate SSL usage */
|
||||||
if (conn->allow_ssl_try)
|
if (conn->allow_ssl_try)
|
||||||
{
|
{
|
||||||
|
@ -837,7 +895,7 @@ connectDBStart(PGconn *conn)
|
||||||
{
|
{
|
||||||
/* Received error - probably protocol mismatch */
|
/* Received error - probably protocol mismatch */
|
||||||
if (conn->Pfdebug)
|
if (conn->Pfdebug)
|
||||||
fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-6.6.\n");
|
fprintf(conn->Pfdebug, "Postmaster reports error, attempting fallback to pre-7.0.\n");
|
||||||
close(conn->sock);
|
close(conn->sock);
|
||||||
conn->allow_ssl_try = FALSE;
|
conn->allow_ssl_try = FALSE;
|
||||||
return connectDBStart(conn);
|
return connectDBStart(conn);
|
||||||
|
@ -849,55 +907,15 @@ connectDBStart(PGconn *conn)
|
||||||
goto connect_errReturn;
|
goto connect_errReturn;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
if (conn->require_ssl && !conn->ssl)
|
||||||
|
|
||||||
/* ----------
|
|
||||||
* Start / make connection. We are hopefully in non-blocking mode
|
|
||||||
* now, but it is possible that:
|
|
||||||
* 1. Older systems will still block on connect, despite the
|
|
||||||
* non-blocking flag. (Anyone know if this is true?)
|
|
||||||
* 2. We are running under Windows, and aren't even trying
|
|
||||||
* to be non-blocking (see above).
|
|
||||||
* 3. We are using SSL.
|
|
||||||
* Thus, we have make arrangements for all eventualities.
|
|
||||||
* ----------
|
|
||||||
*/
|
|
||||||
if (connect(conn->sock, &conn->raddr.sa, conn->raddr_len) < 0)
|
|
||||||
{
|
{
|
||||||
#ifndef WIN32
|
/* Require SSL, but server does not support/want it */
|
||||||
if (errno == EINPROGRESS || errno == 0)
|
|
||||||
#else
|
|
||||||
if (WSAGetLastError() == WSAEINPROGRESS)
|
|
||||||
#endif
|
|
||||||
{
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is fine - we're in non-blocking mode, and the
|
|
||||||
* connection is in progress.
|
|
||||||
*/
|
|
||||||
conn->status = CONNECTION_STARTED;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Something's gone wrong */
|
|
||||||
printfPQExpBuffer(&conn->errorMessage,
|
printfPQExpBuffer(&conn->errorMessage,
|
||||||
"connectDBStart() -- connect() failed: %s\n"
|
"Server does not support SSL when SSL was required.\n");
|
||||||
"\tIs the postmaster running%s at '%s'\n"
|
|
||||||
"\tand accepting connections on %s '%s'?\n",
|
|
||||||
strerror(errno),
|
|
||||||
(family == AF_INET) ? " (with -i)" : "",
|
|
||||||
conn->pghost ? conn->pghost : "localhost",
|
|
||||||
(family == AF_INET) ?
|
|
||||||
"TCP/IP port" : "Unix socket",
|
|
||||||
conn->pgport);
|
|
||||||
goto connect_errReturn;
|
goto connect_errReturn;
|
||||||
}
|
}
|
||||||
}
|
#endif
|
||||||
else
|
|
||||||
{
|
|
||||||
/* We're connected already */
|
|
||||||
conn->status = CONNECTION_MADE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This makes the connection non-blocking, for all those cases which
|
* This makes the connection non-blocking, for all those cases which
|
||||||
|
@ -2485,6 +2503,15 @@ PQsetClientEncoding(PGconn *conn, const char *encoding)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_SSL
|
||||||
|
SSL *PQgetssl(PGconn *conn)
|
||||||
|
{
|
||||||
|
if (!conn)
|
||||||
|
return NULL;
|
||||||
|
return conn->ssl;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
PQtrace(PGconn *conn, FILE *debug_port)
|
PQtrace(PGconn *conn, FILE *debug_port)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: libpq-fe.h,v 1.66 2000/04/14 00:24:52 tgl Exp $
|
* $Id: libpq-fe.h,v 1.67 2000/08/30 14:54:23 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -25,6 +25,9 @@ extern "C"
|
||||||
* such as Oid.
|
* such as Oid.
|
||||||
*/
|
*/
|
||||||
#include "postgres_ext.h"
|
#include "postgres_ext.h"
|
||||||
|
#ifdef USE_SSL
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Application-visible enum types */
|
/* Application-visible enum types */
|
||||||
|
|
||||||
|
@ -222,6 +225,11 @@ extern "C"
|
||||||
extern int PQbackendPID(const PGconn *conn);
|
extern int PQbackendPID(const PGconn *conn);
|
||||||
extern int PQclientEncoding(const PGconn *conn);
|
extern int PQclientEncoding(const PGconn *conn);
|
||||||
extern int PQsetClientEncoding(PGconn *conn, const char *encoding);
|
extern int PQsetClientEncoding(PGconn *conn, const char *encoding);
|
||||||
|
#ifdef USE_SSL
|
||||||
|
/* Get the SSL structure associated with a connection */
|
||||||
|
extern SSL *PQgetssl(PGconn *conn);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* Enable/disable tracing */
|
/* Enable/disable tracing */
|
||||||
extern void PQtrace(PGconn *conn, FILE *debug_port);
|
extern void PQtrace(PGconn *conn, FILE *debug_port);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: libpq-int.h,v 1.26 2000/05/27 04:13:05 momjian Exp $
|
* $Id: libpq-int.h,v 1.27 2000/08/30 14:54:24 momjian Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -263,6 +263,7 @@ struct pg_conn
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
bool allow_ssl_try; /* Allowed to try SSL negotiation */
|
bool allow_ssl_try; /* Allowed to try SSL negotiation */
|
||||||
|
bool require_ssl; /* Require SSL to make connection */
|
||||||
SSL *ssl; /* SSL status, if have SSL connection */
|
SSL *ssl; /* SSL status, if have SSL connection */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue