1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-04-24 15:15:49 +02:00

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

This commit is contained in:
chylex 2022-01-18 14:33:45 +01:00
parent eee72959e6
commit 655d334714
Signed by: chylex
GPG Key ID: 4DE42C8F19A80548
6 changed files with 82 additions and 100 deletions
Configuration
Dialogs/Settings
Management
Program.cs
lib
TweetLib.Utils/IO
TweetTest.Utils/IO

View File

@ -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;
}

View File

@ -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();
}
}
}

View File

@ -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;
}
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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
)