diff --git a/Plugins/Wox.Plugin.Fanyi/HttpRequest.cs b/Plugins/Wox.Plugin.Fanyi/HttpRequest.cs index 8d0939e1ed..dd9b2fdc90 100644 --- a/Plugins/Wox.Plugin.Fanyi/HttpRequest.cs +++ b/Plugins/Wox.Plugin.Fanyi/HttpRequest.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; @@ -10,20 +9,10 @@ using System.Text; //From:http://blog.csdn.net/zhoufoxcn/article/details/6404236 namespace Wox.Plugin.Fanyi { - /// - /// 有关HTTP请求的辅助类 - /// public class HttpRequest { private static readonly string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; - /// - /// 创建GET方式的HTTP请求 - /// - /// 请求的URL - /// 请求的超时时间 - /// 请求的客户端浏览器信息,可以为空 - /// 随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空 - /// + public static HttpWebResponse CreateGetHttpResponse(string url, int? timeout, string userAgent, CookieCollection cookies) { if (string.IsNullOrEmpty(url)) @@ -48,16 +37,7 @@ namespace Wox.Plugin.Fanyi } return request.GetResponse() as HttpWebResponse; } - /// - /// 创建POST方式的HTTP请求 - /// - /// 请求的URL - /// 随同请求POST的参数名称及参数值字典 - /// 请求的超时时间 - /// 请求的客户端浏览器信息,可以为空 - /// 发送HTTP请求时所用的编码 - /// 随同HTTP请求发送的Cookie信息,如果不需要身份验证可以为空 - /// + public static HttpWebResponse CreatePostHttpResponse(string url, IDictionary parameters, int? timeout, string userAgent, Encoding requestEncoding, CookieCollection cookies) { if (string.IsNullOrEmpty(url)) @@ -69,7 +49,6 @@ namespace Wox.Plugin.Fanyi throw new ArgumentNullException("requestEncoding"); } HttpWebRequest request = null; - //如果是发送HTTPS请求 if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) { ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); @@ -101,7 +80,6 @@ namespace Wox.Plugin.Fanyi request.CookieContainer = new CookieContainer(); request.CookieContainer.Add(cookies); } - //如果需要POST数据 if (!(parameters == null || parameters.Count == 0)) { StringBuilder buffer = new StringBuilder(); @@ -129,7 +107,7 @@ namespace Wox.Plugin.Fanyi private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) { - return true; //总是接受 + return true; } } } diff --git a/Plugins/Wox.Plugin.PluginManagement/HttpRequest.cs b/Plugins/Wox.Plugin.PluginManagement/HttpRequest.cs new file mode 100644 index 0000000000..9596edeb71 --- /dev/null +++ b/Plugins/Wox.Plugin.PluginManagement/HttpRequest.cs @@ -0,0 +1,113 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Net.Security; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +//From:http://blog.csdn.net/zhoufoxcn/article/details/6404236 +namespace Wox.Plugin.PluginManagement +{ + public class HttpRequest + { + private static readonly string DefaultUserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)"; + + public static HttpWebResponse CreateGetHttpResponse(string url, int? timeout, string userAgent, CookieCollection cookies) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException("url"); + } + HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest; + request.Method = "GET"; + request.UserAgent = DefaultUserAgent; + if (!string.IsNullOrEmpty(userAgent)) + { + request.UserAgent = userAgent; + } + if (timeout.HasValue) + { + request.Timeout = timeout.Value; + } + if (cookies != null) + { + request.CookieContainer = new CookieContainer(); + request.CookieContainer.Add(cookies); + } + return request.GetResponse() as HttpWebResponse; + } + + public static HttpWebResponse CreatePostHttpResponse(string url, IDictionary parameters, int? timeout, string userAgent, Encoding requestEncoding, CookieCollection cookies) + { + if (string.IsNullOrEmpty(url)) + { + throw new ArgumentNullException("url"); + } + if (requestEncoding == null) + { + throw new ArgumentNullException("requestEncoding"); + } + HttpWebRequest request = null; + if (url.StartsWith("https", StringComparison.OrdinalIgnoreCase)) + { + ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(CheckValidationResult); + request = WebRequest.Create(url) as HttpWebRequest; + request.ProtocolVersion = HttpVersion.Version10; + } + else + { + request = WebRequest.Create(url) as HttpWebRequest; + } + request.Method = "POST"; + request.ContentType = "application/x-www-form-urlencoded"; + + if (!string.IsNullOrEmpty(userAgent)) + { + request.UserAgent = userAgent; + } + else + { + request.UserAgent = DefaultUserAgent; + } + + if (timeout.HasValue) + { + request.Timeout = timeout.Value; + } + if (cookies != null) + { + request.CookieContainer = new CookieContainer(); + request.CookieContainer.Add(cookies); + } + if (!(parameters == null || parameters.Count == 0)) + { + StringBuilder buffer = new StringBuilder(); + int i = 0; + foreach (string key in parameters.Keys) + { + if (i > 0) + { + buffer.AppendFormat("&{0}={1}", key, parameters[key]); + } + else + { + buffer.AppendFormat("{0}={1}", key, parameters[key]); + } + i++; + } + byte[] data = requestEncoding.GetBytes(buffer.ToString()); + using (Stream stream = request.GetRequestStream()) + { + stream.Write(data, 0, data.Length); + } + } + return request.GetResponse() as HttpWebResponse; + } + + private static bool CheckValidationResult(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors) + { + return true; + } + } +} diff --git a/Plugins/Wox.Plugin.PluginManagement/Main.cs b/Plugins/Wox.Plugin.PluginManagement/Main.cs index 43179414d2..fd49442c71 100644 --- a/Plugins/Wox.Plugin.PluginManagement/Main.cs +++ b/Plugins/Wox.Plugin.PluginManagement/Main.cs @@ -2,15 +2,37 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; +using System.Text; +using System.Threading; using System.Windows.Forms; using Newtonsoft.Json; namespace Wox.Plugin.PluginManagement { + public class WoxPlugin + { + public int apiVersion { get; set; } + public List result { get; set; } + } + + public class WoxPluginResult + { + public string downloadUrl; + public string author; + public string description; + public string id; + public string language; + public int like; + public string name; + } + + public class Main : IPlugin { private static string PluginPath = AppDomain.CurrentDomain.BaseDirectory + "Plugins"; private static string PluginConfigName = "plugin.json"; + private static string pluginSearchUrl = "http://www.getwox.com/api/plugin/search/"; private PluginInitContext context; public List Query(Query query) @@ -20,12 +42,12 @@ namespace Wox.Plugin.PluginManagement { results.Add(new Result() { - Title = "wpm list", - SubTitle = "list plugins installed", + Title = "wpm install ", + SubTitle = "search and install wox plugins", IcoPath = "Images\\plugin.png", Action = e => { - context.ChangeQuery("wpm list"); + context.ChangeQuery("wpm install "); return false; } }); @@ -40,26 +62,147 @@ namespace Wox.Plugin.PluginManagement return false; } }); + results.Add(new Result() + { + Title = "wpm list", + SubTitle = "list plugins installed", + IcoPath = "Images\\plugin.png", + Action = e => + { + context.ChangeQuery("wpm list"); + return false; + } + }); return results; } if (query.ActionParameters.Count > 0) { + bool hit = false; switch (query.ActionParameters[0].ToLower()) { case "list": + hit = true; results = ListInstalledPlugins(); break; case "uninstall": + hit = true; results = ListUnInstalledPlugins(query); break; + + case "install": + hit = true; + if (query.ActionParameters.Count > 1) + { + results = InstallPlugin(query); + } + break; + } + + if (!hit) + { + if ("install".Contains(query.ActionParameters[0].ToLower())) + { + results.Add(new Result() + { + Title = "wpm install ", + SubTitle = "search and install wox plugins", + IcoPath = "Images\\plugin.png", + Action = e => + { + context.ChangeQuery("wpm install "); + return false; + } + }); + } + if ("uninstall".Contains(query.ActionParameters[0].ToLower())) + { + results.Add(new Result() + { + Title = "wpm uninstall ", + SubTitle = "uninstall plugin", + IcoPath = "Images\\plugin.png", + Action = e => + { + context.ChangeQuery("wpm uninstall "); + return false; + } + }); + } + if ("list".Contains(query.ActionParameters[0].ToLower())) + { + results.Add(new Result() + { + Title = "wpm list", + SubTitle = "list plugins installed", + IcoPath = "Images\\plugin.png", + Action = e => + { + context.ChangeQuery("wpm list"); + return false; + } + }); + } } } return results; } + private List InstallPlugin(Query query) + { + List results = new List(); + HttpWebResponse response = HttpRequest.CreateGetHttpResponse(pluginSearchUrl + query.ActionParameters[1], null, null, null); + Stream s = response.GetResponseStream(); + if (s != null) + { + StreamReader reader = new StreamReader(s, Encoding.UTF8); + string json = reader.ReadToEnd(); + WoxPlugin o = JsonConvert.DeserializeObject(json); + foreach (WoxPluginResult r in o.result) + { + WoxPluginResult r1 = r; + results.Add(new Result() + { + Title = r.name, + SubTitle = r.description, + IcoPath = "Images\\plugin.png", + Action = e => + { + string folder = Path.Combine(Path.GetTempPath(), "WoxPluginDownload"); + if (!Directory.Exists(folder)) Directory.CreateDirectory(folder); + string filePath = Path.Combine(folder, Guid.NewGuid().ToString() + ".wox"); + + context.StartLoadingBar(); + ThreadPool.QueueUserWorkItem(delegate + { + using (WebClient Client = new WebClient()) + { + try + { + Client.DownloadFile(r1.downloadUrl, filePath); + context.InstallPlugin(filePath); + context.ReloadPlugins(); + } + catch (Exception exception) + { + MessageBox.Show("download plugin " + r.name + "failed. " + exception.Message); + } + finally + { + context.StopLoadingBar(); + } + } + }); + return false; + } + }); + } + } + return results; + } + private List ListUnInstalledPlugins(Query query) { List results = new List(); @@ -90,10 +233,10 @@ namespace Wox.Plugin.PluginManagement private void UnInstalledPlugins(PluginMetadata plugin) { - string content = string.Format("Do you want to uninstall following plugin?\r\n\r\nName: {0}\r\nVersion: {1}\r\nAuthor: {2}",plugin.Name, plugin.Version, plugin.Author); + string content = string.Format("Do you want to uninstall following plugin?\r\n\r\nName: {0}\r\nVersion: {1}\r\nAuthor: {2}", plugin.Name, plugin.Version, plugin.Author); if (MessageBox.Show(content, "Wox", MessageBoxButtons.YesNo) == DialogResult.Yes) { - File.Create(Path.Combine(plugin.PluginDirecotry, "NeedDelete.txt")); + File.Create(Path.Combine(plugin.PluginDirecotry, "NeedDelete.txt")).Close(); MessageBox.Show("This plugin has been removed, restart Wox to take effect"); } } diff --git a/Plugins/Wox.Plugin.PluginManagement/Wox.Plugin.PluginManagement.csproj b/Plugins/Wox.Plugin.PluginManagement/Wox.Plugin.PluginManagement.csproj index e8c7d2ff46..4ce95001e1 100644 --- a/Plugins/Wox.Plugin.PluginManagement/Wox.Plugin.PluginManagement.csproj +++ b/Plugins/Wox.Plugin.PluginManagement/Wox.Plugin.PluginManagement.csproj @@ -44,6 +44,7 @@ + diff --git a/Wox.Plugin/PluginInitContext.cs b/Wox.Plugin/PluginInitContext.cs index 38013797e9..fae6d758d9 100644 --- a/Wox.Plugin/PluginInitContext.cs +++ b/Wox.Plugin/PluginInitContext.cs @@ -25,5 +25,9 @@ namespace Wox.Plugin /// public Action ReloadPlugins { get; set; } + public Action InstallPlugin { get; set; } + + public Action StartLoadingBar { get; set; } + public Action StopLoadingBar { get; set; } } } diff --git a/Wox/Helper/PluginInstaller.cs b/Wox/Helper/PluginInstaller.cs index 1780609713..7288080417 100644 --- a/Wox/Helper/PluginInstaller.cs +++ b/Wox/Helper/PluginInstaller.cs @@ -1,9 +1,7 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; -using System.Text; using System.Windows; using ICSharpCode.SharpZipLib.Zip; using Newtonsoft.Json; @@ -14,7 +12,6 @@ namespace Wox.Helper { public class PluginInstaller { - public static void Install(string path) { if (File.Exists(path)) @@ -46,43 +43,51 @@ namespace Wox.Helper Directory.CreateDirectory(pluginFolerPath); } - string newPluginPath = Path.Combine(pluginFolerPath, plugin.Name); + string newPluginPath = Path.Combine(pluginFolerPath, Guid.NewGuid().ToString()); string content = string.Format( "Do you want to install following plugin?\r\n\r\nName: {0}\r\nVersion: {1}\r\nAuthor: {2}", plugin.Name, plugin.Version, plugin.Author); - if (Directory.Exists(newPluginPath)) + PluginPair existingPlugin = Plugins.AllPlugins.FirstOrDefault(o => o.Metadata.ID == plugin.ID); + + if (existingPlugin != null) { - PluginMetadata existingPlugin = GetMetadataFromJson(newPluginPath); - if (existingPlugin == null || existingPlugin.Name == null) - { - //maybe broken plugin, just delete it - Directory.Delete(newPluginPath, true); - } - else - { content = string.Format( "Do you want to update following plugin?\r\n\r\nName: {0}\r\nOld Version: {1}\r\nNew Version: {2}\r\nAuthor: {3}", - plugin.Name, existingPlugin.Version, plugin.Version, plugin.Author); - } + plugin.Name, existingPlugin.Metadata.Version, plugin.Version, plugin.Author); } MessageBoxResult result = MessageBox.Show(content, "Install plugin", MessageBoxButton.YesNo, MessageBoxImage.Question); if (result == MessageBoxResult.Yes) { - if (Directory.Exists(newPluginPath)) + if (existingPlugin != null && Directory.Exists(existingPlugin.Metadata.PluginDirecotry)) { - Directory.Delete(newPluginPath, true); + File.Create(Path.Combine(existingPlugin.Metadata.PluginDirecotry, "NeedDelete.txt")).Close(); } + UnZip(path, newPluginPath, true); Directory.Delete(tempFoler, true); - if (MainWindow.Initialized) + //exsiting plugins may be has loaded by application, + //if we try to delelte those kind of plugins, we will get a error that indicate the + //file is been used now. + //current solution is to restart wox. Ugly. + //if (MainWindow.Initialized) + //{ + // Plugins.Init(); + //} + if (MessageBox.Show("You have installed plugin " + plugin.Name + " successfully.\r\n Restart Wox to take effect?", "Install plugin", + MessageBoxButton.YesNo, MessageBoxImage.Question) == MessageBoxResult.Yes) { - Plugins.Init(); + ProcessStartInfo Info = new ProcessStartInfo(); + Info.Arguments = "/C ping 127.0.0.1 -n 1 && \"" + + Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "wox.exe") + "\""; + Info.WindowStyle = ProcessWindowStyle.Hidden; + Info.CreateNoWindow = true; + Info.FileName = "cmd.exe"; + Process.Start(Info); + App.Window.CloseApp(); } - - MessageBox.Show("You have installed plugin " + plugin.Name + " successfully."); } } } diff --git a/Wox/MainWindow.xaml.cs b/Wox/MainWindow.xaml.cs index 88eb589bd3..b91482f7f4 100644 --- a/Wox/MainWindow.xaml.cs +++ b/Wox/MainWindow.xaml.cs @@ -41,7 +41,6 @@ namespace Wox private NotifyIcon notifyIcon; private bool queryHasReturn; private ToolTip toolTip = new ToolTip(); - public MainWindow() { InitializeComponent(); @@ -420,8 +419,18 @@ namespace Wox toolTip.IsOpen = true; } + public void StartLoadingBar() + { + Dispatcher.Invoke(new Action(StartProgress)); + } + + public void StopLoadingBar() + { + Dispatcher.Invoke(new Action(StopProgress)); + } + #endregion - + } } \ No newline at end of file diff --git a/Wox/PluginLoader/Plugins.cs b/Wox/PluginLoader/Plugins.cs index 4b3361dc67..41d7033261 100644 --- a/Wox/PluginLoader/Plugins.cs +++ b/Wox/PluginLoader/Plugins.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Text; using System.Threading; using Microsoft.CSharp; +using Wox.Helper; using Wox.Plugin; namespace Wox.PluginLoader @@ -30,14 +31,21 @@ namespace Wox.PluginLoader { Plugins = plugins, CurrentPluginMetadata = metadata, - ChangeQuery = s => App.Window.ChangeQuery(s), - CloseApp = App.Window.CloseApp, - HideApp = App.Window.HideApp, - ShowApp = () => App.Window.ShowApp(), - ShowMsg = (title, subTitle, iconPath) => App.Window.ShowMsg(title, subTitle, iconPath), - OpenSettingDialog = () => App.Window.OpenSettingDialog(), - ShowCurrentResultItemTooltip = (msg) => App.Window.ShowCurrentResultItemTooltip(msg), - ReloadPlugins = ()=> Init() + ChangeQuery = s => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ChangeQuery(s))), + CloseApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.CloseApp())), + HideApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.HideApp())), + ShowApp = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ShowApp())), + ShowMsg = (title, subTitle, iconPath) => App.Window.Dispatcher.Invoke(new Action(() => + App.Window.ShowMsg(title, subTitle, iconPath))), + OpenSettingDialog = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.OpenSettingDialog())), + ShowCurrentResultItemTooltip = (msg) => App.Window.Dispatcher.Invoke(new Action(() => App.Window.ShowCurrentResultItemTooltip(msg))), + ReloadPlugins = () => App.Window.Dispatcher.Invoke(new Action(() => Init())), + InstallPlugin = (filePath) => App.Window.Dispatcher.Invoke(new Action(() => + { + PluginInstaller.Install(filePath); + })), + StartLoadingBar = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.StartLoadingBar())), + StopLoadingBar = () => App.Window.Dispatcher.Invoke(new Action(() => App.Window.StopLoadingBar())), })); } } diff --git a/Wox/PluginLoader/PythonPluginWrapper.cs b/Wox/PluginLoader/PythonPluginWrapper.cs index 4e2843eb26..a021f7796d 100644 --- a/Wox/PluginLoader/PythonPluginWrapper.cs +++ b/Wox/PluginLoader/PythonPluginWrapper.cs @@ -76,6 +76,7 @@ namespace Wox.PluginLoader { string json = null; + //if pythobn plugin folder name is chinese, here will deadlock. IntPtr gs = PythonEngine.AcquireLock(); PyObject module = PythonEngine.ImportModule(moduleName);