Merge lp:~michaelaquilina/synapse-project/pass-plugin into lp:synapse-project
- pass-plugin
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 651 |
Proposed branch: | lp:~michaelaquilina/synapse-project/pass-plugin |
Merge into: | lp:synapse-project |
Diff against target: |
280 lines (+232/-0) 5 files modified
po/POTFILES.in (+1/-0) po/POTFILES.skip (+1/-0) src/plugins/Makefile.am (+1/-0) src/plugins/pass-plugin.vala (+228/-0) src/ui/synapse-main.vala (+1/-0) |
To merge this branch: | bzr merge lp:~michaelaquilina/synapse-project/pass-plugin |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Rico Tzschichholz | Approve | ||
Review via email: mp+281297@code.launchpad.net |
Commit message
Description of the change
Add pass (http://
See this launchpad question for more detail: https:/
Michael Aquilina (michaelaquilina) wrote : | # |
Rico Tzschichholz (ricotz) wrote : | # |
It looks straight forward.
There are bunch of indentation problems, codestyle issues with missing space before "(" and some TODOs.
Better hold the GFile reference to ".password_store" instead of creating it every time.
Michael Aquilina (michaelaquilina) wrote : | # |
Is there a style guide I could follow? I mainly code in python nowadays so I stuck to the conventions I'm used to.
Michael Aquilina (michaelaquilina) wrote : | # |
Looking at the diff, I now see what you're referring to in terms of indents. It looks fine in my editor funnily enough (atom). So I'm not sure what the issue is.
Michael Aquilina (michaelaquilina) wrote : | # |
The indentation issues should be solved. The issue was that piece of code was copied from somewhere to start off with and modified. It contained hard tabs which my editor tried to maintain. I changed it so that it uses "soft" tabs instead which seems to have worked.
Rico Tzschichholz (ricotz) wrote : | # |
Still a bunch of missing spaces before "(" like "printf(
Avoid removing a line from Makefile.am.
Review your strings. Use lower-cases as usual in english.
"Copy encrypted GPG password to clipboard" < this is OK.
"Copied %s Password to Clipboard" < this is not OK.
Not sure what you mean with "soft" tabs, but the synapse source doesn't use tabs only spaces.
Redo you branch in the end to make it consist of only one commit again.
Michael Aquilina (michaelaquilina) wrote : | # |
That extra line being removed is completely extra (it's automatically removed in all modern editors). I feel like that should be left there unless you see a strong reason to keep it? Most people contributing will experience it being removed automatically otherwise - which is minor but still annoying.
As for squashing the commits, I am completely aware of how to do this in git but I cant find any reference for how to do it in bzr. Is there anything you could point me at?
Rico Tzschichholz (ricotz) wrote : | # |
Still some "(" :\
Better call list_passwords() when it would provide actual changes. E.g. on start up and on changes while using a GFileMonitor on the folder.
Regarding "standard::*" only retrieve the information you need, so standard::name and stardard::type.
You will need to use bzr uncommit multiple times and bzr commit again afterwards.
I really dislike when unrelated code-portions are touched for no reason. So please keep the line.
Michael Aquilina (michaelaquilina) wrote : | # |
* Passwords only listed on activation and on changed event in password directory
* Only 'name' 'type' and 'content-type' retrieved by enumerate_children
* Fixed more styling issues
* Reverted removal of extra line at the end of MakeFile.am
Ready for review!
Michael Aquilina (michaelaquilina) wrote : | # |
Hi, is there anything I need to do/fix?
Rico Tzschichholz (ricotz) wrote : | # |
No need for password_monitor and password_store to be public properties, private fields are sufficient.
file.resolve_
if the ordering of file-list is not important then GLib.List.prepend() is sufficient and faster.
Decide between "... password %s" or "... %s password".
Michael Aquilina (michaelaquilina) wrote : | # |
Fixed according to your review comments
Rico Tzschichholz (ricotz) wrote : | # |
No need for password_monitor and password_store to be *properties*, make them *fields*.
Michael Aquilina (michaelaquilina) wrote : | # |
Sorry about that, didnt realise you also meant I should convert the properties to fields. Fixed.
Michael Aquilina (michaelaquilina) wrote : | # |
I seem to have found a bug using FileMonitor, it (obviously now thinking about it) does not recursively track changes in children directories and for that reasons changes to for example "Web/SomeSite/ABC" will not be reflected in the code. Is there a standard approach for dealing with tracking of folders recursively or will I have to write this myself?
Rico Tzschichholz (ricotz) wrote : | # |
Look into using http://
Michael Aquilina (michaelaquilina) wrote : | # |
Fixed the file monitoring bug. Should be good for merging now.
Michael Aquilina (michaelaquilina) wrote : | # |
@Rico is there anything that needs fixing?
Rico Tzschichholz (ricotz) wrote : | # |
https:/
I really don't like the plain re-invocation of update_passwords() you updating all password that is fine, but recreating all monitors without stopping/destroying them is a not good.
Also the infinite/
And use explicitly use GLib.File.
Michael Aquilina (michaelaquilina) wrote : | # |
Limiting the recursion depth to something as low as 2 or 3 wont really work since its common to have passwords organised at that depth. Why would recursing directories cause a problem though? You cant really have an infinite directory structure since following symbolic links is disabled.
I'll add disabling of previous file monitors if that's the main issue.
Michael Aquilina (michaelaquilina) wrote : | # |
I've adding code to cancel previous FileMonitors when they're no longer being used and the other requests you've made. I tried updating to code to avoid re-generating the monitors each time a change is detected. I attempted this by adding/removing a FileMoinitor on a changed event on a directory but this immediately became overly complicated.
If we believe using recursive FileMonitors is the best approach, then I feel like this is as good as it can get unless you know of an alternative?
Rico Tzschichholz (ricotz) wrote : | # |
Alright one more clean up and it should be fine.
- 637. By Michael Aquilina
-
Add pass plugin
Michael Aquilina (michaelaquilina) wrote : | # |
Updated according to your diff
Michael Aquilina (michaelaquilina) wrote : | # |
@Rico, does everything look good?
Michael Aquilina (michaelaquilina) wrote : | # |
Sorry to annoy you @Rico, but any chance you could give this a look some time?
Rico Tzschichholz (ricotz) : | # |
Preview Diff
1 | === modified file 'po/POTFILES.in' |
2 | --- po/POTFILES.in 2016-02-26 16:57:32 +0000 |
3 | +++ po/POTFILES.in 2016-03-03 11:50:29 +0000 |
4 | @@ -33,6 +33,7 @@ |
5 | src/plugins/launchpad-plugin.vala |
6 | src/plugins/locate-plugin.vala |
7 | src/plugins/opensearch.vala |
8 | +src/plugins/pass-plugin.vala |
9 | src/plugins/pastebin-plugin.vala |
10 | src/plugins/pidgin-plugin.vala |
11 | src/plugins/rhythmbox-plugin.vala |
12 | |
13 | === modified file 'po/POTFILES.skip' |
14 | --- po/POTFILES.skip 2016-02-26 16:57:32 +0000 |
15 | +++ po/POTFILES.skip 2016-03-03 11:50:29 +0000 |
16 | @@ -34,6 +34,7 @@ |
17 | src/plugins/launchpad-plugin.c |
18 | src/plugins/locate-plugin.c |
19 | src/plugins/opensearch.c |
20 | +src/plugins/pass-plugin.c |
21 | src/plugins/pastebin-plugin.c |
22 | src/plugins/pidgin-plugin.c |
23 | src/plugins/rhythmbox-plugin.c |
24 | |
25 | === modified file 'src/plugins/Makefile.am' |
26 | --- src/plugins/Makefile.am 2016-02-26 16:57:32 +0000 |
27 | +++ src/plugins/Makefile.am 2016-03-03 11:50:29 +0000 |
28 | @@ -45,6 +45,7 @@ |
29 | launchpad-plugin.vala \ |
30 | locate-plugin.vala \ |
31 | opensearch.vala \ |
32 | + pass-plugin.vala \ |
33 | pastebin-plugin.vala \ |
34 | pidgin-plugin.vala \ |
35 | rhythmbox-plugin.vala \ |
36 | |
37 | === added file 'src/plugins/pass-plugin.vala' |
38 | --- src/plugins/pass-plugin.vala 1970-01-01 00:00:00 +0000 |
39 | +++ src/plugins/pass-plugin.vala 2016-03-03 11:50:29 +0000 |
40 | @@ -0,0 +1,228 @@ |
41 | +/* |
42 | + * Copyright (C) 2015 Michael Aquilina <michaelaquilina@gmail.com> |
43 | + |
44 | + * This program is free software; you can redistribute it and/or modify |
45 | + * it under the terms of the GNU General Public License as published by |
46 | + * the Free Software Foundation; either version 2 of the License, or |
47 | + * (at your option) any later version. |
48 | + * |
49 | + * This program is distributed in the hope that it will be useful, |
50 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
51 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
52 | + * GNU General Public License for more details. |
53 | + * |
54 | + * You should have received a copy of the GNU General Public License |
55 | + * along with this program; if not, write to the Free Software |
56 | + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
57 | + * |
58 | + * Authored by Michael Aquilina <michaelaquilina@gmail.com> |
59 | + * |
60 | + */ |
61 | + |
62 | +namespace Synapse |
63 | +{ |
64 | + public class PassPlugin : Object, Activatable, ItemProvider |
65 | + { |
66 | + private class PassMatch : ActionMatch |
67 | + { |
68 | + public PassMatch (string password_name) |
69 | + { |
70 | + Object (title: password_name, |
71 | + description: _("Copy decrypted PGP password to clipboard"), |
72 | + has_thumbnail: false, icon_name: "dialog-password"); |
73 | + } |
74 | + |
75 | + public override void do_action () |
76 | + { |
77 | + Pid child_pid; |
78 | + int standard_output; |
79 | + int standard_error; |
80 | + |
81 | + try |
82 | + { |
83 | + Process.spawn_async_with_pipes (null, |
84 | + {"pass", "-c", this.title}, |
85 | + null, SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD, |
86 | + null, out child_pid, |
87 | + null, out standard_output, out standard_error |
88 | + ); |
89 | + ChildWatch.add (child_pid, (pid, status) => { |
90 | + Process.close_pid (pid); |
91 | + |
92 | + try |
93 | + { |
94 | + string message, icon_name; |
95 | + if (status == 0) { |
96 | + message = _("Copied %s password to clipboard").printf (this.title); |
97 | + icon_name = "dialog-password"; |
98 | + } else { |
99 | + message = _("Unable to decrypt %s password").printf (this.title); |
100 | + icon_name = "dialog-error"; |
101 | + } |
102 | + |
103 | + var notification = (Notify.Notification) Object.new ( |
104 | + typeof (Notify.Notification), |
105 | + summary: _("Synapse - Pass"), |
106 | + body: message, |
107 | + icon_name: icon_name, |
108 | + null |
109 | + ); |
110 | + notification.show (); |
111 | + } |
112 | + catch (Error err) { |
113 | + warning ("%s", err.message); |
114 | + } |
115 | + }); |
116 | + } |
117 | + catch (SpawnError err) { |
118 | + warning ("%s", err.message); |
119 | + } |
120 | + } |
121 | + } |
122 | + |
123 | + public bool enabled { get; set; default = true; } |
124 | + |
125 | + private File password_store; |
126 | + private List<string> passwords; |
127 | + private List<FileMonitor> monitors; |
128 | + |
129 | + public void activate () |
130 | + { |
131 | + password_store = File.new_for_path ( |
132 | + "%s/.password-store".printf (Environment.get_home_dir ()) |
133 | + ); |
134 | + update_passwords (); |
135 | + } |
136 | + |
137 | + public void deactivate () |
138 | + { |
139 | + } |
140 | + |
141 | + static void register_plugin () |
142 | + { |
143 | + PluginRegistry.get_default ().register_plugin ( |
144 | + typeof (PassPlugin), |
145 | + _("Pass Integration"), |
146 | + _("Quickly place passwords from your password store in the clipboard."), |
147 | + "dialog-password", |
148 | + register_plugin, |
149 | + Environment.find_program_in_path ("pass") != null, |
150 | + _("pass is not installed") |
151 | + ); |
152 | + } |
153 | + |
154 | + static construct |
155 | + { |
156 | + register_plugin (); |
157 | + } |
158 | + |
159 | + public bool handles_query (Query query) |
160 | + { |
161 | + return (QueryFlags.ACTIONS in query.query_type); |
162 | + } |
163 | + |
164 | + private void update_passwords () { |
165 | + foreach (unowned FileMonitor monitor in monitors) { |
166 | + monitor.cancel (); |
167 | + } |
168 | + monitors = null; |
169 | + |
170 | + try { |
171 | + monitors = activate_monitors (password_store); |
172 | + } catch (Error err) { |
173 | + warning ("Unable to monitor password directory: %s", err.message); |
174 | + } |
175 | + try { |
176 | + passwords = list_passwords (password_store, password_store); |
177 | + } catch (Error err) { |
178 | + warning ("Unable to list passwords: %s", err.message); |
179 | + } |
180 | + } |
181 | + |
182 | + private List<FileMonitor> activate_monitors (File directory) throws Error { |
183 | + List<FileMonitor> result = new List<FileMonitor> (); |
184 | + |
185 | + FileEnumerator enumerator = directory.enumerate_children ( |
186 | + FileAttribute.STANDARD_NAME + "," + |
187 | + FileAttribute.STANDARD_TYPE + "," + |
188 | + FileAttribute.STANDARD_IS_HIDDEN, |
189 | + FileQueryInfoFlags.NOFOLLOW_SYMLINKS, |
190 | + null |
191 | + ); |
192 | + |
193 | + FileMonitor monitor = directory.monitor_directory (FileMonitorFlags.NONE, null); |
194 | + monitor.set_rate_limit (500); |
195 | + monitor.changed.connect ((src, dest, event) => { |
196 | + message ("Detected a change (%s) in password store. Reloading", event.to_string ()); |
197 | + update_passwords (); |
198 | + }); |
199 | + result.append (monitor); |
200 | + |
201 | + FileInfo? info = null; |
202 | + while ((info = enumerator.next_file (null)) != null) { |
203 | + if (info.get_is_hidden ()) continue; |
204 | + |
205 | + var target_name = info.get_name (); |
206 | + File target_file = directory.get_child (target_name); |
207 | + if (info.get_file_type () == FileType.DIRECTORY) { |
208 | + result.concat (activate_monitors (target_file)); |
209 | + } |
210 | + } |
211 | + |
212 | + return result; |
213 | + } |
214 | + |
215 | + private List<string> list_passwords (File root, File directory) throws Error { |
216 | + List<string> result = new List<string> (); |
217 | + |
218 | + FileEnumerator enumerator = directory.enumerate_children ( |
219 | + FileAttribute.STANDARD_NAME + "," + |
220 | + FileAttribute.STANDARD_TYPE + "," + |
221 | + FileAttribute.STANDARD_CONTENT_TYPE, |
222 | + FileQueryInfoFlags.NOFOLLOW_SYMLINKS, |
223 | + null |
224 | + ); |
225 | + |
226 | + FileInfo? info = null; |
227 | + while ((info = enumerator.next_file (null)) != null) { |
228 | + File target_file = directory.get_child (info.get_name ()); |
229 | + if (info.get_file_type () == FileType.DIRECTORY) { |
230 | + result.concat (list_passwords (root, target_file)); |
231 | + } |
232 | + else if (info.get_content_type () == "application/pgp-encrypted") { |
233 | + var path = root.get_relative_path (target_file); |
234 | + result.prepend (path.replace (".gpg", "")); |
235 | + } |
236 | + } |
237 | + return result; |
238 | + } |
239 | + |
240 | + public async ResultSet? search (Query query) throws SearchError |
241 | + { |
242 | + var matchers = Query.get_matchers_for_query ( |
243 | + query.query_string, 0, |
244 | + RegexCompileFlags.OPTIMIZE | RegexCompileFlags.CASELESS |
245 | + ); |
246 | + |
247 | + var results = new ResultSet (); |
248 | + foreach (unowned string password in passwords) |
249 | + { |
250 | + foreach (var matcher in matchers) |
251 | + { |
252 | + if (matcher.key.match (password)) { |
253 | + results.add (new PassMatch (password), MatchScore.GOOD); |
254 | + break; |
255 | + } |
256 | + } |
257 | + } |
258 | + |
259 | + // make sure this method is called before returning any results |
260 | + query.check_cancellable (); |
261 | + if (results.size > 0) { |
262 | + return results; |
263 | + } else { |
264 | + return null; |
265 | + } |
266 | + } |
267 | + } |
268 | +} |
269 | |
270 | === modified file 'src/ui/synapse-main.vala' |
271 | --- src/ui/synapse-main.vala 2016-02-27 15:19:21 +0000 |
272 | +++ src/ui/synapse-main.vala 2016-03-03 11:50:29 +0000 |
273 | @@ -174,6 +174,7 @@ |
274 | typeof (ChromiumPlugin), |
275 | typeof (FileOpPlugin), |
276 | typeof (PidginPlugin), |
277 | + typeof (PassPlugin), |
278 | typeof (ChatActions), |
279 | typeof (ZealPlugin), |
280 | #if HAVE_ZEITGEIST |
Any comments on this? Is there an active maintainer to merge this in?