diff --git a/Plugins/Wox.Plugin.BrowserBookmark/ChromeBookmarks.cs b/Plugins/Wox.Plugin.BrowserBookmark/ChromeBookmarks.cs new file mode 100644 index 0000000000..521342014d --- /dev/null +++ b/Plugins/Wox.Plugin.BrowserBookmark/ChromeBookmarks.cs @@ -0,0 +1,108 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Wox.Infrastructure; + +namespace Wox.Plugin.BrowserBookmark +{ + public class ChromeBookmarks + { + private List bookmarks = new List(); + + public ChromeBookmarks() + { + bookmarks.Clear(); + LoadChromeBookmarks(); + + bookmarks = bookmarks.Distinct().ToList(); + } + + public List GetBookmarks(string search = null) + { + if (string.IsNullOrEmpty(search)) return bookmarks; + + var fuzzyMatcher = FuzzyMatcher.Create(search); + var returnList = bookmarks.Where(o => MatchProgram(o, fuzzyMatcher)).ToList(); + + return returnList; + } + + 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; + } + + 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..54567fa0c8 --- /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 queryBookmarks = @"SELECT url, title + FROM moz_places + WHERE id in ( + SELECT bm.fk FROM moz_bookmarks bm WHERE bm.fk NOT NULL + ) + AND ( url LIKE '%{0}%' OR title LIKE '%{0}%' ) + ORDER BY visit_count DESC + LIMIT 20 + "; + + private const string queryTopBookmarks = @"SELECT url, title + FROM moz_places + WHERE id in ( + SELECT bm.fk FROM moz_bookmarks bm WHERE bm.fk NOT NULL + ) + ORDER BY visit_count DESC + LIMIT 20 + "; + + private const string dbPathFormat = "Data Source ={0};Version=3;New=False;Compress=True;"; + + public List GetBookmarks(string search = null, bool top = false) + { + // Create the query command for the given case + string query = top ? queryTopBookmarks : string.Format(queryBookmarks, search); + + return GetResults(query); + } + + /// + /// Searches the places.sqlite db based on the given query and returns the results + /// + private List GetResults(string query) + { + // 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(query, 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 profilesPath = Environment.ExpandEnvironmentVariables(@"%appdata%\Mozilla\Firefox\Profiles\"); + var folders = new DirectoryInfo(profilesPath).GetDirectories().Select(x => x.FullName).ToList(); + + // Look for the default profile folder + return string.Format(@"{0}\places.sqlite", + folders.FirstOrDefault(d => File.Exists(d + @"\places.sqlite") && d.EndsWith(".default")) + ?? folders.First(d => File.Exists(d + @"\places.sqlite"))); + } + } + } + + 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/Main.cs b/Plugins/Wox.Plugin.BrowserBookmark/Main.cs index d0045ce56a..9c2a9aa4e5 100644 --- a/Plugins/Wox.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Wox.Plugin.BrowserBookmark/Main.cs @@ -1,9 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; using System.Linq; -using System.Text; -using System.Text.RegularExpressions; using Wox.Infrastructure; namespace Wox.Plugin.BrowserBookmark @@ -11,40 +7,37 @@ namespace Wox.Plugin.BrowserBookmark public class Main : IPlugin { private PluginInitContext context; - private List bookmarks = new List(); + + private ChromeBookmarks chromeBookmarks = new ChromeBookmarks(); + private FirefoxBookmarks mozBookmarks = new FirefoxBookmarks(); public void Init(PluginInitContext context) { - bookmarks.Clear(); - LoadChromeBookmarks(); - - bookmarks = bookmarks.Distinct().ToList(); this.context = context; } public List Query(Query query) { - if (query.ActionParameters.Count == 0) + 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 = new List(); + + // Add Firefox bookmarks + returnList.AddRange(mozBookmarks.GetBookmarks(param, topResults)); + // Add Chrome bookmarks + returnList.AddRange(chromeBookmarks.GetBookmarks(param)); + + if (!topResults) { - return bookmarks.Select(c => new Result() - { - Title = c.Name, - SubTitle = "Bookmark: " + c.Url, - IcoPath = @"Images\bookmark.png", - Score = 5, - Action = (e) => - { - context.HideApp(); - context.ShellRun(c.Url); - return true; - } - }).ToList(); + // Since we mixed chrome and firefox bookmarks, we should order them again + var fuzzyMatcher = FuzzyMatcher.Create(param); + returnList = returnList.Where(o => MatchProgram(o, fuzzyMatcher)).ToList(); + returnList = returnList.OrderByDescending(o => o.Score).ToList(); } - - - var fuzzyMather = FuzzyMatcher.Create(query.GetAllRemainingParameter()); - List returnList = bookmarks.Where(o => MatchProgram(o, fuzzyMather)).ToList(); - returnList = returnList.OrderByDescending(o => o.Score).ToList(); + return returnList.Select(c => new Result() { Title = c.Name, @@ -68,87 +61,5 @@ namespace Wox.Plugin.BrowserBookmark return false; } - - 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()); - } - - public string Name - { - get { return "Bookmarks"; } - } - - public string IcoPath - { - get { return @"Images\bookmark.png"; } - } - - public string Description - { - get { return "System workflow"; } - } - } } diff --git a/Plugins/Wox.Plugin.BrowserBookmark/Wox.Plugin.BrowserBookmark.csproj b/Plugins/Wox.Plugin.BrowserBookmark/Wox.Plugin.BrowserBookmark.csproj index 01add3401b..911e7e0ea2 100644 --- a/Plugins/Wox.Plugin.BrowserBookmark/Wox.Plugin.BrowserBookmark.csproj +++ b/Plugins/Wox.Plugin.BrowserBookmark/Wox.Plugin.BrowserBookmark.csproj @@ -11,6 +11,8 @@ Wox.Plugin.BrowserBookmark v3.5 512 + ..\..\ + true true @@ -34,6 +36,14 @@ + + False + ..\..\packages\System.Data.SQLite.Core.1.0.93.0\lib\net20\System.Data.SQLite.dll + + + False + ..\..\packages\System.Data.SQLite.Linq.1.0.93.0\lib\net20\System.Data.SQLite.Linq.dll + @@ -42,6 +52,8 @@ + + @@ -56,6 +68,8 @@ + + PreserveNewest @@ -64,8 +78,15 @@ Always + + Always + + + Always + +