1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-12-03 15:38:21 +01:00

Fix login session export not working across different computers after a Chromium update

This commit is contained in:
2022-01-18 14:33:45 +01:00
parent eee72959e6
commit 655d334714
6 changed files with 82 additions and 100 deletions

View File

@@ -12,8 +12,6 @@ namespace TweetDuck.Configuration {
// internal args // internal args
public const string ArgRestart = "-restart"; public const string ArgRestart = "-restart";
public const string ArgImportCookies = "-importcookies";
public const string ArgDeleteCookies = "-deletecookies";
public const string ArgUpdated = "-updated"; public const string ArgUpdated = "-updated";
// class data and methods // class data and methods
@@ -30,8 +28,6 @@ namespace TweetDuck.Configuration {
public static CommandLineArgs GetCurrentClean() { public static CommandLineArgs GetCurrentClean() {
CommandLineArgs args = Current.Clone(); CommandLineArgs args = Current.Clone();
args.RemoveFlag(ArgRestart); args.RemoveFlag(ArgRestart);
args.RemoveFlag(ArgImportCookies);
args.RemoveFlag(ArgDeleteCookies);
args.RemoveFlag(ArgUpdated); args.RemoveFlag(ArgUpdated);
return args; return args;
} }

View File

@@ -2,12 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Windows.Forms; using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Management; using TweetDuck.Management;
using TweetLib.Core; using TweetLib.Core;
using TweetLib.Core.Features.Plugins; using TweetLib.Core.Features.Plugins;
using TweetLib.Core.Systems.Configuration; using TweetLib.Core.Systems.Configuration;
using TweetLib.Utils.Static;
namespace TweetDuck.Dialogs.Settings { namespace TweetDuck.Dialogs.Settings {
sealed partial class DialogSettingsManage : Form { sealed partial class DialogSettingsManage : Form {
@@ -29,10 +27,6 @@ namespace TweetDuck.Dialogs.Settings {
} }
} }
private bool SelectedItemsForceRestart {
get => _selectedItems.HasFlag(ProfileManager.Items.Session);
}
public bool IsRestarting { get; private set; } public bool IsRestarting { get; private set; }
public bool ShouldReloadBrowser { get; private set; } public bool ShouldReloadBrowser { get; private set; }
@@ -146,6 +140,10 @@ namespace TweetDuck.Dialogs.Settings {
App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested; App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested;
if (SelectedItems.HasFlag(ProfileManager.Items.Session)) {
ProfileManager.DeleteAuthCookie();
}
if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)) { if (SelectedItems.HasFlag(ProfileManager.Items.PluginData)) {
Program.Config.Plugins.Reset(); Program.Config.Plugins.Reset();
@@ -156,13 +154,8 @@ namespace TweetDuck.Dialogs.Settings {
} }
} }
if (SelectedItemsForceRestart) { 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(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[] { Arguments.ArgDeleteCookies } : StringUtils.EmptyArray); RestartProgram();
}
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();
}
} }
ShouldReloadBrowser = true; ShouldReloadBrowser = true;
@@ -180,13 +173,8 @@ namespace TweetDuck.Dialogs.Settings {
App.ConfigManager.SaveAll(); App.ConfigManager.SaveAll();
App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested; App.ConfigManager.ProgramRestartRequested -= Config_ProgramRestartRequested;
if (SelectedItemsForceRestart) { 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(SelectedItems.HasFlag(ProfileManager.Items.Session) ? new string[] { Arguments.ArgImportCookies } : StringUtils.EmptyArray); RestartProgram();
}
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();
}
} }
} }
@@ -235,16 +223,16 @@ namespace TweetDuck.Dialogs.Settings {
btnContinue.Enabled = _selectedItems != ProfileManager.Items.None; btnContinue.Enabled = _selectedItems != ProfileManager.Items.None;
if (currentState == State.Import) { if (currentState == State.Import) {
btnContinue.Text = SelectedItemsForceRestart ? "Import && Restart" : "Import Profile"; btnContinue.Text = "Import Profile";
} }
else if (currentState == State.Reset) { 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; IsRestarting = true;
Program.Restart(extraArgs); Program.Restart();
} }
} }
} }

View File

@@ -2,6 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using CefSharp;
using TweetDuck.Dialogs; using TweetDuck.Dialogs;
using TweetLib.Core; using TweetLib.Core;
using TweetLib.Core.Features.Plugins; using TweetLib.Core.Features.Plugins;
@@ -10,13 +12,10 @@ using TweetLib.Utils.IO;
namespace TweetDuck.Management { namespace TweetDuck.Management {
sealed class ProfileManager { sealed class ProfileManager {
private static readonly string CookiesPath = Path.Combine(App.StoragePath, "Cookies"); private const string AuthCookieUrl = "https://twitter.com";
private static readonly string LocalPrefsPath = Path.Combine(App.StoragePath, "LocalPrefs.json"); private const string AuthCookieName = "auth_token";
private const string AuthCookieDomain = ".twitter.com";
private static readonly string TempCookiesPath = Path.Combine(App.StoragePath, "CookiesTmp"); private const string AuthCookiePath = "/";
private static readonly string TempLocalPrefsPath = Path.Combine(App.StoragePath, "LocalPrefsTmp.json");
private const int SessionFileCount = 2;
[Flags] [Flags]
public enum Items { public enum Items {
@@ -62,8 +61,14 @@ namespace TweetDuck.Management {
} }
if (items.HasFlag(Items.Session)) { if (items.HasFlag(Items.Session)) {
stream.WriteFile("cookies", CookiesPath); string authToken = ReadAuthCookie();
stream.WriteFile("localprefs", LocalPrefsPath);
if (authToken != null) {
stream.WriteString("cookie.auth", authToken);
}
else {
FormMessage.Warning("Export Profile", "Could not find any login session.", FormMessage.OK);
}
} }
stream.Flush(); stream.Flush();
@@ -98,6 +103,7 @@ namespace TweetDuck.Management {
case "cookies": case "cookies":
case "localprefs": case "localprefs":
case "cookie.auth":
items |= Items.Session; items |= Items.Session;
break; break;
} }
@@ -112,7 +118,7 @@ namespace TweetDuck.Management {
public bool Import(Items items) { public bool Import(Items items) {
try { try {
var missingPlugins = new HashSet<string>(); 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))) { using (CombinedFileStream stream = new CombinedFileStream(new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.None))) {
CombinedFileStream.Entry entry; CombinedFileStream.Entry entry;
@@ -154,17 +160,30 @@ namespace TweetDuck.Management {
break; break;
case "cookies": case "cookies":
case "localprefs":
if (items.HasFlag(Items.Session)) { if (items.HasFlag(Items.Session)) {
entry.WriteToFile(TempCookiesPath); oldCookies = true;
sessionFiles.Add(entry.KeyName);
} }
break; break;
case "localprefs": case "cookie.auth":
if (items.HasFlag(Items.Session)) { if (items.HasFlag(Items.Session)) {
entry.WriteToFile(TempLocalPrefsPath); using ICookieManager cookies = Cef.GetGlobalCookieManager();
sessionFiles.Add(entry.KeyName);
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; break;
@@ -172,10 +191,8 @@ namespace TweetDuck.Management {
} }
} }
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); 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; return false;
} }
@@ -190,35 +207,6 @@ namespace TweetDuck.Management {
} }
} }
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) { private static IEnumerable<PathInfo> EnumerateFilesRelative(string root) {
if (Directory.Exists(root)) { if (Directory.Exists(root)) {
int rootLength = root.Length; int rootLength = root.Length;
@@ -238,5 +226,22 @@ namespace TweetDuck.Management {
this.Relative = fullPath.Substring(rootLength).TrimStart(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar); // strip leading separator character 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;
}
} }
} }

View File

@@ -97,13 +97,6 @@ namespace TweetDuck {
} }
public void BeforeLaunch() { public void BeforeLaunch() {
if (Arguments.HasFlag(Arguments.ArgImportCookies)) {
ProfileManager.ImportCookies();
}
else if (Arguments.HasFlag(Arguments.ArgDeleteCookies)) {
ProfileManager.DeleteCookies();
}
if (Arguments.HasFlag(Arguments.ArgUpdated)) { if (Arguments.HasFlag(Arguments.ArgUpdated)) {
WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, InstallerFolder), 8000); WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, InstallerFolder), 8000);
WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, "Service Worker"), 4000); WindowsUtils.TryDeleteFolderWhenAble(Path.Combine(App.StoragePath, "Service Worker"), 4000);
@@ -175,10 +168,8 @@ namespace TweetDuck {
} }
} }
public static void Restart(params string[] extraArgs) { public static void Restart() {
CommandLineArgs args = Arguments.GetCurrentClean(); RestartWithArgs(Arguments.GetCurrentClean());
CommandLineArgs.ReadStringArray('-', extraArgs, args);
RestartWithArgs(args);
} }
public static void RestartWithArgs(CommandLineArgs args) { public static void RestartWithArgs(CommandLineArgs args) {

View File

@@ -28,21 +28,23 @@ namespace TweetLib.Utils.IO {
} }
public void WriteFile(string[] identifier, string path) { 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); WriteStreamImpl(JoinIdentifier(identifier), fileStream);
} }
public void WriteFile(string identifier, string path) { 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); WriteStreamImpl(ValidateIdentifier(identifier), fileStream);
} }
public void WriteStream(string[] identifier, Stream sourceStream) { public void WriteString(string[] identifier, string contents) {
WriteStreamImpl(JoinIdentifier(identifier), sourceStream); using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
WriteStreamImpl(JoinIdentifier(identifier), memoryStream);
} }
public void WriteStream(string identifier, Stream sourceStream) { public void WriteString(string identifier, string contents) {
WriteStreamImpl(ValidateIdentifier(identifier), sourceStream); using var memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(contents));
WriteStreamImpl(ValidateIdentifier(identifier), memoryStream);
} }
private void WriteStreamImpl(string identifier, Stream sourceStream) { private void WriteStreamImpl(string identifier, Stream sourceStream) {

View File

@@ -26,12 +26,12 @@ type internal TestData =
static member singleFile = static member singleFile =
TestData.setup (fun f -> 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 = static member singleFileWithMultiIdentifier =
TestData.setup (fun f -> 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 = static member singleFileStreams =
@@ -40,9 +40,9 @@ type internal TestData =
static member threeFiles = static member threeFiles =
TestData.setup (fun f -> TestData.setup (fun f ->
f.WriteStream("File 1", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 1"))) f.WriteString("File 1", "Contents of\nFile 1")
f.WriteStream("File 2", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 2"))) f.WriteString("File 2", "Contents of\nFile 2")
f.WriteStream("File 3", new MemoryStream(Encoding.UTF8.GetBytes("Contents of\nFile 3"))) f.WriteString("File 3", "Contents of\nFile 3")
) )
@@ -52,21 +52,21 @@ module Validation =
let ``an identifier containing '|' throws`` () = let ``an identifier containing '|' throws`` () =
TestData.setup (fun f -> TestData.setup (fun f ->
Assert.Throws<ArgumentException>(fun () -> Assert.Throws<ArgumentException>(fun () ->
f.WriteStream("File|1", new MemoryStream(Array.empty)) f.WriteString("File|1", "")
) |> ignore ) |> ignore
) )
[<Fact>] [<Fact>]
let ``an identifier 255 bytes long does not throw`` () = let ``an identifier 255 bytes long does not throw`` () =
TestData.setup (fun f -> TestData.setup (fun f ->
f.WriteStream(String.replicate 255 "a", new MemoryStream(Array.empty)) f.WriteString(String.replicate 255 "a", "")
) )
[<Fact>] [<Fact>]
let ``an identifier 256 bytes long throws`` () = let ``an identifier 256 bytes long throws`` () =
TestData.setup (fun f -> TestData.setup (fun f ->
Assert.Throws<ArgumentOutOfRangeException>(fun () -> Assert.Throws<ArgumentOutOfRangeException>(fun () ->
f.WriteStream(String.replicate 256 "a", new MemoryStream(Array.empty)) f.WriteString(String.replicate 256 "a", "")
) |> ignore ) |> ignore
) )