using QobuzDownloaderX.Properties; using System; 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 LoginForm : Form { public LoginForm() { InitializeComponent(); } private void QobuzDownloaderX_FormClosing(Object sender, FormClosingEventArgs e) { Application.Exit(); } QobuzDownloaderX qbdlx = new QobuzDownloaderX(); public string appSecret { get; set; } string errorLog = Path.GetDirectoryName(Application.ExecutablePath) + "\\Latest_Error.log"; string dllCheck = Path.GetDirectoryName(Application.ExecutablePath) + "\\taglib-sharp.dll"; private void LoginForm_Load(object sender, EventArgs e) { // Get and display version number. verNumLabel2.Text = Assembly.GetExecutingAssembly().GetName().Version.ToString(); 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. appidTextbox.Text = Settings.Default.savedAppID.ToString(); emailTextbox.Text = Settings.Default.savedEmail.ToString(); passwordTextbox.Text = Settings.Default.savedPassword.ToString(); } 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 md5Button_Click(object sender, EventArgs e) { string plainTextPW = passwordTextbox.Text; // 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; } } } private void loginButton_Click(object sender, EventArgs e) { var passMD5CheckLog = Regex.Match(passwordTextbox.Text, "(?^[0-9a-f]{32}$)").Groups; var passMD5Check = passMD5CheckLog[1].Value; if (passMD5Check == null | passMD5Check == "") { loginText.Text = "Password not MD5! Hit \"MD5\" before logging in!"; return; } // Save info locally to be used on next launch. Settings.Default.savedEmail = emailTextbox.Text; Settings.Default.savedAppID = appidTextbox.Text; Settings.Default.savedPassword = passwordTextbox.Text; Settings.Default.Save(); loginText.Text = "Logging in + obtaining app_secret..."; loginBG.RunWorkerAsync(); } private void loginBG_DoWork(object sender, DoWorkEventArgs e) { loginBG.WorkerSupportsCancellation = true; // Create WebRequest to login using login information from input textboxes. WebRequest wr = WebRequest.Create("https://www.qobuz.com/api.json/0.2/user/login?email=" + emailTextbox.Text + "&password=" + passwordTextbox.Text + "&app_id=" + appidTextbox.Text); try { // Grab info to be displayed and used. WebResponse ws = wr.GetResponse(); StreamReader sr = new StreamReader(ws.GetResponseStream()); string loginRequest = sr.ReadToEnd(); string text = loginRequest; // Grab display name var displayNameLog = Regex.Match(loginRequest, "\"display_name\":\"(?.*?)\",\\\"").Groups; var displayName = displayNameLog[1].Value; qbdlx.displayName = displayName; // Grab account type var accountTypeLog = Regex.Match(loginRequest, "short_label\":\"(?\\w+)").Groups; var accountType = accountTypeLog[1].Value; qbdlx.accountType = accountType; // Grab authentication token var userAuth = Regex.Match(loginRequest, "\"user_auth_token\":\"(?.*?)\\\"}").Groups; var userAuthToken = userAuth[1].Value; // Set user_auth_token qbdlx.userAuth = userAuthToken; loginText.Invoke(new Action(() => loginText.Text = "Login Successful! Getting app_secret...")); } catch (Exception ex) { // If connection to API fails, show error info. string error = ex.ToString(); loginText.Invoke(new Action(() => loginText.Text = "Login Failed. Error Log saved")); System.IO.File.WriteAllText(errorLog, error); wr.Abort(); return; } wr.Abort(); getSecretBG.RunWorkerAsync(); loginBG.CancelAsync(); } private void getSecretBG_DoWork(object sender, DoWorkEventArgs e) { getSecretBG.WorkerSupportsCancellation = true; WebClient bundleURLClient = new WebClient(); string bundleHTML = bundleURLClient.DownloadString("https://play.qobuz.com/"); // Grab link to bundle.js var bundleLog = Regex.Match(bundleHTML, "