Merge branch 'master' into InterAfrica
This commit is contained in:
@@ -404,6 +404,24 @@ CFunctionCore * CApplication::GetFunction( const char * Name )
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
CDataMember * CApplication::GetAddress( const char * SearchAddress )
|
||||
{
|
||||
CDataMember * AddressDef = NULL;
|
||||
char NamePath[100];
|
||||
char * Address;
|
||||
|
||||
// Address renamed?
|
||||
sprintf( NamePath, "Application/Addresses/Rename/%s", SearchAddress );
|
||||
if (!(Address = (char*)Config->GetChStr( NamePath, NULL, false ))) {
|
||||
Address = (char*)SearchAddress;
|
||||
}
|
||||
|
||||
// Get address def
|
||||
AddressDef = AddressList->GetChild( Address );
|
||||
return AddressDef;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool CApplication::Run( bool TerminateOnError )
|
||||
{
|
||||
bool CleanTerminate = true;
|
||||
|
||||
@@ -102,6 +102,8 @@ public:
|
||||
CFunctionCore * AddFunction( const char * Type, const char * Name );
|
||||
CFunctionCore * GetFunction( const char * Name );
|
||||
|
||||
CDataMember * GetAddress( const char * SearchAddress );
|
||||
|
||||
// Run Application
|
||||
bool Run( bool TerminateOnError );
|
||||
};
|
||||
|
||||
@@ -207,7 +207,7 @@ char const * GetTimeStr( const char * TimeSeparator )
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
char const * GetDateTimeStr( const char * DateSeparator, const char * TimeSeparator )
|
||||
char const * GetDateTimeStr( const char * DateSeparator, const char * TimeSeparator, const char * Separator )
|
||||
{
|
||||
unsigned char Day;
|
||||
unsigned char Month;
|
||||
@@ -221,8 +221,9 @@ char const * GetDateTimeStr( const char * DateSeparator, const char * TimeSepar
|
||||
GetTime( Hours, Minutes, Seconds );
|
||||
|
||||
// Build String
|
||||
sprintf( ReturnStr, "%04d%s%02d%s%02d %02d%s%02d%s%02d",
|
||||
sprintf( ReturnStr, "%04d%s%02d%s%02d%s%02d%s%02d%s%02d",
|
||||
Year, ((DateSeparator)? DateSeparator : ""), Month, ((DateSeparator)? DateSeparator : ""), Day,
|
||||
Separator,
|
||||
Hours, ((TimeSeparator)? TimeSeparator : ""), Minutes, ((TimeSeparator)? TimeSeparator : ""), Seconds );
|
||||
|
||||
return (ReturnStr);
|
||||
@@ -417,11 +418,12 @@ char const * BuildTimeStr( unsigned char Hours, unsigned char Minutes, unsigned
|
||||
|
||||
char const * BuildDateTimeStr( unsigned char Day, unsigned char Month, unsigned Year,
|
||||
unsigned char Hours, unsigned char Minutes, unsigned char Seconds,
|
||||
const char * DateSeparator, const char * TimeSeparator )
|
||||
const char * DateSeparator, const char * TimeSeparator, const char * Separator )
|
||||
{
|
||||
// Build String
|
||||
sprintf( ReturnStr, "%04d%s%02d%s%02d %02d%s%02d%s%02d",
|
||||
sprintf( ReturnStr, "%04d%s%02d%s%02d%s%02d%s%02d%s%02d",
|
||||
Year, ((DateSeparator)? DateSeparator : ""), Month, ((DateSeparator)? DateSeparator : ""), Day,
|
||||
Separator,
|
||||
Hours, ((TimeSeparator)? TimeSeparator : ""), Minutes, ((TimeSeparator)? TimeSeparator : ""), Seconds );
|
||||
|
||||
return (ReturnStr);
|
||||
@@ -457,7 +459,8 @@ char const * BuildTimeStr( const time_t EpochTime, bool LocalTime, const char *
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
char const * BuildDateTimeStr( const time_t EpochTime, bool LocalTime, const char * DateSeparator, const char * TimeSeparator )
|
||||
char const * BuildDateTimeStr( const time_t EpochTime, bool LocalTime,
|
||||
const char * DateSeparator, const char * TimeSeparator, const char * Separator )
|
||||
{
|
||||
unsigned char Day;
|
||||
unsigned char Month;
|
||||
|
||||
@@ -29,7 +29,7 @@ bool SetDateTime( unsigned char Day, unsigned char Month, unsigned Year,
|
||||
|
||||
char const * GetDateStr( const char * DateSeparator = "/" );
|
||||
char const * GetTimeStr( const char * TimeSeparator = ":" );
|
||||
char const * GetDateTimeStr( const char * DateSeparator = "/", const char * TimeSeparator = ":" );
|
||||
char const * GetDateTimeStr( const char * DateSeparator = "/", const char * TimeSeparator = ":", const char * Separator = " " );
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -50,11 +50,12 @@ char const * BuildDateStr( unsigned char Day, unsigned char Month, unsigned Yea
|
||||
char const * BuildTimeStr( unsigned char Hours, unsigned char Minutes, unsigned char Seconds, const char * TimeSeparator = ":" );
|
||||
char const * BuildDateTimeStr( unsigned char Day, unsigned char Month, unsigned Year,
|
||||
unsigned char Hours, unsigned char Minutes, unsigned char Seconds,
|
||||
const char * DateSeparator = "/", const char * TimeSeparator = ":" );
|
||||
const char * DateSeparator = "/", const char * TimeSeparator = ":", const char * Separator = " " );
|
||||
|
||||
char const * BuildDateStr( const time_t EpochTime, bool LocalTime, const char * DateSeparator = "/" );
|
||||
char const * BuildTimeStr( const time_t EpochTime, bool LocalTime, const char * TimeSeparator = ":" );
|
||||
char const * BuildDateTimeStr( const time_t EpochTime, bool LocalTime, const char * DateSeparator = "/", const char * TimeSeparator = ":" );
|
||||
char const * BuildDateTimeStr( const time_t EpochTime, bool LocalTime,
|
||||
const char * DateSeparator = "/", const char * TimeSeparator = ":", const char * Separator = " " );
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -352,13 +352,12 @@ bool CJSONparse::ReadFromBuffer( const char * BasePath )
|
||||
|
||||
// Parse Root Object
|
||||
SkipWhiteSpace();
|
||||
if (!ParseObject( BaseMember ) && !Error && !ParseArray( BaseMember )) {
|
||||
if (!Error) {
|
||||
if (!ParseObject( BaseMember ) && !Error && !ParseArray( BaseMember ) && !Error) {
|
||||
Error = true;
|
||||
CharNo += BufPos-Mark;
|
||||
sprintf( ErrorText, "First entry in file must be an Object or Array on line %d:%d", LineNo, CharNo );
|
||||
}
|
||||
FreeBuffer();
|
||||
if (Error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -368,7 +367,6 @@ bool CJSONparse::ReadFromBuffer( const char * BasePath )
|
||||
Error = true;
|
||||
CharNo += BufPos-Mark;
|
||||
sprintf( ErrorText, "No content expected after Root object on line %d:%d", LineNo, CharNo );
|
||||
FreeBuffer();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -63,19 +63,10 @@ CSelectableBare::~CSelectableBare()
|
||||
CDataMember * CSelectableBare::GetHandleAddress( THandle * Handle, const char * HandleRef )
|
||||
{
|
||||
CDataMember * AddressDef = NULL;
|
||||
char NamePath[100];
|
||||
char * Address;
|
||||
|
||||
// Handle renamed?
|
||||
sprintf( NamePath, "Application/Addresses/Rename/%s", HandleRef );
|
||||
if (!(Address = (char*)Application->Config->GetChStr( NamePath, NULL, false ))) {
|
||||
Address = (char*)HandleRef;
|
||||
}
|
||||
|
||||
// Get address def
|
||||
if ((AddressDef = Application->AddressList->GetChild( Address ))) {
|
||||
if ((AddressDef = Application->GetAddress( HandleRef ))) {
|
||||
if (Log) Log->Message( LogLevel, dlMedium, "%s/%s: Handle '%s' - Use address '%s' ('%s')",
|
||||
ProcessName, Name, Handle->Name, Address, HandleRef );
|
||||
ProcessName, Name, Handle->Name, AddressDef->GetName(), HandleRef );
|
||||
}
|
||||
return AddressDef;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
// redA Libraries
|
||||
#include "ApplicationCore.h"
|
||||
#include "SelectableCore.h"
|
||||
#include "UtilCore.h"
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
@@ -40,7 +41,7 @@ CFunctionCore * NewSelectableCore( const char * Name ) {
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Resolve action handlder
|
||||
// Resolve action handler
|
||||
void ResolveHandler( int Signal, siginfo_t * SignalInfo, void * Context )
|
||||
{
|
||||
TResolveReq * ResolveReq;
|
||||
@@ -106,7 +107,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
CDataMember * SerialConfig;
|
||||
THandle * Handle;
|
||||
char * Type;
|
||||
char * Name;
|
||||
char * PortName;
|
||||
char * Address;
|
||||
char * Port;
|
||||
char * ParityText;
|
||||
@@ -132,7 +133,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
Type = (char*)HandleConfig->GetChStr( "Type", "TCPclient", true );
|
||||
if (!strcasecmp( Type, "Serial" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Port/Name", NULL )) && (AddressDef = Application->AddressList->GetChild( Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Port/Name", NULL )) && (AddressDef = Application->AddressList->GetChild( PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
} else {
|
||||
Address = (char*)HandleConfig->GetChStr( "Port/Address", NULL, true ); // Get default value
|
||||
@@ -176,7 +177,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
}
|
||||
else if (!strcasecmp( Type, "LinePrinter" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Port/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Port/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
} else {
|
||||
Address = (char*)HandleConfig->GetChStr( "Port/Address", NULL, true ); // Get default value
|
||||
@@ -186,7 +187,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
}
|
||||
else if (!strcasecmp( Type, "UNIXserver" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
} else {
|
||||
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
||||
@@ -197,7 +198,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
}
|
||||
else if (!strcasecmp( Type, "UNIXclient" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
} else {
|
||||
Address = (char*)HandleConfig->GetChStr( "Socket/Address", NULL, true ); // Get default Address value
|
||||
@@ -207,7 +208,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
}
|
||||
else if (!strcasecmp( Type, "UDPserver" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
||||
} else {
|
||||
@@ -219,7 +220,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
}
|
||||
else if (!strcasecmp( Type, "UDPclient" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
||||
} else {
|
||||
@@ -231,7 +232,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
}
|
||||
else if (!strcasecmp( Type, "TCPserver" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
||||
} else {
|
||||
@@ -244,7 +245,7 @@ bool CSelectableCore::Init( CDataMember * FunctionConfig )
|
||||
}
|
||||
else if (!strcasecmp( Type, "TCPclient" ))
|
||||
{
|
||||
if ((Name = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, Name ))) {
|
||||
if ((PortName = (char*)HandleConfig->GetChStr( "Socket/Name", NULL )) && (AddressDef = GetHandleAddress( Handle, PortName ))) {
|
||||
Address = (char*)AddressDef->GetChStr( "Address", NULL, true ); // Get address list value
|
||||
Port = (char*)AddressDef->GetChStr( "Port", "0", true ); // Get AddressList Port value
|
||||
} else {
|
||||
|
||||
@@ -223,13 +223,6 @@ protected:
|
||||
// Buffer operations
|
||||
virtual bool ProcessInputBuffer( THandle * Handle, bool Force );
|
||||
|
||||
// Convert string to lower case
|
||||
inline char * strlcase( char * Str ) {
|
||||
for (char * Ch = Str; *Ch; Ch++ )
|
||||
*Ch = tolower(*Ch);
|
||||
return Str;
|
||||
}
|
||||
|
||||
public:
|
||||
// Life Cycle
|
||||
CSelectableBare( const char * Name, const char * Type = TYPE_SELECTABLE );
|
||||
|
||||
110
UtilCore.cpp
110
UtilCore.cpp
@@ -119,6 +119,40 @@ char * BytesToBinStr( const char * Bytes, const int Len, const char * Separator,
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
char * IntToBinStr( const int Bytes, const int Len, const char * Separator, char * OutBuf )
|
||||
{
|
||||
char * TempBuf;
|
||||
char * BufPos;
|
||||
bool First = true;
|
||||
char SepLen = (Separator)? strlen(Separator) : 0;
|
||||
u_int8_t Byte;
|
||||
|
||||
// Select/create output buffer
|
||||
if (!OutBuf) {
|
||||
TempBuf = ReturnStr;
|
||||
}
|
||||
else {
|
||||
// if (!*OutBuf)
|
||||
// *OutBuf = (char*)malloc( Len*(8+SepLen)+1 );
|
||||
// TempBuf = *OutBuf;
|
||||
TempBuf = OutBuf;
|
||||
}
|
||||
BufPos = TempBuf;
|
||||
|
||||
// Print each byte as 8-bit binary
|
||||
for (int i=Len-1; i>=0; i--) {
|
||||
Byte = (Bytes >> (i*8)) & 0xFF;
|
||||
sprintf( BufPos, "%s%c%c%c%c%c%c%c%c", ((First || !Separator)? "" : Separator),
|
||||
(Byte & 0x80)?'1':'0', (Byte & 0x40)?'1':'0', (Byte & 0x20)?'1':'0', (Byte & 0x10)?'1':'0',
|
||||
(Byte & 0x08)?'1':'0', (Byte & 0x04)?'1':'0', (Byte & 0x02)?'1':'0', (Byte & 0x01)?'1':'0' );
|
||||
BufPos += (First)? 8 : 8+SepLen;
|
||||
First = false;
|
||||
}
|
||||
*BufPos = 0;
|
||||
return TempBuf;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
char * HexStrToBytes( const char * Str, const int Len, const char * Separator, char * OutBuf )
|
||||
{
|
||||
char * TempBuf;
|
||||
@@ -179,3 +213,79 @@ char * StrSearch( const char * Haystack, const char * Needle, const int Haystac
|
||||
return NULL;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool UrlEncode( const char * Input, char * Output, char ** EndPos )
|
||||
{
|
||||
int c;
|
||||
char h[] = "0123456789abcdef";
|
||||
char * Ipos = (char*)Input;
|
||||
char * Opos = Output;
|
||||
|
||||
if (!Ipos || !Opos) {
|
||||
if (*EndPos) *EndPos = NULL;
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((c = *Ipos)) {
|
||||
if ((('a' <= c) && (c <= 'z')) ||
|
||||
(('A' <= c) && (c <= 'Z')) ||
|
||||
(('0' <= c) && (c <= '9')) ||
|
||||
(c == '-') || (c == '_') || (c == '.')) {
|
||||
*Opos = *Ipos;
|
||||
Opos++;
|
||||
}
|
||||
else if (c == ' ') {
|
||||
*Opos = '+';
|
||||
Opos++;
|
||||
}
|
||||
else {
|
||||
Opos[0] = '%';
|
||||
Opos[1] = h[c >> 4];
|
||||
Opos[2] = h[c & 0x0f];
|
||||
Opos += 3;
|
||||
}
|
||||
Ipos++;
|
||||
}
|
||||
*Opos = 0;
|
||||
if (EndPos) *EndPos = Opos;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
bool UrlDecode( const char * Input, char * Output, char ** EndPos )
|
||||
{
|
||||
char c, c1, c2;
|
||||
char * Ipos = (char*)Input;
|
||||
char * Opos = Output;
|
||||
|
||||
if (!Ipos || !Opos) {
|
||||
if (EndPos) *EndPos = Output;
|
||||
return false;
|
||||
}
|
||||
|
||||
while ((c = *Ipos)) {
|
||||
if (c == '%') {
|
||||
if (!(c1 = tolower(Ipos[1])) ||
|
||||
!(c2 = tolower(Ipos[2])) ||
|
||||
!isxdigit(c1) || !isxdigit(c2))
|
||||
return false;
|
||||
c1 = (c1 <= '9')? (c1 - '0') : (c1 - 'a' + 10);
|
||||
c2 = (c2 <= '9')? (c2 - '0') : (c2 - 'a' + 10);
|
||||
*Opos = 16*c1 + c2;
|
||||
Ipos += 3;
|
||||
}
|
||||
else if (c == '+') {
|
||||
*Opos = ' ';
|
||||
Ipos++;
|
||||
}
|
||||
else {
|
||||
*Opos = c;
|
||||
Ipos++;
|
||||
}
|
||||
Opos++;
|
||||
}
|
||||
*Opos = 0;
|
||||
if (EndPos) *EndPos = Opos;
|
||||
return true;
|
||||
}
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
23
UtilCore.h
23
UtilCore.h
@@ -9,7 +9,7 @@
|
||||
#define REDACORE_UTILCORE_H_
|
||||
|
||||
// Standard C/C++ Libraries
|
||||
/* none */
|
||||
#include <ctype.h>
|
||||
|
||||
// redA Libraries
|
||||
/* none */
|
||||
@@ -18,8 +18,10 @@
|
||||
|
||||
// Convert raw bytes to string
|
||||
char * BytesToSafeStr( const char * Bytes, const int Len, const bool NoCrLf = false, const char SpecChar = '.', char * OutBuf = NULL );
|
||||
char * BytesToHexStr( const char * Bytes, const int Len, const char *Separator = " ", char * OutBuf = NULL );
|
||||
char * BytesToBinStr( const char * Bytes, const int Len, const char *Separator = " ", char * OutBuf = NULL );
|
||||
char * BytesToHexStr( const char * Bytes, const int Len, const char * Separator = " ", char * OutBuf = NULL );
|
||||
char * BytesToBinStr( const char * Bytes, const int Len, const char * Separator = " ", char * OutBuf = NULL );
|
||||
|
||||
char * IntToBinStr( const int Bytes, const int Len, const char * Separator = " ", char * OutBuf = NULL );
|
||||
|
||||
// Convert string to raw bytes
|
||||
inline u_int8_t HexToInt( char Digit ) {
|
||||
@@ -31,9 +33,24 @@ inline u_int8_t HexToInt( char Digit ) {
|
||||
char * HexStrToBytes( const char * Str, const int Len, const char * Separator = NULL, char * OutBuf = NULL );
|
||||
char * BinStrToBytes( const char * Str, const int Len, const char * Separator = NULL, char * OutBuf = NULL );
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// Search string data
|
||||
char * StrSearch( const char * Haystack, const char * Needle, const int hLen = 0, const int nLen = 0 );
|
||||
|
||||
// Convert string to lower case
|
||||
inline char * strlcase( char * Str ) {
|
||||
for (char * Ch = Str; *Ch; Ch++ )
|
||||
*Ch = tolower(*Ch);
|
||||
return Str;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
// URL encoding & decoding
|
||||
bool UrlEncode( const char * Input, char * Output, char ** EndPos = NULL );
|
||||
bool UrlDecode( const char * Input, char * Output, char ** EndPos = NULL );
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#endif /* REDACORE_UTILCORE_H_ */
|
||||
|
||||
Reference in New Issue
Block a user