2021-06-08 00:02:25 +00:00
|
|
|
|
using Flow.Launcher.Core.Resource;
|
|
|
|
|
|
using System;
|
2016-01-06 21:34:42 +00:00
|
|
|
|
using System.Collections.Generic;
|
2014-07-06 14:57:11 +00:00
|
|
|
|
using System.Diagnostics;
|
2021-02-14 05:56:27 +00:00
|
|
|
|
using System.IO;
|
2014-07-10 15:57:08 +00:00
|
|
|
|
using System.Reflection;
|
2020-12-30 05:40:42 +00:00
|
|
|
|
using System.Text.Json;
|
2014-07-10 15:57:08 +00:00
|
|
|
|
using System.Threading;
|
2016-05-05 20:15:13 +00:00
|
|
|
|
using System.Threading.Tasks;
|
2014-12-26 14:51:04 +00:00
|
|
|
|
using System.Windows.Forms;
|
2020-04-21 09:12:17 +00:00
|
|
|
|
using Flow.Launcher.Infrastructure.Logger;
|
|
|
|
|
|
using Flow.Launcher.Plugin;
|
2021-02-08 06:37:37 +00:00
|
|
|
|
using JetBrains.Annotations;
|
2014-07-06 14:57:11 +00:00
|
|
|
|
|
2020-04-21 09:12:17 +00:00
|
|
|
|
namespace Flow.Launcher.Core.Plugin
|
2014-07-06 14:57:11 +00:00
|
|
|
|
{
|
2014-12-26 11:36:43 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Represent the plugin that using JsonPRC
|
2016-05-05 00:57:03 +00:00
|
|
|
|
/// every JsonRPC plugin should has its own plugin instance
|
2014-12-26 11:36:43 +00:00
|
|
|
|
/// </summary>
|
2021-02-08 06:37:37 +00:00
|
|
|
|
internal abstract class JsonRPCPlugin : IAsyncPlugin, IContextMenu
|
2014-07-06 14:57:11 +00:00
|
|
|
|
{
|
|
|
|
|
|
protected PluginInitContext context;
|
2016-06-21 23:42:24 +00:00
|
|
|
|
public const string JsonRPC = "JsonRPC";
|
2014-07-06 14:57:11 +00:00
|
|
|
|
|
2014-12-26 11:36:43 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The language this JsonRPCPlugin support
|
|
|
|
|
|
/// </summary>
|
2016-05-05 00:57:03 +00:00
|
|
|
|
public abstract string SupportedLanguage { get; set; }
|
2014-07-06 14:57:11 +00:00
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
protected abstract Task<Stream> ExecuteQueryAsync(Query query, CancellationToken token);
|
2014-12-26 11:36:43 +00:00
|
|
|
|
protected abstract string ExecuteCallback(JsonRPCRequestModel rpcRequest);
|
2017-04-11 13:34:04 +00:00
|
|
|
|
protected abstract string ExecuteContextMenu(Result selectedResult);
|
2014-07-06 14:57:11 +00:00
|
|
|
|
|
2017-04-11 13:34:04 +00:00
|
|
|
|
public List<Result> LoadContextMenus(Result selectedResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
string output = ExecuteContextMenu(selectedResult);
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
return DeserializedResult(output);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log.Exception($"|JsonRPCPlugin.LoadContextMenus|Exception on result <{selectedResult}>", e);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-10 05:06:35 +00:00
|
|
|
|
private static readonly JsonSerializerOptions _options = new()
|
|
|
|
|
|
{
|
|
|
|
|
|
Converters =
|
|
|
|
|
|
{
|
|
|
|
|
|
new JsonObjectConverter()
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2014-07-07 15:05:06 +00:00
|
|
|
|
|
2021-02-14 05:59:59 +00:00
|
|
|
|
private async Task<List<Result>> DeserializedResultAsync(Stream output)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (output == Stream.Null) return null;
|
|
|
|
|
|
|
2021-06-08 00:02:25 +00:00
|
|
|
|
var queryResponseModel = await
|
|
|
|
|
|
JsonSerializer.DeserializeAsync<JsonRPCQueryResponseModel>(output, _options);
|
2021-02-14 05:59:59 +00:00
|
|
|
|
|
|
|
|
|
|
return ParseResults(queryResponseModel);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private List<Result> DeserializedResult(string output)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(output)) return null;
|
|
|
|
|
|
|
2021-06-08 00:02:25 +00:00
|
|
|
|
var queryResponseModel =
|
|
|
|
|
|
JsonSerializer.Deserialize<JsonRPCQueryResponseModel>(output, _options);
|
2021-02-14 05:59:59 +00:00
|
|
|
|
return ParseResults(queryResponseModel);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-08 00:02:25 +00:00
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
private List<Result> ParseResults(JsonRPCQueryResponseModel queryResponseModel)
|
|
|
|
|
|
{
|
|
|
|
|
|
var results = new List<Result>();
|
|
|
|
|
|
if (queryResponseModel.Result == null) return null;
|
|
|
|
|
|
|
2021-06-07 05:03:59 +00:00
|
|
|
|
if (!string.IsNullOrEmpty(queryResponseModel.DebugMessage))
|
2021-02-14 05:59:59 +00:00
|
|
|
|
{
|
|
|
|
|
|
context.API.ShowMsg(queryResponseModel.DebugMessage);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
foreach (JsonRPCResult result in queryResponseModel.Result)
|
|
|
|
|
|
{
|
|
|
|
|
|
result.Action = c =>
|
2017-04-11 13:32:51 +00:00
|
|
|
|
{
|
2021-02-14 05:56:27 +00:00
|
|
|
|
if (result.JsonRPCAction == null) return false;
|
2014-10-22 08:20:50 +00:00
|
|
|
|
|
2021-06-10 03:21:58 +00:00
|
|
|
|
if (string.IsNullOrEmpty(result.JsonRPCAction.Method))
|
2021-02-14 05:56:27 +00:00
|
|
|
|
{
|
2021-06-10 03:21:58 +00:00
|
|
|
|
return !result.JsonRPCAction.DontHideAfterAction;
|
|
|
|
|
|
}
|
2021-06-10 05:06:35 +00:00
|
|
|
|
|
2021-06-10 03:21:58 +00:00
|
|
|
|
if (result.JsonRPCAction.Method.StartsWith("Flow.Launcher."))
|
|
|
|
|
|
{
|
2021-06-10 05:06:35 +00:00
|
|
|
|
ExecuteFlowLauncherAPI(result.JsonRPCAction.Method["Flow.Launcher.".Length..],
|
2021-06-10 03:21:58 +00:00
|
|
|
|
result.JsonRPCAction.Parameters);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var actionResponse = ExecuteCallback(result.JsonRPCAction);
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(actionResponse))
|
2017-04-11 13:32:51 +00:00
|
|
|
|
{
|
2021-06-10 03:21:58 +00:00
|
|
|
|
return !result.JsonRPCAction.DontHideAfterAction;
|
2021-02-14 05:56:27 +00:00
|
|
|
|
}
|
2021-06-10 05:06:35 +00:00
|
|
|
|
|
|
|
|
|
|
var jsonRpcRequestModel = JsonSerializer.Deserialize<JsonRPCRequestModel>(actionResponse, _options);
|
|
|
|
|
|
|
|
|
|
|
|
if (jsonRpcRequestModel?.Method?.StartsWith("Flow.Launcher.") ?? false)
|
2021-02-14 05:56:27 +00:00
|
|
|
|
{
|
2021-06-10 05:06:35 +00:00
|
|
|
|
ExecuteFlowLauncherAPI(jsonRpcRequestModel.Method["Flow.Launcher.".Length..],
|
2021-06-10 03:21:58 +00:00
|
|
|
|
jsonRpcRequestModel.Parameters);
|
2017-04-11 13:32:51 +00:00
|
|
|
|
}
|
2021-02-14 05:56:27 +00:00
|
|
|
|
}
|
2021-02-08 06:37:37 +00:00
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
return !result.JsonRPCAction.DontHideAfterAction;
|
|
|
|
|
|
};
|
|
|
|
|
|
results.Add(result);
|
2014-07-06 14:57:11 +00:00
|
|
|
|
}
|
2021-02-08 06:37:37 +00:00
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
return results;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-04-21 12:16:10 +00:00
|
|
|
|
private void ExecuteFlowLauncherAPI(string method, object[] parameters)
|
2014-07-10 15:57:08 +00:00
|
|
|
|
{
|
2014-12-26 14:51:04 +00:00
|
|
|
|
MethodInfo methodInfo = PluginManager.API.GetType().GetMethod(method);
|
2015-02-03 10:32:16 +00:00
|
|
|
|
if (methodInfo != null)
|
2014-07-10 15:57:08 +00:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2014-12-26 14:51:04 +00:00
|
|
|
|
methodInfo.Invoke(PluginManager.API, parameters);
|
2014-07-10 15:57:08 +00:00
|
|
|
|
}
|
2016-01-06 21:34:42 +00:00
|
|
|
|
catch (Exception)
|
2014-07-10 15:57:08 +00:00
|
|
|
|
{
|
|
|
|
|
|
#if (DEBUG)
|
2021-02-08 06:37:37 +00:00
|
|
|
|
throw;
|
2014-07-10 15:57:08 +00:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2014-07-07 15:05:06 +00:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Execute external program and return the output
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="fileName"></param>
|
|
|
|
|
|
/// <param name="arguments"></param>
|
2021-02-08 06:37:37 +00:00
|
|
|
|
/// <param name="token">Cancellation Token</param>
|
2014-07-07 15:05:06 +00:00
|
|
|
|
/// <returns></returns>
|
2021-02-14 05:56:27 +00:00
|
|
|
|
protected Task<Stream> ExecuteAsync(string fileName, string arguments, CancellationToken token = default)
|
2014-07-18 12:00:55 +00:00
|
|
|
|
{
|
2021-02-14 05:56:27 +00:00
|
|
|
|
ProcessStartInfo start = new ProcessStartInfo
|
|
|
|
|
|
{
|
|
|
|
|
|
FileName = fileName,
|
|
|
|
|
|
Arguments = arguments,
|
|
|
|
|
|
UseShellExecute = false,
|
|
|
|
|
|
CreateNoWindow = true,
|
|
|
|
|
|
RedirectStandardOutput = true,
|
|
|
|
|
|
RedirectStandardError = true
|
|
|
|
|
|
};
|
2021-02-14 04:02:59 +00:00
|
|
|
|
return ExecuteAsync(start, token);
|
2014-07-18 12:00:55 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-08 06:43:17 +00:00
|
|
|
|
protected string Execute(ProcessStartInfo startInfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
using var process = Process.Start(startInfo);
|
|
|
|
|
|
if (process == null) return string.Empty;
|
2021-02-14 05:56:27 +00:00
|
|
|
|
|
2021-02-08 06:43:17 +00:00
|
|
|
|
using var standardOutput = process.StandardOutput;
|
|
|
|
|
|
var result = standardOutput.ReadToEnd();
|
|
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(result))
|
|
|
|
|
|
{
|
|
|
|
|
|
using var standardError = process.StandardError;
|
|
|
|
|
|
var error = standardError.ReadToEnd();
|
|
|
|
|
|
if (!string.IsNullOrEmpty(error))
|
|
|
|
|
|
{
|
|
|
|
|
|
Log.Error($"|JsonRPCPlugin.Execute|{error}");
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Log.Error("|JsonRPCPlugin.Execute|Empty standard output and standard error.");
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (result.StartsWith("DEBUG:"))
|
|
|
|
|
|
{
|
2021-06-10 05:06:35 +00:00
|
|
|
|
MessageBox.Show(new Form
|
|
|
|
|
|
{
|
|
|
|
|
|
TopMost = true
|
|
|
|
|
|
}, result.Substring(6));
|
2021-02-08 06:43:17 +00:00
|
|
|
|
return string.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log.Exception(
|
|
|
|
|
|
$"|JsonRPCPlugin.Execute|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>",
|
|
|
|
|
|
e);
|
|
|
|
|
|
return string.Empty;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
protected async Task<Stream> ExecuteAsync(ProcessStartInfo startInfo, CancellationToken token = default)
|
2014-07-06 14:57:11 +00:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2021-02-03 09:50:36 +00:00
|
|
|
|
using var process = Process.Start(startInfo);
|
|
|
|
|
|
if (process == null)
|
2014-07-06 14:57:11 +00:00
|
|
|
|
{
|
2021-02-08 06:43:17 +00:00
|
|
|
|
Log.Error("|JsonRPCPlugin.ExecuteAsync|Can't start new process");
|
2021-02-14 05:56:27 +00:00
|
|
|
|
return Stream.Null;
|
2021-02-03 09:50:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
var result = process.StandardOutput.BaseStream;
|
2021-02-09 06:03:22 +00:00
|
|
|
|
|
|
|
|
|
|
token.ThrowIfCancellationRequested();
|
|
|
|
|
|
|
2021-02-14 05:56:27 +00:00
|
|
|
|
if (!process.StandardError.EndOfStream)
|
2021-02-03 09:50:36 +00:00
|
|
|
|
{
|
2021-02-08 06:37:37 +00:00
|
|
|
|
using var standardError = process.StandardError;
|
|
|
|
|
|
var error = await standardError.ReadToEndAsync();
|
2021-02-09 06:03:22 +00:00
|
|
|
|
|
2021-02-08 06:37:37 +00:00
|
|
|
|
if (!string.IsNullOrEmpty(error))
|
2014-07-06 14:57:11 +00:00
|
|
|
|
{
|
2021-02-08 06:43:17 +00:00
|
|
|
|
Log.Error($"|JsonRPCPlugin.ExecuteAsync|{error}");
|
2021-02-14 05:56:27 +00:00
|
|
|
|
return Stream.Null;
|
2017-01-30 00:26:11 +00:00
|
|
|
|
}
|
2021-02-08 06:37:37 +00:00
|
|
|
|
|
2021-02-08 06:43:17 +00:00
|
|
|
|
Log.Error("|JsonRPCPlugin.ExecuteAsync|Empty standard output and standard error.");
|
2021-02-14 05:56:27 +00:00
|
|
|
|
return Stream.Null;
|
2021-02-03 09:50:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-08 06:37:37 +00:00
|
|
|
|
return result;
|
2014-07-06 14:57:11 +00:00
|
|
|
|
}
|
2016-05-05 20:15:13 +00:00
|
|
|
|
catch (Exception e)
|
2014-07-06 14:57:11 +00:00
|
|
|
|
{
|
2021-02-08 06:37:37 +00:00
|
|
|
|
Log.Exception(
|
2021-02-08 06:43:17 +00:00
|
|
|
|
$"|JsonRPCPlugin.ExecuteAsync|Exception for filename <{startInfo.FileName}> with argument <{startInfo.Arguments}>",
|
2021-02-08 06:37:37 +00:00
|
|
|
|
e);
|
2021-02-14 05:56:27 +00:00
|
|
|
|
return Stream.Null;
|
2014-07-06 14:57:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-08 06:37:37 +00:00
|
|
|
|
public async Task<List<Result>> QueryAsync(Query query, CancellationToken token)
|
|
|
|
|
|
{
|
2021-02-14 05:56:27 +00:00
|
|
|
|
var output = await ExecuteQueryAsync(query, token);
|
2021-02-08 06:37:37 +00:00
|
|
|
|
try
|
|
|
|
|
|
{
|
2021-02-14 05:56:27 +00:00
|
|
|
|
return await DeserializedResultAsync(output);
|
2021-02-08 06:37:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
Log.Exception($"|JsonRPCPlugin.Query|Exception when query <{query}>", e);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-06-07 05:03:59 +00:00
|
|
|
|
public virtual Task InitAsync(PluginInitContext context)
|
2014-07-06 14:57:11 +00:00
|
|
|
|
{
|
2021-02-08 06:37:37 +00:00
|
|
|
|
this.context = context;
|
|
|
|
|
|
return Task.CompletedTask;
|
2014-07-06 14:57:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-06-07 05:03:59 +00:00
|
|
|
|
}
|