diff --git a/Configuration/Arguments.cs b/Configuration/Arguments.cs index 83ec2cee..9f78fa95 100644 --- a/Configuration/Arguments.cs +++ b/Configuration/Arguments.cs @@ -12,8 +12,6 @@ static class Arguments { // internal args public const string ArgRestart = "-restart"; - public const string ArgImportCookies = "-importcookies"; - public const string ArgDeleteCookies = "-deletecookies"; public const string ArgUpdated = "-updated"; // class data and methods @@ -30,8 +28,6 @@ public static string GetValue(string key) { public static CommandLineArgs GetCurrentClean() { CommandLineArgs args = Current.Clone(); args.RemoveFlag(ArgRestart); - args.RemoveFlag(ArgImportCookies); - args.RemoveFlag(ArgDeleteCookies); args.RemoveFlag(ArgUpdated); return args; } diff --git a/Dialogs/Settings/DialogSettingsManage.cs b/Dialogs/Settings/DialogSettingsManage.cs index 44977fd1..9053c024 100644 --- a/Dialogs/Settings/DialogSettingsManage.cs +++ b/Dialogs/Settings/DialogSettingsManage.cs @@ -2,12 +2,10 @@ using System.Collections.Generic; using System.IO; using System.Windows.Forms; -using TweetDuck.Configuration; using TweetDuck.Management; using TweetLib.Core; using TweetLib.Core.Features.Plugins; using TweetLib.Core.Systems.Configuration; -using TweetLib.Utils.Static; namespace TweetDuck.Dialogs.Settings { sealed partial class DialogSettingsManage : Form { @@ -29,10 +27,6 @@ private ProfileManager.Items SelectedItems { } } - private bool SelectedItemsForceRestart { - get => _selectedItems.HasFlag(ProfileManager.Items.Session); - } - public bool IsRestarting { get; private set; } public bool ShouldReloadBrowser { get; private set; } @@ -146,6 +140,10 @@ private void btnContinue_Click(object sender, EventArgs e) { App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested; + if (SelectedItems.HasFlag(ProfileManager.Items.Session)) { + ProfileManager.DeleteAuthCookie(); + } + if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)) { Program.Config.Plugins.Reset(); @@ -156,13 +154,8 @@ private void btnContinue_Click(object sender, EventArgs e) { } } - if (SelectedItemsForceRestart) { - RestartProgram(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[] { Arguments.ArgDeleteCookies } : StringUtils.EmptyArray); - } - else if (requestedRestartFromConfig) { - if (FormMessage.Information("Profile Reset", "The application must restart for some of the restored options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) { - RestartProgram(); - } + if (requestedRestartFromConfig && FormMessage.Information("Profile Reset", "The application must restart for some of the restored options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) { + RestartProgram(); } ShouldReloadBrowser = true; @@ -180,13 +173,8 @@ private void btnContinue_Click(object sender, EventArgs e) { App.ConfigManager.SaveAll(); App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested; - if (SelectedItemsForceRestart) { - RestartProgram(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[] { Arguments.ArgImportCookies } : StringUtils.EmptyArray); - } - else if (requestedRestartFromConfig) { - if (FormMessage.Information("Profile Import", "The application must restart for some of the imported options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) { - RestartProgram(); - } + if (requestedRestartFromConfig && FormMessage.Information("Profile Import", "The application must restart for some of the imported options to take place. Do you want to restart now?", FormMessage.Yes, FormMessage.No)) { + RestartProgram(); } } @@ -235,16 +223,16 @@ private void SetFlag(ProfileManager.Items flag, bool enable) { btnContinue.Enabled = _selectedItems != ProfileManager.Items.None; if (currentState == State.Import) { - btnContinue.Text = SelectedItemsForceRestart ? "Import && Restart" : "Import Profile"; + btnContinue.Text = "Import Profile"; } else if (currentState == State.Reset) { - btnContinue.Text = SelectedItemsForceRestart ? "Restore && Restart" : "Restore Defaults"; + btnContinue.Text = "Restore Defaults"; } } - private void RestartProgram(params string[] extraArgs) { + private void RestartProgram() { IsRestarting = true; - Program.Restart(extraArgs); + Program.Restart(); } } } diff --git a/Management/ProfileManager.cs b/Management/ProfileManager.cs index 2d43b2c0..7fe49ab0 100644 --- a/Management/ProfileManager.cs +++ b/Management/ProfileManager.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; +using CefSharp; using TweetDuck.Dialogs; using TweetLib.Core; using TweetLib.Core.Features.Plugins; @@ -10,13 +12,10 @@ namespace TweetDuck.Management { sealed class ProfileManager { - private static readonly string CookiesPath = Path.Combine(App.StoragePath, "Cookies"); - private static readonly string LocalPrefsPath = Path.Combine(App.StoragePath, "LocalPrefs.json"); - - private static readonly string TempCookiesPath = Path.Combine(App.StoragePath, "CookiesTmp"); - private static readonly string TempLocalPrefsPath = Path.Combine(App.StoragePath, "LocalPrefsTmp.json"); - - private const int SessionFileCount = 2; + private const string AuthCookieUrl = "https://twitter.com"; + private const string AuthCookieName = "auth_token"; + private const string AuthCookieDomain = ".twitter.com"; + private const string AuthCookiePath = "/"; [Flags] public enum Items { @@ -62,8 +61,14 @@ public bool Export(Items items) { } if (items.HasFlag(Items.Session)) { - stream.WriteFile("cookies", CookiesPath); - stream.WriteFile("localprefs", LocalPrefsPath); + string authToken = ReadAuthCookie(); + + if (authToken != null) { + stream.WriteString("cookie.auth", authToken); + } + else { + FormMessage.Warning("Export Profile", "Could not find any login session.", FormMessage.OK); + } } stream.Flush(); @@ -98,6 +103,7 @@ public Items FindImportItems() { case "cookies": case "localprefs": + case "cookie.auth": items |= Items.Session; break; } @@ -112,7 +118,7 @@ public Items FindImportItems() { public bool Import(Items items) { try { var missingPlugins = new HashSet<string>(); - var sessionFiles = new HashSet<string>(); + bool oldCookies = false; using (CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))) { CombinedFileStream.Entry entry; @@ -154,17 +160,30 @@ public bool Import(Items items) { break; case "cookies": + case "localprefs": if (items.HasFlag(Items.Session)) { - entry.WriteToFile(TempCookiesPath); - sessionFiles.Add(entry.KeyName); + oldCookies = true; } break; - case "localprefs": + case "cookie.auth": if (items.HasFlag(Items.Session)) { - entry.WriteToFile(TempLocalPrefsPath); - sessionFiles.Add(entry.KeyName); + using ICookieManager cookies = Cef.GetGlobalCookieManager(); + + var _ = cookies.SetCookieAsync(AuthCookieUrl, new Cookie { + Name = AuthCookieName, + Domain = AuthCookieDomain, + Path = AuthCookiePath, + Value = Encoding.UTF8.GetString(entry.Contents), + Expires = DateTime.Now.Add(TimeSpan.FromDays(365 * 5)), + HttpOnly = true, + Secure = true + }).ContinueWith(t => { + // ReSharper disable once AccessToDisposedClosure + // ReSharper disable once ConvertToLambdaExpression + return cookies.FlushStoreAsync(); + }).Result; } break; @@ -172,10 +191,8 @@ public bool Import(Items items) { } } - if (items.HasFlag(Items.Session) && sessionFiles.Count != SessionFileCount) { + if (items.HasFlag(Items.Session) && oldCookies) { FormMessage.Error("Profile Import Error", "Cannot import login session from an older version of TweetDuck.", FormMessage.OK); - File.Delete(TempCookiesPath); - File.Delete(TempLocalPrefsPath); return false; } @@ -190,35 +207,6 @@ public bool Import(Items items) { } } - public static void ImportCookies() { - if (File.Exists(TempCookiesPath) && File.Exists(TempLocalPrefsPath)) { - try { - if (File.Exists(CookiesPath)) { - File.Delete(CookiesPath); - } - - if (File.Exists(LocalPrefsPath)) { - File.Delete(LocalPrefsPath); - } - - File.Move(TempCookiesPath, CookiesPath); - File.Move(TempLocalPrefsPath, LocalPrefsPath); - } catch (Exception e) { - App.ErrorHandler.HandleException("Profile Import Error", "Could not import the cookie file to restore login session.", true, e); - } - } - } - - public static void DeleteCookies() { - try { - if (File.Exists(CookiesPath)) { - File.Delete(CookiesPath); - } - } catch (Exception e) { - App.ErrorHandler.HandleException("Session Reset Error", "Could not remove the cookie file to reset the login session.", true, e); - } - } - private static IEnumerable<PathInfo> EnumerateFilesRelative(string root) { if (Directory.Exists(root)) { int rootLength = root.Length; @@ -238,5 +226,22 @@ public PathInfo(string fullPath, int rootLength) { this.Relative = fullPath.Substring(rootLength).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); // strip leading separator character } } + + private static string ReadAuthCookie() { + using var cookieManager = Cef.GetGlobalCookieManager(); + + foreach (var cookie in cookieManager.VisitUrlCookiesAsync(AuthCookieUrl, true).Result) { + if (cookie.Name == AuthCookieName && cookie.Domain == AuthCookieDomain && cookie.Path == AuthCookiePath && cookie.HttpOnly && cookie.Secure) { + return cookie.Value; + } + } + + return null; + } + + public static void DeleteAuthCookie() { + using var cookieManager = Cef.GetGlobalCookieManager(); + var _ = cookieManager.DeleteCookiesAsync(AuthCookieUrl, "auth_token").Result; + } } } diff --git a/Program.cs b/Program.cs index bd428034..baecdef1 100644 --- a/Program.cs +++ b/Program.cs @@ -97,13 +97,6 @@ public bool TryLockDataFolder(string lockFile) { } public void BeforeLaunch() { - if (Arguments.HasFlag(Arguments.ArgImportCookies)) { - ProfileManager.ImportCookies(); - } - else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)) { - ProfileManager.DeleteCookies(); - } - if (Arguments.HasFlag(Arguments.ArgUpdated)) { WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, InstallerFolder), 8000); WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, "Service Worker"), 4000); @@ -175,10 +168,8 @@ private static void OnUnhandledException(object sender, UnhandledExceptionEventA } } - public static void Restart(params string[] extraArgs) { - CommandLineArgs args = Arguments.GetCurrentClean(); - CommandLineArgs.ReadStringArray('-', extraArgs, args); - RestartWithArgs(args); + public static void Restart() { + RestartWithArgs(Arguments.GetCurrentClean()); } public static void RestartWithArgs(CommandLineArgs args) { diff --git a/lib/TweetLib.Utils/IO/CombinedFileStream.cs b/lib/TweetLib.Utils/IO/CombinedFileStream.cs index 6567fc9b..78ac3b23 100644 --- a/lib/TweetLib.Utils/IO/CombinedFileStream.cs +++ b/lib/TweetLib.Utils/IO/CombinedFileStream.cs @@ -28,21 +28,23 @@ public CombinedFileStream(Stream stream) { } public void WriteFile(string[] identifier, string path) { - using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); WriteStreamImpl(JoinIdentifier(identifier), fileStream); } public void WriteFile(string identifier, string path) { - using FileStream fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + using var fileStream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); WriteStreamImpl(ValidateIdentifier(identifier), fileStream); } - public void WriteStream(string[] identifier, Stream sourceStream) { - WriteStreamImpl(JoinIdentifier(identifier), sourceStream); + public void WriteString(string[] identifier, string contents) { + using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(contents)); + WriteStreamImpl(JoinIdentifier(identifier), memoryStream); } - public void WriteStream(string identifier, Stream sourceStream) { - WriteStreamImpl(ValidateIdentifier(identifier), sourceStream); + public void WriteString(string identifier, string contents) { + using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(contents)); + WriteStreamImpl(ValidateIdentifier(identifier), memoryStream); } private void WriteStreamImpl(string identifier, Stream sourceStream) { diff --git a/lib/TweetTest.Utils/IO/TestCombinedFileStream.fs b/lib/TweetTest.Utils/IO/TestCombinedFileStream.fs index 9100bec9..e18b7253 100644 --- a/lib/TweetTest.Utils/IO/TestCombinedFileStream.fs +++ b/lib/TweetTest.Utils/IO/TestCombinedFileStream.fs @@ -26,12 +26,12 @@ type internal TestData = static member singleFile = TestData.setup (fun f -> - f.WriteStream("File 1", new MemoryStream(Encoding.UTF8.GetBytes("test file\n123"))) + f.WriteString("File 1", "test file\n123") ) static member singleFileWithMultiIdentifier = TestData.setup (fun f -> - f.WriteStream([| "File 1"; "A"; "B" |], new MemoryStream(Encoding.UTF8.GetBytes("test file\n123"))) + f.WriteString([| "File 1"; "A"; "B" |], "test file\n123") ) static member singleFileStreams = @@ -40,9 +40,9 @@ type internal TestData = static member threeFiles = TestData.setup (fun f -> - f.WriteStream("File 1", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 1"))) - f.WriteStream("File 2", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 2"))) - f.WriteStream("File 3", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 3"))) + f.WriteString("File 1", "Contents of\nFile 1") + f.WriteString("File 2", "Contents of\nFile 2") + f.WriteString("File 3", "Contents of\nFile 3") ) @@ -52,21 +52,21 @@ module Validation = let ``an identifier containing '|' throws`` () = TestData.setup (fun f -> Assert.Throws<ArgumentException>(fun () -> - f.WriteStream("File|1", new MemoryStream(Array.empty)) + f.WriteString("File|1", "") ) |> ignore ) [<Fact>] let ``an identifier 255 bytes long does not throw`` () = TestData.setup (fun f -> - f.WriteStream(String.replicate 255 "a", new MemoryStream(Array.empty)) + f.WriteString(String.replicate 255 "a", "") ) [<Fact>] let ``an identifier 256 bytes long throws`` () = TestData.setup (fun f -> Assert.Throws<ArgumentOutOfRangeException>(fun () -> - f.WriteStream(String.replicate 256 "a", new MemoryStream(Array.empty)) + f.WriteString(String.replicate 256 "a", "") ) |> ignore )