Commit 98fb84f5 authored by Neoony's avatar Neoony
Browse files

v0.1 WIP28 -Lobby plugin now has a new dependency, cURL:...

v0.1 WIP28 -Lobby plugin now has a new dependency, cURL: https://forums.alliedmods.net/showthread.php?t=152216 -Added showing XP support of servers when voting
parent 5e5a9ba3
Pipeline #5892 passed with stages
in 3 minutes
......@@ -14,7 +14,11 @@ https://gaming.stackexchange.com/questions/172407/sourcebans-smx-database-failur
**SOCKET** Extension:
https://forums.alliedmods.net/showthread.php?t=67640
**This is included with the plugin.**
**This is included with the plugin.**
**cURL** Extension:
https://forums.alliedmods.net/showthread.php?t=152216
**This is included with the plugin.**
--
**Server Commands:**
......
......@@ -20,8 +20,9 @@
#include <sdkhooks>
//#include <emputils>
#include <socket>
#include <cURL>
#define PluginVer "v0.1 WIP27"
#define PluginVer "v0.1 WIP28"
public Plugin myinfo =
{
......@@ -91,6 +92,12 @@ char RSaddrx[10][256];
int curPlayersSQL[10];
int RSbots[10];
//XP
char XPaddrx[10][256];
int XPserverhasxp[10] = 0;
int XPsqlon[10];
char XPonstring[10][10];
//Refresh servers and Socket related
char RSIPaddr[10][256];
int RSPort[10];
......@@ -810,7 +817,7 @@ public Action QueryServersData(int args)
//char[] ServerIndexChar = new char[64];
//IntToString(ServerIndex, ServerIndexChar, 64);
char GetInfoOfServer[1024];
Format(GetInfoOfServer, sizeof(GetInfoOfServer), "SELECT name, addr, players, maxplayers, map FROM `LobbyDB`.`servers` WHERE players <= maxplayers - %i AND password=0 AND isenabled=1 AND name NOT LIKE '%%LOBBY%%' AND addr!='80.112.155.100:27015' AND addr!='80.112.155.100:27017' AND addr!='80.112.155.100:27016' ORDER BY players DESC LIMIT %i,1;", GetClientCount(), ServerIndex);
Format(GetInfoOfServer, sizeof(GetInfoOfServer), "SELECT name, addr, players, maxplayers, map, xp FROM `LobbyDB`.`servers` WHERE players <= maxplayers - %i AND password=0 AND isenabled=1 AND name NOT LIKE '%%LOBBY%%' AND addr!='80.112.155.100:27015' AND addr!='80.112.155.100:27017' AND addr!='80.112.155.100:27016' ORDER BY players DESC LIMIT %i,1;", GetClientCount(), ServerIndex);
//PrintToServer("%s", ServerIndexChar);
SQL_TQuery(hDatabase, InfoOfServer, GetInfoOfServer, ServerIndex);
if (GetConVarInt(l_debug) == 1)
......@@ -892,6 +899,18 @@ public void InfoOfServer(Handle hDriver, Handle hResult, const char[] sError, in
SQL_FetchString(hResult, 3, maxplayersx[ServerIndex], 256);
//Map
SQL_FetchString(hResult, 4, mapx[ServerIndex], 256);
//XP
XPsqlon[ServerIndex] = SQL_FetchInt(hResult, 5);
if (XPsqlon[ServerIndex] == 1)
{
Format(XPonstring[ServerIndex], 10, "ON");
}
if (XPsqlon[ServerIndex] != 1)
{
Format(XPonstring[ServerIndex], 10, "OFF");
}
//PrintToServer("[Database] maxplayers: %s", maxplayersx[ServerIndex]);
//PrintToServer("[Database] rowCount: %i", rowCount);
......@@ -987,7 +1006,7 @@ public Action StartVote()
for(int ServerCount=rowCount; ServerCount>=1; ServerCount--)
{
int ServerIndex = ServerCount - 1;
Format(ServerVoteItem[ServerIndex], 256, "%s [max %s players] \n %s", namex[ServerIndex], maxplayersx[ServerIndex], mapx[ServerIndex]);
Format(ServerVoteItem[ServerIndex], 256, "%s [max %s players] [XP %s] \n %s", namex[ServerIndex], maxplayersx[ServerIndex], XPonstring[ServerIndex], mapx[ServerIndex]);
AddMenuItem(VoteHandle, addrx[ServerIndex], ServerVoteItem[ServerIndex]);
//PrintToServer("IP: %s", addrx[ServerIndex]);
}
......@@ -1231,7 +1250,7 @@ public void RSAllServersAddr(Handle RShDriver, Handle RShResult, const char[] sE
//char[] ServerIndexChar = new char[64];
//IntToString(ServerIndex, ServerIndexChar, 64);
char RSGetAllServersAddrEach[1024];
Format(RSGetAllServersAddrEach, sizeof(RSGetAllServersAddrEach), "SELECT addr, players, bots FROM `LobbyDB`.`servers` WHERE password=0 AND isenabled=1 AND name NOT LIKE '%%LOBBY%%' AND addr!='80.112.155.100:27015' AND addr!='80.112.155.100:27017' ORDER BY players DESC LIMIT %i,1;", ServerIndex);
Format(RSGetAllServersAddrEach, sizeof(RSGetAllServersAddrEach), "SELECT addr, players, bots, xp FROM `LobbyDB`.`servers` WHERE password=0 AND isenabled=1 AND name NOT LIKE '%%LOBBY%%' AND addr!='80.112.155.100:27015' AND addr!='80.112.155.100:27017' ORDER BY players DESC LIMIT %i,1;", ServerIndex);
//PrintToServer("%s", ServerIndexChar);
SQL_TQuery(hDatabase, RSInfoOfServer, RSGetAllServersAddrEach, ServerIndex);
}
......@@ -1265,6 +1284,13 @@ public void RSInfoOfServer(Handle RShDriver, Handle RShResult, const char[] sErr
//PrintToServer("[Database] curPlayersSQL %i : %i", ServerIndex, curPlayersSQL[ServerIndex]);
RSbots[ServerIndex] = SQL_FetchInt(RShResult, 2);
//PrintToServer("[Database] RSbots %i : %i", ServerIndex, RSbots[ServerIndex]);
XPsqlon[ServerIndex] = SQL_FetchInt(RShResult, 3);
//XP IP
Format(XPaddrx[ServerIndex], 256, RSaddrx[ServerIndex]);
//PrintToServer(XPaddrx[ServerIndex]);
XPCheckURL(ServerIndex);
//Vanilla server IP replacement, as it is a local server
//Vanilla IP:
......@@ -1469,7 +1495,9 @@ public OnSocketReceive(Handle socket, char[] receiveData, const int dataSize, in
PrintToServer("[L] ServerInfo[%i]: %s", ServerIndexx, serverInfo[ServerIndexx]);
}
if (curPlayersSQL[ServerIndexx] != numPlayersS[ServerIndexx])
//XPCheckURL(ServerIndexx);
if (curPlayersSQL[ServerIndexx] != numPlayersS[ServerIndexx] || XPserverhasxp[ServerIndexx] != XPsqlon[ServerIndexx])
{
UpdateSQL(ServerIndexx);
}
......@@ -1557,7 +1585,7 @@ public Action UpdateSQL(int ServerIndexx)
if (hDatabase != null)
{
char UpdateServer[512];
Format(UpdateServer, sizeof(UpdateServer), "UPDATE `LobbyDB`.`servers` SET players = '%i', maxplayers = '%i' WHERE addr = '%s';", numPlayersS[ServerIndexx], maxPlayersS[ServerIndexx], RSaddrx[ServerIndexx]);
Format(UpdateServer, sizeof(UpdateServer), "UPDATE `LobbyDB`.`servers` SET players = '%i', maxplayers = '%i', xp = '%i' WHERE addr = '%s';", numPlayersS[ServerIndexx], maxPlayersS[ServerIndexx], XPserverhasxp[ServerIndexx], RSaddrx[ServerIndexx]);
SQL_TQuery(hDatabase, UpdateServer_Handle, UpdateServer, ServerIndexx);
}
if (hDatabase == null && GetConVarInt(l_debug) == 1)
......@@ -1623,3 +1651,61 @@ public void UpdateServer_Handle(Handle uhDriver, Handle uhResult, const char[] s
}
}
public Action XPCheckURL(int ServerIndexx)
{
Handle curl = curl_easy_init();
//Error
if(curl == INVALID_HANDLE)
{
if (GetConVarInt(l_debug) == 1)
{
PrintToServer("Could not check for server XP status");
}
}
if(curl != INVALID_HANDLE)
{
curl_easy_setopt_function(curl, CURLOPT_WRITEFUNCTION, ReadFunction, ServerIndexx);
char XPServerURLString[256];
Format(XPServerURLString, sizeof(XPServerURLString), "http://api.steampowered.com/ISteamApps/GetServersAtAddress/v0001?addr=%s", XPaddrx[ServerIndexx]);
curl_easy_setopt_string(curl, CURLOPT_URL, XPServerURLString);
curl_easy_perform_thread(curl, OnComplete);
}
}
public ReadFunction(Handle hndl, const char[] buffer, const bytes, const nmemb, int ServerIndexx)
{
if (GetConVarInt(l_debug) == 1)
{
PrintToServer("Buffer: %s", buffer);
}
if (StrContains(buffer, "Listed as official server for app in CDDB") != -1)
{
if (GetConVarInt(l_debug) == 1)
{
PrintToServer("Server %i has XP on", ServerIndexx);
}
XPserverhasxp[ServerIndexx] = 1;
}
if (StrContains(buffer, "Listed as official server for app in CDDB") == -1)
{
if (GetConVarInt(l_debug) == 1)
{
PrintToServer("Server %i has XP off", ServerIndexx);
}
XPserverhasxp[ServerIndexx] = 0;
}
return bytes*nmemb;
}
public OnComplete(Handle hndl, CURLcode code, any data)
{
CloseHandle(hndl);
}
#if defined _cURL_included
#endinput
#endif
#define _cURL_included
#include <cURL_header>
/*
========================================
The Following CURLOPT_* NOT support:
ERRORBUFFER // use curl_get_error_buffer
WRITEINFO // ???
PROGRESSFUNCTION // unused
PROGRESSDATA // same
HEADERFUNCTION // unused
DEBUGFUNCTION // unused
DEBUGDATA // same
SHARE // unsupport
PRIVATE // unsupport
SSL_CTX_FUNC // unused
SSL_CTX_DATA // same
IOCTLFUNCTION // unused
IOCTLDATA // same
CONV_FROM_NETWORK_FUNC // unused
CONV_TO_NETWORK_FUNC // unused
CONV_FROM_UTF8_FUNC // unused
SOCKOPTFUNCTION // unused
SOCKOPTDATA // unused
OPENSOCKETFUNCTION // used
OPENSOCKETDATA // used
COPYPOSTFIELDS // unsupport
SEEKFUNCTION // unused
SEEKDATA // unused
SOCKS5_GSSAPI_SERVICE // unsupport
SOCKS5_GSSAPI_NEC // unsupport
SSH_KEYFUNCTION // unsupport
SSH_KEYDATA // unsupport
INTERLEAVEFUNCTION // unsupport
CHUNK_BGN_FUNC // unsupport
CHUNK_END_FUNC // unsupport
FNMATCH_FUNC // unsupport
CHUNK_DATA // unsupport
FNMATCH_DATA // unsupport
TLSAUTH_USERNAME // unsupport, require tls-srp
TLSAUTH_PASSWORD // unsupport, require tls-srp
TLSAUTH_TYPE // unsupport, require tls-srp
CLOSESOCKETFUNCTION // unsupport
CLOSESOCKETDATA // unsupport
========================================*/
/*
========================================
The Following CURLOPT_* supports the "file://" notation.
COOKIEFILE
COOKIEJAR
RANDOM_FILE
EGDSOCKET
SSLKEY
CAPATH
NETRC_FILE
SSH_PUBLIC_KEYFILE
SSH_PRIVATE_KEYFILE
_CRLFILE
ISSUERCERT
SSH_KNOWNHOSTS
========================================*/
/*
========================================
The Following CURLINFO_* NOT support:
CURLINFO_SLIST
========================================*/
/*
========================================
The Following CURLFORM_* NOT support:
CURLFORM_PTRNAME
CURLFORM_PTRCONTENTS
CURLFORM_ARRAY
CURLFORM_BUFFER
CURLFORM_BUFFERPTR
CURLFORM_BUFFERLENGTH
CURLFORM_STREAM
========================================*/
/*************************************************************************************************/
/******************************************** OPTIONS ********************************************/
/*************************************************************************************************/
/**
* The Send & Receive Action
* Using on CURL_OnSend, CURL_OnReceive
* SendRecv_Act_GOTO_SEND = go to send
* SendRecv_Act_GOTO_RECV = go to receive
* SendRecv_Act_GOTO_WAIT = go to wait
* SendRecv_Act_GOTO_END = end the connection
* SendRecv_Act_GOTO_SEND_NO_WAIT = go to send but no select
* SendRecv_Act_GOTO_RECV_NO_WAIT = go to receive but no select
* To see how it work? see curl_echo_test.sp & curl_rcon_test.sp examples
*/
enum SendRecv_Act {
SendRecv_Act_NOTHING = 0,
SendRecv_Act_GOTO_SEND,
SendRecv_Act_GOTO_RECV,
SendRecv_Act_GOTO_WAIT,
SendRecv_Act_GOTO_END,
SendRecv_Act_GOTO_SEND_NO_WAIT,
SendRecv_Act_GOTO_RECV_NO_WAIT,
SendRecv_Act_LAST,
};
/**
* Hash type for curl_hash_file, curl_hash_string
*/
enum Openssl_Hash {
Openssl_Hash_MD5 = 0,
Openssl_Hash_MD4,
Openssl_Hash_MD2,
Openssl_Hash_SHA,
Openssl_Hash_SHA1,
Openssl_Hash_SHA224,
Openssl_Hash_SHA256,
Openssl_Hash_SHA384,
Openssl_Hash_SHA512,
Openssl_Hash_RIPEMD160,
};
/*************************************************************************************************/
/******************************************* CALLBACKS *******************************************/
/*************************************************************************************************/
/**
* called if curl_easy_perform_thread() or curl_easy_send_recv() Complete
* @ param Handle hndl The curl handle
* @ param CURLcode code The CURLcode code, see cURL_header.inc
* @ param any data Data passed to curl_easy_perform_thread()
* @ noreturn
*/
funcenum CURL_OnComplete
{
public(Handle:hndl, CURLcode: code),
public(Handle:hndl, CURLcode: code , any:data),
};
/**
* called if curl_easy_send_recv() before sending data
* @ param Handle hndl The curl handle
* @ param CURLcode code The last CURLcode code, see cURL_header.inc
* @ param cell_t last_sent_dataSize The last sent datasize
* @ param any data Data passed to curl_easy_send_recv()
* @ return SendRecv_Act
*/
funcenum CURL_OnSend
{
SendRecv_Act:public(Handle:hndl, CURLcode: code, const last_sent_dataSize),
SendRecv_Act:public(Handle:hndl, CURLcode: code, const last_sent_dataSize, any:data),
}
/**
* called if curl_easy_send_recv() after received data
* @ param Handle hndl The curl handle
* @ param CURLcode code The CURLcode code, see cURL_header.inc
* @ param String dataSize The received datasize
* @ param any data Data passed to curl_easy_send_recv()
* @ return SendRecv_Act
*/
funcenum CURL_OnReceive
{
SendRecv_Act:public(Handle:hndl, CURLcode: code, const String:receiveData[], const dataSize),
SendRecv_Act:public(Handle:hndl, CURLcode: code, const String:receiveData[], const dataSize, any:data),
}
/**
* called if Openssl_Hash_file() after hashed the file
* @ param bool success True on success, false if hash file fail
* @ param String buffer The hash string
* @ param any data Data passed to Openssl_Hash_file()
* @ noreturn
*/
funcenum Openssl_Hash_Complete
{
public(const bool:success, const String:buffer[]),
public(const bool:success, const String:buffer[], any:data),
}
funcenum CURL_Function_CB
{
// CURLOPT_WRITEFUNCTION
public(Handle:hndl, const String:buffer[], const bytes, const nmemb),
public(Handle:hndl, const String:buffer[], const bytes, const nmemb, any:data),
// CURLOPT_READFUNCTION
public(Handle:hndl, const bytes, const nmemb),
public(Handle:hndl, const bytes, const nmemb, any:data),
}
/*************************************************************************************************/
/******************************************** NATIVES ********************************************/
/*************************************************************************************************/
/**
* Create a curl handle
* @ return Handle The curl handle. Returns INVALID_HANDLE on failure
*/
native Handle:curl_easy_init();
/**
* Set a curl option for CURLOPTTYPE_OBJECTPOINT type
*
* @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential.
* @ param CURLoption opt The option to add (see enum CURLoption for details).
* @ param String buffer The value to set the option to.
* @ return bool 1 on success. 0 = The option not accept string or unsupport.
*/
native bool:curl_easy_setopt_string(Handle:hndl, CURLoption:opt, const char[] buffer);
/**
* Set a curl option for CURLOPTTYPE_LONG type
*
* @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential.
* @ param CURLoption opt The option to add (see enum CURLoption for details).
* @ param cell_t value The value to set the option to.
* @ return bool 1 on success. 0 = The option not accept integer or unsupport.
*/
native bool:curl_easy_setopt_int(Handle:hndl, CURLoption:opt, value);
/**
* Set a curl option for CURLOPTTYPE_LONG type
* @ example"
new opt[][2] = {
{_:CURLOPT_NOPROGRESS,1},
{_:CURLOPT_VERBOSE,0}
};
*
* @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential.
* @ param cell_t array The option array to add (see enum CURLoption for details).
* @ param cell_t array_size The array size.
* @ return bool 1 on success. 0 = The option not accept integer or unsupport.
*/
native bool:curl_easy_setopt_int_array(Handle:hndl, array[][2], array_size);
/**
* Set a curl option for CURLOPTTYPE_OFF_T type
*
* @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential.
* @ param CURLoption opt The option to add (see enum CURLoption for details).
* @ param String buffer The value to set the option to.
* @ return bool 1 on success. 0 = The option not accept string or unsupport.
*/
native bool:curl_easy_setopt_int64(Handle:hndl, CURLoption:opt, const String:buffer[]);
/**
* Set a curl option for CURLOPTTYPE_OBJECTPOINT type
* @ note only accept the following handle type
curl_OpenFile()
curl_httppost()
curl_slist()
*
* @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential.
* @ param CURLoption opt The option to add (see enum CURLoption for details).
* @ param Handle other_hndl The other handle to set the option to.
* @ return bool 1 on success. 0 = The option not accept string or unsupport.
*/
native bool:curl_easy_setopt_handle(Handle:hndl, CURLoption:opt, Handle:other_hndl);
/**
* Set a curl option for CURLOPTTYPE_FUNCTIONPOINT type
*
* @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential.
* @ param CURLoption opt The option to add (see enum CURLoption for details).
* @ param CURL_Function_CB callback The value to set the option to.
* @ param cell_t value Value to set.
* @ return bool 1 on success. 0 = The option unsupport or invalid callback function.
*/
native bool:curl_easy_setopt_function(Handle:hndl, CURLoption:opt, CURL_Function_CB:callback, any:value=0);
/**
* Load all CURLoption to curl Handle
* @ note
* Using curl_easy_perform_thread() will load option in thread
* Use this on curl_easy_perform or check all CURLoption are valid or not
* Only can use one time for each curl handle
* @ return The CURLcode code, see cURL_header.inc
*/
native CURLcode:curl_load_opt(Handle:hndl);
/**
* Perform a file transfer
* @ return The CURLcode code, see cURL_header.inc
*/
native CURLcode:curl_easy_perform(Handle:hndl);
/**
* Perform a file transfer, using thread
* @ param Handle hndl The handle of the curl to be used. May be INVALID_HANDLE if not essential.
* @ param CURL_OnComplete perform_callback The complete callback.
* @ param cell_t value Value to set.
* @ noreturn
*/
native curl_easy_perform_thread(Handle:hndl, CURL_OnComplete:perform_callback, any:value=0);
/**
* Create a send & receive function for a connected curl handle
* @ param Handle hndl The handle of the curl to be used.
* @ param CURL_OnSend send_callback The send callback.
* @ param CURL_OnReceive receive_callback The receive callback.
* @ param CURL_OnComplete complete_callback The complete callback.
* @ param SendRecv_Act act The first SendRecv_Act action
* @ param cell_t send_timeout Send timeout value in milliseconds.
* @ param cell_t recv_timeout Receive timeout value in milliseconds.
* @ param cenn_t recv_buffer_Size Receive buffer size.
* @ param cell_t value Value to set.
* @ noreturn
*/
native curl_easy_send_recv(Handle:hndl, CURL_OnSend:send_callback, CURL_OnReceive:receive_callback, CURL_OnComplete:complete_callback, SendRecv_Act:act, send_timeout, recv_timeout, recv_buffer_Size = 1024, any:value=0);
/**
* Send a signal to a send & receive curl handle
* @ param Handle hndl The handle of the send & receive curl to be used.
* @ param SendRecv_Act act The SendRecv_Act action after the singal
* @ return bool 1 on success. 0 = not a curl_easy_send_recv() curl, or not running/waiting
*/
native bool:curl_send_recv_Signal(Handle:hndl, SendRecv_Act:act);
/**
* Check send & receive curl handle is Waiting or not
* @ param Handle hndl The handle of the send & receive curl to be used.
* @ return bool 1 = is waiting. 0 = not a curl_easy_send_recv() curl, or not running/waiting
*/
native bool:curl_send_recv_IsWaiting(Handle:hndl);
/**
* Send the send buffer for send & receive curl handle
* @ param Handle hndl The handle of the send & receive curl to be used.
* @ param cell_t data The data to send
* @ param cell_t size if specified the \0 terminator will not be included
* @ noreturn
*/
native curl_set_send_buffer(Handle:hndl, const String:data[], size=-1);
/**
* Send the receive data size for send & receive curl handle
* @ param Handle hndl The handle of the send & receive curl to be used.
* @ param cell_t size The receive size
* @ noreturn
*/
native curl_set_receive_size(Handle:hndl, size);
/**
* Set send timeout for curl_easy_send_recv()
* @ param Handle hndl The handle of the send & receive curl to be used.
* @ param cell_t timeout How long will try to send data before it timeout (milliseconds).
* @ noreturn
*/
native curl_set_send_timeout(Handle:hndl, timeout);
/**
* Set receive timeout for curl_easy_send_recv()
* @ param Handle hndl The handle of the send & receive curl to be used.
* @ param cell_t timeout How long will try to receive data before it timeout (milliseconds).
* @ noreturn
*/
native curl_set_recv_timeout(Handle:hndl, timeout);
/**
* Get CURLOPT_ERRORBUFFER error string in curl handle
* @ param Handle hndl The handle of the curl to be used.
* @ param String buffer Destination string buffer to copy to.
* @ param cell_t maxlen Destination buffer length (includes null terminator).
* @ noreturn
*/
native curl_get_error_buffer(Handle:hndl, String:buffer[], maxlen);
/**
* Extract information from a curl handle. (CURLINFO_STRING only)
* @ param Handle hndl The handle of the curl to be used.
* @ param CURLINFO info The enum CURLINFO, see cURL_header.inc
* @ param String buffer Destination string buffer to copy to.
* @ param cell_t maxlen Destination buffer length (includes null terminator).
* @ return The CURLcode code, see cURL_header.inc
*/
native CURLcode:curl_easy_getinfo_string(Handle:hndl, CURLINFO:info, String:buffer[], maxlen);
/**
* Extract information from a curl handle. (CURLINFO_LONG, CURLINFO_DOUBLE only)
* @ param Handle hndl The handle of the curl to be used.
* @ param CURLINFO info The enum CURLINFO, see cURL_header.inc
* @ param value Variable to store the value.
* @ return The CURLcode code, see cURL_header.inc
*/
native CURLcode:curl_easy_getinfo_int(Handle:hndl, CURLINFO:info, &any:value);
/**
* URL encodes the given string
* @ param Handle hndl The handle of the curl to be used.
* @ param String url The string to encodes.
* @ param String buffer Destination string buffer to copy to.
* @ param cell_t maxlen Destination buffer length (includes null terminator).
* @ return 1 on success.
*/
native bool:curl_easy_escape(Handle:hndl, const String:url[], String:buffer[], maxlen);
/**
* URL decodes the given string
* @ param Handle hndl The handle of the curl to be used.
* @ param String url The string to dencodes.
* @ param String buffer Destination string buffer to copy to.
* @ param cell_t maxlen Destination buffer length (includes null terminator).
* @ return The output length.
*/
native curl_easy_unescape(Handle:hndl, const String:url[], String:buffer[], maxlen);
/**
* Return string describing error code
* @ param CURLcode code The CURLcode code, see cURL_header.inc
* @ param String buffer Destination string buffer to copy to.
* @ param cell_t maxlen Destination buffer length (includes null terminator).
* @ noreturn
*/
native curl_easy_strerror(CURLcode: code, String:buffer[], maxlen);
/**
* Returns the libcurl version string
* @ param String buffer Destination string buffer to copy to.
* @ param cell_t maxlen Destination buffer length (includes null terminator).
* @ noreturn
*/
native curl_version(String:buffer[], maxlen);
/**