mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-05-11 02:34:08 +02:00
Add tdp:// scheme for plugins (with 'root/' to access root files)
This commit is contained in:
parent
464e758b94
commit
458eeeccda
@ -16,6 +16,7 @@
|
||||
using TweetDuck.Dialogs.Settings;
|
||||
using TweetDuck.Management;
|
||||
using TweetDuck.Management.Analytics;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Updates;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
@ -65,7 +66,7 @@ public bool IsWaiting{
|
||||
private VideoPlayer videoPlayer;
|
||||
private AnalyticsManager analytics;
|
||||
|
||||
public FormBrowser(){
|
||||
public FormBrowser(PluginSchemeFactory pluginScheme){
|
||||
InitializeComponent();
|
||||
|
||||
Text = Program.BrandName;
|
||||
@ -74,6 +75,7 @@ public FormBrowser(){
|
||||
this.plugins.Reloaded += plugins_Reloaded;
|
||||
this.plugins.Executed += plugins_Executed;
|
||||
this.plugins.Reload();
|
||||
pluginScheme.Setup(plugins);
|
||||
|
||||
this.notification = new FormNotificationTweet(this, plugins);
|
||||
this.notification.Show();
|
||||
|
@ -114,6 +114,8 @@ private void browser_LoadingStateChanged(object sender, LoadingStateChangedEvent
|
||||
browser.AddWordToDictionary(word);
|
||||
}
|
||||
|
||||
Cef.AddCrossOriginWhitelistEntry(TwitterUrls.TweetDeck, PluginSchemeFactory.Name, "", true);
|
||||
|
||||
browser.BeginInvoke(new Action(OnBrowserReady));
|
||||
browser.LoadingStateChanged -= browser_LoadingStateChanged;
|
||||
}
|
||||
|
35
Plugins/PluginSchemeFactory.cs
Normal file
35
Plugins/PluginSchemeFactory.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using System.Net;
|
||||
using CefSharp;
|
||||
using TweetLib.Core.Browser;
|
||||
using TweetLib.Core.Features.Plugins;
|
||||
|
||||
namespace TweetDuck.Plugins{
|
||||
sealed class PluginSchemeFactory : ISchemeHandlerFactory{
|
||||
public const string Name = PluginSchemeHandler<IResourceHandler>.Name;
|
||||
|
||||
private readonly PluginSchemeHandler<IResourceHandler> handler = new PluginSchemeHandler<IResourceHandler>(new ResourceProvider());
|
||||
|
||||
internal void Setup(PluginManager plugins){
|
||||
handler.Setup(plugins);
|
||||
}
|
||||
|
||||
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request){
|
||||
return handler.Process(request.Url);
|
||||
}
|
||||
|
||||
private sealed class ResourceProvider : IResourceProvider<IResourceHandler>{
|
||||
public IResourceHandler Status(HttpStatusCode code, string message){
|
||||
return ResourceHandler.ForErrorMessage(message, code);
|
||||
}
|
||||
|
||||
public IResourceHandler File(byte[] bytes, string extension){
|
||||
if (bytes.Length == 0){
|
||||
return Status(HttpStatusCode.NoContent, "File is empty."); // FromByteArray crashes CEF internals with no contents
|
||||
}
|
||||
else{
|
||||
return ResourceHandler.FromByteArray(bytes, ResourceHandler.GetMimeType(extension));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
14
Program.cs
14
Program.cs
@ -11,6 +11,7 @@
|
||||
using TweetDuck.Configuration;
|
||||
using TweetDuck.Dialogs;
|
||||
using TweetDuck.Management;
|
||||
using TweetDuck.Plugins;
|
||||
using TweetDuck.Resources;
|
||||
using TweetDuck.Utils;
|
||||
using TweetLib.Core;
|
||||
@ -168,6 +169,17 @@ private static void Main(){
|
||||
LogSeverity = Arguments.HasFlag(Arguments.ArgLogging) ? LogSeverity.Info : LogSeverity.Disable
|
||||
#endif
|
||||
};
|
||||
|
||||
var pluginScheme = new PluginSchemeFactory();
|
||||
|
||||
settings.RegisterScheme(new CefCustomScheme{
|
||||
SchemeName = PluginSchemeFactory.Name,
|
||||
IsStandard = false,
|
||||
IsSecure = true,
|
||||
IsCorsEnabled = true,
|
||||
IsCSPBypassing = true,
|
||||
SchemeHandlerFactory = pluginScheme
|
||||
});
|
||||
|
||||
CommandLineArgs.ReadCefArguments(Config.User.CustomCefArgs).ToDictionary(settings.CefCommandLineArgs);
|
||||
BrowserUtils.SetupCefArgs(settings.CefCommandLineArgs);
|
||||
@ -176,7 +188,7 @@ private static void Main(){
|
||||
|
||||
Win.Application.ApplicationExit += (sender, args) => ExitCleanup();
|
||||
|
||||
FormBrowser mainForm = new FormBrowser();
|
||||
FormBrowser mainForm = new FormBrowser(pluginScheme);
|
||||
Resources.Initialize(mainForm);
|
||||
Win.Application.Run(mainForm);
|
||||
|
||||
|
@ -53,6 +53,7 @@
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Plugins\PluginSchemeFactory.cs" />
|
||||
<Compile Include="Version.cs" />
|
||||
<Compile Include="Configuration\Arguments.cs" />
|
||||
<Compile Include="Configuration\ConfigManager.cs" />
|
||||
|
8
lib/TweetLib.Core/Browser/IResourceProvider.cs
Normal file
8
lib/TweetLib.Core/Browser/IResourceProvider.cs
Normal file
@ -0,0 +1,8 @@
|
||||
using System.Net;
|
||||
|
||||
namespace TweetLib.Core.Browser{
|
||||
public interface IResourceProvider<T>{
|
||||
T Status(HttpStatusCode code, string message);
|
||||
T File(byte[] bytes, string extension);
|
||||
}
|
||||
}
|
@ -47,7 +47,7 @@ internal int GetTokenFromPlugin(Plugin plugin){
|
||||
return token;
|
||||
}
|
||||
|
||||
private Plugin? GetPluginFromToken(int token){
|
||||
internal Plugin? GetPluginFromToken(int token){
|
||||
return tokens.TryGetValue(token, out Plugin plugin) ? plugin : null;
|
||||
}
|
||||
|
||||
|
@ -16,14 +16,14 @@ public sealed class PluginManager{
|
||||
public IEnumerable<InjectedHTML> NotificationInjections => bridge.NotificationInjections;
|
||||
|
||||
public IPluginConfig Config { get; }
|
||||
|
||||
|
||||
public event EventHandler<PluginErrorEventArgs>? Reloaded;
|
||||
public event EventHandler<PluginErrorEventArgs>? Executed;
|
||||
|
||||
private readonly string pluginFolder;
|
||||
private readonly string pluginDataFolder;
|
||||
|
||||
private readonly PluginBridge bridge;
|
||||
internal readonly PluginBridge bridge;
|
||||
private IScriptExecutor? browserExecutor;
|
||||
|
||||
private readonly HashSet<Plugin> plugins = new HashSet<Plugin>();
|
||||
|
69
lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs
Normal file
69
lib/TweetLib.Core/Features/Plugins/PluginSchemeHandler.cs
Normal file
@ -0,0 +1,69 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using TweetLib.Core.Browser;
|
||||
using TweetLib.Core.Features.Plugins.Enums;
|
||||
|
||||
namespace TweetLib.Core.Features.Plugins{
|
||||
public sealed class PluginSchemeHandler<T> where T : class{
|
||||
public const string Name = "tdp";
|
||||
|
||||
private readonly IResourceProvider<T> resourceProvider;
|
||||
private PluginBridge? bridge = null;
|
||||
|
||||
public PluginSchemeHandler(IResourceProvider<T> resourceProvider){
|
||||
this.resourceProvider = resourceProvider;
|
||||
}
|
||||
|
||||
public void Setup(PluginManager plugins){
|
||||
if (this.bridge != null){
|
||||
throw new InvalidOperationException("Plugin scheme handler is already setup.");
|
||||
}
|
||||
|
||||
this.bridge = plugins.bridge;
|
||||
}
|
||||
|
||||
public T? Process(string url){
|
||||
if (!Uri.TryCreate(url, UriKind.Absolute, out var uri) || uri.Scheme != Name || !int.TryParse(uri.Authority, out var identifier)){
|
||||
return null;
|
||||
}
|
||||
|
||||
var segments = uri.Segments.Select(segment => segment.TrimEnd('/')).Where(segment => !string.IsNullOrEmpty(segment)).ToArray();
|
||||
|
||||
if (segments.Length > 0){
|
||||
var handler = segments[0] switch{
|
||||
"root" => DoReadRootFile(identifier, segments),
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (handler != null){
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
return resourceProvider.Status(HttpStatusCode.BadRequest, "Bad URL path: " + uri.AbsolutePath);
|
||||
}
|
||||
|
||||
private T? DoReadRootFile(int identifier, string[] segments){
|
||||
string path = string.Join("/", segments, 1, segments.Length - 1);
|
||||
|
||||
Plugin? plugin = bridge?.GetPluginFromToken(identifier);
|
||||
string fullPath = plugin == null ? string.Empty : plugin.GetFullPathIfSafe(PluginFolder.Root, path);
|
||||
|
||||
if (fullPath.Length == 0){
|
||||
return resourceProvider.Status(HttpStatusCode.Forbidden, "File path has to be relative to the plugin root folder.");
|
||||
}
|
||||
|
||||
try{
|
||||
return resourceProvider.File(File.ReadAllBytes(fullPath), Path.GetExtension(path));
|
||||
}catch(FileNotFoundException){
|
||||
return resourceProvider.Status(HttpStatusCode.NotFound, "File not found.");
|
||||
}catch(DirectoryNotFoundException){
|
||||
return resourceProvider.Status(HttpStatusCode.NotFound, "Directory not found.");
|
||||
}catch(Exception e){
|
||||
return resourceProvider.Status(HttpStatusCode.InternalServerError, e.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user