mirror of
https://foundry.openuru.org/gitblit/r/CWE-ou-minkata.git
synced 2025-07-14 14:37:41 +00:00
fix plClient -SkipLoginDialog
This commit is contained in:
@ -158,13 +158,16 @@ static TGUNIXAppClose pTGUNIXAppClose;
|
|||||||
//============================================================================
|
//============================================================================
|
||||||
struct LoginDialogParam {
|
struct LoginDialogParam {
|
||||||
ENetError authError;
|
ENetError authError;
|
||||||
wchar accountName[kMaxAccountNameLength];
|
char username[kMaxAccountNameLength];
|
||||||
|
ShaDigest namePassHash;
|
||||||
|
bool remember;
|
||||||
|
int focus;
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool AuthenticateNetClientComm(ENetError* result, HWND parentWnd);
|
static bool AuthenticateNetClientComm(ENetError* result, HWND parentWnd);
|
||||||
static void GetCryptKey(UInt32* cryptKey, unsigned size);
|
static void GetCryptKey(UInt32* cryptKey, unsigned size);
|
||||||
static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHash, bool remember_password);
|
static void SaveUserPass (LoginDialogParam *pLoginParam, char *password);
|
||||||
static void LoadUserPass (const wchar *accountName, char *username, ShaDigest *pNamePassHash, bool *pRemember, int *pFocus);
|
static void LoadUserPass (LoginDialogParam *pLoginParam);
|
||||||
static void AuthFailedStrings (ENetError authError,
|
static void AuthFailedStrings (ENetError authError,
|
||||||
const char **ppStr1, const char **ppStr2,
|
const char **ppStr1, const char **ppStr2,
|
||||||
const wchar **ppWStr);
|
const wchar **ppWStr);
|
||||||
@ -196,15 +199,8 @@ static void TGDoCiderDetection ()
|
|||||||
pTGUNIXAppClose = (TGUNIXAppClose)GetProcAddress (hMod, "TGUNIXAppClose");
|
pTGUNIXAppClose = (TGUNIXAppClose)GetProcAddress (hMod, "TGUNIXAppClose");
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool TGRunLoginDialog (const wchar *accountName)
|
static bool TGRunLoginDialog (LoginDialogParam *pLoginParam)
|
||||||
{
|
{
|
||||||
ShaDigest NamePassHash;
|
|
||||||
char Username[kMaxAccountNameLength + 5];
|
|
||||||
int Focus;
|
|
||||||
bool bRemember = false;
|
|
||||||
|
|
||||||
LoadUserPass (accountName, Username, &NamePassHash, &bRemember, &Focus);
|
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
LPVOID pApp;
|
LPVOID pApp;
|
||||||
@ -221,17 +217,17 @@ static bool TGRunLoginDialog (const wchar *accountName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send user/pwd/remember
|
// Send user/pwd/remember
|
||||||
pTGUNIXAppWriteLine (pApp, Username);
|
pTGUNIXAppWriteLine (pApp, pLoginParam->username);
|
||||||
if (bRemember)
|
if (pLoginParam->remember)
|
||||||
pTGUNIXAppWriteLine (pApp, FAKE_PASS_STRING);
|
pTGUNIXAppWriteLine (pApp, FAKE_PASS_STRING);
|
||||||
else
|
else
|
||||||
pTGUNIXAppWriteLine (pApp, "");
|
pTGUNIXAppWriteLine (pApp, "");
|
||||||
if (bRemember)
|
if (pLoginParam->remember)
|
||||||
pTGUNIXAppWriteLine (pApp, "y");
|
pTGUNIXAppWriteLine (pApp, "y");
|
||||||
else
|
else
|
||||||
pTGUNIXAppWriteLine (pApp, "n");
|
pTGUNIXAppWriteLine (pApp, "n");
|
||||||
|
|
||||||
if (!pTGUNIXAppReadLine (pApp, Username, sizeof (Username)))
|
if (!pTGUNIXAppReadLine (pApp, pLoginParam->username, sizeof (pLoginParam->username)))
|
||||||
{
|
{
|
||||||
pTGUNIXAppClose (pApp);
|
pTGUNIXAppClose (pApp);
|
||||||
hsMessageBox ("Incomplete or corrupted installation!\nUnable to locate Login dialog",
|
hsMessageBox ("Incomplete or corrupted installation!\nUnable to locate Login dialog",
|
||||||
@ -240,13 +236,13 @@ static bool TGRunLoginDialog (const wchar *accountName)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if user selected 'Cancel'
|
// Check if user selected 'Cancel'
|
||||||
if (StrCmp (Username, "text:", 5) != 0)
|
if (StrCmp (pLoginParam->username, "text:", 5) != 0)
|
||||||
{
|
{
|
||||||
pTGUNIXAppClose (pApp);
|
pTGUNIXAppClose (pApp);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
memmove (Username, Username + 5, StrLen (Username) - 5);
|
memmove (pLoginParam->username, pLoginParam->username + 5, StrLen (pLoginParam->username) - 5);
|
||||||
Username[StrLen (Username) - 5] = '\0';
|
pLoginParam->username[StrLen (pLoginParam->username) - 5] = '\0';
|
||||||
|
|
||||||
char Password[kMaxPasswordLength];
|
char Password[kMaxPasswordLength];
|
||||||
if (!pTGUNIXAppReadLine (pApp, Password, sizeof (Password)))
|
if (!pTGUNIXAppReadLine (pApp, Password, sizeof (Password)))
|
||||||
@ -268,11 +264,8 @@ static bool TGRunLoginDialog (const wchar *accountName)
|
|||||||
|
|
||||||
pTGUNIXAppClose (pApp);
|
pTGUNIXAppClose (pApp);
|
||||||
|
|
||||||
bRemember = false;
|
pLoginParam->remember = (Remember[0] == 'y');
|
||||||
if (Remember[0] == 'y')
|
SaveUserPass (pLoginParam, Password);
|
||||||
bRemember = true;
|
|
||||||
|
|
||||||
SaveUserPass (Username, Password, &NamePassHash, bRemember);
|
|
||||||
|
|
||||||
// Do login & see if it failed
|
// Do login & see if it failed
|
||||||
ENetError auth;
|
ENetError auth;
|
||||||
@ -1001,7 +994,7 @@ BOOL CALLBACK UruTOSDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM l
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHash, bool remember_password)
|
static void SaveUserPass (LoginDialogParam *pLoginParam, char *password)
|
||||||
{
|
{
|
||||||
UInt32 cryptKey[4];
|
UInt32 cryptKey[4];
|
||||||
ZeroMemory(cryptKey, sizeof(cryptKey));
|
ZeroMemory(cryptKey, sizeof(cryptKey));
|
||||||
@ -1010,7 +1003,7 @@ static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHa
|
|||||||
wchar wusername[kMaxAccountNameLength];
|
wchar wusername[kMaxAccountNameLength];
|
||||||
wchar wpassword[kMaxPasswordLength];
|
wchar wpassword[kMaxPasswordLength];
|
||||||
|
|
||||||
StrToUnicode(wusername, username, arrsize(wusername));
|
StrToUnicode(wusername, pLoginParam->username, arrsize(wusername));
|
||||||
|
|
||||||
// if the password field is the fake string then we've already
|
// if the password field is the fake string then we've already
|
||||||
// loaded the namePassHash from the file
|
// loaded the namePassHash from the file
|
||||||
@ -1024,24 +1017,24 @@ static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHa
|
|||||||
if (StrLen(domain) == 0 || StrCmpI(domain, L"gametap") == 0) {
|
if (StrLen(domain) == 0 || StrCmpI(domain, L"gametap") == 0) {
|
||||||
CryptDigest(
|
CryptDigest(
|
||||||
kCryptSha1,
|
kCryptSha1,
|
||||||
pNamePassHash,
|
&pLoginParam->namePassHash,
|
||||||
StrLen(password) * sizeof(password[0]),
|
StrLen(password) * sizeof(password[0]),
|
||||||
password
|
password
|
||||||
);
|
);
|
||||||
|
|
||||||
if (IsMachineLittleEndian()) {
|
if (IsMachineLittleEndian()) {
|
||||||
pNamePassHash->data[0] = ToBigEndian(pNamePassHash->data[0]);
|
pLoginParam->namePassHash.data[0] = ToBigEndian(pLoginParam->namePassHash.data[0]);
|
||||||
pNamePassHash->data[1] = ToBigEndian(pNamePassHash->data[1]);
|
pLoginParam->namePassHash.data[1] = ToBigEndian(pLoginParam->namePassHash.data[1]);
|
||||||
pNamePassHash->data[2] = ToBigEndian(pNamePassHash->data[2]);
|
pLoginParam->namePassHash.data[2] = ToBigEndian(pLoginParam->namePassHash.data[2]);
|
||||||
pNamePassHash->data[3] = ToBigEndian(pNamePassHash->data[3]);
|
pLoginParam->namePassHash.data[3] = ToBigEndian(pLoginParam->namePassHash.data[3]);
|
||||||
pNamePassHash->data[4] = ToBigEndian(pNamePassHash->data[4]);
|
pLoginParam->namePassHash.data[4] = ToBigEndian(pLoginParam->namePassHash.data[4]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
CryptHashPassword(wusername, wpassword, pNamePassHash);
|
CryptHashPassword(wusername, wpassword, &pLoginParam->namePassHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetCommSetAccountUsernamePassword(wusername, *pNamePassHash);
|
NetCommSetAccountUsernamePassword(wusername, pLoginParam->namePassHash);
|
||||||
if (TGIsCider)
|
if (TGIsCider)
|
||||||
NetCommSetAuthTokenAndOS(nil, L"mac");
|
NetCommSetAuthTokenAndOS(nil, L"mac");
|
||||||
else
|
else
|
||||||
@ -1061,26 +1054,25 @@ static void SaveUserPass (char *username, char *password, ShaDigest *pNamePassHa
|
|||||||
if (stream)
|
if (stream)
|
||||||
{
|
{
|
||||||
stream->Write(sizeof(cryptKey), cryptKey);
|
stream->Write(sizeof(cryptKey), cryptKey);
|
||||||
stream->WriteSafeString(username);
|
stream->WriteSafeString(pLoginParam->username);
|
||||||
stream->Writebool(remember_password);
|
stream->Writebool(pLoginParam->remember);
|
||||||
if (remember_password)
|
if (pLoginParam->remember)
|
||||||
stream->Write(sizeof(pNamePassHash->data), pNamePassHash->data);
|
stream->Write(sizeof(pLoginParam->namePassHash.data), pLoginParam->namePassHash.data);
|
||||||
stream->Close();
|
stream->Close();
|
||||||
delete stream;
|
delete stream;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void LoadUserPass (const wchar *accountName, char *username, ShaDigest *pNamePassHash, bool *pRemember,
|
static void LoadUserPass (LoginDialogParam *pLoginParam)
|
||||||
int *pFocus)
|
|
||||||
{
|
{
|
||||||
UInt32 cryptKey[4];
|
UInt32 cryptKey[4];
|
||||||
ZeroMemory(cryptKey, sizeof(cryptKey));
|
ZeroMemory(cryptKey, sizeof(cryptKey));
|
||||||
GetCryptKey(cryptKey, arrsize(cryptKey));
|
GetCryptKey(cryptKey, arrsize(cryptKey));
|
||||||
|
|
||||||
char* temp;
|
char* temp;
|
||||||
*pRemember = false;
|
pLoginParam->remember = false;
|
||||||
username[0] = '\0';
|
pLoginParam->username[0] = '\0';
|
||||||
|
|
||||||
wchar fileAndPath[MAX_PATH];
|
wchar fileAndPath[MAX_PATH];
|
||||||
PathGetInitDirectory(fileAndPath, arrsize(fileAndPath));
|
PathGetInitDirectory(fileAndPath, arrsize(fileAndPath));
|
||||||
@ -1104,21 +1096,21 @@ static void LoadUserPass (const wchar *accountName, char *username, ShaDigest *p
|
|||||||
|
|
||||||
if (temp)
|
if (temp)
|
||||||
{
|
{
|
||||||
StrCopy(username, temp, kMaxAccountNameLength);
|
StrCopy(pLoginParam->username, temp, kMaxAccountNameLength);
|
||||||
delete temp;
|
delete temp;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
username[0] = '\0';
|
|
||||||
|
|
||||||
*pRemember = stream->Readbool();
|
pLoginParam->remember = stream->Readbool();
|
||||||
|
|
||||||
if (*pRemember)
|
if (pLoginParam->remember)
|
||||||
{
|
{
|
||||||
stream->Read(sizeof(pNamePassHash->data), pNamePassHash->data);
|
stream->Read(sizeof(pLoginParam->namePassHash.data), pLoginParam->namePassHash.data);
|
||||||
*pFocus = IDOK;
|
pLoginParam->focus = IDOK;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
*pFocus = IDC_URULOGIN_PASSWORD;
|
{
|
||||||
|
pLoginParam->focus = IDC_URULOGIN_PASSWORD;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
stream->Close();
|
stream->Close();
|
||||||
@ -1173,8 +1165,7 @@ void StatusCallback(void *param)
|
|||||||
|
|
||||||
BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
|
||||||
{
|
{
|
||||||
static ShaDigest namePassHash;
|
static LoginDialogParam* pLoginParam;
|
||||||
static LoginDialogParam* loginParam;
|
|
||||||
static bool showAuthFailed = false;
|
static bool showAuthFailed = false;
|
||||||
|
|
||||||
switch( uMsg )
|
switch( uMsg )
|
||||||
@ -1183,28 +1174,21 @@ BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM
|
|||||||
{
|
{
|
||||||
s_loginDlgRunning = true;
|
s_loginDlgRunning = true;
|
||||||
_beginthread(StatusCallback, 0, hwndDlg);
|
_beginthread(StatusCallback, 0, hwndDlg);
|
||||||
loginParam = (LoginDialogParam*)lParam;
|
pLoginParam = (LoginDialogParam*)lParam;
|
||||||
|
|
||||||
SetWindowText(hwndDlg, "Login");
|
SetWindowText(hwndDlg, "Login");
|
||||||
SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(gHInst, MAKEINTRESOURCE(IDI_ICON_DIRT)));
|
SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)LoadIcon(gHInst, MAKEINTRESOURCE(IDI_ICON_DIRT)));
|
||||||
|
|
||||||
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
EnableWindow(GetDlgItem(hwndDlg, IDOK), FALSE);
|
||||||
|
|
||||||
char username[kMaxAccountNameLength];
|
SetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, pLoginParam->username);
|
||||||
bool remember_password = false;
|
CheckDlgButton(hwndDlg, IDC_URULOGIN_REMEMBERPASS, pLoginParam->remember ? BST_CHECKED : BST_UNCHECKED);
|
||||||
|
if (pLoginParam->remember)
|
||||||
int focus_control = IDC_URULOGIN_USERNAME;
|
|
||||||
|
|
||||||
LoadUserPass (loginParam->accountName, username, &namePassHash, &remember_password, &focus_control);
|
|
||||||
|
|
||||||
SetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username);
|
|
||||||
CheckDlgButton(hwndDlg, IDC_URULOGIN_REMEMBERPASS, remember_password ? BST_CHECKED : BST_UNCHECKED);
|
|
||||||
if (remember_password)
|
|
||||||
SetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, FAKE_PASS_STRING);
|
SetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, FAKE_PASS_STRING);
|
||||||
|
|
||||||
SetFocus(GetDlgItem(hwndDlg, focus_control));
|
SetFocus(GetDlgItem(hwndDlg, pLoginParam->focus));
|
||||||
|
|
||||||
if (IS_NET_ERROR(loginParam->authError))
|
if (IS_NET_ERROR(pLoginParam->authError))
|
||||||
{
|
{
|
||||||
showAuthFailed = true;
|
showAuthFailed = true;
|
||||||
}
|
}
|
||||||
@ -1254,25 +1238,22 @@ BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM
|
|||||||
bool ok = (LOWORD(wParam) == IDOK);
|
bool ok = (LOWORD(wParam) == IDOK);
|
||||||
if (ok)
|
if (ok)
|
||||||
{
|
{
|
||||||
char username[kMaxAccountNameLength];
|
|
||||||
char password[kMaxPasswordLength];
|
char password[kMaxPasswordLength];
|
||||||
bool remember_password = false;
|
|
||||||
|
|
||||||
GetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, username, kMaxAccountNameLength);
|
GetDlgItemText(hwndDlg, IDC_URULOGIN_USERNAME, pLoginParam->username, kMaxAccountNameLength);
|
||||||
GetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, password, kMaxPasswordLength);
|
GetDlgItemText(hwndDlg, IDC_URULOGIN_PASSWORD, password, kMaxPasswordLength);
|
||||||
remember_password = (IsDlgButtonChecked(hwndDlg, IDC_URULOGIN_REMEMBERPASS) == BST_CHECKED);
|
pLoginParam->remember = (IsDlgButtonChecked(hwndDlg, IDC_URULOGIN_REMEMBERPASS) == BST_CHECKED);
|
||||||
|
|
||||||
SaveUserPass (username, password, &namePassHash, remember_password);
|
SaveUserPass (pLoginParam, password);
|
||||||
|
|
||||||
LoginDialogParam loginParam;
|
MemSet(&pLoginParam->authError, 0, sizeof(pLoginParam->authError));
|
||||||
MemSet(&loginParam, 0, sizeof(loginParam));
|
bool cancelled = AuthenticateNetClientComm(&pLoginParam->authError, hwndDlg);
|
||||||
bool cancelled = AuthenticateNetClientComm(&loginParam.authError, hwndDlg);
|
|
||||||
|
|
||||||
if (IS_NET_SUCCESS(loginParam.authError) && !cancelled)
|
if (IS_NET_SUCCESS(pLoginParam->authError) && !cancelled)
|
||||||
EndDialog(hwndDlg, ok);
|
EndDialog(hwndDlg, ok);
|
||||||
else {
|
else {
|
||||||
if (!cancelled)
|
if (!cancelled)
|
||||||
::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)&loginParam);
|
::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)pLoginParam);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
NetCommDisconnect();
|
NetCommDisconnect();
|
||||||
@ -1312,7 +1293,7 @@ BOOL CALLBACK UruLoginDialogProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM
|
|||||||
{
|
{
|
||||||
case AUTH_FAILED_TIMER:
|
case AUTH_FAILED_TIMER:
|
||||||
KillTimer(hwndDlg, AUTH_FAILED_TIMER);
|
KillTimer(hwndDlg, AUTH_FAILED_TIMER);
|
||||||
::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)loginParam);
|
::DialogBoxParam(gHInst, MAKEINTRESOURCE( IDD_AUTHFAILED ), hwndDlg, AuthFailedDialogProc, (LPARAM)pLoginParam);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
case AUTH_LOGIN_TIMER:
|
case AUTH_LOGIN_TIMER:
|
||||||
@ -1583,21 +1564,23 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
|
|||||||
|
|
||||||
curl_global_init(CURL_GLOBAL_ALL);
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
|
|
||||||
wchar acctName[kMaxAccountNameLength];
|
|
||||||
bool needExit = false;
|
bool needExit = false;
|
||||||
LoginDialogParam loginParam;
|
LoginDialogParam loginParam;
|
||||||
MemSet(&loginParam, 0, sizeof(loginParam));
|
MemSet(&loginParam, 0, sizeof(loginParam));
|
||||||
|
LoadUserPass(&loginParam);
|
||||||
|
|
||||||
if (!doIntroDialogs) {
|
if (!doIntroDialogs && loginParam.remember) {
|
||||||
ENetError auth;
|
ENetError auth;
|
||||||
|
|
||||||
|
wchar wusername[kMaxAccountNameLength];
|
||||||
|
StrToUnicode(wusername, loginParam.username, arrsize(wusername));
|
||||||
|
NetCommSetAccountUsernamePassword(wusername, loginParam.namePassHash);
|
||||||
bool cancelled = AuthenticateNetClientComm(&auth, NULL);
|
bool cancelled = AuthenticateNetClientComm(&auth, NULL);
|
||||||
|
|
||||||
if (IS_NET_ERROR(auth) || cancelled) {
|
if (IS_NET_ERROR(auth) || cancelled) {
|
||||||
doIntroDialogs = true;
|
doIntroDialogs = true;
|
||||||
|
|
||||||
loginParam.authError = auth;
|
loginParam.authError = auth;
|
||||||
StrCopy(loginParam.accountName, acctName, arrsize(loginParam.accountName));
|
|
||||||
|
|
||||||
if (cancelled)
|
if (cancelled)
|
||||||
{
|
{
|
||||||
@ -1608,7 +1591,7 @@ int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, int nC
|
|||||||
|
|
||||||
if (doIntroDialogs) {
|
if (doIntroDialogs) {
|
||||||
if (TGIsCider)
|
if (TGIsCider)
|
||||||
needExit = !TGRunLoginDialog (loginParam.accountName);
|
needExit = !TGRunLoginDialog(&loginParam);
|
||||||
else if (::DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_URULOGIN_MAIN ), NULL, UruLoginDialogProc, (LPARAM)&loginParam ) <= 0)
|
else if (::DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_URULOGIN_MAIN ), NULL, UruLoginDialogProc, (LPARAM)&loginParam ) <= 0)
|
||||||
needExit = true;
|
needExit = true;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user