using QobuzDownloaderX.Properties; using System; using System.Runtime.InteropServices; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Net; using System.Text; using System.IO; using System.Diagnostics; using System.Threading.Tasks; using System.Windows.Forms; using System.Security.Cryptography; using System.Reflection; using System.Text.RegularExpressions; using System.Net.Http; using System.Net.Http.Headers; using System.Drawing.Imaging; using TagLib.Flac; using QobuzDownloaderX; namespace QobuzDownloaderX { public partial class LoginFrm : Form { public LoginFrm() { InitializeComponent(); } public const int WM_NCLBUTTONDOWN = 0xA1; public const int HT_CAPTION = 0x2; [DllImportAttribute("user32.dll")] public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam); [DllImportAttribute("user32.dll")] public static extern bool ReleaseCapture(); private void QobuzDownloaderX_FormClosing(Object sender, FormClosingEventArgs e) { Application.Exit(); } QobuzDownloaderX qbdlx = new QobuzDownloaderX(); public string appSecret { get; set; } public string appID { get; set; } public string userID { get; set; } public string userAuthToken { get; set; } public string altLoginValue { get; set; } string errorLog = Path.GetDirectoryName(Application.ExecutablePath) + "\\Latest_Error.log"; string dllCheck = Path.GetDirectoryName(Application.ExecutablePath) + "\\taglib-sharp.dll"; static string GetMd5Hash(MD5 md5Hash, string input) { // Convert the input string to a byte array and compute the hash. byte[] data = md5Hash.ComputeHash(Encoding.UTF8.GetBytes(input)); // Create a new Stringbuilder to collect the bytes and create a string. StringBuilder sBuilder = new StringBuilder(); // Loop through each byte of the hashed data and format each one as a hexadecimal string. for (int i = 0; i < data.Length; i++) { sBuilder.Append(data[i].ToString("x2")); } // Return the hexadecimal string. return sBuilder.ToString(); } // Verify a hash against a string. static bool VerifyMd5Hash(MD5 md5Hash, string input, string hash) { // Hash the input. string hashOfInput = GetMd5Hash(md5Hash, input); // Create a StringComparer an compare the hashes. StringComparer comparer = StringComparer.OrdinalIgnoreCase; if (0 == comparer.Compare(hashOfInput, hash)) { return true; } else { return false; } } private void LoginFrm_Load(object sender, EventArgs e) { try { WebClient versionURLClient = new WebClient(); // Run through TLS to allow secure connection. ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; // Set user-agent to Firefox. versionURLClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0"); string versionHTML = versionURLClient.DownloadString("https://api.github.com/repos/ImAiiR/QobuzDownloaderX/releases/latest"); // Grab latest version number var versionLog = Regex.Match(versionHTML, "\"tag_name\": \"(?.*?)\",").Groups; var version = versionLog[1].Value; // Grab changelog var changesLog = Regex.Match(versionHTML, "\"body\": \"(?.*?)\"").Groups; var changes = changesLog[1].Value; string currentVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString(); string newVersion = version; if (currentVersion.Contains(newVersion)) { // Do nothing. All is good. } else { DialogResult dialogResult = MessageBox.Show("New version of QBDLX is available!\r\n\r\nInstalled version - " + currentVersion + "\r\nLatest version - "+ newVersion + "\r\n\r\nChangelog Below\r\n==============\r\n" + changes.Replace("\\r\\n", "\r\n") + "\r\n==============\r\n\r\nWould you like to update?", "QBDLX | Update Available", MessageBoxButtons.YesNo); if (dialogResult == DialogResult.Yes) { // If "Yes" is clicked, open GitHub page and close QBDLX. Process.Start("https://github.com/ImAiiR/QobuzDownloaderX/releases/latest"); Application.Exit(); } else if (dialogResult == DialogResult.No) { // Ignore the update until next open. } } } catch { DialogResult dialogResult = MessageBox.Show("Connection to GitHub to check for an update has failed.\r\nWould you like to check for an update manually?\r\n\r\nYour current version is " + Assembly.GetExecutingAssembly().GetName().Version.ToString(), "QBDLX | GitHub Connection Failed", MessageBoxButtons.YesNo); if (dialogResult == DialogResult.Yes) { // If "Yes" is clicked, open GitHub page and close QBDLX. Process.Start("https://github.com/ImAiiR/QobuzDownloaderX/releases/latest"); Application.Exit(); } else if (dialogResult == DialogResult.No) { // Ignore the update until next open. } } // Get and display version number. verNumLabel2.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString(); // Check for taglib-sharp.dll if (!System.IO.File.Exists(dllCheck)) { MessageBox.Show("taglib-sharp.dll missing from folder!\r\nPlease Make sure the DLL is in the same folder as QobuzDownloaderX.exe!", "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); Application.Exit(); } // Bring to center of screen. CenterToScreen(); if (Properties.Settings.Default.UpgradeRequired) { Properties.Settings.Default.Upgrade(); Properties.Settings.Default.UpgradeRequired = false; Properties.Settings.Default.Save(); } // Set saved settings to correct places. emailTextbox.Text = Settings.Default.savedEmail.ToString(); passwordTextbox.Text = Settings.Default.savedPassword.ToString(); userIdTextbox.Text = Settings.Default.savedUserID.ToString(); userAuthTokenTextbox.Text = Settings.Default.savedUserAuthToken.ToString(); altLoginValue = Settings.Default.savedAltLoginValue.ToString(); // Set alt login mode & label text based on saved value if (altLoginValue == "0") { // Change alt login label text altLoginLabel.Text = "Can't login? Click here"; // Hide alt login methods altLoginTutLabel.Visible = false; userIdTextbox.Visible = false; userAuthTokenTextbox.Visible = false; // Unhide standard login methods emailTextbox.Visible = true; passwordTextbox.Visible = true; } else if (altLoginValue == "1") { // Change alt login label text altLoginLabel.Text = "Login normally? Click here"; // Hide standard login methods emailTextbox.Visible = false; passwordTextbox.Visible = false; // Unhide alt login methods altLoginTutLabel.Visible = true; userIdTextbox.Visible = true; userAuthTokenTextbox.Visible = true; } // Set values for email textbox. if (emailTextbox.Text != "Email") { emailTextbox.ForeColor = Color.FromArgb(186, 186, 186); } if (emailTextbox.Text == null | emailTextbox.Text == "") { emailTextbox.ForeColor = Color.FromArgb(88, 92, 102); emailTextbox.Text = "Email"; } // Set values for user_id textbox. if (userIdTextbox.Text != "user_id") { userIdTextbox.ForeColor = Color.FromArgb(186, 186, 186); } if (userIdTextbox.Text == null | userIdTextbox.Text == "") { userIdTextbox.ForeColor = Color.FromArgb(88, 92, 102); userIdTextbox.Text = "user_id"; } // Set values for password textbox. if (passwordTextbox.Text != "Password") { passwordTextbox.PasswordChar = '*'; passwordTextbox.UseSystemPasswordChar = false; passwordTextbox.ForeColor = Color.FromArgb(186, 186, 186); } if (passwordTextbox.Text == null | passwordTextbox.Text == "") { passwordTextbox.ForeColor = Color.FromArgb(88, 92, 102); passwordTextbox.UseSystemPasswordChar = true; passwordTextbox.Text = "Password"; } // Set values for user_auth_token textbox. if (userAuthTokenTextbox.Text != "user_auth_token") { userAuthTokenTextbox.PasswordChar = '*'; userAuthTokenTextbox.UseSystemPasswordChar = false; userAuthTokenTextbox.ForeColor = Color.FromArgb(186, 186, 186); } if (userAuthTokenTextbox.Text == null | userAuthTokenTextbox.Text == "") { userAuthTokenTextbox.ForeColor = Color.FromArgb(88, 92, 102); userAuthTokenTextbox.UseSystemPasswordChar = true; userAuthTokenTextbox.Text = "user_auth_token"; } } private void loginButton_Click(object sender, EventArgs e) { // Hide alt login label until job is finished or failed altLoginLabel.Visible = false; // If logging in normally (email & password) if (altLoginValue == "0") { #region Normal Login if (emailTextbox.Text == "Email" | emailTextbox.Text == null | emailTextbox.Text == "") { // If there's no email typed in. loginText.Invoke(new Action(() => loginText.Text = "No email, please input email first.")); return; } if (passwordTextbox.Text == "Password") { // If there's no password typed in. loginText.Invoke(new Action(() => loginText.Text = "No password typed, please input password first.")); return; } string plainTextPW = passwordTextbox.Text; var passMD5CheckLog = Regex.Match(plainTextPW, "(?^[0-9a-f]{32}$)").Groups; var passMD5Check = passMD5CheckLog[1].Value; if (passMD5Check == null | passMD5Check == "") { // Generate the MD5 hash using the string created above. using (MD5 md5PassHash = MD5.Create()) { string hashedPW = GetMd5Hash(md5PassHash, plainTextPW); if (VerifyMd5Hash(md5PassHash, plainTextPW, hashedPW)) { // If the MD5 hash is verified, proceed to get the streaming URL. passwordTextbox.Text = hashedPW; } else { // If the hash can't be verified. loginText.Invoke(new Action(() => loginText.Text = "Hashing failed. Please retry.")); return; } } } // Save info locally to be used on next launch. Settings.Default.savedEmail = emailTextbox.Text; Settings.Default.savedPassword = passwordTextbox.Text; Settings.Default.savedAltLoginValue = altLoginValue; Settings.Default.Save(); loginText.Text = "Getting App ID and Secret..."; loginButton.Enabled = false; getSecretBG.RunWorkerAsync(); #endregion } // If logging in the alternate way (user_id & user_auth_token) else { #region Alt Login if (userIdTextbox.Text == "user_id" | userIdTextbox.Text == null | userIdTextbox.Text == "") { // If there's no email typed in. loginText.Invoke(new Action(() => loginText.Text = "No user_id, please input user_id first.")); return; } if (userAuthTokenTextbox.Text == "user_auth_token") { // If there's no password typed in. loginText.Invoke(new Action(() => loginText.Text = "No user_auth_token typed, please input user_auth_token first.")); return; } // Set user_id & user_auth_token to login. userID = userIdTextbox.Text; userAuthToken = userAuthTokenTextbox.Text; // Save info locally to be used on next launch. Settings.Default.savedUserID = userIdTextbox.Text; Settings.Default.savedUserAuthToken = userAuthTokenTextbox.Text; Settings.Default.savedAltLoginValue = altLoginValue; Settings.Default.Save(); loginText.Text = "Getting App ID and Secret..."; loginButton.Enabled = false; getSecretBG.RunWorkerAsync(); #endregion } } private void getSecretBG_DoWork(object sender, DoWorkEventArgs e) { getSecretBG.WorkerSupportsCancellation = true; WebClient bundleURLClient = new WebClient(); string bundleHTML = bundleURLClient.DownloadString("https://play.qobuz.com/login"); // Grab link to bundle.js var bundleLog = Regex.Match(bundleHTML, "