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:
parent
eee72959e6
commit
655d334714
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
Program.cs
13
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) {
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user