3
# Copyright (c) 2011 David Calle <davidc@framli.eu>
4
# Copyright (c) 2011 Michael Hall <mhall119@gmail.com>
6
# This program is free software: you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation, either version 3 of the License, or
9
# (at your option) any later version.
11
# This program is distributed in the hope that it will be useful,
12
# but WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
# GNU General Public License for more details.
16
# You should have received a copy of the GNU General Public License
17
# along with this program. If not, see <http://www.gnu.org/licenses/>.
23
from gi.repository import GLib, GObject, Gio
24
from gi.repository import Dee
25
# FIXME: Some weird bug in Dee or PyGI makes Dee fail unless we probe
26
# it *before* we import the Unity module... ?!
27
_m = dir(Dee.SequenceModel)
28
from gi.repository import Unity
30
BUS_NAME = "net.launchpad.lens.sshsearch"
31
SSHCONFIG = os.path.join('~', '.ssh', 'config')
32
SSHCONFIG_EXPAND = os.path.expanduser(SSHCONFIG)
33
KNOWN_HOSTS = os.path.join('~', '.ssh', 'known_hosts')
34
KNOWN_HOSTS_EXPAND = os.path.expanduser(KNOWN_HOSTS)
35
TERMINAL_APP = 'gnome-terminal'
36
TERMINAL_APP_MIMETYPE = 'application-x-desktop'
37
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
42
# The path for the Lens *must* also match the one in our .lens file
43
self._lens = Unity.Lens.new ("/net/launchpad/lens/sshsearch", "sshsearch")
44
self._scope = Unity.Scope.new ("/net/launchpad/lens/sshsearch/main")
45
self._scope.connect ("notify::active-search", self.on_search_changed)
46
#self._scope.connect ("notify::active-global-search", self.on_search_changed)
47
self._scope.connect ("filters-changed", self.on_search_changed);
48
self._scope.connect ("notify::active", self.on_search_changed);
49
self._scope.connect ("activate-uri", self.on_activate_uri);
52
self._lens.props.search_hint = "SSH Scope"
53
self._lens.props.visible = True;
54
self._lens.props.search_in_global = False;
58
cats.append (Unity.Category.new (SSHCONFIG,
59
Gio.ThemedIcon.new(os.path.join(SCRIPT_DIR, 'sshsearch-lens.png')),
60
Unity.CategoryRenderer.VERTICAL_TILE))
61
cats.append (Unity.Category.new (KNOWN_HOSTS,
62
Gio.ThemedIcon.new(os.path.join(SCRIPT_DIR, 'sshsearch-lens.png')),
63
Unity.CategoryRenderer.VERTICAL_TILE))
64
self._lens.props.categories = cats
68
self._lens.props.filters = filters
70
self._lens.add_local_scope (self._scope);
73
self._config_hosts = self.__read_config()
74
self._known_hosts = self.__read_known_hosts()
75
print self._config_hosts
76
print self._known_hosts
78
def __read_config(self):
79
c = paramiko.SSHConfig()
80
c.parse(open(SSHCONFIG_EXPAND))
81
return [h['host'].lower() for h in c._config if h['host'] != '*']
83
def __read_known_hosts(self):
84
h = paramiko.HostKeys(KNOWN_HOSTS_EXPAND)
87
def on_search_changed (self, entry, *args):
88
search = self._scope.props.active_search or None
90
search_string = search.props.search_string.lower() or None
93
results = self._scope.props.results_model
94
self.update_results_model (search_string, results)
96
def update_results_model(self, search, model):
97
if search is None or search == '':
101
found = [host for host in self._config_hosts if host.find(search) >= 0]
103
model.append('ssh://config/%s' % host,
106
TERMINAL_APP_MIMETYPE,
109
found = [host for host in self._known_hosts if host.find(search) >= 0]
111
model.append('ssh://known_hosts/%s' % host,
114
TERMINAL_APP_MIMETYPE,
117
def on_activate_uri (self, scope, uri):
118
host = uri.split('/')[-1]
119
GLib.spawn_command_line_async('%s -e "ssh %s"' % (TERMINAL_APP, host))
120
return Unity.ActivationResponse(handled=Unity.HandledType.HIDE_DASH, goto_uri='')
123
if __name__ == "__main__":
124
session_bus_connection = Gio.bus_get_sync (Gio.BusType.SESSION, None)
125
session_bus = Gio.DBusProxy.new_sync (session_bus_connection, 0, None,
126
'org.freedesktop.DBus',
127
'/org/freedesktop/DBus',
128
'org.freedesktop.DBus', None)
129
result = session_bus.call_sync('RequestName',
130
GLib.Variant ("(su)", (BUS_NAME, 0x4)),
133
# Unpack variant response with signature "(u)". 1 means we got it.
134
result = result.unpack()[0]
137
print >> sys.stderr, "Failed to own name %s. Bailing out." % BUS_NAME
141
GObject.MainLoop().run()