20 Commits

Author SHA1 Message Date
ba9fa303ba newlines in readme 2018-04-23 22:23:54 +03:00
1b9acdcc4a first stable release 2018-04-23 22:07:22 +03:00
b7fdcf119d blank password for remove password field 2018-04-22 19:53:12 +03:00
b48081be65 fixed TypeError: must be str, not bytes with python3 2018-04-22 18:10:22 +03:00
32013bdb8d Add home/end handlers, some minor fixes 2018-04-22 15:02:36 +03:00
450b30b196 fixed 'selected' list out of range 2018-04-08 16:27:16 +03:00
8e930115a5 fixed add new group name string 2018-04-08 12:33:27 +03:00
ec8a2ca14a minor fixes 2018-04-08 12:23:29 +03:00
9c361bbe00 removed possibility to create alias and group with spaces 2018-04-08 12:12:04 +03:00
37a20a0c48 add cursor to curses 2018-04-08 11:31:20 +03:00
e430142fdc groups added 2018-04-05 00:31:37 +03:00
c8069554eb Merge pull request #5 from GHPS/ZSH-Completion
Added full ZSH completion
2018-01-20 11:02:54 +03:00
GHPS
6fdcf8ae6d Added full ZSH completion 2018-01-19 16:20:33 +01:00
c528af6414 Merge pull request #4 from GHPS/Better-Scriptability
Better Scriptability by Removing Unnecessary Comma in List Output
2017-11-22 12:15:07 +03:00
GS
9ea667f201 Better Scriptability by Removing Unnecessary Comma in List Output 2017-11-21 21:24:24 +01:00
2c8f072499 Merge pull request #3 from GHPS/master
Added Python 3.x support
2017-11-20 09:57:30 +03:00
GHPS
68bb493c44 Added Python 3.x support 2017-11-19 23:34:25 +01:00
e75a733d0b README fix 2017-09-13 12:05:17 +03:00
c057e1e8ed Adding autocompetion instructions to README and fixing lack of 'exec_string' parameter crashing 2017-09-13 12:03:00 +03:00
0266d29308 Merge pull request #2 from jeff-99/master
added bash autocompletion script for sshch aliases
2017-09-12 20:48:32 +03:00
6 changed files with 433 additions and 143 deletions

17
LICENSE
View File

@@ -4,27 +4,26 @@ sshch is released under the DWTW license
This program is free software; you can redistribute it and/or modify it under the terms of the Do What Thou Wilt License. This program is free software; you can redistribute it and/or modify it under the terms of the Do What Thou Wilt License.
DO WHAT THAU WILT Boundless Public License
DO WHAT THOU WILT
TO PUBLIC LICENSE TO PUBLIC LICENSE
Version 2.5 Version 2.55
Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it is allowed as long as the name is changed. Everyone is permitted to copy and distribute verbatim or modified copies of this license document, and changing it in full or in part is allowed without any restrictions.
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. Do what thou wilt shall be the whole of the Law. 0. Do what thou wilt shall be the whole of the Law.
Anyone is allowed to copy and distribute the copies of this license agreement in whole or in part, as well as modify it without any other limitations. DWTWL a license with a single requirement: DO WHAT THOU WILT
DWTW a license with a single requirement: DO WHAT THOU WILT
The license provides more freedom than any other one (such as GPL or BSD) and does not require saving the license text on copying. The license provides more freedom than any other one (such as GPL or BSD) and does not require saving the license text on copying.
DWTW an accomplished and eligible license for free text (including the software, documentation and artwork). DWTWL an accomplished and eligible license for free text, code and any other symbols (including the software, documentation and artwork).
The license does not contain "no warranty" clause. DWTW can be used in countries that do not legally acknowledge the transition to public domain. The license does not contain a "no warranty" clause. DWTWL can be used in countries that do not legally acknowledge the transition to public domain.
Summary: Summary:
An author-creator gives his or her source code to the world for free, without becoming distracted by worldly thinking regarding how and why the others will use it. An author-creator gives their source code to the world for free, without becoming distracted by worldly thinking regarding how and why the others will use it.

View File

@@ -1,25 +1,48 @@
SSH connection manager with curses interface SSH connection and aliases manager with curses and command line interface
====== ======
sshch is released under DWTWL 2.5 license sshch is released under DWTWL 2.55 license
sshch compatible with pyhon2 and python3, no additional libraries are required
### Screenshot ### Screenshot
![sshch](https://raw.githubusercontent.com/zlaxy/sshch/master/sshch_screenshot.png) ![sshch](https://raw.githubusercontent.com/zlaxy/sshch/master/sshch_screenshot.png)
### Installing ### Installing
To install for all users: **You can install a release version from pip:**
```bash
pip install sshch
``` ```
**Manual installation from the package or git repository also available:**
To install for all users:
```bash
sudo python setup.py install sudo python setup.py install
``` ```
To install just for current user: To install just for current user:
``` ```bash
mkdir ~/.local/bin mkdir ~/.local/bin
cp sshch/sshch ~/.local/bin/ cp sshch/sshch ~/.local/bin/
``` ```
### Using ### Using
To run curses interface: To run curses interface:
``` ```bash
sshch sshch
``` ```
To run command line help: To run command line help:
``` ```bash
sshch -h sshch -h
``` ```
**If you want to use unsafe 'password' feature you must install 'sshpass' first.** For exit from current ssh session press `Ctrl+D`.
**Additional Features**
- If you want to use unsafe 'password' feature you must install `sshpass` first.
- If you want to use bash autocompletion function with sshch, copy autocompletion script to /etc/bash_completion.d/:
```bash
sudo cp completion/sshch_bash_completion.sh /etc/bash_completion.d/sshch
```
(changes will come into effect with new bash session)
- If you want to use zsh autocompletion:
1) Place File in a Directory where ZSH can find it
-> Search Path is Stored in $fpath
-> echo $fpath
2) Rename File to '_sshch'

View File

@@ -2,7 +2,7 @@ _sshch_complete()
{ {
local cur_word alias_list local cur_word alias_list
cur_word="${COMP_WORDS[COMP_CWORD]}" cur_word="${COMP_WORDS[COMP_CWORD]}"
alias_list=`sshch -l | sed 's/,//g'` alias_list=`sshch -l`
COMPREPLY=($(compgen -W "$alias_list" -- $cur_word)) COMPREPLY=($(compgen -W "$alias_list" -- $cur_word))
return 0 return 0
} }

View File

@@ -0,0 +1,27 @@
#compdef sshch
#
# ZSH Completion for SSHCH
# Usage:
# 1) Place File in a Directory where ZSH can find it
# -> Search Path is Stored in $fpath
# -> echo $fpath
# 2) Rename File to '_sshch'
#
_arguments '::aliasname:->getAlias' \
'-e[Edit Alias]:aliasname:->getAlias' '--edit[Edit Alias]:aliasname:->getAlias' \
'-p[Set Password]:aliasname:->getAlias' '--password[Set Password]:aliasname:->getAlias' \
'-r[Remove Alias]:aliasname:->getAlias' '--remove[Remove Alias]:aliasname:->getAlias' \
'-k[Keep Connection]:aliasname:->getAlias' '--keep[Keep Connection]:aliasname:->getAlias' \
'-a[Add Alias]' '--add[Add Alias]'\
'-c[Add Command for Executing Alias]' '--command[Add Command for Executing Alias]'\
'-h[Show Help Message]' '--help[Show Help Message]'\
'-l[List Existing Alias]' '--list[List Existing Alias]'\
'-f[List Existing Alias with Connection String]' '--fulllist[List Existing Alias with Connection String]'\
'--version[Show Program Version]'
case "$state" in
getAlias)
local -a alias_list
alias_list=($(sshch -l))
_values -s ' ' 'Aliases' $alias_list
;;
esac

View File

@@ -7,22 +7,27 @@ def main():
setup(name='sshch', setup(name='sshch',
author='zlaxy', author='zlaxy',
author_email='zlaxyi@gmail.com',
url='https://github.com/zlaxy/sshch/', url='https://github.com/zlaxy/sshch/',
description='Ssh connection manager', description='Ssh connection and aliases manager',
license='DWTWL 2.5', long_description='SSH connection and aliases manager with curses and command line interface',
version='0.7', long_description_content_type='text/x-rst',
license='DWTWL 2.55',
version='1.0',
py_modules=['sshch'], py_modules=['sshch'],
scripts=['sshch/sshch'], scripts=['sshch/sshch'],
keywords='sshch ssh aliases manager',
python_requires='>=2.6, !=3.0.*, !=3.1.*, !=3.2.*, <4',
# http://pypi.python.org/pypi?%3Aaction=list_classifiers # http://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[ classifiers=[
'Development Status :: 4 - Beta', 'Development Status :: 5 - Production/Stable',
'Environment :: Console :: Curses', 'Environment :: Console :: Curses',
'Intended Audience :: System Administrators', 'Intended Audience :: System Administrators',
'License :: Freeware', 'License :: Freeware',
'Natural Language :: English', 'Natural Language :: English',
'Operating System :: POSIX', 'Operating System :: POSIX',
'Programming Language :: Python :: 2.7', 'Programming Language :: Python',
'Topic :: Internet', 'Topic :: Internet',
'Topic :: System :: Networking', 'Topic :: System :: Networking',
'Topic :: System :: Systems Administration', 'Topic :: System :: Systems Administration',

View File

@@ -1,24 +1,30 @@
#!/usr/bin/env python #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import division from __future__ import division
from __future__ import print_function
try:
import configparser
except ImportError:
import ConfigParser as configparser # Python 2.x import
from os import path from os import path
from sys import argv from sys import argv
from math import ceil from math import ceil
from getpass import getpass from getpass import getpass
from optparse import OptionParser from optparse import OptionParser
from curses import textpad, panel
import ConfigParser
import subprocess import subprocess
import base64 import base64
import curses
import time import time
import curses
from curses import textpad, panel
# https://github.com/zlaxy/sshch # https://github.com/zlaxy/sshch
version = "0.7" version = "1.0"
# path to conf file, default: ~/.config/sshch.conf # path to conf file, default: ~/.config/sshch.conf
conf_file = path.expanduser("~") + '/.config/sshch.conf' conf_file = path.expanduser("~") + '/.config/sshch.conf'
# expand groups by default
expand_default = True
def AddNewAlias(alias): def AddNewAlias(alias):
@@ -27,7 +33,7 @@ def AddNewAlias(alias):
conf.write(open(conf_file, "w")) conf.write(open(conf_file, "w"))
return True return True
else: else:
return "error: '" + alias + "' already exists" return "error: '" + alias + "' alias or group already exists"
def SetAliasString(alias, string): def SetAliasString(alias, string):
@@ -35,9 +41,19 @@ def SetAliasString(alias, string):
conf.write(open(conf_file, "w")) conf.write(open(conf_file, "w"))
def SetGroupString(alias, string):
conf.set(alias, "group", string)
conf.write(open(conf_file, "w"))
def SetPassword(alias, string): def SetPassword(alias, string):
if string == "" or string == b'':
conf.remove_option(alias, "password")
else:
string = string.encode()
string = base64.b64encode(base64.b16encode( string = base64.b64encode(base64.b16encode(
base64.b32encode(string))) base64.b32encode(string)))
string = string.decode('utf-8')
conf.set(alias, "password", string) conf.set(alias, "password", string)
conf.write(open(conf_file, "w")) conf.write(open(conf_file, "w"))
@@ -53,16 +69,24 @@ def ConnectAlias(alias, command=False):
if conf.has_option(alias, "password"): if conf.has_option(alias, "password"):
password = base64.b32decode(base64.b16decode( password = base64.b32decode(base64.b16decode(
base64.b64decode(conf.get(alias, "password")))) base64.b64decode(conf.get(alias, "password"))))
exec_string = 'sshpass -p "' + password + '" ' exec_string = 'sshpass -p "' + password.decode('utf-8') + '" '
if conf.has_option(alias, "exec_string"):
exec_string = exec_string + conf.get(alias, "exec_string") exec_string = exec_string + conf.get(alias, "exec_string")
if command: if command:
exec_string = exec_string + " " + command exec_string = exec_string + " " + command
# Variables bellow is newer used
subprocess.Popen(exec_string, shell=True).communicate()[0] subprocess.Popen(exec_string, shell=True).communicate()[0]
def HoldConnection(alias): def HoldConnection(alias):
print "Connecting to " + alias + ". Press CTRL+C to cancel." groups = []
connectaliases = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
if alias in groups:
print("Can't hold connection with group.")
else:
print("Connecting to " + alias + ". Press CTRL+C to cancel.")
time.sleep(1) time.sleep(1)
while True: while True:
ConnectAlias(alias) ConnectAlias(alias)
@@ -70,88 +94,185 @@ def HoldConnection(alias):
def CMDAdd(alias): def CMDAdd(alias):
alias = alias.split()[0].strip()
result = AddNewAlias(alias) result = AddNewAlias(alias)
if result: if result == True:
prompt_add = ("".join(["Enter connection string for new alias ", prompt_add = ("".join(["Enter connection string for new alias ",
"(example: ssh user@somehost.com):\n"])) "(example: ssh user@somehost.com):\n"]))
string = "" string = ""
while string == "": while string == "":
string = raw_input(prompt_add) string = input(prompt_add)
SetAliasString(alias, string) SetAliasString(alias, string)
else: else:
print result print(result)
def CMDGroup(group):
group = group.split()[0].strip()
result = AddNewAlias(group)
if result == True:
prompt_add = ("".join(["Enter aliases for new group ",
"(example: alias1 alias2):\n"]))
string = ""
while string == "":
string = input(prompt_add)
SetGroupString(group, string)
else:
print(result)
def CMDEdit(alias): def CMDEdit(alias):
if conf.has_section(alias): if conf.has_section(alias):
if conf.has_option(alias, "exec_string"):
prompt_edit = ("".join(["Enter connection string for existing alias ", prompt_edit = ("".join(["Enter connection string for existing alias ",
"(example: ssh user@somehost.com):\n"])) "(example: ssh user@somehost.com):\n"]))
string = "" string = ""
while string == "": while string == "":
string = raw_input(prompt_edit) string = input(prompt_edit)
SetAliasString(alias, string) SetAliasString(alias, string)
elif conf.has_option(alias, "group"):
prompt_edit = ("".join(["Enter aliases for existing group ",
"(example: alias1 alias2):\n"]))
string = ""
while string == "":
string = input(prompt_edit)
SetGroupString(alias, string)
else: else:
print "error: '" + alias + "' alias is not exists" print("error: '" + alias + "' is not correct alias or group")
else:
print("error: '" + alias + "' alias or group does not exists")
def CMDPassword(alias): def CMDPassword(alias):
groups = []
connectaliases = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
if alias in groups:
print("Can't set password for group.")
else:
if conf.has_section(alias): if conf.has_section(alias):
prompt_pass = ("[UNSAFE] Enter password for sshpass: ") prompt_pass = ("[UNSAFE] Enter password for sshpass (Ctrl+C - cancel, blank - clear password):\n")
string = "" string = ""
string = getpass(prompt_pass) string = getpass(prompt_pass)
if not string == "":
SetPassword(alias, string) SetPassword(alias, string)
else: else:
print "error: '" + alias + "' alias is not exists" print("error: '" + alias + "' alias does not exists")
def CMDRemove(alias): def CMDRemove(alias):
if conf.has_section(alias): if conf.has_section(alias):
prompt_remove = ("Type 'yes' if you sure to remove '" + alias + "' alias: ") prompt_remove = ("Type 'yes' if you sure to remove '" + alias + "' alias or group: ")
string = raw_input(prompt_remove) string = input(prompt_remove)
if string == "yes": if string == "yes":
RemoveAliases([alias]) RemoveAliases([alias])
else: else:
print "'" + alias + "' alias was not deleted." print("'" + alias + "' alias or group was not deleted.")
else: else:
print "error: '" + alias + "' alias is not exists." print("error: '" + alias + "' alias or group does not exists.")
def CMDConnect(aliases, command=False): def CMDConnect(aliases, command=False):
groups = []
connectaliases = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
for alias in aliases: for alias in aliases:
if conf.has_section(alias): if alias in groups:
print "Connecting to " + alias + "..." group_aliases = conf.get(alias, "group").split()
ConnectAlias(alias, command) for ga in group_aliases:
print "... " + alias + " session finished." if not ga in connectaliases:
connectaliases.append(ga)
else: else:
print "error: '" + alias + "' alias is not exists" if not alias in connectaliases:
connectaliases.append(alias)
for alias in connectaliases:
if conf.has_section(alias):
print("Connecting to " + alias + "...")
ConnectAlias(alias, command)
print("... " + alias + " session finished.")
else:
print("error: '" + alias + "' alias does not exists")
def CMDList(option, opt, value, parser): def CMDList(option, opt, value, parser):
print ', '.join(str(p) for p in conf.sections()) print(' '.join(str(p) for p in conf.sections()))
def GetTreeList(strings=True, expandlist=True):
aliases = []
groups = []
resultalias = []
resultstring = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
elif conf.has_option(a, "exec_string"):
aliases.append(a)
for g in groups:
resultalias.append(g)
resultstring.append(">> "+g)
group_aliases = conf.get(g, "group").split()
for ga in group_aliases:
if expandlist == False:
pass
elif expandlist == True or g in expandlist:
if conf.has_option(ga, "exec_string"):
resultalias.append(ga+" "+g)
result = "".join([" ", str(ga), " (", (conf.get(ga, "exec_string") if
conf.has_option(ga, "exec_string") else ""), ")",
(" [password]" if conf.has_option(ga, "password") else "")])
resultstring.append(result)
try:
aliases.remove(ga)
except ValueError:
pass
for a in aliases:
resultalias.append(a)
result = "".join([str(a), " (", (conf.get(a, "exec_string") if
conf.has_option(a, "exec_string") else ""), ")",
(" [password]" if conf.has_option(a, "password") else "")])
resultstring.append(result)
if strings:
return resultstring;
else:
return resultalias;
def CMDFullList(option, opt, value, parser): def CMDFullList(option, opt, value, parser):
for p in conf.sections(): for p in GetTreeList():
to_print = "".join([str(p), " - ", conf.get(p, "exec_string"), print (p)
(" [password]" if conf.has_option(p, "password") else ""), "\n"])
print(to_print)
def CursesConnect(screen, aliases, command=False): def CursesConnect(screen, aliases, command=False):
curses.endwin() curses.endwin()
groups = []
connectaliases = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
for alias in aliases: for alias in aliases:
print "Connecting to " + alias + "..." if alias in groups:
group_aliases = conf.get(alias, "group").split()
for ga in group_aliases:
if not ga in connectaliases:
connectaliases.append(ga)
else:
if not alias in connectaliases:
connectaliases.append(alias)
for alias in connectaliases:
print("Connecting to " + alias + "...")
ConnectAlias(alias, command) ConnectAlias(alias, command)
print "... " + alias + " session finished." print("... " + alias + " session finished.")
print "Press 'enter' to continue." print("Press 'enter' to continue.")
screen.getch() screen.getch()
def CursesExit(error=False): def CursesExit(error=False):
curses.endwin() curses.endwin()
if error: if error:
print error print(error)
exit() exit()
@@ -210,7 +331,7 @@ def CursesPanel(screen, h, w, y, x, text,
position = position - 1 position = position - 1
hidden_password = hidden_password[0:-1] hidden_password = hidden_password[0:-1]
if keych > 31 and keych < 127: if keych > 31 and keych < 127:
hidden_password += curses.keyname(keych) hidden_password += curses.keyname(keych).decode('utf-8')
sub_window.addstr(1, position, "*", text_colorpair) sub_window.addstr(1, position, "*", text_colorpair)
if position < w - 4: if position < w - 4:
position += 1 position += 1
@@ -253,24 +374,29 @@ def CMDOptions():
opts.add_option('-a', '--add', action="store", type="string", opts.add_option('-a', '--add', action="store", type="string",
dest="add", metavar="alias", default=False, dest="add", metavar="alias", default=False,
help="add new alias for connection string") help="add new alias for connection string")
opts.add_option('-g', '--group', action="store", type="string",
dest="group", metavar="group", default=False,
help="add new group for aliases")
opts.add_option('-c', '--command', action="store", type="string", opts.add_option('-c', '--command', action="store", type="string",
dest="command", metavar="command", default=False, dest="command", metavar="command", default=False,
help="add command for executing alias") help="execute command for aliases or group")
opts.add_option('-k', '--keep', action="store", type="string", opts.add_option('-k', '--keep', action="store", type="string",
dest="keep", metavar="alias", default=False, dest="keep", metavar="alias", default=False,
help="hold connection with specified alias") help="hold connection with specified alias")
opts.add_option('-e', '--edit', action="store", type="string", opts.add_option('-e', '--edit', action="store", type="string",
dest='edit', metavar="alias", default=False, dest='edit', metavar="alias", default=False,
help="edit existing connection string") help="edit existing alias or group")
opts.add_option('-p', '--password', action="store", type="string", opts.add_option('-p', '--password', action="store", type="string",
dest='password', metavar="alias", default=False, dest='password', metavar="alias", default=False,
help="set and store password for sshpass [UNSAFE]") help="set and store password for sshpass [UNSAFE]")
opts.add_option('-r', '--remove', action="store", type="string", opts.add_option('-r', '--remove', action="store", type="string",
dest='remove', metavar="alias", default=False, dest='remove', metavar="alias", default=False,
help="remove existing alias of connection string") help="remove existing alias or group")
options, alias = opts.parse_args() options, alias = opts.parse_args()
if options.add: if options.add:
CMDAdd(options.add) CMDAdd(options.add)
if options.group:
CMDGroup(options.group)
if options.edit: if options.edit:
CMDEdit(options.edit) CMDEdit(options.edit)
if options.password: if options.password:
@@ -288,19 +414,33 @@ def CMDOptions():
def CursesMain(): def CursesMain():
help_screen = ("".join([" Press:\n", help_screen = ("".join([" Press:\n",
" 'z'/'x' or arrows - navigation\n", " 'z'/'x' or arrows - navigation\n",
" 'a'/'F2' - add new alias\n", " 'a'/'F2' - add new alias (without spaces)\n",
" 'e'/'F4' - edit existing alias\n", " 'g'/'F5' - add new group (spaces will be stripped)\n",
" 'e'/'F4' - edit existing alias/group\n",
" 'p'/'F6' - set alias's password for sshpass [UNSAFE]\n", " 'p'/'F6' - set alias's password for sshpass [UNSAFE]\n",
" 'space'/'insert' - select\n", " 'space'/'insert' - select\n",
" 'r'/'F8' - remove selected alias/aliases\n", " 'r'/'F8' - remove selected alias/aliases\n",
" 'c'/'F3' - execute specific command with selected alias/aliases\n", " 'c'/'F3' - execute specific command with selected alias/aliases\n",
" 'k'/'F7' - hold connection with selected alias\n", " 'k'/'F7' - hold connection with selected alias\n",
" 'enter'/'F9' - connect to selected alias/aliases\n", " 'enter'/'F9' - connect to selected alias/aliases,\n",
" expand/collapse group\n",
" 'q'/'F10' - quit\n", " 'q'/'F10' - quit\n",
" Run program with '--help' option to view command line help.\n", " Run program with '--help' option to view command line help.\n",
" Also, you can edit config file manually:\n", " Also, you can edit config file manually:\n",
" ", conf_file])) " ", conf_file]))
strings = conf.sections() if expand_default == True:
groups = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
expanded = groups
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
elif expand_default == False:
groups = []
expanded = groups
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings) row_num = len(strings)
selected_strings = [" " for i in range(0, row_num + 1)] selected_strings = [" " for i in range(0, row_num + 1)]
screen = curses.initscr() screen = curses.initscr()
@@ -327,13 +467,7 @@ def CursesMain():
box.addnstr(1, 1, "There aren't any aliases yet. Press 'a' to add new one.", box.addnstr(1, 1, "There aren't any aliases yet. Press 'a' to add new one.",
width - 6, highlight_text) width - 6, highlight_text)
else: else:
if conf.has_option(strings[i - 1], "password"): exec_string = ["[", selected_strings[i], "] ", stringsfull[i - 1]]
password = " [password]"
else:
password = ""
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
strings[i - 1], " (", conf.get(strings[i - 1], "exec_string"),
")", password]
if (i == position): if (i == position):
box.addnstr(i, 2, "".join(exec_string), width - 6, highlight_text) box.addnstr(i, 2, "".join(exec_string), width - 6, highlight_text)
else: else:
@@ -354,53 +488,125 @@ def CursesMain():
help_screen, normal_text, highlight_text) help_screen, normal_text, highlight_text)
if key_pressed == ord('a') or key_pressed == ord( if key_pressed == ord('a') or key_pressed == ord(
'A') or key_pressed == curses.KEY_F2: 'A') or key_pressed == curses.KEY_F2:
curses.curs_set(1)
new_alias_textpad = CursesTextpad(screen, 1, width - 8, new_alias_textpad = CursesTextpad(screen, 1, width - 8,
(height // 2) - 1, 4, "Enter new alias:", "", (height // 2) - 1, 4, "Enter new alias:", "",
normal_text, highlight_text) normal_text, highlight_text)
add_alias = new_alias_textpad.edit(CursesTextpadConfirm) add_alias = new_alias_textpad.edit(CursesTextpadConfirm)
if not add_alias.rstrip() == "": add_alias = add_alias.split()[0].strip()
add_result = AddNewAlias(add_alias.rstrip()) if not add_alias == "":
if not add_result: add_result = AddNewAlias(add_alias)
CursesPanel(screen, 3, if add_result == True:
width - 6, (height // 2) - 1, 3, add_result,
normal_text, highlight_text)
else:
add_string = "" add_string = ""
while add_string.rstrip() == "": while add_string == "":
string_textpad = CursesTextpad(screen, 3, string_textpad = CursesTextpad(screen, 3,
width - 8, (height // 2) - 1, 4, width - 8, (height // 2) - 1, 4,
"Enter full execution string:", "Enter full execution string:",
"ssh ", normal_text, highlight_text) "ssh ", normal_text, highlight_text)
add_string = string_textpad.edit( add_string = string_textpad.edit(
CursesTextpadConfirm) CursesTextpadConfirm)
SetAliasString(add_alias.rstrip(), SetAliasString(add_alias,
add_string.replace("\n", "").rstrip()) add_string.replace("\n", "").rstrip())
strings = conf.sections() strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings) row_num = len(strings)
selected_strings.append(" ") selected_strings.append(" ")
pages = int(ceil(row_num / max_row)) pages = int(ceil(row_num / max_row))
box.refresh() box.refresh()
else:
curses.curs_set(0)
CursesPanel(screen, 3,
width - 6, (height // 2) - 1, 3, add_result,
normal_text, highlight_text)
curses.curs_set(0)
if key_pressed == ord('g') or key_pressed == ord(
'G') or key_pressed == curses.KEY_F5:
curses.curs_set(1)
new_group_textpad = CursesTextpad(screen, 1, width - 8,
(height // 2) - 1, 4, "Enter group name (without spaces):", "",
normal_text, highlight_text)
add_group = new_group_textpad.edit(CursesTextpadConfirm)
add_group = add_group.split()[0].strip()
if not add_group == "":
add_result = AddNewAlias(add_group)
if add_result == True:
add_string = ""
while add_string.rstrip() == "":
string_textpad = CursesTextpad(screen, 3,
width - 8, (height // 2) - 1, 4,
"Enter aliases for new group (example: alias1 alias2):",
"", normal_text, highlight_text)
add_string = string_textpad.edit(
CursesTextpadConfirm)
SetGroupString(add_group,
add_string.replace("\n", "").rstrip())
expanded.append(add_group)
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings)
selected_strings = [" " for i in range(0, row_num + 1)]
pages = int(ceil(row_num / max_row))
box.refresh()
else:
curses.curs_set(0)
CursesPanel(screen, 3,
width - 6, (height // 2) - 1, 3, add_result,
normal_text, highlight_text)
curses.curs_set(0)
if (key_pressed == ord('e') or key_pressed == ord( if (key_pressed == ord('e') or key_pressed == ord(
'E') or key_pressed == curses.KEY_F4) and row_num != 0: 'E') or key_pressed == curses.KEY_F4) and row_num != 0:
edit_string = "" edit_string = ""
groups = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
curses.curs_set(1)
if strings[position - 1] in groups:
while edit_string.rstrip() == "":
string_textpad = CursesTextpad(screen, 3, width - 8,
(height // 2) - 1, 4, "Enter new aliases for existing group:",
(conf.get(strings[position - 1],
"group") if conf.has_option(strings[position - 1],
"group") else ""),
normal_text, highlight_text)
edit_string = string_textpad.edit(CursesTextpadConfirm)
SetGroupString(strings[position - 1],
edit_string.replace("\n", "").rstrip())
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings)
selected_strings.append(" ")
pages = int(ceil(row_num / max_row))
box.refresh()
else:
while edit_string.rstrip() == "": while edit_string.rstrip() == "":
string_textpad = CursesTextpad(screen, 3, width - 8, string_textpad = CursesTextpad(screen, 3, width - 8,
(height // 2) - 1, 4, "Enter new execution string:", (height // 2) - 1, 4, "Enter new execution string:",
conf.get(strings[position - 1], "exec_string"), (conf.get(strings[position - 1].split()[0].strip(),
"exec_string") if conf.has_option(strings[position - 1].split()[0].strip(),
"exec_string") else ""),
normal_text, highlight_text) normal_text, highlight_text)
edit_string = string_textpad.edit(CursesTextpadConfirm) edit_string = string_textpad.edit(CursesTextpadConfirm)
SetAliasString(strings[position - 1], SetAliasString(strings[position - 1].split()[0].strip(),
edit_string.replace("\n", "").rstrip()) edit_string.replace("\n", "").rstrip())
strings = conf.sections() strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
curses.curs_set(0)
if (key_pressed == ord('p') or key_pressed == ord( if (key_pressed == ord('p') or key_pressed == ord(
'P') or key_pressed == curses.KEY_F6) and row_num != 0: 'P') or key_pressed == curses.KEY_F6) and row_num != 0:
groups = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
if not strings[position - 1].split()[0].strip() in groups:
set_password = "" set_password = ""
set_password = CursesPanel(screen, 4, width - 6, set_password = CursesPanel(screen, 4, width - 6,
(height // 2) - 1, 3, (height // 2) - 1, 3,
" Enter user password for sshpass and press 'enter':\n>", " Enter user password for sshpass and press 'enter':\n>",
normal_text, highlight_text, "password") normal_text, highlight_text, "password")
if not set_password == "": SetPassword(strings[position - 1].split()[0].strip(), set_password)
SetPassword(strings[position - 1], set_password) strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
if (key_pressed == ord('r') or key_pressed == ord( if (key_pressed == ord('r') or key_pressed == ord(
'R') or key_pressed == curses.KEY_F8 or key_pressed == ( 'R') or key_pressed == curses.KEY_F8 or key_pressed == (
curses.KEY_DC)) and row_num != 0: curses.KEY_DC)) and row_num != 0:
@@ -414,14 +620,15 @@ def CursesMain():
str(len(selected)), " selected aliases? (y/N)"])) str(len(selected)), " selected aliases? (y/N)"]))
else: else:
remove_confirm = ("".join(["Are you sure to remove '", remove_confirm = ("".join(["Are you sure to remove '",
strings[position - 1], "' alias? (y/N)"])) strings[position - 1].split()[0].strip(), "' alias? (y/N)"]))
selected.append(strings[position - 1]) selected.append(strings[position - 1].split()[0].strip())
remove_result = CursesPanel(screen, 4, width - 6, remove_result = CursesPanel(screen, 4, width - 6,
(height // 2) - 1, 3, remove_confirm, normal_text, (height // 2) - 1, 3, remove_confirm, normal_text,
highlight_text, "remove") highlight_text, "remove")
if remove_result == "confirm": if remove_result == "confirm":
RemoveAliases(selected) RemoveAliases(selected)
strings = conf.sections() strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings) row_num = len(strings)
selected_strings = [" " for i in range(0, row_num + 1)] selected_strings = [" " for i in range(0, row_num + 1)]
pages = int(ceil(row_num / max_row)) pages = int(ceil(row_num / max_row))
@@ -435,7 +642,8 @@ def CursesMain():
if selected_strings[i] == "*": if selected_strings[i] == "*":
selected.append(strings[i - 1]) selected.append(strings[i - 1])
if not len(selected) > 0: if not len(selected) > 0:
selected.append(strings[position - 1]) selected.append(strings[position - 1].split()[0].strip())
curses.curs_set(1)
command_textpad = CursesTextpad(screen, 3, width - 8, command_textpad = CursesTextpad(screen, 3, width - 8,
(height // 2) - 1, 4, (height // 2) - 1, 4,
"".join([ "".join([
@@ -445,19 +653,42 @@ def CursesMain():
command_string = command_textpad.edit(CursesTextpadConfirm) command_string = command_textpad.edit(CursesTextpadConfirm)
CursesConnect(screen, selected, CursesConnect(screen, selected,
command_string.replace("\n", "").rstrip()) command_string.replace("\n", "").rstrip())
curses.curs_set(0)
if (key_pressed == ord('k') or key_pressed == ord('K') or if (key_pressed == ord('k') or key_pressed == ord('K') or
key_pressed == (curses.KEY_F7)) and row_num != 0: key_pressed == (curses.KEY_F7)) and row_num != 0:
groups = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
if not strings[position - 1] in groups:
curses.endwin() curses.endwin()
HoldConnection(strings[position - 1]) HoldConnection(strings[position - 1].split()[0].strip())
if (key_pressed == ord("\n") or key_pressed == ( if (key_pressed == ord("\n") or key_pressed == (
curses.KEY_F9)) and row_num != 0: curses.KEY_F9)) and row_num != 0:
groups = []
for a in conf.sections():
if conf.has_option(a, "group"):
groups.append(a)
if strings[position - 1] in groups:
if strings[position - 1] in expanded:
expanded.remove(strings[position - 1])
else:
expanded.append(strings[position - 1])
strings = GetTreeList(False, expanded)
stringsfull = GetTreeList(True, expanded)
row_num = len(strings)
selected_strings.append(" ")
pages = int(ceil(row_num / max_row))
box.refresh()
else:
selected = [] selected = []
for i in range(1, row_num + 1): for i in range(1, row_num + 1):
if selected_strings[i] == "*": if selected_strings[i] == "*":
selected.append(strings[i - 1]) selected.append(strings[i - 1])
if not len(selected) > 0: if not len(selected) > 0:
selected.append(strings[position - 1]) selected.append(strings[position - 1].split()[0].strip())
CursesConnect(screen, selected) CursesConnect(screen, selected)
selected_strings = [" " for i in range(0, row_num + 1)]
if (key_pressed == 32 or key_pressed == ( if (key_pressed == 32 or key_pressed == (
curses.KEY_IC)) and row_num != 0: curses.KEY_IC)) and row_num != 0:
if selected_strings[position] == ' ': if selected_strings[position] == ' ':
@@ -519,6 +750,12 @@ def CursesMain():
if page < pages: if page < pages:
page = page + 1 page = page + 1
position = (1 + (max_row * (page - 1))) position = (1 + (max_row * (page - 1)))
if key_pressed == curses.KEY_HOME:
page = 1
position = 1
if key_pressed == curses.KEY_END:
page = pages
position = row_num
box.erase() box.erase()
screen.border(0) screen.border(0)
box.border(0) box.border(0)
@@ -528,13 +765,7 @@ def CursesMain():
box.addnstr(1, 1, "There aren't any aliases yet. Press 'a' to add new one.", box.addnstr(1, 1, "There aren't any aliases yet. Press 'a' to add new one.",
width - 6, highlight_text) width - 6, highlight_text)
else: else:
if conf.has_option(strings[i - 1], "password"): exec_string = ["[", selected_strings[i], "] ", stringsfull[i - 1]]
password = " [password]"
else:
password = ""
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
strings[i - 1], " (", conf.get(strings[i - 1], "exec_string"),
")", password]
if (i + (max_row * (page - 1)) == (position + (max_row * (page - 1)))): if (i + (max_row * (page - 1)) == (position + (max_row * (page - 1)))):
box.addnstr(i - (max_row * (page - 1)), 2, "".join( box.addnstr(i - (max_row * (page - 1)), 2, "".join(
exec_string), width - 6, highlight_text) exec_string), width - 6, highlight_text)
@@ -550,7 +781,12 @@ def CursesMain():
if __name__ == "__main__": if __name__ == "__main__":
conf = ConfigParser.RawConfigParser() try:
input = raw_input # Fix for Python 2.x
except NameError:
pass
conf = configparser.RawConfigParser()
if not path.exists(conf_file): if not path.exists(conf_file):
open(conf_file, 'w') open(conf_file, 'w')
conf.read(conf_file) conf.read(conf_file)
@@ -559,15 +795,15 @@ if __name__ == "__main__":
CMDOptions() CMDOptions()
except KeyboardInterrupt: except KeyboardInterrupt:
exit() exit()
except ConfigParser.Error: except configparser.Error:
print ("Error: can't parse your config file, please check it manually or make new one") print("Error: can't parse your config file, please check it manually or make new one")
exit() exit()
else: else:
try: try:
CursesMain() CursesMain()
except KeyboardInterrupt: except KeyboardInterrupt:
CursesExit() CursesExit()
except ConfigParser.NoOptionError: except configparser.NoOptionError:
CursesExit("".join(["Error: can't parse your config file, please ", CursesExit("".join(["Error: can't parse your config file, please ",
"check it manually or make new one"])) "check it manually or make new one"]))
except curses.error: except curses.error: