#pragma dynamic 4194304 // Increases stack space to 4mb, needed for encryption #include #include // Core includes #include "steamcore/bigint.sp" #include "steamcore/rsa.sp" #define AUTOLOAD_EXTENSIONS #define REQUIRE_EXTENSIONS #include #define PLUGIN_URL "" #define PLUGIN_VERSION "1.7.1" #define PLUGIN_NAME "SteamCore" #define PLUGIN_AUTHOR "Statik" public Plugin:myinfo = { name = PLUGIN_NAME, author = PLUGIN_AUTHOR, description = "Sourcemod natives to interact with Steam functions.", version = PLUGIN_VERSION, url = PLUGIN_URL } new bool:DEBUG = false; new const TIMER_UPDATE_TIME = 6; new const TOKEN_LIFETIME = 50; new const Float:TIMEOUT_TIME = 10.0; new Handle:cvarUsername; new Handle:cvarPassword; new Handle:cvarDebug; new String:username[32] = ""; new String:passphrase[32] = ""; new String:sessionToken[32] = ""; new String:sessionCookie[256] = ""; new bool:isLogged = false; new bool:isBusy = false; new Handle:request; new caller; new timeSinceLastLogin; new Handle:hTimeIncreaser; new Handle:timeoutTimer; new bool:connectionInterrupted; new Handle:callbackHandle; new Handle:callbackPlugin; new Function:callbackFunction; new Handle:finalRequest; new SteamWorksHTTPRequestCompleted:finalFunction; // =================================================================================== // =================================================================================== public APLRes:AskPluginLoad2(Handle:plugin, bool:late, String:error[], err_max) { // Native creation CreateNative("IsSteamCoreBusy", nativeIsSteamCoreBusy); CreateNative("SteamGroupAnnouncement", nativeGroupAnnouncement); CreateNative("SteamGroupInvite", nativeGroupInvite); RegPluginLibrary("steamcore"); return APLRes_Success; } public OnPluginStart() { // Callbacks callbackHandle = CreateForward(ET_Ignore, Param_Cell, Param_Cell, Param_Cell, Param_Cell); // Timers hTimeIncreaser = CreateTimer(TIMER_UPDATE_TIME*60.0, timeIncreaser, INVALID_HANDLE, TIMER_REPEAT); // Convars CreateConVar("steamcore_version", PLUGIN_VERSION, "SteamCore Version", FCVAR_SPONLY | FCVAR_DONTRECORD | FCVAR_NOTIFY); cvarUsername = CreateConVar("sc_username", "", "Steam login username.", FCVAR_PROTECTED); cvarPassword = CreateConVar("sc_password", "", "Steam login password.", FCVAR_PROTECTED); cvarDebug = CreateConVar("sc_debug", "0", "Toggles debugging.", 0, true, 0.0, true, 1.0); HookConVarChange(cvarUsername, OnLoginInfoChange); HookConVarChange(cvarPassword, OnLoginInfoChange); HookConVarChange(cvarDebug, OnDebugStatusChange); timeSinceLastLogin = TOKEN_LIFETIME; AutoExecConfig(true, "plugin.steamcore"); } public OnLoginInfoChange(Handle:cvar, const String:oldVal[], const String:newVal[]) { isLogged = false; } public OnDebugStatusChange(Handle:cvar, const String:oldVal[], const String:newVal[]) { DEBUG = bool:StringToInt(newVal); } public Action:timeIncreaser(Handle:timer) { timeSinceLastLogin += TIMER_UPDATE_TIME; PrintDebug(0, "\n============================================================================\n"); PrintDebug(0, "Time since last login: %i minutes.", timeSinceLastLogin); if (timeSinceLastLogin >= TOKEN_LIFETIME) { isLogged = false; PrintDebug(0, "Expired token lifetime (%i)", TOKEN_LIFETIME); } return Plugin_Continue; } public OnConfigsExecuted() { DEBUG = GetConVarBool(FindConVar("sc_debug")); if (timeSinceLastLogin > 10) { PrintDebug(0, "\n============================================================================\n"); PrintDebug(0, "Logging in to keep login alive..."); startRequest(0, INVALID_HANDLE, INVALID_FUNCTION, INVALID_HANDLE, INVALID_FUNCTION); // Starts an empty login request } } // =================================================================================== // =================================================================================== public nativeIsSteamCoreBusy(Handle:plugin, numParams) { return _:isBusy; } public nativeGroupAnnouncement(Handle:plugin, numParams) { decl String:title[256]; decl String:body[1024]; decl String:groupID[64]; new client = GetNativeCell(1); GetNativeString(2, title, sizeof(title)); GetNativeString(3, body, sizeof(body)); GetNativeString(4, groupID, sizeof(groupID)); decl String:URL[128]; Format(URL, sizeof(URL), "http://steamcommunity.com/gid/%s/announcements", groupID); PrintDebug(client, "\n============================================================================\n"); PrintDebug(client, "Preparing request to: \n%s...", URL); PrintDebug(client, "Title: \n%s", title); PrintDebug(client, "Body: \n%s", body); PrintDebug(client, "Verifying login..."); new Handle:_finalRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodPOST, URL); SteamWorks_SetHTTPRequestHeaderValue(_finalRequest, "Cookie", sessionCookie); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "action", "post"); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "sessionID", sessionToken); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "headline", title); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "body", body); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "languages[0][headline]", title); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "languages[0][body]", body); startRequest(client, _finalRequest, cbkGroupAnnouncement, plugin, GetNativeFunction(5)); } public nativeGroupInvite(Handle:plugin, numParams) { decl String:invitee[64]; decl String:groupID[64]; new client = GetNativeCell(1); GetNativeString(2, invitee, sizeof invitee); GetNativeString(3, groupID, sizeof groupID); decl String:URL[] = "http://steamcommunity.com/actions/GroupInvite"; PrintDebug(client, "\n============================================================================\n"); PrintDebug(client, "Preparing request to: \n%s...", URL); PrintDebug(client, "Invitee community ID: \n%s", invitee); PrintDebug(client, "Group community ID: \n%s", groupID); PrintDebug(client, "Verifying login..."); new Handle:_finalRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, URL); SteamWorks_SetHTTPRequestHeaderValue(_finalRequest, "Accept", "*/*"); SteamWorks_SetHTTPRequestHeaderValue(_finalRequest, "Accept-Encoding", "gzip, deflate"); SteamWorks_SetHTTPRequestHeaderValue(_finalRequest, "User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)"); SteamWorks_SetHTTPRequestHeaderValue(_finalRequest, "Cookie", sessionCookie); //SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "json", "1"); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "type", "groupInvite"); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "sessionID", sessionToken); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "group", groupID); SteamWorks_SetHTTPRequestGetOrPostParameter(_finalRequest, "invitee", invitee); startRequest(client, _finalRequest, cbkGroupInvite, plugin, GetNativeFunction(4)); } // =================================================================================== // =================================================================================== startRequest(client, Handle:_finalRequest, SteamWorksHTTPRequestCompleted:_finalFunction, Handle:_callbackPlugin, Function:_callbackFunction) { if (isBusy) { PrintDebug(client, "\n============================================================================\n"); PrintDebug(client, "Plugin is busy with other task at this time, rejecting request..."); if (_callbackFunction != INVALID_FUNCTION) // There is an actual function callback { new pluginIteratorNumber = GetPluginIteratorNumber(_callbackPlugin); new Handle:datapack; CreateDataTimer(0.1, tmrBusyCallback, datapack); WritePackCell(datapack, client); WritePackCell(datapack, pluginIteratorNumber); WritePackFunction(datapack, Function:_callbackFunction); CloseHandle(_finalRequest); } return; } isBusy = true; connectionInterrupted = false; caller = client; finalRequest = _finalRequest; finalFunction = _finalFunction; callbackPlugin = _callbackPlugin; callbackFunction = _callbackFunction; PrintDebug(caller, "\n============================================================================\n"); if (callbackFunction != INVALID_FUNCTION) // There is an actual function callback { AddToForward(callbackHandle, callbackPlugin, callbackFunction); if (isLogged) { PrintDebug(caller, "Already logged in, executing request..."); SteamWorks_SetHTTPCallbacks(finalRequest, finalFunction) SteamWorks_SendHTTPRequest(finalRequest); startTimeoutTimer(); return; } } GetConVarString(cvarUsername, username, sizeof(username)); GetConVarString(cvarPassword, passphrase, sizeof(passphrase)); if (StrEqual(username, "") || StrEqual(passphrase, "")) { PrintDebug(caller, "Invalid login information, check cvars. ABORTED."); onRequestResult(caller, false, 0x03); // Invalid login information return; } request = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, "http://steamcommunity.com/login/getrsakey/"); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "username", username); SteamWorks_SetHTTPCallbacks(request, cbkRsaKeyRequest); SteamWorks_SendHTTPRequest(request); startTimeoutTimer(); PrintDebug(caller, "Obtaining RSA Key from steamcommunity.com/login/getrsakey..."); } startTimeoutTimer() { stopTimeoutTimer(); timeoutTimer = CreateTimer(TIMEOUT_TIME, tmrTimeout); } stopTimeoutTimer() { if (timeoutTimer != INVALID_HANDLE) { KillTimer(timeoutTimer); timeoutTimer = INVALID_HANDLE; } } public Action:tmrTimeout(Handle:timer) { PrintDebug(caller, "Connection timed out."); connectionInterrupted = true; onRequestResult(caller, false, 0x02); timeoutTimer = INVALID_HANDLE; } public Action:tmrBusyCallback(Handle:timer, Handle:pack) { ResetPack(pack); new client = ReadPackCell(pack); new pluginIteratorNumber = ReadPackCell(pack); new Handle:callbackPl = FindPluginFromNumber(pluginIteratorNumber); new Function:callbackFunc = ReadPackFunction(pack); new bool:success = RemoveFromForward(callbackHandle, callbackPlugin, callbackFunction); new functionCount = GetForwardFunctionCount(callbackHandle); PrintDebug(caller, "Removing main callback from forward - Result: %i, - Forward Function Count: %i", success, functionCount); success = AddToForward(callbackHandle, callbackPl, callbackFunc); functionCount = GetForwardFunctionCount(callbackHandle); PrintDebug(caller, "Adding temporal callback from forward - Result: %i, - Forward Function Count: %i", success, functionCount); // Start function call Call_StartForward(callbackHandle); // Push parameters one at a time Call_PushCell(client); Call_PushCell(false); Call_PushCell(0x01); // Plugin is busy Call_PushCell(0); // Finish the call new result = Call_Finish(); PrintDebug(caller, "Temporal callback calling error code: %i (0: Success)", result); success = RemoveFromForward(callbackHandle, callbackPl, callbackFunc); functionCount = GetForwardFunctionCount(callbackHandle); PrintDebug(caller, "Removing temporal callback from forward - Result: %i, - Forward Function Count: %i", success, functionCount); success = AddToForward(callbackHandle, callbackPlugin, callbackFunction); functionCount = GetForwardFunctionCount(callbackHandle); PrintDebug(caller, "Re-adding main callback from forward - Result: %i, - Forward Function Count: %i", success, functionCount); callbackPl = INVALID_HANDLE; callbackFunc = INVALID_FUNCTION; PrintDebug(caller, "Task rejected."); } public cbkRsaKeyRequest(Handle:response, bool:failure, bool:requestSuccessful, EHTTPStatusCode:statusCode) { stopTimeoutTimer(); if (connectionInterrupted) { CloseHandle(request); request = INVALID_HANDLE; return; } if (response == INVALID_HANDLE || !requestSuccessful || statusCode != k_EHTTPStatusCode200OK) { PrintDebug(caller, "RSA Key request failed (%i). Status Code: %i. ABORTED", requestSuccessful, statusCode); onRequestResult(caller, false, 0x04); // Failed http RSA Key request CloseHandle(request); request = INVALID_HANDLE; return; } new bodySize; SteamWorks_GetHTTPResponseBodySize(request, bodySize); decl String:responseBody[bodySize]; SteamWorks_GetHTTPResponseBodyData(request, responseBody, bodySize); PrintDebug(caller, responseBody); if (StrContains(responseBody, "\"success\":true", false) == -1) { PrintDebug(caller, "Could not get RSA Key, aborting..."); onRequestResult(caller, false, 0x05); // RSA Key response failed, unknown reason CloseHandle(request); request = INVALID_HANDLE; return; } new Handle:regex; regex = CompileRegex("\"publickey_mod\":\"(.*?)\""); MatchRegex(regex, responseBody); decl String:rsaPublicMod[1024]; GetRegexSubString(regex, 1, rsaPublicMod, sizeof(rsaPublicMod)); CloseHandle(regex); regex = INVALID_HANDLE; PrintDebug(caller, "RSA KEY MODULUS (%i): \n%s", strlen(rsaPublicMod), rsaPublicMod); regex = CompileRegex("\"publickey_exp\":\"(.*?)\""); MatchRegex(regex, responseBody); decl String:rsaPublicExp[16]; GetRegexSubString(regex, 1, rsaPublicExp, sizeof(rsaPublicExp)); CloseHandle(regex); regex = INVALID_HANDLE; PrintDebug(caller, "RSA KEY EXPONENT (%i): %s", strlen(rsaPublicExp), rsaPublicExp); regex = CompileRegex("\"timestamp\":\"(.*?)\""); MatchRegex(regex, responseBody); decl String:steamTimestamp[16]; GetRegexSubString(regex, 1, steamTimestamp, sizeof(steamTimestamp)); CloseHandle(regex); regex = INVALID_HANDLE; PrintDebug(caller, "STEAM TIMESTAMP (%i): %s", strlen(steamTimestamp), steamTimestamp); PrintDebug(caller, "\n============================================================================\n"); PrintDebug(caller, "Encrypting passphrase ******** with RSA public key..."); decl String:encryptedPassword[1024]; rsaEncrypt(rsaPublicMod, rsaPublicExp, passphrase, encryptedPassword, sizeof(encryptedPassword)); PrintDebug(caller, "Encrypted passphrase with RSA cryptosystem (%i): \n%s", strlen(encryptedPassword), encryptedPassword); decl numericPassword[1024]; hexString2BigInt(encryptedPassword, numericPassword, sizeof(numericPassword)); encodeBase64(numericPassword, strlen(rsaPublicMod),encryptedPassword, sizeof(encryptedPassword)); PrintDebug(caller, "Encoded encrypted passphrase with base64 algorithm (%i): \n%s", strlen(encryptedPassword), encryptedPassword); CloseHandle(request); request = INVALID_HANDLE; PrintDebug(caller, "\n============================================================================\n"); PrintDebug(caller, "Logging in to steamcommunity.com/login/dologin/..."); request = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, "https://steamcommunity.com/login/dologin/"); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "password", encryptedPassword); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "username", username); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "twofactorcode", ""); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "emailauth", ""); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "loginfriendlyname", ""); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "captchagid", ""); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "captcha_text", ""); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "emailsteamid", ""); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "rsatimestamp", steamTimestamp); SteamWorks_SetHTTPRequestGetOrPostParameter(request, "remember_login", "false"); SteamWorks_SetHTTPCallbacks(request, cbkLoginRequest); SteamWorks_SendHTTPRequest(request); startTimeoutTimer(); } public cbkLoginRequest(Handle:response, bool:failure, bool:requestSuccessful, EHTTPStatusCode:statusCode) { stopTimeoutTimer(); if (connectionInterrupted) { CloseHandle(request); request = INVALID_HANDLE; return; } if (response == INVALID_HANDLE || !requestSuccessful || statusCode != k_EHTTPStatusCode200OK) { PrintDebug(caller, "Login request failed (%i). Status Code: %i. ABORTED", requestSuccessful, statusCode); onRequestResult(caller, false, 0x06); // Failed htpps login request CloseHandle(request); request = INVALID_HANDLE; return; } new bodySize; SteamWorks_GetHTTPResponseBodySize(response, bodySize); decl String:responseBody[bodySize]; SteamWorks_GetHTTPResponseBodyData(response, responseBody, bodySize); new Handle:regex; regex = CompileRegex("\"success\":(.*?),"); MatchRegex(regex, responseBody); decl String:successString[20]; GetRegexSubString(regex, 1, successString, sizeof(successString)); CloseHandle(regex); regex = INVALID_HANDLE; if (strcmp(successString, "true") != 0) // successString != true { PrintDebug(caller, "Aborted logging, incorrect response body (%i): \n%s", strlen(responseBody), responseBody); onRequestResult(caller, false, 0x07); // Incorrect login data, required captcha or e-mail confirmation (Steam Guard) CloseHandle(request); request = INVALID_HANDLE; return; } new cookieSize; SteamWorks_GetHTTPResponseHeaderSize(response, "Set-Cookie", cookieSize); SteamWorks_GetHTTPResponseHeaderValue(response, "Set-Cookie", sessionCookie, cookieSize); // Cleaning cookie ReplaceString(sessionCookie, sizeof sessionCookie, "path=/,", ""); ReplaceString(sessionCookie, sizeof sessionCookie, "path=/; httponly,", ""); ReplaceString(sessionCookie, sizeof sessionCookie, "path=/; secure; httponly", ""); PrintDebug(caller, "Success, got response (%i): \n%s", strlen(responseBody), responseBody); PrintDebug(caller, "Stored Cookie (%i): \n%s", strlen(sessionCookie), sessionCookie); CloseHandle(request); request = INVALID_HANDLE; PrintDebug(caller, "\n============================================================================\n"); PrintDebug(caller, "Logging successful, obtaining session token..."); request = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, "http://steamcommunity.com/profiles/RedirectToHome"); SteamWorks_SetHTTPRequestHeaderValue(request, "Cookie", sessionCookie); SteamWorks_SetHTTPCallbacks(request, cbkTokenRequest); SteamWorks_SendHTTPRequest(request); startTimeoutTimer(); } public cbkTokenRequest(Handle:response, bool:failure, bool:requestSuccessful, EHTTPStatusCode:statusCode) { stopTimeoutTimer(); if (connectionInterrupted) { CloseHandle(request); request = INVALID_HANDLE; return; } if (response == INVALID_HANDLE || !requestSuccessful || statusCode != k_EHTTPStatusCode200OK) { PrintDebug(caller, "Session Token request failed (%i). Status Code: %i. ABORTED", requestSuccessful, statusCode); onRequestResult(caller, false, 0x08); // Failed http token request CloseHandle(request); request = INVALID_HANDLE; return; } new bodySize; SteamWorks_GetHTTPResponseBodySize(response, bodySize); decl String:responseBody[bodySize]; SteamWorks_GetHTTPResponseBodyData(response, responseBody, bodySize); new Handle:regex; regex = CompileRegex("g_steamID = (.*?);"); MatchRegex(regex, responseBody); decl String:steamId[20]; GetRegexSubString(regex, 1, steamId, sizeof(steamId)); CloseHandle(regex); regex = INVALID_HANDLE; regex = CompileRegex("g_sessionID = \"(.*?)\""); MatchRegex(regex, responseBody); GetRegexSubString(regex, 1, sessionToken, sizeof(sessionToken)); CloseHandle(regex); regex = INVALID_HANDLE; if (strcmp(steamId, "false") == 0) // steamId == false { PrintDebug(caller, "Could not get session token. Got: \"%s\". Incorrect Cookie?", steamId); onRequestResult(caller, false, 0x09); // Invalid session token. Incorrect cookie? CloseHandle(request); request = INVALID_HANDLE; return; } isLogged = true; // Cleaning cookie ReplaceString(sessionCookie, sizeof sessionCookie, "path=/; httponly,", ""); ReplaceString(sessionCookie, sizeof sessionCookie, "path=/; secure; httponly", ""); Format(sessionCookie, sizeof sessionCookie, "Steam_Language=english; sessionid=%s; %s", sessionToken, sessionCookie); PrintDebug(caller, "Session token successfully acquired (%i): %s", strlen(sessionToken), sessionToken); PrintDebug(caller, "Current session for Steam ID (%i): %s", strlen(steamId), steamId); PrintDebug(caller, "Appended session token to clean cookie, actual cookie (%i): \n%s", strlen(sessionCookie), sessionCookie); if (finalRequest != INVALID_HANDLE) { PrintDebug(caller, "\n============================================================================\n"); PrintDebug(caller, "Executing final request..."); SteamWorks_SetHTTPCallbacks(finalRequest, finalFunction); SteamWorks_SendHTTPRequest(finalRequest); startTimeoutTimer(); } else { PrintDebug(caller, "There is no final request, logged in successfully."); onRequestResult(caller, true); } CloseHandle(request); request = INVALID_HANDLE; } public cbkGroupAnnouncement(Handle:response, bool:failure, bool:requestSuccessful, EHTTPStatusCode:statusCode) { stopTimeoutTimer(); if (connectionInterrupted) { CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; return; } if (response == INVALID_HANDLE || !requestSuccessful || statusCode != k_EHTTPStatusCode200OK) { PrintDebug(caller, "Group announcement request failed (%i). Status Code: %i", requestSuccessful, statusCode); onRequestResult(caller, false, 0x10); // Failed http group announcement request CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; return; } new cookieSize; SteamWorks_GetHTTPResponseHeaderSize(response, "Set-Cookie", cookieSize); decl String:cookie[cookieSize]; SteamWorks_GetHTTPResponseHeaderValue(response, "Set-Cookie", cookie, cookieSize); new bodySize; SteamWorks_GetHTTPResponseBodySize(response, bodySize); decl String:responseBody[bodySize]; SteamWorks_GetHTTPResponseBodyData(response, responseBody, bodySize); new Handle:regex; regex = CompileRegex("steamLogin=(.*?);"); MatchRegex(regex, cookie); decl String:steamLogin[20]; GetRegexSubString(regex, 1, steamLogin, sizeof(steamLogin)); CloseHandle(regex); regex = INVALID_HANDLE; regex = CompileRegex("(.*?)"); MatchRegex(regex, responseBody); decl String:title[40]; GetRegexSubString(regex, 1, title, sizeof(title)); CloseHandle(regex); regex = INVALID_HANDLE; if (strcmp(steamLogin, "deleted") == 0) { isLogged = false; PrintDebug(caller, "Invalid steam login token."); onRequestResult(caller, false, 0x11); // Invalid steam login token CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; return; } if (strcmp(title, "Steam Community :: Error") == 0) { PrintDebug(caller, "Form error on request."); onRequestResult(caller, false, 0x12); // Form error on request CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; return; } onRequestResult(caller, true); CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; } public cbkGroupInvite(Handle:response, bool:failure, bool:requestSuccessful, EHTTPStatusCode:statusCode) { stopTimeoutTimer(); if (connectionInterrupted) { CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; return; } if (response == INVALID_HANDLE || !requestSuccessful || statusCode != k_EHTTPStatusCode200OK) { PrintDebug(caller, "Group invite request failed (%i). Status Code: %i", requestSuccessful, statusCode); onRequestResult(caller, false, 0x20); // Failed http group invite request CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; return; } new bodySize; SteamWorks_GetHTTPResponseBodySize(response, bodySize); decl String:responseBody[bodySize]; SteamWorks_GetHTTPResponseBodyData(response, responseBody, bodySize); new Handle:regex; regex = CompileRegex("<\\/results>", PCRE_DOTALL); MatchRegex(regex, responseBody); decl String:result[2048]; GetRegexSubString(regex, 1, result, sizeof(result)); CloseHandle(regex); regex = INVALID_HANDLE; if (!StrEqual(result, "OK")) { PrintDebug(caller, "Error: "); PrintDebug(caller, result); if (StrEqual(result, "The invitation to that player failed. Please try again.\n\nError code: 19")) { PrintDebug(caller, "Invite failed. Incorrect invitee id on request or another error."); onRequestResult(caller, false, 0x21); // Incorrect invitee or another error } else if (StrEqual(result, "Missing Data")) { PrintDebug(caller, "Invite failed. Incorrect group id or missing data on request."); onRequestResult(caller, false, 0x22); // Incorrect Group ID or missing data. } else if (StrEqual(result, "Missing or invalid form session key")) { isLogged = false; PrintDebug(caller, "Invite failed. Plugin is not logged in. Try again to login."); onRequestResult(caller, false, 0x23); // Logged out. Retry to login } else if (StrEqual(result, "You do not have permission to invite to the group specified.")) { PrintDebug(caller, "Invite failed. Inviter account is not a member of the group or does not have permissions to invite."); onRequestResult(caller, false, 0x24); // Account does not have permissions to invite. } else if (StrEqual(result, "Your account does not meet the requirements to use this feature. Visit Steam Support for more information.")) { PrintDebug(caller, "Invite failed. Account is limited, only full Steam accounts can send group invites."); onRequestResult(caller, false, 0x25); // Limited account. Only full Steam accounts can send Steam group invites } else { PrintDebug(caller, "Invite failed. Unknown error response received when sending the group invite."); onRequestResult(caller, false, 0x26); // Unknown error } } else { if (StrContains(responseBody, "") != -1) { PrintDebug(caller, "Invite failed. Invitee has already received an invite or is already on the group."); onRequestResult(caller, false, 0x27); // Invitee has already received an invite or is already on the group. } else { PrintDebug(caller, "Group invite sent."); onRequestResult(caller, true); // Success } } PrintDebug(caller, "Response body (%i):\n %s", strlen(responseBody), responseBody); CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; } onRequestResult(client, bool:success, errorCode=0, any:data=0) { isBusy = false; PrintDebug(caller, "\n============================================================================\n"); PrintDebug(caller, "Final request result: %i - Error Code : %i", success, errorCode); if (success) { timeSinceLastLogin = 0; KillTimer(hTimeIncreaser); hTimeIncreaser = CreateTimer(TIMER_UPDATE_TIME*60.0, timeIncreaser, INVALID_HANDLE, TIMER_REPEAT); } // In case there was an error before the last request was executed, they are freed. else if (errorCode > 0 && errorCode <= 0x0A) { if (finalRequest != INVALID_HANDLE) CloseHandle(finalRequest); finalRequest = INVALID_HANDLE; finalFunction = INVALID_FUNCTION; } if (callbackFunction != INVALID_FUNCTION) { PrintDebug(caller, "Calling callback..."); // Start function call Call_StartForward(callbackHandle); // Push parameters one at a time Call_PushCell(client); // Client Call_PushCell(success); // Success Call_PushCell(errorCode); // Error code Call_PushCell(data); // Extra data, in this case nothing // Finish the call new result = Call_Finish(); PrintDebug(caller, "Callback calling error code: %i (0: Success)", result); removeCallback(); } caller = 0; } removeCallback() { new bool:removed = RemoveFromForward(callbackHandle, callbackPlugin, callbackFunction); new functionCount = GetForwardFunctionCount(callbackHandle); PrintDebug(caller, "Removing callback from forward - Result: %i, - Forward Function Count: %i", removed, functionCount); callbackFunction = INVALID_FUNCTION; } // =================================================================================== // =================================================================================== // Obtains the plugin index in a plugin iterator GetPluginIteratorNumber(Handle:plugin) { new pluginNumber = 0; decl String:pluginName[256]; decl String:auxPluginName[256]; GetPluginFilename(plugin, pluginName, sizeof(pluginName)); new Handle:pluginIterator = GetPluginIterator(); while (MorePlugins(pluginIterator)) { pluginNumber++; GetPluginFilename(ReadPlugin(pluginIterator), auxPluginName, sizeof(auxPluginName)); if (StrEqual(pluginName, auxPluginName)) break; } CloseHandle(pluginIterator); pluginIterator = INVALID_HANDLE; return pluginNumber; } Handle:FindPluginFromNumber(pluginNumber) { new Handle:pluginIterator = GetPluginIterator(); new Handle:plugin; for (new i = 0; i < pluginNumber; i++) { if (!MorePlugins(pluginIterator)) { plugin = INVALID_HANDLE; break; } plugin = ReadPlugin(pluginIterator); } CloseHandle(pluginIterator); pluginIterator = INVALID_HANDLE; return plugin; } // =================================================================================== // =================================================================================== PrintDebug(client, const String:format[], any:...) { if (DEBUG) { decl String:text[1024]; VFormat(text, sizeof(text), format, 3); if (client == 0) PrintToServer(text); else if (IsClientInGame(client)) PrintToConsole(client, text); } } stock GetCaller() { return caller; }