MQL5Book/Services/p5/ServiceAccount.mq5
super.admin 1c8e83ce31 convert
2025-05-30 16:09:41 +02:00

137 lines
5 KiB
MQL5

//+------------------------------------------------------------------+
//| ServiceAccount.mq5 |
//| Copyright 2021, MetaQuotes Ltd. |
//| https://www.mql5.com |
//+------------------------------------------------------------------+
#property service
input long MasterAccount = 123456789;
input string Prefix = "!A_";
//+------------------------------------------------------------------+
//| T type pair of values to byte array |
//+------------------------------------------------------------------+
template<typename T>
union ByteOverlay2
{
T values[2];
uchar bytes[sizeof(T) * 2];
ByteOverlay2(const T v1, const T v2) { values[0] = v1; values[1] = v2; }
};
//+------------------------------------------------------------------+
//| Common generator of a signature for given account numbers |
//+------------------------------------------------------------------+
string Cipher(const long data1, const long data2)
{
// TODO: replace the secret with your own hard to break byte set
// TODO: for CRYPT_AES128/CRYPT_AES256 use 16/32 bytes long array
const static uchar secret[] = {'S', 'E', 'C', 'R', 'E', 'T', '0'};
ByteOverlay2<long> bo(data1, data2);
// TODO: embed time limit into the cipher, for example, 1 day long:
// const long day = 60 * 60 * 24;
// ByteOverlay3<long> bo(TimeCurrent() / day * day, data1, data2);
// OR computer session long
// ByteOverlay3<long> bo((TimeLocal() - GetTickCount() / 1000), data1, data2);
uchar result[];
if(CryptEncode(CRYPT_DES, bo.bytes, secret, result) > 0)
{
uchar dummy[], text[];
if(CryptEncode(CRYPT_BASE64, result, dummy, text) > 0)
{
return CharArrayToString(text);
}
}
return NULL;
}
//+------------------------------------------------------------------+
//| Lookup list of licensed accounts (single one in this example) |
//| and optional dependent accounts |
//+------------------------------------------------------------------+
bool CheckAccounts()
{
const long accounts[] = {MasterAccount}; // populate this array
for(int i = 0; i < ArraySize(accounts); ++i)
{
if(IsCurrentAccountAuthorizedByMaster(accounts[i])) return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Example of validation of current account against master account |
//+------------------------------------------------------------------+
bool IsCurrentAccountAuthorizedByMaster(const long data)
{
const long a = AccountInfoInteger(ACCOUNT_LOGIN);
if(a == data) return true;
const string s = Cipher(data, a);
if(a != 0 && GlobalVariableGet(Prefix + s) == a)
{
Print("Sub-License is active: ", s);
return true;
}
return false;
}
//+------------------------------------------------------------------+
//| Service program start function |
//+------------------------------------------------------------------+
void OnStart()
{
static long account = 0; // previous account
// example of check-up in an external program
if(CheckAccounts())
{
// act on success as appropriate
}
for( ; !IsStopped(); )
{
// account must be logged in, connected, and not in investor mode
const bool c = TerminalInfoInteger(TERMINAL_CONNECTED)
&& AccountInfoInteger(ACCOUNT_TRADE_ALLOWED);
const long a = c ? AccountInfoInteger(ACCOUNT_LOGIN) : 0;
if(account != a)
{
if(a != 0)
{
if(account != 0) // previous and current accounts are authorized
{
const string signature = Cipher(account, a);
PrintFormat("Account %I64d registered by %I64d: %s", a, account, signature);
// save info about logged accounts binding
if(StringLen(signature) > 0)
{
GlobalVariableTemp(Prefix + signature);
GlobalVariableSet(Prefix + signature, a);
}
}
else
{
PrintFormat("New account %I64d detected", a);
}
// remember current account
account = a;
}
}
Sleep(1000);
}
}
//+------------------------------------------------------------------+
/*
example output (adjust master account number in inputs)
[launch service instance]
New account 123456789 detected
[change account]
Account 5555555 registered by 123456789: jdVKxUswBiNlZzDAnV3yxw==
[re-launch service instance]
Sub-License is active: jdVKxUswBiNlZzDAnV3yxw==
Account 123456789 registered by 5555555: ZWcwwJ1d8seN1UrFSzAGIw==
*/
//+------------------------------------------------------------------+