diff --git a/.gitignore b/.gitignore index dc46e18..88dbff1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1 @@ .vs/ - -packages/ diff --git a/Newtonsoft.Json.dll b/Newtonsoft.Json.dll new file mode 100644 index 0000000..81639f9 Binary files /dev/null and b/Newtonsoft.Json.dll differ diff --git a/PSLauncher.sln b/PSLauncher.sln index 26a2fc4..932894d 100644 --- a/PSLauncher.sln +++ b/PSLauncher.sln @@ -1,46 +1,34 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.32901.82 +# Visual Studio 14 +VisualStudioVersion = 14.0.23107.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PSLauncher", "PSLauncher\PSLauncher.csproj", "{0D6A691E-95FE-4056-8979-190A9A35BDC7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 LiveSupport|Any CPU = LiveSupport|Any CPU - LiveSupport|x64 = LiveSupport|x64 LiveSupport|x86 = LiveSupport|x86 Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x64.ActiveCfg = Debug|x64 - {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x64.Build.0 = Debug|x64 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x86.ActiveCfg = Debug|x86 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Debug|x86.Build.0 = Debug|x86 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|Any CPU.ActiveCfg = LiveSupport|Any CPU {0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|Any CPU.Build.0 = LiveSupport|Any CPU - {0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x64.ActiveCfg = LiveSupport|x64 - {0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x64.Build.0 = LiveSupport|x64 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x86.ActiveCfg = LiveSupport|x86 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.LiveSupport|x86.Build.0 = LiveSupport|x86 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|Any CPU.ActiveCfg = Release|Any CPU {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|Any CPU.Build.0 = Release|Any CPU - {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x64.ActiveCfg = Release|x64 - {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x64.Build.0 = Release|x64 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x86.ActiveCfg = Release|x86 {0D6A691E-95FE-4056-8979-190A9A35BDC7}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {92FA1014-24E9-4E6D-A263-B80A2AD72F3C} - EndGlobalSection EndGlobal diff --git a/PSLauncher/ClientINI.cs b/PSLauncher/ClientINI.cs index 11c97eb..b5c096d 100644 --- a/PSLauncher/ClientINI.cs +++ b/PSLauncher/ClientINI.cs @@ -31,16 +31,16 @@ namespace PSLauncher FileVersionInfo fvi = FileVersionInfo.GetVersionInfo(assembly.Location); string version = fvi.FileVersion; - string contents = "# FILE AUTOGENERATED BY PSForever Launcher " + version + Environment.NewLine; - contents += "[network]" + Environment.NewLine; + string contents = "# FILE AUTOGENERATED BY PSForever Launcher " + version + "\n"; + contents += "[network]\n"; for(int i = 0; i < entries.Count; i++) { ServerEntry entry = entries[i]; - contents += "# Name: " + entry.name + Environment.NewLine; + contents += "# Name: " + entry.name + "\n"; // we only want login0 to be used, but have the rest written there for manual editing as well - contents += String.Format("{3}login{0}={1}:{2}" + Environment.NewLine, i, entry.hostname, entry.port, + contents += String.Format("{3}login{0}={1}:{2}\n", i, entry.hostname, entry.port, i == 0 ? "" : "#"); } diff --git a/PSLauncher/LauncherForm.Designer.cs b/PSLauncher/LauncherForm.Designer.cs index 6330118..c385cf1 100644 --- a/PSLauncher/LauncherForm.Designer.cs +++ b/PSLauncher/LauncherForm.Designer.cs @@ -187,7 +187,7 @@ this.launchGame.TabIndex = 2; this.launchGame.Text = "Launch"; this.launchGame.UseVisualStyleBackColor = false; - this.launchGame.Click += new System.EventHandler(this.launchGame_Click); + this.launchGame.Click += new System.EventHandler(this.button2_Click); // // launchMessage // @@ -204,7 +204,7 @@ // this.spinner.Enabled = false; this.spinner.Image = ((System.Drawing.Image)(resources.GetObject("spinner.Image"))); - this.spinner.Location = new System.Drawing.Point(22, 66); + this.spinner.Location = new System.Drawing.Point(114, 67); this.spinner.Name = "spinner"; this.spinner.Size = new System.Drawing.Size(26, 24); this.spinner.TabIndex = 24; @@ -232,13 +232,13 @@ this.copy, this.saveToFile}); this.outputRightClick.Name = "outputRightClick"; - this.outputRightClick.Size = new System.Drawing.Size(167, 70); + this.outputRightClick.Size = new System.Drawing.Size(165, 70); // // selectAll // this.selectAll.Name = "selectAll"; this.selectAll.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.A))); - this.selectAll.Size = new System.Drawing.Size(166, 22); + this.selectAll.Size = new System.Drawing.Size(164, 22); this.selectAll.Text = "Select All"; this.selectAll.Click += new System.EventHandler(this.selectAll_Click); // @@ -247,14 +247,14 @@ this.copy.Name = "copy"; this.copy.ShortcutKeyDisplayString = ""; this.copy.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.C))); - this.copy.Size = new System.Drawing.Size(166, 22); + this.copy.Size = new System.Drawing.Size(164, 22); this.copy.Text = "Copy"; this.copy.Click += new System.EventHandler(this.copy_Click); // // saveToFile // this.saveToFile.Name = "saveToFile"; - this.saveToFile.Size = new System.Drawing.Size(166, 22); + this.saveToFile.Size = new System.Drawing.Size(164, 22); this.saveToFile.Text = "Save to file ..."; this.saveToFile.Click += new System.EventHandler(this.saveToFile_Click); // diff --git a/PSLauncher/LauncherForm.cs b/PSLauncher/LauncherForm.cs index 799c0f7..8bab2b0 100644 --- a/PSLauncher/LauncherForm.cs +++ b/PSLauncher/LauncherForm.cs @@ -1,12 +1,16 @@ -using Newtonsoft.Json; +using Microsoft.Win32; +using Newtonsoft.Json.Linq; using PSLauncher.Properties; using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.IO; -using System.Net.Http; -using System.Net.Http.Json; +using System.Linq; +using System.Net; +using System.Runtime.InteropServices; using System.Text; +using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; @@ -16,16 +20,12 @@ namespace PSLauncher public enum LaunchDomain { Live, - PSForever, - Dev, - DevSSL + PSForever } public enum GameState { Stopped, - Authenticating, - Validating, Launching, Running, Stopping @@ -33,59 +33,30 @@ namespace PSLauncher public partial class LauncherForm : Form { - private HttpClient httpClient; - private Process psProcess; + Process psProcess; + string USER_AGENT = "Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0"; + int DEFAULT_WEB_TIMEOUT = 5000; bool bGameRunning = false; - int DEFAULT_WEB_TIMEOUT = 5; GameState gameState = GameState.Stopped; - LaunchDomain domain = LaunchDomain.PSForever; - System.Drawing.Size oldSize = new System.Drawing.Size(0, 0); + LaunchDomain domain = LaunchDomain.Live; + Dictionary domains = new Dictionary() { - { LaunchDomain.Live, "https://lpj.daybreakgames.com/ps/live/" }, - { LaunchDomain.PSForever, "https://login.psforever.net/psf/live/" }, - { LaunchDomain.Dev, "http://localhost:9001/psf/live/" }, - { LaunchDomain.DevSSL, "http://localhost:9001/psf/live/" } - }; - - Dictionary userAgents = new Dictionary() - { - { LaunchDomain.Live, "Mozilla/5.0 (Windows NT 6.1; rv:31.0) Gecko/20100101 Firefox/31.0" }, - { LaunchDomain.PSForever, string.Format("PSF Launcher v{0}", Program.launcherVersion) }, - { LaunchDomain.Dev, string.Format("PSF Launcher v{0}", Program.launcherVersion) }, - { LaunchDomain.DevSSL, string.Format("PSF Launcher v{0}", Program.launcherVersion) } + { LaunchDomain.Live, "https://lpj.daybreakgames.com/ps/live" }, + { LaunchDomain.PSForever, "https://login.psforever.net/psf/live/login" } }; public LauncherForm() { - // - // init form components - // InitializeComponent(); - // - // init http client - // - httpClient = new HttpClient - { - BaseAddress = new Uri(domains[domain]), - Timeout = TimeSpan.FromSeconds(DEFAULT_WEB_TIMEOUT) - }; - - httpClient.DefaultRequestHeaders.Clear(); - httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(userAgents[domain]); - httpClient.DefaultRequestHeaders.Accept.ParseAdd("application/json"); - - // - // set form icon - // this.Icon = System.Drawing.Icon.ExtractAssociatedIcon(Application.ExecutablePath); #if DEBUG - //Settings.Default.Reset(); + Settings.Default.Reset(); Console.SetOut(new Util.ControlWriter(this.ps_consoleOutput)); #endif skipLauncher.Checked = Settings.Default.SkipLauncher; @@ -110,7 +81,7 @@ namespace PSLauncher private void LauncherForm_FormClosing(object sender, FormClosingEventArgs e) { - if (this.bGameRunning && Settings.Default.OutputShown) + if (this.bGameRunning) { DialogResult res = MessageBox.Show( "Are you sure you want to exit while managing PlanetSide PID " + psProcess.Id + "?" + Environment.NewLine + "You won't see any debugging output if you do.", "Confirm exit", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); @@ -128,14 +99,6 @@ namespace PSLauncher gameState = GameState.Stopped; } - private void startAuthenticating() - { - setButtonState(GameState.Authenticating); - progressShown(true); - - gameState = GameState.Authenticating; - } - private void startLaunching() { setButtonState(GameState.Launching); @@ -150,48 +113,20 @@ namespace PSLauncher { switch (state) { - case GameState.Stopped: - this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 255, 128); - this.launchGame.Enabled = true; - this.launchGame.Text = "Launch"; - - // allow modification if launcher is not skipped - if (!skipLauncher.Checked) - { - this.username.Enabled = true; - this.password.Enabled = true; - } - - break; - - case GameState.Authenticating: - this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 128, 255); - this.launchGame.Enabled = false; - this.launchGame.Text = "Authenticating"; - - // disallow modification because we are launching - this.username.Enabled = false; - this.password.Enabled = false; - - break; - - case GameState.Validating: - this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 128, 255); - this.launchGame.Enabled = false; - this.launchGame.Text = "Validating"; - break; - case GameState.Launching: this.launchGame.Enabled = false; - this.launchGame.Text = "Launching"; + this.launchGame.Text = "Launching..."; break; - case GameState.Running: this.launchGame.BackColor = System.Drawing.Color.FromArgb(255, 128, 128); this.launchGame.Enabled = true; this.launchGame.Text = "Kill"; break; - + case GameState.Stopped: + this.launchGame.BackColor = System.Drawing.Color.FromArgb(128, 255, 128); + this.launchGame.Enabled = true; + this.launchGame.Text = "Launch"; + break; case GameState.Stopping: this.launchGame.Enabled = false; this.launchGame.Text = "Killing..."; @@ -200,14 +135,6 @@ namespace PSLauncher }); } - private void setButtonValidationState(int counter, int fileCount) - { - this.SafeInvoke(a => - { - this.launchGame.Text = string.Format("Validating {0}/{1}", counter, fileCount); - }); - } - private void setErrorMessage(string error) { this.SafeInvoke(a => @@ -223,7 +150,7 @@ namespace PSLauncher }); } - private void launchGame_Click(object sender, EventArgs e) + private void button2_Click(object sender, EventArgs e) { if(gameState == GameState.Running) // kill command { @@ -256,36 +183,18 @@ namespace PSLauncher // Build arguments List arguments = new List(); + if (skipLauncher.Checked) + arguments.Add("/K:StagingTest"); + if (Settings.Default.CoreCombat) arguments.Add("/CC"); if (Settings.Default.ExtraArgs != "") arguments.Add(Settings.Default.ExtraArgs); - WriteClientINI(); - - if (skipLauncher.Checked) + // Rewrite client.ini if selected + if(Settings.Default.GenerateClientINI) { - arguments.Add("/K:StagingTest"); - - LaunchStaging(); - } - else - { - Launch(arguments); - } - - return; - - // - // functions used only in this function - // - - void WriteClientINI() - { - // Rewrite client.ini if selected - if (!Settings.Default.GenerateClientINI) return; - string inipath = Path.Combine(Path.GetDirectoryName(psExe), "client.ini"); ClientINI ini = new ClientINI(inipath); @@ -293,7 +202,7 @@ namespace PSLauncher { ini.writeEntries(Util.LoadServerList(), serverSelection.SelectedIndex); } - catch (IOException exp) + catch(IOException exp) { setErrorMessage("Failed to write INI file"); addLine(String.Format("ClientINI: error - '{0}' ({1})", exp.Message, inipath)); @@ -301,10 +210,10 @@ namespace PSLauncher } } - void LaunchStaging() + if (skipLauncher.Checked) { // magic string to login to planetside from the actual game - if (!startPlanetSide(psExe, Path.GetDirectoryName(psExe), String.Join(" ", arguments))) + if(!startPlanetSide(psExe, Path.GetDirectoryName(psExe), String.Join(" ", arguments))) { gameStopped(); } @@ -312,15 +221,18 @@ namespace PSLauncher { gameRunning(); } - } - - void Launch(List _arguments) + } + else { - startLaunching(); + setErrorMessage("Login disabled. Use skip launcher"); + gameStopped(); + return; + + /*startLaunching(); Task.Factory.StartNew(() => { - if (!this.doLogin(_arguments)) + if(!this.doLogin()) { gameStopped(); } @@ -328,478 +240,326 @@ namespace PSLauncher { gameRunning(); } - }); + });*/ } } - class DefaultResponse + HttpWebResponse netGetSession(string endpoint, CookieContainer cookies) { - public int Status { get; set; } - }; + string hostname = domains[domain]; + HttpWebRequest req = WebRequest.Create(hostname + endpoint) as HttpWebRequest; + req.CookieContainer = cookies; + req.CookieContainer.MaxCookieSize = 4000; + req.Method = "GET"; + req.UserAgent = USER_AGENT; + req.Timeout = DEFAULT_WEB_TIMEOUT; - class ErrorResponse : DefaultResponse - { - //[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] - public string ErrorText { get; set; } + return req.GetResponse() as HttpWebResponse; } - class VersionResponse : DefaultResponse + bool doLogin() { - public Int64 ReleaseDate { get; set; } - public string VersionString { get; set; } - } + long ts = (long)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds; - class TokenResponse : DefaultResponse - { - public string Token { get; set; } - } + string path = Settings.Default.PSPath; + string psExe = Path.Combine(path, SettingsForm.PS_EXE_NAME); - class ValidationResponse : DefaultResponse - { - public string[] Files { get; set; } - } + ///////////////////////////////////////////////////////////////// + // Step 1: Establish Session ID + ///////////////////////////////////////////////////////////////// - class GameTokenResponse : DefaultResponse - { - public string GameToken { get; set; } - } + String endpoint = domains[domain]; + CookieContainer reqCookies = new CookieContainer(); + HttpWebRequest req; + HttpWebResponse r; - class LoginRequestBody - { - public string Username { get; set; } - public string Password { get; set; } - public string Launcher { get; set; } - public int Mode { get; set; } - } - - class ValidateRequestBody - { - public string Launcher { get; set; } - public string Files { get; set; } - } - - void handleErrorResponse(ref string _errorBody) - { - var errorResponse = JsonConvert.DeserializeObject(_errorBody); - string errorMessage; - addLine("====="); - addLine("The launcher received an error:"); - - switch(errorResponse.Status) - { - case 100: - errorMessage = "Launcher Update required."; - break; - - case 101: - errorMessage = "Launcher is corrupted, please download the latest PSF Launcher."; - break; - - case 102: - errorMessage = "Launcher token expired, please retry."; - break; - - case 103: - errorMessage = "Gamefiles are corrupted, please replace them."; - break; - - case 104: - errorMessage = "Launcher is no longer supported, please download the latest PSF Launcher."; - break; - - case 200: - errorMessage = "This account does not yet support Launcher login, please sign in via \"Skip Launcher\" once."; - break; - - case 201: - errorMessage = "Wrong Username or Password."; - break; - - case 202: - errorMessage = "This account is inactive, please contact the PSF Support on Discord."; - break; - - case 300: - errorMessage = "There has been a database error, please try again later."; - break; - - default: - errorMessage = "Please report this error to the PSF Support on Discord\nError: " + errorResponse.Status; - break; - } - - // print the launcher message - addLine(errorMessage); - - // print the message from the error response - if ( errorResponse.ErrorText.Length != 0 ) - { - addLine(errorResponse.ErrorText); - } - - addLine("====="); - } - - void checkLauncherVersion() - { - addLine("Checking for new PSF Launcher version..."); - - HttpResponseMessage respVersion; try { - respVersion = httpClient.GetAsync("version").Result; + r = netGetSession("/?t=43323", reqCookies); } - catch + catch (WebException x) { - addLine("Error: Version GET did not return"); - return; - } - - if (!respVersion.IsSuccessStatusCode) - { - addLine("Error: Version GET status " + respVersion.StatusCode); - return; - } - - var result = respVersion.Content.ReadAsStringAsync().Result; - var versionResponse = JsonConvert.DeserializeObject(result); - - if (versionResponse.Status != 0) - { - handleErrorResponse(ref result); - - return; - } - - var newestVersion = new List(versionResponse.VersionString.Split('.')); - var localVersion = new List(Program.launcherVersion.Split('.')); - - if ( newestVersion.Count != localVersion.Count ) - { - addLine("====="); - addLine("Could not compare launcher versions. Launching may fail."); - addLine(string.Format("Local version: v{0} - latest version: v{1}", localVersion, newestVersion)); - addLine("Please update if there is a newer version of PSF Launcher."); - addLine("====="); - - return; - } - - bool newerVersionAvailable = false; - for (int i = 0; i < newestVersion.Count; i++) - { - var nv = int.Parse(newestVersion[i]); - var lv = int.Parse(localVersion[i]); - - if (lv < nv) + if (x.Status != WebExceptionStatus.TrustFailure) { - newerVersionAvailable = true; - break; - } - } - - if ( newerVersionAvailable ) - { - DateTime parsedTime = Util.UnixTimestampToDateTime(versionResponse.ReleaseDate); - - addLine("There is a newer version of the PSF Launcher. Launching may fail."); - - addLine( - string.Format( - "Version information: v{0} released {1}", - versionResponse.VersionString, - parsedTime.ToLocalTime().ToLongDateString() - ) - ); - - addLine("====="); - } - else - { - addLine("PSF Launcher is on the latest version."); - } - - addLine(""); - - return; - } - - bool sendLoginData() - { - var username = this.username.Text; - var password = this.password.Text; - var passwordHash = Util.CalculateStringHash(EHashingAlgoType.SHA256, username + password); - - var loginRequestBody = new LoginRequestBody - { - Username = this.username.Text, - Password = passwordHash, - Launcher = Program.launcherHash, - Mode = 0 - }; - - HttpResponseMessage respLogin; - try - { - respLogin = httpClient.PostAsJsonAsync("login", loginRequestBody).Result; - } - catch (Exception e) - { - addLine("Error: Login POST did not return"); - addLine(e.InnerException.Message); - return false; - } - - if (!respLogin.IsSuccessStatusCode) - { - addLine("Error: Login GET status " + respLogin.StatusCode); - return false; - } - - var result = respLogin.Content.ReadAsStringAsync().Result; - var loginResponse = JsonConvert.DeserializeObject(result); - - if (loginResponse.Status != 0) - { - handleErrorResponse(ref result); - - return false; - } - - httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", loginResponse.Token); - - return true; - } - - bool getFilesToValidate(ref List _filesToValidate) - { - HttpResponseMessage respValidateGet; - try - { - respValidateGet = httpClient.GetAsync("validate").Result; - } - catch (Exception e) - { - addLine("Error: Validate GET did not return"); - addLine(e.Message); - return false; - } - - if (!respValidateGet.IsSuccessStatusCode) - { - addLine("Error: Validate GET status " + respValidateGet.StatusCode); - return false; - } - - var result = respValidateGet.Content.ReadAsStringAsync().Result; - var validateGetResponse = JsonConvert.DeserializeObject(result); - - if (validateGetResponse.Status != 0) - { - handleErrorResponse(ref result); - - return false; - } - - _filesToValidate.AddRange(validateGetResponse.Files); - - return true; - } - - bool validateFiles(List_filesToValidate, ref string _fileHashResult) - { - string basePath = Settings.Default.PSPath; - List fileHashes = new List(); - - for (int index = 0; index < _filesToValidate.Count; index++) - { - setButtonValidationState(index + 1, _filesToValidate.Count); - - string filePath = _filesToValidate[index]; - - // get absolute path - var absPath = Path.GetFullPath(Path.Combine(basePath, filePath)); - - // make sure path is within planetside directory - if (!absPath.StartsWith(basePath)) - { - addLine("Error: Filepath for validation is outside of planetside directory"); + addLine("Failed to gather initial session: " + x.Message); return false; } - FileStream fileHandle; + DialogResult res = MessageBox.Show("DBG's HTTPS certificate has failed to verify. This means that their certificate has expired " + + "or you may be getting Man-in-the-middled (attacked). If you are under attack, your credentials could be lost. Continue regardless?", + "Certificate error", MessageBoxButtons.YesNo, MessageBoxIcon.Warning); + + if (res != DialogResult.Yes) + { + return false; + } + + // WARNING: once we hit yes, all further SSL errors will be ignored + // https://stackoverflow.com/questions/2675133/c-sharp-ignore-certificate-errors + ServicePointManager.ServerCertificateValidationCallback += + (sender, cert, chain, sslPolicyErrors) => true; + try { - fileHandle = File.OpenRead(absPath); + r = netGetSession("/?t=43323", reqCookies); } - catch + catch (WebException x2) { - addLine("Error: Required file not found: " + filePath); + addLine("Failed to gather initial session: " + x2.Message); return false; } - - fileHashes.Add(Util.CalculateFileHash(fileHandle)); - - fileHandle.Close(); } - _fileHashResult = Util.CalculateStringHash(string.Join("", fileHashes)); + // Note: we must manually add secure cookies and CookieContainer is crap + // See http://thomaskrehbiel.com/post/1690-cookiecontainer_httpwebrequest_and_secure_cookies/ - return true; - } + reqCookies.Add(new Uri(endpoint), r.Cookies); - bool sendValidationResult(string _hashResult) - { - var validateResponseBody = new ValidateRequestBody + addLine("PSWeb: session started"); + r.Close(); + + ///////////////////////////////////////////////////////////////// + // Step 2: Try logging in + ///////////////////////////////////////////////////////////////// + + req = WebRequest.Create(endpoint + "/login?t=43323") as HttpWebRequest; + req.CookieContainer = reqCookies; + req.Method = "POST"; + req.UserAgent = USER_AGENT; + req.Timeout = DEFAULT_WEB_TIMEOUT; + req.Headers.Add("X-Requested-With", "XMLHttpRequest"); + req.Headers.Add("Origin", "https://lp.soe.com"); + + req.ContentType = "application/x-www-form-urlencoded"; + req.Referer = endpoint + "/?t=43323"; + + NameValueCollection query = new NameValueCollection(); + query.Add("username", username.Text); + query.Add("password", password.Text); + query.Add("rememberPassword", "false"); + query.Add("ts", ts.ToString()); + + var postdata = Encoding.ASCII.GetBytes(query.ToQueryString()); + //addLine(query.ToQueryString()); + + req.ContentLength = postdata.Length; + + using (var stream = req.GetRequestStream()) { - Launcher = Program.launcherHash, - Files = _hashResult - }; + stream.Write(postdata, 0, postdata.Length); + } - HttpResponseMessage respValidatePost; try { - respValidatePost = httpClient.PostAsJsonAsync("validate", validateResponseBody).Result; + r = req.GetResponse() as HttpWebResponse; } - catch + catch (WebException x) { - addLine("Error: Validate POST did not return"); + string txt; + + using (HttpWebResponse respExcept = (HttpWebResponse)x.Response) + { + if (respExcept != null && respExcept.GetResponseStream().CanRead) + { + StreamReader r2 = new StreamReader(respExcept.GetResponseStream()); + txt = r2.ReadToEnd(); + respExcept.Close(); + } + else + { + txt = ""; + addLine("Login failed: " + x.Message); + return false; + } + } + + string errorDetail = ""; + + try + { + JObject obj2 = JObject.Parse(txt); + errorDetail = (string)obj2["error"]; + } + catch (Newtonsoft.Json.JsonException x2) + { + errorDetail = "Json parse error: " + x2.Message; + } + + if (errorDetail == "INVALID_ACCOUNT_ID") // not sure if we still get this... + { + setErrorMessage("Unknown username"); + } + else if (errorDetail == "NEED_PASSWORD_RESET") + { + setErrorMessage("Your account needs a password reset"); + } + else if (errorDetail == "BAD_LOGIN") + { + setErrorMessage("Bad password or username"); + } + else // unrecognized! + { + setErrorMessage("Unknown error - see output window"); + addLine("Login failure: " + x.Status); + addLine("Error: " + errorDetail); + addLine(txt); + } + return false; } - if (!respValidatePost.IsSuccessStatusCode) + if (!r.GetResponseStream().CanRead) { - addLine("Error: Validate POST status " + respValidatePost.StatusCode); - return false; - } - - var result = respValidatePost.Content.ReadAsStringAsync().Result; - var validatePostResponse = JsonConvert.DeserializeObject(result); - - if (validatePostResponse.Status != 0) - { - handleErrorResponse(ref result); + setErrorMessage("Unknown error - see output window"); + addLine("No login response received"); + addLine("Status: " + r.StatusCode); return false; } - // update to validated token - httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", validatePostResponse.Token); + StreamReader reader = new StreamReader(r.GetResponseStream()); + string text = reader.ReadToEnd(); - return true; - } + //addLine(r.Headers["Set-Cookie"]); + reqCookies.Add(new Uri(endpoint), r.Cookies); + + string result = ""; + r.Close(); + addLine("PSWeb: logged in"); - bool getGameToken(ref string _gameToken) - { - HttpResponseMessage respGameToken; try { - respGameToken = httpClient.GetAsync("gametoken").Result; + JObject obj = JObject.Parse(text); + result = (string)obj["result"]; } - catch + catch (Newtonsoft.Json.JsonException x2) { - addLine("Error: GameToken GET did not return"); + result = "Json parse error: " + x2.Message; + } + + if (result != "SUCCESS") + { + setErrorMessage("Unknown error - see output window"); + addLine("Bad login response: " + result); + addLine("Status: " + r.StatusCode); + addLine(text); + return false; } + - if (!respGameToken.IsSuccessStatusCode) + ///////////////////////////////////////////////////////////////// + // Step 3: Fetch the login token + ///////////////////////////////////////////////////////////////// + + req = WebRequest.Create(endpoint + "/get_play_session?t=43323") as HttpWebRequest; + req.CookieContainer = reqCookies; + req.Method = "GET"; + req.UserAgent = USER_AGENT; + req.Timeout = DEFAULT_WEB_TIMEOUT; + req.Headers.Add("Origin", "https://lp.soe.com"); + req.Referer = endpoint + "/login?t=43323"; + req.Headers.Add("X-Requested-With", "XMLHttpRequest"); + req.Accept = "*/*"; + + try { - addLine("Error: GameToken GET status " + respGameToken.StatusCode); - return false; + r = req.GetResponse() as HttpWebResponse; } - - var result = respGameToken.Content.ReadAsStringAsync().Result; - var gameTokenResponse = JsonConvert.DeserializeObject(result); - - if (gameTokenResponse.Status != 0) + catch (WebException x) { - handleErrorResponse(ref result); + string txt; + + using (HttpWebResponse respExcept = (HttpWebResponse)x.Response) + { + if (respExcept != null && respExcept.GetResponseStream().CanRead) + { + StreamReader r2 = new StreamReader(respExcept.GetResponseStream()); + txt = r2.ReadToEnd(); + } + else + { + txt = ""; + } + } + + string errorDetail = ""; + + try + { + JObject obj2 = JObject.Parse(txt); + errorDetail = (string)obj2["result"]; + } + catch (Newtonsoft.Json.JsonException x2) + { + errorDetail = "Json parse error: " + x2.Message; + } + + if (errorDetail == "RE_LOGIN") + { + setErrorMessage("Failed to fetch token: bad login"); + } + else // unrecognized! + { + setErrorMessage("Unknown error - see output window"); + } + + addLine("Get token failure: " + x.Status); + addLine("Error: " + errorDetail); + addLine(txt); return false; } - _gameToken = gameTokenResponse.GameToken; - - return true; - } - - bool doLogin(List _arguments) - { - string path = Settings.Default.PSPath; - string psExe = Path.GetFullPath(Path.Combine(path, SettingsForm.PS_EXE_NAME)); - - // clear previous login token - httpClient.DefaultRequestHeaders.Authorization = null; - - if ( !psExe.StartsWith(path) ) + if (!r.GetResponseStream().CanRead) { - addLine("Error: planetside.exe outside of planetside directory: " + psExe); + setErrorMessage("Unknown error - see output window"); + addLine("No login response received"); + addLine("Status: " + r.StatusCode); + return false; } - addLine(""); - addLine("Start launching"); - addLine(""); + reader = new StreamReader(r.GetResponseStream()); + text = reader.ReadToEnd(); - // start authentication - setButtonState(GameState.Authenticating); + result = ""; + r.Close(); - // - // get current launcher version - // - checkLauncherVersion(); + string token = ""; - // - // send login data - // - if ( !sendLoginData() ) + try { + JObject obj = JObject.Parse(text); + result = (string)obj["result"]; + token = (string)obj["launchArgs"]; + //addLine(text); + } + catch (Newtonsoft.Json.JsonException x2) + { + result = "Json parse error: " + x2.Message; + token = ""; + } + + if (result != "SUCCESS") + { + setErrorMessage("Failed to get token"); + addLine("Bad token response: " + result); + addLine("Status: " + r.StatusCode); + addLine(text); + return false; } - // - // get file validation info - // - List filesToValidate = new List(); - if ( !getFilesToValidate(ref filesToValidate) ) - { - return false; - } + addLine("PSWeb: got launch args " + token); - // - // validate files - // - setButtonState(GameState.Validating); + string launch_args = token; + string ExtraLaunchArgs = Settings.Default.ExtraArgs; - string hashResult = ""; - if ( !validateFiles(filesToValidate, ref hashResult) ) - { - return false; - } - - // - // send validtation result - // - if ( !sendValidationResult(hashResult) ) - { - return false; - } - - // finished validating - setButtonState(GameState.Launching); - - // - // get game token - // - string gameToken = ""; - if ( !getGameToken(ref gameToken) ) - { - return false; - } - - _arguments.Add("/K:" + gameToken); - - return startPlanetSide(psExe, path, String.Join(" ", _arguments)); + if (ExtraLaunchArgs != String.Empty) + launch_args += " " + ExtraLaunchArgs; + + return startPlanetSide(psExe, path, launch_args); } bool startPlanetSide(string exe, string workingDir, string args) @@ -816,11 +576,7 @@ namespace PSLauncher psProcess.OutputDataReceived += new DataReceivedEventHandler(ps_OutputDataReceived); psProcess.EnableRaisingEvents = true; - addLine(String.Format("ProcessStart: \"{0}\" {1}", exe, args)); - - addLine(""); - addLine("LAUNCHING"); - addLine(""); + addLine("ProcessStart: \"" + exe + "\" " + args); if (!psProcess.Start()) { @@ -882,11 +638,13 @@ namespace PSLauncher { this.spinner.Visible = true; this.spinner.Enabled = true; + this.launchGame.Visible = false; } else { this.spinner.Visible = false; this.spinner.Enabled = false; + this.launchGame.Visible = true; } }); } diff --git a/PSLauncher/PSLauncher.csproj b/PSLauncher/PSLauncher.csproj index b3afe34..958fbce 100644 --- a/PSLauncher/PSLauncher.csproj +++ b/PSLauncher/PSLauncher.csproj @@ -1,5 +1,5 @@  - + Debug x86 @@ -10,9 +10,8 @@ Properties PSLauncher PSForever_Launcher - v4.8 - - + v4.0 + Client 512 false @@ -28,7 +27,7 @@ false true 0 - 1.3.0.0 + 1.0.0.0 false true true @@ -38,21 +37,19 @@ true full false - bin\x86\Debug\ + bin\Debug\ DEBUG;TRACE prompt 4 - false x86 pdbonly true - bin\x86\Release\ + bin\Release\ TRACE prompt 4 - false bin\x86\LiveSupport\ @@ -69,7 +66,6 @@ true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true - false true @@ -86,11 +82,11 @@ true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true - false bin\Release\ - TRACE + + true none AnyCPU @@ -104,13 +100,12 @@ ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true false - 4 + 0 true - false bin\LiveSupport\ - TRACE;HACKS + HACKS true pdbonly AnyCPU @@ -123,7 +118,6 @@ true ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules true - false 793DD622BA49EF7D3494640F6D9646D8D289304B @@ -150,80 +144,14 @@ Properties\app.manifest - - true - bin\x64\Debug\ - DEBUG;TRACE - full - x64 - 7.3 - prompt - - - bin\x64\Release\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - - - bin\x64\LiveSupport\ - TRACE - true - pdbonly - x64 - 7.3 - prompt - MinimumRecommendedRules.ruleset - - - PSLauncher.Program - - - false - - - ..\packages\Microsoft.Bcl.AsyncInterfaces.7.0.0\lib\net462\Microsoft.Bcl.AsyncInterfaces.dll - - - ..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll + + ..\Newtonsoft.Json.dll - - ..\packages\System.Buffers.4.5.1\lib\net461\System.Buffers.dll - - - ..\packages\System.Memory.4.5.5\lib\net461\System.Memory.dll - - - - ..\packages\System.Net.Http.Json.7.0.1\lib\net462\System.Net.Http.Json.dll - - - - ..\packages\System.Numerics.Vectors.4.5.0\lib\net46\System.Numerics.Vectors.dll - - - ..\packages\System.Runtime.CompilerServices.Unsafe.6.0.0\lib\net461\System.Runtime.CompilerServices.Unsafe.dll - - - ..\packages\System.Text.Encodings.Web.7.0.0\lib\net462\System.Text.Encodings.Web.dll - - - ..\packages\System.Text.Json.7.0.3\lib\net462\System.Text.Json.dll - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - - - ..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll - @@ -285,7 +213,6 @@ SettingsForm.cs - SettingsSingleFileGenerator @@ -298,9 +225,9 @@ - + False - Microsoft .NET Framework 4.8 %28x86 und x64%29 + Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 true @@ -320,88 +247,6 @@ - - - False - - - - - Exclude - True - Assembly - - - False - - - - - Exclude - True - Assembly - - - False - - - - - Exclude - True - Assembly - - - False - - - - - Exclude - True - Assembly - - - False - - - - - Exclude - True - Assembly - - - False - - - - - Exclude - True - Assembly - - - False - - - - - Exclude - True - Assembly - - - False - - - - - Exclude - True - Assembly - -