#!/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 notify2 import sys import shlex import re _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) notify2.init("sshplus") notify2.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 [] hostlist=open(_SSHMENU_FILE,"r").read() lines = hostlist.split("\n") lines.remove("items: ") #get rid of the first instance app_list = [] smflag=0 # Flag to ignore submenu title items smtitle="" # To hold the title while searching for parameters smparams="" # To hold parameters values stackMenuIndex = [] try: for line in lines: if re.search("title:",line): if smflag == 1: smtitle=line.split(":", 1)[1] continue smflag=1 smtitle=line.split(":", 1)[1] elif re.search("sshparams:",line): smparams=line.split(":", 1)[1] smflag=2 elif re.search("items:",line): app_list.append({ 'name': 'FOLDER', 'cmd': "SSHmenu", 'args':"", }) stackMenuIndex.append(len(app_list) - 1) elif re.search("type: menu",line): if smflag == 1: app_list[stackMenuIndex.pop()]["cmd"] = smtitle app_list.append({ 'name': 'FOLDER', 'cmd': "", 'args':"", }) smflag = 0 if smflag == 2: arglist = ("-x ssh " + smparams).split(" ") for a in arglist: if a == "": arglist.remove("") app_list.append({ 'name': smtitle, 'cmd': 'gnome-terminal', 'args': arglist, }) smflag=0 return app_list except: print "error in line:" + line return [] 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 app_list2 = get_sshmenuconfig() if app_list2 != []: app_list = app_list + ["sep",{'name': 'LABEL','cmd': "SSHmenu",'args': ''}] + app_list2 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("sshplu", "gnome-netstatus-tx", appindicator.CATEGORY_APPLICATION_STATUS) ind.set_label("Launch") ind.set_status(appindicator.STATUS_ACTIVE) appmenu = build_menu() ind.set_menu(appmenu) gtk.main()