diff --git a/Plugins/Wox.Plugin.BrowserBookmark/Bookmark.cs b/Plugins/Wox.Plugin.BrowserBookmark/Bookmark.cs new file mode 100644 index 0000000000..700f253e8f --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/Bookmark.cs @@ -0,0 +1,58 @@ +using BinaryAnalysis.UnidecodeSharp; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + + +namespace Wox.Plugin.BrowserBookmark +{ + public class Bookmark : IEquatable, IEqualityComparer + { + private string m_Name; + public string Name + { + get + { + return m_Name; + } + set + { + m_Name = value; + PinyinName = m_Name.Unidecode(); + } + } + public string PinyinName { get; private set; } + public string Url { get; set; } + public string Source { get; set; } + public int Score { get; set; } + + /* TODO: since Source maybe unimportant, we just need to compare Name and Url */ + public bool Equals(Bookmark other) + { + return Equals(this, other); + } + + public bool Equals(Bookmark x, Bookmark y) + { + if (Object.ReferenceEquals(x, y)) return true; + if (Object.ReferenceEquals(x, null) || Object.ReferenceEquals(y, null)) + return false; + + return x.Name == y.Name && x.Url == y.Url; + } + + public int GetHashCode(Bookmark bookmark) + { + if (Object.ReferenceEquals(bookmark, null)) return 0; + int hashName = bookmark.Name == null ? 0 : bookmark.Name.GetHashCode(); + int hashUrl = bookmark.Url == null ? 0 : bookmark.Url.GetHashCode(); + return hashName ^ hashUrl; + } + + public override int GetHashCode() + { + return GetHashCode(this); + } + } +} diff --git a/Plugins/Wox.Plugin.BrowserBookmark/ChromeBookmarks.cs b/Plugins/Wox.Plugin.BrowserBookmark/ChromeBookmarks.cs new file mode 100644 index 0000000000..16e5c328cb --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/ChromeBookmarks.cs @@ -0,0 +1,87 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text.RegularExpressions; + +namespace Wox.Plugin.BrowserBookmark +{ + public class ChromeBookmarks + { + private List bookmarks = new List(); + + public List GetBookmarks() + { + bookmarks.Clear(); + LoadChromeBookmarks(); + + return bookmarks; + } + + private void ParseChromeBookmarks(String path, string source) + { + if (!File.Exists(path)) return; + + string all = File.ReadAllText(path); + Regex nameRegex = new Regex("\"name\": \"(?.*?)\""); + MatchCollection nameCollection = nameRegex.Matches(all); + Regex typeRegex = new Regex("\"type\": \"(?.*?)\""); + MatchCollection typeCollection = typeRegex.Matches(all); + Regex urlRegex = new Regex("\"url\": \"(?.*?)\""); + MatchCollection urlCollection = urlRegex.Matches(all); + + List names = (from Match match in nameCollection select match.Groups["name"].Value).ToList(); + List types = (from Match match in typeCollection select match.Groups["type"].Value).ToList(); + List urls = (from Match match in urlCollection select match.Groups["url"].Value).ToList(); + + int urlIndex = 0; + for (int i = 0; i < names.Count; i++) + { + string name = DecodeUnicode(names[i]); + string type = types[i]; + if (type == "url") + { + string url = urls[urlIndex]; + urlIndex++; + + if (url == null) continue; + if (url.StartsWith("javascript:", StringComparison.OrdinalIgnoreCase)) continue; + if (url.StartsWith("vbscript:", StringComparison.OrdinalIgnoreCase)) continue; + + bookmarks.Add(new Bookmark() + { + Name = name, + Url = url, + Source = source + }); + } + } + } + + private void LoadChromeBookmarks(string path, string name) + { + if (!Directory.Exists(path)) return; + var paths = Directory.GetDirectories(path); + + foreach (var profile in paths) + { + if (File.Exists(Path.Combine(profile, "Bookmarks"))) + ParseChromeBookmarks(Path.Combine(profile, "Bookmarks"), name + (Path.GetFileName(profile) == "Default" ? "" : (" (" + Path.GetFileName(profile) + ")"))); + } + } + + private void LoadChromeBookmarks() + { + String platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + LoadChromeBookmarks(Path.Combine(platformPath, @"Google\Chrome\User Data"), "Google Chrome"); + LoadChromeBookmarks(Path.Combine(platformPath, @"Google\Chrome SxS\User Data"), "Google Chrome Canary"); + LoadChromeBookmarks(Path.Combine(platformPath, @"Chromium\User Data"), "Chromium"); + } + + private String DecodeUnicode(String dataStr) + { + Regex reg = new Regex(@"(?i)\\[uU]([0-9a-f]{4})"); + return reg.Replace(dataStr, m => ((char)Convert.ToInt32(m.Groups[1].Value, 16)).ToString()); + } + } +} \ No newline at end of file diff --git a/Plugins/Wox.Plugin.BrowserBookmark/FirefoxBookmarks.cs b/Plugins/Wox.Plugin.BrowserBookmark/FirefoxBookmarks.cs new file mode 100644 index 0000000000..fec331a76e --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/FirefoxBookmarks.cs @@ -0,0 +1,89 @@ +using System; +using System.Collections.Generic; +using System.Data.SQLite; +using System.IO; +using System.Linq; + +namespace Wox.Plugin.BrowserBookmark +{ + public class FirefoxBookmarks + { + private const string queryAllBookmarks = @"SELECT moz_places.url, moz_bookmarks.title + FROM moz_places + INNER JOIN moz_bookmarks ON ( + moz_bookmarks.fk NOT NULL AND moz_bookmarks.fk = moz_places.id + ) + ORDER BY moz_places.visit_count DESC + "; + + private const string dbPathFormat = "Data Source ={0};Version=3;New=False;Compress=True;"; + + /// + /// Searches the places.sqlite db and returns all bookmarks + /// + public List GetBookmarks() + { + // Return empty list if the places.sqlite file cannot be found + if (string.IsNullOrEmpty(PlacesPath) || !File.Exists(PlacesPath)) + return new List(); + + // create the connection string and init the connection + string dbPath = string.Format(dbPathFormat, PlacesPath); + var dbConnection = new SQLiteConnection(dbPath); + + // Open connection to the database file and execute the query + dbConnection.Open(); + var reader = new SQLiteCommand(queryAllBookmarks, dbConnection).ExecuteReader(); + + // return results in List format + return reader.Select(x => new Bookmark() + { + Name = (x["title"] is DBNull) ? string.Empty : x["title"].ToString(), + Url = x["url"].ToString() + }).ToList(); + } + + /// + /// Path to places.sqlite + /// + private string PlacesPath + { + get + { + var profileFolderPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Mozilla\Firefox"); + var profileIni = Path.Combine(profileFolderPath, @"profiles.ini"); + + if (!File.Exists(profileIni)) + return string.Empty; + + // get firefox default profile directory from profiles.ini + string ini; + using (var sReader = new StreamReader(profileIni)) { + ini = sReader.ReadToEnd(); + } + var lines = ini.Split(new string[] { "\r\n" }, StringSplitOptions.None).ToList(); + + var index = lines.IndexOf("Default=1"); + if (index > 3) { + var relative = lines[index - 2].Split('=')[1]; + var profiePath = lines[index - 1].Split('=')[1]; + return relative == "0" + ? profiePath + @"\places.sqlite" + : Path.Combine(profileFolderPath, profiePath) + @"\places.sqlite"; + } + return string.Empty; + } + } + } + + public static class Extensions + { + public static IEnumerable Select(this SQLiteDataReader reader, Func projection) + { + while (reader.Read()) + { + yield return projection(reader); + } + } + } +} diff --git a/Plugins/Wox.Plugin.BrowserBookmark/Images/bookmark.png b/Plugins/Wox.Plugin.BrowserBookmark/Images/bookmark.png new file mode 100644 index 0000000000..b8aee3564e Binary files /dev/null and b/Plugins/Wox.Plugin.BrowserBookmark/Images/bookmark.png differ diff --git a/Plugins/Wox.Plugin.BrowserBookmark/Main.cs b/Plugins/Wox.Plugin.BrowserBookmark/Main.cs new file mode 100644 index 0000000000..f0a595f2fb --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/Main.cs @@ -0,0 +1,72 @@ +using System.Collections.Generic; +using System.Linq; +using Wox.Infrastructure; + +namespace Wox.Plugin.BrowserBookmark +{ + public class Main : IPlugin + { + private PluginInitContext context; + + // TODO: periodically refresh the Cache? + private List cachedBookmarks = new List(); + + public void Init(PluginInitContext context) + { + this.context = context; + + // Cache all bookmarks + var chromeBookmarks = new ChromeBookmarks(); + var mozBookmarks = new FirefoxBookmarks(); + + //TODO: Let the user select which browser's bookmarks are displayed + // Add Firefox bookmarks + cachedBookmarks.AddRange(mozBookmarks.GetBookmarks()); + // Add Chrome bookmarks + cachedBookmarks.AddRange(chromeBookmarks.GetBookmarks()); + + cachedBookmarks = cachedBookmarks.Distinct().ToList(); + } + + public List Query(Query query) + { + string param = query.GetAllRemainingParameter().TrimStart(); + + // Should top results be returned? (true if no search parameters have been passed) + var topResults = string.IsNullOrEmpty(param); + + var returnList = cachedBookmarks; + + if (!topResults) + { + // Since we mixed chrome and firefox bookmarks, we should order them again + var fuzzyMatcher = FuzzyMatcher.Create(param); + returnList = cachedBookmarks.Where(o => MatchProgram(o, fuzzyMatcher)).ToList(); + returnList = returnList.OrderByDescending(o => o.Score).ToList(); + } + + return returnList.Select(c => new Result() + { + Title = c.Name, + SubTitle = "Bookmark: " + c.Url, + IcoPath = @"Images\bookmark.png", + Score = 5, + Action = (e) => + { + context.API.HideApp(); + System.Diagnostics.Process.Start(c.Url); + return true; + } + }).ToList(); + } + + private bool MatchProgram(Bookmark bookmark, FuzzyMatcher matcher) + { + if ((bookmark.Score = matcher.Evaluate(bookmark.Name).Score) > 0) return true; + if ((bookmark.Score = matcher.Evaluate(bookmark.PinyinName).Score) > 0) return true; + if ((bookmark.Score = matcher.Evaluate(bookmark.Url).Score / 10) > 0) return true; + + return false; + } + } +} diff --git a/Plugins/Wox.Plugin.BrowserBookmark/Properties/AssemblyInfo.cs b/Plugins/Wox.Plugin.BrowserBookmark/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..982c549994 --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Wox.Plugin.BrowserBookmark")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Oracle Corporation")] +[assembly: AssemblyProduct("Wox.Plugin.BrowserBookmark")] +[assembly: AssemblyCopyright("Copyright © Oracle Corporation 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7dd2e33e-d029-4661-8f1d-594e82cef077")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Plugins/Wox.Plugin.BrowserBookmark/Wox.Plugin.BrowserBookmark.csproj b/Plugins/Wox.Plugin.BrowserBookmark/Wox.Plugin.BrowserBookmark.csproj new file mode 100644 index 0000000000..f0a36fb65f --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/Wox.Plugin.BrowserBookmark.csproj @@ -0,0 +1,96 @@ + + + + + Debug + AnyCPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37} + Library + Properties + Wox.Plugin.BrowserBookmark + Wox.Plugin.BrowserBookmark + v4.5.2 + 512 + ..\..\ + true + + + + true + full + false + ..\..\Output\Debug\Plugins\Wox.Plugin.BrowserBookmark\ + DEBUG;TRACE + prompt + 4 + false + + + pdbonly + true + ..\..\Output\Release\Plugins\Wox.Plugin.BrowserBookmark\ + TRACE + prompt + 4 + false + + + + + + + ..\..\packages\System.Data.SQLite.Core.1.0.93.0\lib\net20\System.Data.SQLite.dll + True + + + + ..\..\packages\UnidecodeSharp.1.0.0.0\lib\net35\UnidecodeSharp.dll + True + + + + + + + + + + + + + + + PreserveNewest + + + + + Always + + + Always + + + Always + + + + + {4fd29318-a8ab-4d8f-aa47-60bc241b8da3} + Wox.Infrastructure + + + {8451ecdd-2ea4-4966-bb0a-7bbc40138e80} + Wox.Plugin + + + + + + \ No newline at end of file diff --git a/Plugins/Wox.Plugin.BrowserBookmark/app.config b/Plugins/Wox.Plugin.BrowserBookmark/app.config new file mode 100644 index 0000000000..90cf1cc290 --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/app.config @@ -0,0 +1,4 @@ + + + + diff --git a/Plugins/Wox.Plugin.BrowserBookmark/packages.config b/Plugins/Wox.Plugin.BrowserBookmark/packages.config new file mode 100644 index 0000000000..5abe85e328 --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Plugins/Wox.Plugin.BrowserBookmark/plugin.json b/Plugins/Wox.Plugin.BrowserBookmark/plugin.json new file mode 100644 index 0000000000..7d40e4bfc8 --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/plugin.json @@ -0,0 +1,12 @@ +{ + "ID":"0ECADE17459B49F587BF81DC3A125110", + "ActionKeyword":"b", + "Name":"Browser Bookmarks", + "Description":"Search your browser bookmarks", + "Author":"qianlifeng, Ioannis G.", + "Version":"1.1", + "Language":"csharp", + "Website":"http://www.getwox.com/plugin", + "ExecuteFileName":"Wox.Plugin.browserBookmark.dll", + "IcoPath":"Images\\bookmark.png" +} diff --git a/Plugins/Wox.Plugin.BrowserBookmark/x64/SQLite.Interop.dll b/Plugins/Wox.Plugin.BrowserBookmark/x64/SQLite.Interop.dll new file mode 100644 index 0000000000..877b4d78e6 Binary files /dev/null and b/Plugins/Wox.Plugin.BrowserBookmark/x64/SQLite.Interop.dll differ diff --git a/Plugins/Wox.Plugin.BrowserBookmark/x86/SQLite.Interop.dll b/Plugins/Wox.Plugin.BrowserBookmark/x86/SQLite.Interop.dll new file mode 100644 index 0000000000..ccb5e03b6b Binary files /dev/null and b/Plugins/Wox.Plugin.BrowserBookmark/x86/SQLite.Interop.dll differ diff --git a/Wox.sln b/Wox.sln index 69bfd7c94d..cbceab9b88 100644 --- a/Wox.sln +++ b/Wox.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.271 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Test", "Wox.Test\Wox.Test.csproj", "{FF742965-9A80-41A5-B042-D6C7D3A21708}" ProjectSection(ProjectDependencies) = postProject @@ -22,6 +22,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox", "Wox\Wox.csproj", "{D {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} {787B8AA6-CA93-4C84-96FE-DF31110AD1C4} = {787B8AA6-CA93-4C84-96FE-DF31110AD1C4} {F35190AA-4758-4D9E-A193-E3BDF6AD3567} = {F35190AA-4758-4D9E-A193-E3BDF6AD3567} + {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {9B130CC5-14FB-41FF-B310-0A95B6894C37} {FDED22C8-B637-42E8-824A-63B5B6E05A3A} = {FDED22C8-B637-42E8-824A-63B5B6E05A3A} {A3DCCBCA-ACC1-421D-B16E-210896234C26} = {A3DCCBCA-ACC1-421D-B16E-210896234C26} {049490F0-ECD2-4148-9B39-2135EC346EBE} = {049490F0-ECD2-4148-9B39-2135EC346EBE} @@ -71,6 +72,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.Shell", "Plugins EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.Calculator", "Plugins\Wox.Plugin.Calculator\Wox.Plugin.Calculator.csproj", "{59BD9891-3837-438A-958D-ADC7F91F6F7E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Wox.Plugin.BrowserBookmark", "Plugins\Wox.Plugin.BrowserBookmark\Wox.Plugin.BrowserBookmark.csproj", "{9B130CC5-14FB-41FF-B310-0A95B6894C37}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -298,6 +301,18 @@ Global {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x64.Build.0 = Release|Any CPU {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.ActiveCfg = Release|Any CPU {59BD9891-3837-438A-958D-ADC7F91F6F7E}.Release|x86.Build.0 = Release|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|x64.ActiveCfg = Debug|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|x64.Build.0 = Debug|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|x86.ActiveCfg = Debug|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Debug|x86.Build.0 = Debug|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|Any CPU.Build.0 = Release|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|x64.ActiveCfg = Release|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|x64.Build.0 = Release|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|x86.ActiveCfg = Release|Any CPU + {9B130CC5-14FB-41FF-B310-0A95B6894C37}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -316,5 +331,9 @@ Global {03FFA443-5F50-48D5-8869-F3DF316803AA} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {C21BFF9C-2C99-4B5F-B7C9-A5E6DDDB37B0} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} {59BD9891-3837-438A-958D-ADC7F91F6F7E} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} + {9B130CC5-14FB-41FF-B310-0A95B6894C37} = {3A73F5A7-0335-40D8-BF7C-F20BE5D0BA87} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {F26ACB50-3F6C-4907-B0C9-1ADACC1D0DED} EndGlobalSection EndGlobal