#!/usr/bin/python # -*- coding: utf-8 -*- # # SSHplus # A remote connect utlity, sshmenu compatible clone, and application starter. # # (C) 2011 Anil Gulecha # Based on sshlist, incorporating changes by Benjamin Heil's simplestarter # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # # Instructions # # 1. Copy file sshplus.py (this file) to /usr/local/bin # 2. Edit file .sshplus in your home directory to add menu entries, each # line in the format NAME|COMMAND|ARGS # 3. Launch sshplus.py # 4. Or better yet, add it to gnome startup programs list so it's run on login. import gobject import gtk import appindicator import os import pynotify import sys import shlex _VERSION = "1.0" _SETTINGS_FILE = os.getenv("HOME") + "/.sshplus" _SSHMENU_FILE = os.getenv("HOME") + "/.sshmenu" _ABOUT_TXT = """A simple application starter as appindicator. To add items to the menu, edit the file .sshplus in your home directory. Each entry must be on a new line in this format: NAME|COMMAND|ARGS If the item is clicked in the menu, COMMAND with arguments ARGS will be executed. ARGS can be empty. To insert a separator, add a line which only contains "sep". Lines starting with "#" will be ignored. You can set an unclickable label with the prefix "label:". Items from sshmenu configuration will be automatically added (except nested items). To insert a nested menu, use the prefix "folder:menu name". Subsequent items will be inserted in this menu, until a line containing an empty folder name is found: "folder:". After that, subsequent items get inserted in the parent menu. That means that more than one level of nested menus can be created. Example file: Show top|gnome-terminal|-x top sep # this is a comment label:SSH connections # create a folder named "Home" folder:Home SSH Ex|gnome-terminal|-x ssh user@1.2.3.4 # to mark the end of items inside "Home", specify and empty folder: folder: # this item appears in the main menu SSH Ex|gnome-terminal|-x ssh user@1.2.3.4 label:RDP connections RDP Ex|rdesktop|-T "RDP-Server" -r sound:local 1.2.3.4 Copyright 2011 Anil Gulecha Incorporating changes from simplestarter, Benjamin Heil, http://www.bheil.net Released under GPL3, http://www.gnu.org/licenses/gpl-3.0.html""" def menuitem_response(w, item): if item == '_about': show_help_dlg(_ABOUT_TXT) elif item == '_refresh': newmenu = build_menu() ind.set_menu(newmenu) pynotify.Notification("SSHplus refreshed", "Menu list was refreshed from %s" % _SETTINGS_FILE).show() elif item == '_quit': sys.exit(0) elif item == 'folder': pass else: print item os.spawnvp(os.P_NOWAIT, item['cmd'], [item['cmd']] + item['args']) os.wait3(os.WNOHANG) def show_help_dlg(msg, error=False): if error: dlg_icon = gtk.MESSAGE_ERROR else: dlg_icon = gtk.MESSAGE_INFO md = gtk.MessageDialog(None, 0, dlg_icon, gtk.BUTTONS_OK) try: md.set_markup("SSHplus %s" % _VERSION) md.format_secondary_markup(msg) md.run() finally: md.destroy() def add_separator(menu): separator = gtk.SeparatorMenuItem() separator.show() menu.append(separator) def add_menu_item(menu, caption, item=None): menu_item = gtk.MenuItem(caption) if item: menu_item.connect("activate", menuitem_response, item) else: menu_item.set_sensitive(False) menu_item.show() menu.append(menu_item) return menu_item def get_sshmenuconfig(): if not os.path.exists(_SSHMENU_FILE): return None hostlist=open(os.getenv("HOME")+"/.sshmenu","r").read() smenutitle=[] smenuparams=[] try: for line in hostlist.split("\n"): if line[2:7] == "title": smenutitle.append(line.split(":")[1]) if line[2:11] == "sshparams": smenuparams.append(line.split(":")[1]) if len(smenutitle) == len(smenuparams): if len(smenutitle) != 0: return (smenutitle,smenuparams) else: if len(smenuparams) != 0: return (smenuparams,smenuparams) return None except: return None def get_sshplusconfig(): if not os.path.exists(_SETTINGS_FILE): return [] app_list = [] f = open(_SETTINGS_FILE, "r") try: for line in f.readlines(): line = line.rstrip() if not line or line.startswith('#'): continue elif line == "sep": app_list.append('sep') elif line.startswith('label:'): app_list.append({ 'name': 'LABEL', 'cmd': line[6:], 'args': '' }) elif line.startswith('folder:'): app_list.append({ 'name': 'FOLDER', 'cmd': line[7:], 'args': '' }) else: try: name, cmd, args = line.split('|', 2) app_list.append({ 'name': name, 'cmd': cmd, 'args': [n.replace("\n", "") for n in shlex.split(args)], }) except ValueError: print "The following line has errors and will be ignored:\n%s" % line finally: f.close() return app_list def build_menu(): if not os.path.exists(_SETTINGS_FILE) and not os.path.exists(_SSHMENU_FILE) : show_help_dlg("ERROR: No .sshmenu or .sshplus file found in home directory\n\n%s" % \ _ABOUT_TXT, error=True) sys.exit(1) app_list = get_sshplusconfig() #Add sshmenu config items if any sshmenuitems = get_sshmenuconfig() if sshmenuitems != None: app_list.append("sep") app_list.append({ 'name': 'LABEL', 'cmd': "SSHmenu", 'args': '' }) titles=sshmenuitems[0] sshparams=sshmenuitems[1] for i in range(len(titles)): app_list.append({ 'name': titles[i], 'cmd': "gnome-terminal", 'args': [n.replace("\n", "") for n in shlex.split("-x ssh " + sshparams[i])], }) menu = gtk.Menu() menus = [menu] for app in app_list: if app == "sep": add_separator(menus[-1]) elif app['name'] == "FOLDER" and not app['cmd']: if len(menus) > 1: menus.pop() elif app['name'] == "FOLDER": menu_item = add_menu_item(menus[-1], app['cmd'], 'folder') menus.append(gtk.Menu()) menu_item.set_submenu(menus[-1]) elif app['name'] == "LABEL": add_menu_item(menus[-1], app['cmd'], None) else: add_menu_item(menus[-1], app['name'], app) add_separator(menu) add_menu_item(menu, 'Refresh', '_refresh') add_menu_item(menu, 'About', '_about') add_separator(menu) add_menu_item(menu, 'Quit', '_quit') return menu if __name__ == "__main__": ind = appindicator.Indicator("sshplus", "stock_connect", appindicator.CATEGORY_APPLICATION_STATUS) #ind.set_label("Launch") ind.set_status(appindicator.STATUS_ACTIVE) appmenu = build_menu() ind.set_menu(appmenu) gtk.main()