Compare commits
No commits in common. "master" and "GHPS/ZSH-Completion" have entirely different histories.
master
...
GHPS/ZSH-C
21
LICENSE
21
LICENSE
|
@ -4,26 +4,27 @@ 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.
|
||||||
|
|
||||||
Boundless Public License
|
DO WHAT THAU WILT
|
||||||
DO WHAT THOU WILT
|
|
||||||
TO PUBLIC LICENSE
|
TO PUBLIC LICENSE
|
||||||
|
|
||||||
Version 2.55
|
Version 2.5
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
||||||
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.
|
||||||
|
|
||||||
DWTWL – a license with a single requirement: DO WHAT THOU WILT
|
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.
|
||||||
|
|
||||||
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 – a license with a single requirement: DO WHAT THOU WILT
|
||||||
|
|
||||||
DWTWL – an accomplished and eligible license for free text, code and any other symbols (including the software, documentation and artwork).
|
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 does not contain a "no warranty" clause. DWTWL can be used in countries that do not legally acknowledge the transition to public domain.
|
DWTW – an accomplished and eligible license for free text (including the software, documentation and artwork).
|
||||||
|
|
||||||
Summary:
|
The license does not contain "no warranty" clause. DWTW can be used in countries that do not legally acknowledge the transition to public domain.
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
|
42
README.md
42
README.md
|
@ -1,49 +1,31 @@
|
||||||
SSH connection and aliases manager with curses and command line interface
|
SSH connection manager with curses interface
|
||||||
======
|
======
|
||||||
sshch is released under DWTWL 2.55 license
|
sshch is released under DWTWL 2.5 license
|
||||||
|
|
||||||
sshch compatible with pyhon2 and python3, no additional libraries are required
|
|
||||||
### Screenshot
|
### Screenshot
|
||||||
![sshch](https://dev.ussr.win/zlax/sshch/raw/branch/master/sshch_screenshot.png)
|
![sshch](https://raw.githubusercontent.com/zlaxy/sshch/master/sshch_screenshot.png)
|
||||||
|
|
||||||
### Installing
|
### Installing
|
||||||
**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:
|
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
|
||||||
```
|
```
|
||||||
For exit from current ssh session press `Ctrl+D`.
|
**If you want to use unsafe 'password' feature you must install 'sshpass' first.**
|
||||||
|
|
||||||
**Additional Features**
|
If you want to use bash autocompletion function with sshch, copy autocompletion script to /etc/bash_completion.d/:
|
||||||
- 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)
|
sudo cp sshch_bash_completion.sh /etc/bash_completion.d/sshch
|
||||||
- If you want to use zsh autocompletion:
|
```
|
||||||
1) Place File in a Directory where ZSH can find it
|
(changes will come into effect with new bash session)
|
||||||
|
|
||||||
-> Search Path is Stored in $fpath
|
|
||||||
|
|
||||||
-> echo $fpath
|
|
||||||
2) Rename File to '_sshch'
|
|
17
setup.py
17
setup.py
|
@ -7,27 +7,22 @@ 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://gitlab.com/zlax/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.8',
|
||||||
long_description_content_type='text/x-rst',
|
|
||||||
license='DWTWL 2.55',
|
|
||||||
version='1.09.7',
|
|
||||||
py_modules=['sshch'],
|
py_modules=['sshch'],
|
||||||
scripts=['sshch/sshch'],
|
scripts=['sshch/sshch'],
|
||||||
keywords='sshch ssh aliases curses 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 :: 5 - Production/Stable',
|
'Development Status :: 4 - Beta',
|
||||||
'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',
|
'Programming Language :: Python :: 2.7',
|
||||||
'Topic :: Internet',
|
'Topic :: Internet',
|
||||||
'Topic :: System :: Networking',
|
'Topic :: System :: Networking',
|
||||||
'Topic :: System :: Systems Administration',
|
'Topic :: System :: Systems Administration',
|
||||||
|
|
751
sshch/sshch
751
sshch/sshch
|
@ -1,4 +1,4 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
from __future__ import division
|
from __future__ import division
|
||||||
|
@ -7,8 +7,7 @@ try:
|
||||||
import configparser
|
import configparser
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import ConfigParser as configparser # Python 2.x import
|
import ConfigParser as configparser # Python 2.x import
|
||||||
import locale
|
|
||||||
locale.setlocale(locale.LC_ALL, '')
|
|
||||||
from os import path
|
from os import path
|
||||||
from sys import argv
|
from sys import argv
|
||||||
from math import ceil
|
from math import ceil
|
||||||
|
@ -19,24 +18,11 @@ import base64
|
||||||
import time
|
import time
|
||||||
import curses
|
import curses
|
||||||
from curses import textpad, panel
|
from curses import textpad, panel
|
||||||
from threading import Thread
|
|
||||||
|
|
||||||
# https://gitlab.com/zlax/sshch
|
# https://github.com/zlaxy/sshch
|
||||||
version = "1.09.7"
|
version = "0.9"
|
||||||
# expand groups by default
|
# path to conf file, default: ~/.config/sshch.conf
|
||||||
expand_default = True
|
conf_file = path.expanduser("~") + '/.config/sshch.conf'
|
||||||
# path to conf dir and file, default: ~/.config/sshch.conf
|
|
||||||
conf_dir = path.expanduser("~") + '/.config'
|
|
||||||
conf_file = conf_dir + '/' + 'sshch.conf'
|
|
||||||
|
|
||||||
|
|
||||||
class GroupTree(object):
|
|
||||||
"""Group object with relatives information"""
|
|
||||||
def __init__(self, group):
|
|
||||||
self.group = group
|
|
||||||
self.aliases = []
|
|
||||||
self.children = []
|
|
||||||
self.parent = []
|
|
||||||
|
|
||||||
|
|
||||||
def AddNewAlias(alias):
|
def AddNewAlias(alias):
|
||||||
|
@ -45,7 +31,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 + "' alias or group already exists"
|
return "error: '" + alias + "' already exists"
|
||||||
|
|
||||||
|
|
||||||
def SetAliasString(alias, string):
|
def SetAliasString(alias, string):
|
||||||
|
@ -53,20 +39,10 @@ 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'':
|
string = base64.b64encode(base64.b16encode(
|
||||||
conf.remove_option(alias, "password")
|
base64.b32encode(string)))
|
||||||
else:
|
conf.set(alias, "password", string)
|
||||||
string = string.encode()
|
|
||||||
string = base64.b64encode(base64.b16encode(
|
|
||||||
base64.b32encode(string)))
|
|
||||||
string = string.decode('utf-8')
|
|
||||||
conf.set(alias, "password", string)
|
|
||||||
conf.write(open(conf_file, "w"))
|
conf.write(open(conf_file, "w"))
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,96 +52,31 @@ def RemoveAliases(aliases):
|
||||||
conf.write(open(conf_file, "w"))
|
conf.write(open(conf_file, "w"))
|
||||||
|
|
||||||
|
|
||||||
def ConvertPassword(password):
|
def ConnectAlias(alias, command=False):
|
||||||
password_string = "'"
|
|
||||||
for char in password:
|
|
||||||
if char == "'":
|
|
||||||
password_string += "'"+'"'+"'"+'"'+"'"
|
|
||||||
elif char == '"':
|
|
||||||
password_string += "''"+'"'+"''"
|
|
||||||
elif char == ';':
|
|
||||||
password_string += "'"+r'\;'+"'"
|
|
||||||
elif char == "\\":
|
|
||||||
password_string += "'"+'"'+"\\"+"\\"+'"'+"'"
|
|
||||||
else:
|
|
||||||
password_string += char
|
|
||||||
password_string += "'"
|
|
||||||
return password_string
|
|
||||||
|
|
||||||
|
|
||||||
def Connect(aliases, command=False, threading=False, screen=False):
|
|
||||||
if screen:
|
|
||||||
curses.endwin()
|
|
||||||
groups = []
|
|
||||||
connectaliases = []
|
|
||||||
for a in conf.sections():
|
|
||||||
if conf.has_option(a, "group"):
|
|
||||||
groups.append(a)
|
|
||||||
for alias in aliases:
|
|
||||||
if alias in groups:
|
|
||||||
group_aliases = GroupChildAliases(alias)
|
|
||||||
for ga in group_aliases:
|
|
||||||
connectaliases.append(ga)
|
|
||||||
else:
|
|
||||||
connectaliases.append(alias)
|
|
||||||
connectaliases = setSeq(connectaliases)
|
|
||||||
threads = {}
|
|
||||||
for alias in connectaliases:
|
|
||||||
if conf.has_section(alias):
|
|
||||||
print("Connecting to " + alias + "...")
|
|
||||||
if threading:
|
|
||||||
threads[alias] = Thread(target=ConnectAlias, args=(alias,
|
|
||||||
command, True))
|
|
||||||
threads[alias].start()
|
|
||||||
else:
|
|
||||||
ConnectAlias(alias, command)
|
|
||||||
if not threading:
|
|
||||||
print("... " + alias + " session finished.")
|
|
||||||
else:
|
|
||||||
print("error: '" + alias + "' alias does not exists")
|
|
||||||
if screen:
|
|
||||||
print("Press 'enter' to continue.")
|
|
||||||
screen.getch()
|
|
||||||
|
|
||||||
|
|
||||||
def ConnectAlias(alias, command=False, threading=False):
|
|
||||||
exec_string = ""
|
exec_string = ""
|
||||||
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 " + ConvertPassword(password.decode('utf-8')) + " "
|
exec_string = 'sshpass -p "' + password + '" '
|
||||||
if conf.has_option(alias, "exec_string"):
|
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
|
||||||
try:
|
# Variables bellow is newer used
|
||||||
subprocess.Popen(exec_string, shell=True).communicate()[0]
|
subprocess.Popen(exec_string, shell=True).communicate()[0]
|
||||||
except:
|
|
||||||
pass
|
|
||||||
if threading:
|
|
||||||
print ("... "+alias+" session output finished.")
|
|
||||||
|
|
||||||
|
|
||||||
def HoldConnection(alias):
|
def HoldConnection(alias):
|
||||||
groups = []
|
print("Connecting to " + alias + ". Press CTRL+C to cancel.")
|
||||||
connectaliases = []
|
time.sleep(1)
|
||||||
for a in conf.sections():
|
while True:
|
||||||
if conf.has_option(a, "group"):
|
ConnectAlias(alias)
|
||||||
groups.append(a)
|
time.sleep(5)
|
||||||
if alias in groups:
|
|
||||||
print("Can't hold connection with group.")
|
|
||||||
else:
|
|
||||||
print("Connecting to " + alias + ". Press CTRL+C to cancel.")
|
|
||||||
time.sleep(1)
|
|
||||||
while True:
|
|
||||||
ConnectAlias(alias)
|
|
||||||
time.sleep(5)
|
|
||||||
|
|
||||||
|
|
||||||
def CMDAdd(alias):
|
def CMDAdd(alias):
|
||||||
alias = alias.split()[0].strip()
|
|
||||||
result = AddNewAlias(alias)
|
result = AddNewAlias(alias)
|
||||||
if result == True:
|
if result:
|
||||||
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 = ""
|
||||||
|
@ -176,179 +87,71 @@ def CMDAdd(alias):
|
||||||
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 ",
|
"(example: ssh user@somehost.com):\n"]))
|
||||||
"alias (example: ssh user@somehost.com):\n"]))
|
string = ""
|
||||||
string = ""
|
while string == "":
|
||||||
while string == "":
|
string = 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:
|
|
||||||
print("error: '" + alias + "' is not correct alias or group")
|
|
||||||
else:
|
else:
|
||||||
print("error: '" + alias + "' alias or group does not exists")
|
print("error: '" + alias + "' alias is not exists")
|
||||||
|
|
||||||
|
|
||||||
def CMDPassword(alias):
|
def CMDPassword(alias):
|
||||||
groups = []
|
if conf.has_section(alias):
|
||||||
connectaliases = []
|
prompt_pass = ("[UNSAFE] Enter password for sshpass: ")
|
||||||
for a in conf.sections():
|
string = ""
|
||||||
if conf.has_option(a, "group"):
|
string = getpass(prompt_pass)
|
||||||
groups.append(a)
|
if not string == "":
|
||||||
if alias in groups:
|
|
||||||
print("Can't set password for group.")
|
|
||||||
else:
|
|
||||||
if conf.has_section(alias):
|
|
||||||
prompt_pass = ("[UNSAFE] Enter password for sshpass (Ctrl+C" +
|
|
||||||
" - cancel, blank - clear password):\n")
|
|
||||||
string = ""
|
|
||||||
string = getpass(prompt_pass)
|
|
||||||
SetPassword(alias, string)
|
SetPassword(alias, string)
|
||||||
else:
|
else:
|
||||||
print("error: '" + alias + "' alias does not exists")
|
print("error: '" + alias + "' alias is 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 +
|
prompt_remove = ("Type 'yes' if you sure to remove '" + alias + "' alias: ")
|
||||||
"' alias or group: ")
|
|
||||||
string = input(prompt_remove)
|
string = input(prompt_remove)
|
||||||
if string == "yes":
|
if string == "yes":
|
||||||
RemoveAliases([alias])
|
RemoveAliases([alias])
|
||||||
else:
|
else:
|
||||||
print("'" + alias + "' alias or group was not deleted.")
|
print("'" + alias + "' alias was not deleted.")
|
||||||
else:
|
else:
|
||||||
print("error: '" + alias + "' alias or group does not exists.")
|
print("error: '" + alias + "' alias is not exists.")
|
||||||
|
|
||||||
|
|
||||||
|
def CMDConnect(aliases, command=False):
|
||||||
|
for alias in aliases:
|
||||||
|
if conf.has_section(alias):
|
||||||
|
print("Connecting to " + alias + "...")
|
||||||
|
ConnectAlias(alias, command)
|
||||||
|
print("... " + alias + " session finished.")
|
||||||
|
else:
|
||||||
|
print("error: '" + alias + "' alias is 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 setSeq(seq):
|
|
||||||
# py2&3 fastest way to remove duplicates from a list:
|
|
||||||
# https://www.rupython.com/x432-4-496.html
|
|
||||||
seen = set()
|
|
||||||
seen_add = seen.add
|
|
||||||
return [x for x in seq if not (x in seen or seen_add(x))]
|
|
||||||
|
|
||||||
|
|
||||||
def GroupChildRecursion(group, childaliases, treelist):
|
|
||||||
if treelist[group].children:
|
|
||||||
for a in treelist[group].children:
|
|
||||||
GroupChildRecursion(a, childaliases, treelist)
|
|
||||||
if treelist[group].aliases:
|
|
||||||
for a in treelist[group].aliases:
|
|
||||||
childaliases.append(a)
|
|
||||||
return childaliases
|
|
||||||
|
|
||||||
|
|
||||||
def GroupChildAliases(group):
|
|
||||||
aliases = []
|
|
||||||
groups = []
|
|
||||||
rootgroups = []
|
|
||||||
treelist = {}
|
|
||||||
for a in conf.sections():
|
|
||||||
if conf.has_option(a, "group"):
|
|
||||||
groups.append(a)
|
|
||||||
elif conf.has_option(a, "exec_string"):
|
|
||||||
aliases.append(a)
|
|
||||||
rootaliases = list(aliases)
|
|
||||||
for g in groups:
|
|
||||||
treelist[g] = GroupTree(g)
|
|
||||||
group_aliases = conf.get(g, "group").split()
|
|
||||||
for ga in group_aliases:
|
|
||||||
if ga in groups:
|
|
||||||
treelist[g].children.append(ga)
|
|
||||||
elif ga in aliases:
|
|
||||||
treelist[g].aliases.append(ga)
|
|
||||||
try:
|
|
||||||
rootaliases.remove(ga)
|
|
||||||
except ValueError:
|
|
||||||
pass
|
|
||||||
for g in groups:
|
|
||||||
if treelist[g].children:
|
|
||||||
for child in treelist[g].children:
|
|
||||||
treelist[child].parent.append(g)
|
|
||||||
for g in groups:
|
|
||||||
if not treelist[g].parent:
|
|
||||||
rootgroups.append(g)
|
|
||||||
if group == False:
|
|
||||||
return aliases, groups, rootaliases, rootgroups, treelist
|
|
||||||
else:
|
|
||||||
childaliases = GroupChildRecursion(group, [], treelist)
|
|
||||||
return setSeq(childaliases)
|
|
||||||
|
|
||||||
|
|
||||||
def GroupTreeRecursion(level, group, treelist, resultalias, resultstring,
|
|
||||||
expandlist, previousgroups):
|
|
||||||
if group in previousgroups:
|
|
||||||
return resultalias, resultstring
|
|
||||||
previousgroups.append(group)
|
|
||||||
resultalias.append(group)
|
|
||||||
if expandlist == True or group in expandlist:
|
|
||||||
resultstring.append(' '*(level-1)+">> "+group)
|
|
||||||
for g in treelist[group].children:
|
|
||||||
resultalias, resultstring = GroupTreeRecursion(level+1, g,
|
|
||||||
treelist, resultalias, resultstring, expandlist, previousgroups)
|
|
||||||
for ga in treelist[group].aliases:
|
|
||||||
if conf.has_option(ga, "exec_string"):
|
|
||||||
resultalias.append(ga)
|
|
||||||
result = "".join([' '*(level-1)+" ~ ", 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)
|
|
||||||
else:
|
|
||||||
resultstring.append(' '*(level-1)+"<> "+group)
|
|
||||||
return resultalias, resultstring
|
|
||||||
|
|
||||||
|
|
||||||
def GetTreeList(strings=True, expandlist=True):
|
|
||||||
resultalias = []
|
|
||||||
resultstring = []
|
|
||||||
aliases, groups, rootaliases, rootgroups, treelist = GroupChildAliases(False)
|
|
||||||
for g in rootgroups:
|
|
||||||
resultalias, resultstring = GroupTreeRecursion(1, g, treelist,
|
|
||||||
resultalias, resultstring, expandlist, [])
|
|
||||||
for a in rootaliases:
|
|
||||||
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 GetTreeList():
|
for p in conf.sections():
|
||||||
print (p)
|
to_print = "".join([str(p), " - ", (conf.get(p, "exec_string") if
|
||||||
|
conf.has_option(p, "exec_string") else ""),
|
||||||
|
(" [password]" if conf.has_option(p, "password") else "")])
|
||||||
|
print(to_print)
|
||||||
|
|
||||||
|
|
||||||
|
def CursesConnect(screen, aliases, command=False):
|
||||||
|
curses.endwin()
|
||||||
|
for alias in aliases:
|
||||||
|
print("Connecting to " + alias + "...")
|
||||||
|
ConnectAlias(alias, command)
|
||||||
|
print("... " + alias + " session finished.")
|
||||||
|
print("Press 'enter' to continue.")
|
||||||
|
screen.getch()
|
||||||
|
|
||||||
|
|
||||||
def CursesExit(error=False):
|
def CursesExit(error=False):
|
||||||
|
@ -358,21 +161,9 @@ def CursesExit(error=False):
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
class CursesTextpadEsc(Exception):
|
|
||||||
"ESC key has been pressed"
|
|
||||||
|
|
||||||
|
|
||||||
def CursesTextpadConfirm(value):
|
def CursesTextpadConfirm(value):
|
||||||
if value == 10: # Enter
|
if value == 10:
|
||||||
value = 7
|
value = 7
|
||||||
elif value == 27: # Esc
|
|
||||||
raise CursesTextpadEsc()
|
|
||||||
elif value == curses.KEY_DC: # Del
|
|
||||||
value = curses.ascii.EOT
|
|
||||||
elif value == curses.KEY_HOME: # Home
|
|
||||||
value = curses.ascii.SOH
|
|
||||||
elif value == curses.KEY_END: # End
|
|
||||||
value = curses.ascii.ENQ
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
|
|
||||||
|
@ -425,7 +216,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).decode('utf-8')
|
hidden_password += curses.keyname(keych)
|
||||||
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
|
||||||
|
@ -445,20 +236,19 @@ def CMDOptions():
|
||||||
|
|
||||||
def format_epilog(self, formatter):
|
def format_epilog(self, formatter):
|
||||||
return self.epilog
|
return self.epilog
|
||||||
|
|
||||||
usage = "usage: %prog [options] [aliases]"
|
usage = "usage: %prog [options] [aliases]"
|
||||||
progname = path.basename(__file__)
|
progname = path.basename(__file__)
|
||||||
epilog = ("".join(["Examples:\n ",
|
epilog = ("".join(["Examples:\n ",
|
||||||
progname, " existingalias\n ",
|
progname, " existingalias\n ",
|
||||||
progname, " -a newremoteserver\n ",
|
progname, " -a newremoteserver\n ",
|
||||||
progname, " --edit=newremoteserver -p newremoteserver\n ",
|
progname, " --edit=newremoteserver -p newremoteserver\n ",
|
||||||
progname, ' -c "ls -l" newremoteserver\n ',
|
progname, ' -c "ls -l" newremoteserver\n ',
|
||||||
progname, " -c reboot existingalias newremoteserver\n",
|
progname, " -c reboot existingalias newremoteserver\n",
|
||||||
"Examples of connection string:\n ",
|
"Examples of connection string:\n ",
|
||||||
"ssh user@somehost.com\n ",
|
"ssh user@somehost.com\n ",
|
||||||
"ssh gates@8.8.8.8 -p 667\n ",
|
"ssh gates@8.8.8.8 -p 667\n ",
|
||||||
"ssh root@somehost.com -t tmux a\n",
|
"ssh root@somehost.com -t tmux a\n",
|
||||||
"Also, you can edit the config file manually: ", conf_file, "\n"]))
|
"Also, you can edit config file manually: ", conf_file, "\n"]))
|
||||||
opts = FormatedParser(usage=usage, version="%prog " + version,
|
opts = FormatedParser(usage=usage, version="%prog " + version,
|
||||||
epilog=epilog)
|
epilog=epilog)
|
||||||
opts.add_option('-l', '--list', action="callback",
|
opts.add_option('-l', '--list', action="callback",
|
||||||
|
@ -469,33 +259,24 @@ 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="execute command for aliases and groups")
|
help="add command for executing alias")
|
||||||
opts.add_option('-t', '--thread', action="store", type="string",
|
|
||||||
dest="thread", metavar="command", default=False,
|
|
||||||
help="parallel command execution for aliases and groups " +
|
|
||||||
"(ssh key authentication or set password required)" )
|
|
||||||
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 alias or group")
|
help="edit existing connection string")
|
||||||
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 or group")
|
help="remove existing alias of connection string")
|
||||||
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:
|
||||||
|
@ -504,46 +285,28 @@ def CMDOptions():
|
||||||
CMDRemove(options.remove)
|
CMDRemove(options.remove)
|
||||||
if options.keep:
|
if options.keep:
|
||||||
HoldConnection(options.keep)
|
HoldConnection(options.keep)
|
||||||
if options.thread:
|
if alias:
|
||||||
Connect(alias, options.thread, True)
|
CMDConnect(alias, options.command)
|
||||||
elif alias:
|
|
||||||
Connect(alias, options.command)
|
|
||||||
|
|
||||||
# curses template from: https://stackoverflow.com/a/30828805/6224462
|
# curses template from: https://stackoverflow.com/a/30828805/6224462
|
||||||
|
|
||||||
|
|
||||||
def CursesMain():
|
def CursesMain():
|
||||||
help_screen = ("".join([" Press:\n",
|
help_screen = ("".join([" Press:\n",
|
||||||
" 'z'/'x', 'w'/'s' or arrows - navigation\n",
|
" 'z'/'x' or arrows - navigation\n",
|
||||||
" 'a'/'F2' - add new alias (without spaces)\n",
|
" 'a'/'F2' - add new alias\n",
|
||||||
" 'g'/'F5' - add new group (spaces will be stripped)\n",
|
" 'e'/'F4' - edit existing alias\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' - command execution for alias (group/aliases - in turn)\n",
|
" 'k'/'F7' - hold connection with selected alias\n",
|
||||||
" 't'/'F11' - parallel command execution for aliases and groups\n",
|
" 'enter'/'F9' - connect to selected alias/aliases\n",
|
||||||
" (ssh key authentication or set password required)\n",
|
" 'q'/'F10' - quit\n",
|
||||||
" 'k'/'F7' - hold connection with selected alias\n",
|
" Run program with '--help' option to view command line help.\n",
|
||||||
" 'enter'/'F9' - connect to selected alias/aliases,\n",
|
" Also, you can edit config file manually:\n",
|
||||||
" expand/collapse group\n",
|
" ", conf_file]))
|
||||||
" 'q'/'F10' - quit\n",
|
strings = conf.sections()
|
||||||
" Run program with '--help' option to view command line help.\n",
|
|
||||||
" Also, you can edit the config file manually:\n",
|
|
||||||
" ", conf_file]))
|
|
||||||
if expand_default == True:
|
|
||||||
groups = []
|
|
||||||
for a in conf.sections():
|
|
||||||
if conf.has_option(a, "group"):
|
|
||||||
groups.append(a)
|
|
||||||
expanded = list(groups)
|
|
||||||
strings = GetTreeList(False, expanded)
|
|
||||||
stringsfull = GetTreeList(True, expanded)
|
|
||||||
elif expand_default == False:
|
|
||||||
groups = []
|
|
||||||
expanded = []
|
|
||||||
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()
|
||||||
|
@ -570,7 +333,14 @@ 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:
|
||||||
exec_string = ["[", selected_strings[i], "] ", stringsfull[i - 1]]
|
if conf.has_option(strings[i - 1], "password"):
|
||||||
|
password = " [password]"
|
||||||
|
else:
|
||||||
|
password = ""
|
||||||
|
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
|
||||||
|
strings[i - 1], " (", (conf.get(strings[i - 1],
|
||||||
|
"exec_string") if conf.has_option(strings[i - 1],
|
||||||
|
"exec_string") else ""), ")", 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:
|
||||||
|
@ -591,151 +361,55 @@ 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)
|
||||||
try:
|
add_alias = new_alias_textpad.edit(CursesTextpadConfirm)
|
||||||
add_alias = new_alias_textpad.edit(CursesTextpadConfirm)
|
if not add_alias.rstrip() == "":
|
||||||
except CursesTextpadEsc:
|
add_result = AddNewAlias(add_alias.rstrip())
|
||||||
add_alias = ""
|
if not add_result:
|
||||||
if not add_alias == "":
|
CursesPanel(screen, 3,
|
||||||
add_alias = add_alias.split()[0].strip()
|
width - 6, (height // 2) - 1, 3, add_result,
|
||||||
if not add_alias == "":
|
normal_text, highlight_text)
|
||||||
add_result = AddNewAlias(add_alias)
|
else:
|
||||||
if add_result == True:
|
|
||||||
add_string = ""
|
add_string = ""
|
||||||
try:
|
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()
|
||||||
except CursesTextpadEsc:
|
|
||||||
RemoveAliases([add_alias])
|
|
||||||
add_alias = ""
|
|
||||||
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)
|
|
||||||
try:
|
|
||||||
add_group = new_group_textpad.edit(CursesTextpadConfirm)
|
|
||||||
except CursesTextpadEsc:
|
|
||||||
add_group = ""
|
|
||||||
if not add_group == "":
|
|
||||||
add_group = add_group.split()[0].strip()
|
|
||||||
if not add_group == "":
|
|
||||||
add_result = AddNewAlias(add_group)
|
|
||||||
if add_result == True:
|
|
||||||
add_string = ""
|
|
||||||
try:
|
|
||||||
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)
|
|
||||||
except CursesTextpadEsc:
|
|
||||||
RemoveAliases([add_group])
|
|
||||||
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 = []
|
while edit_string.rstrip() == "":
|
||||||
for a in conf.sections():
|
string_textpad = CursesTextpad(screen, 3, width - 8,
|
||||||
if conf.has_option(a, "group"):
|
(height // 2) - 1, 4, "Enter new execution string:",
|
||||||
groups.append(a)
|
(conf.get(strings[position - 1],
|
||||||
curses.curs_set(1)
|
"exec_string") if conf.has_option(strings[position - 1],
|
||||||
if strings[position - 1] in groups:
|
"exec_string") else ""),
|
||||||
while edit_string.rstrip() == "":
|
normal_text, highlight_text)
|
||||||
string_textpad = CursesTextpad(screen, 3, width - 8,
|
edit_string = string_textpad.edit(CursesTextpadConfirm)
|
||||||
(height // 2) - 1, 4, "Enter new aliases for existing group:",
|
SetAliasString(strings[position - 1],
|
||||||
(conf.get(strings[position - 1],
|
edit_string.replace("\n", "").rstrip())
|
||||||
"group") if conf.has_option(strings[position - 1],
|
strings = conf.sections()
|
||||||
"group") else ""),
|
|
||||||
normal_text, highlight_text)
|
|
||||||
try:
|
|
||||||
edit_string = string_textpad.edit(CursesTextpadConfirm)
|
|
||||||
except CursesTextpadEsc:
|
|
||||||
edit_string = conf.get(strings[position - 1],
|
|
||||||
"group") if conf.has_option(strings[position - 1],
|
|
||||||
"group") else ""
|
|
||||||
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() == "":
|
|
||||||
string_textpad = CursesTextpad(screen, 3, width - 8,
|
|
||||||
(height // 2) - 1, 4, "Enter new execution 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)
|
|
||||||
try:
|
|
||||||
edit_string = string_textpad.edit(CursesTextpadConfirm)
|
|
||||||
except CursesTextpadEsc:
|
|
||||||
edit_string = conf.get(strings[position - 1].split()[0].strip(),
|
|
||||||
"exec_string") if conf.has_option(strings[position - 1].split()[0].strip(),
|
|
||||||
"exec_string") else ""
|
|
||||||
SetAliasString(strings[position - 1].split()[0].strip(),
|
|
||||||
edit_string.replace("\n", "").rstrip())
|
|
||||||
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 = []
|
set_password = ""
|
||||||
for a in conf.sections():
|
set_password = CursesPanel(screen, 4, width - 6,
|
||||||
if conf.has_option(a, "group"):
|
(height // 2) - 1, 3,
|
||||||
groups.append(a)
|
" Enter user password for sshpass and press 'enter':\n>",
|
||||||
if not strings[position - 1].split()[0].strip() in groups:
|
normal_text, highlight_text, "password")
|
||||||
set_password = ""
|
if not set_password == "":
|
||||||
set_password = CursesPanel(screen, 4, width - 6,
|
SetPassword(strings[position - 1], set_password)
|
||||||
(height // 2) - 1, 3,
|
|
||||||
" Enter user password for sshpass and press 'enter':\n>",
|
|
||||||
normal_text, highlight_text, "password")
|
|
||||||
SetPassword(strings[position - 1].split()[0].strip(), 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:
|
||||||
|
@ -749,15 +423,14 @@ 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].split()[0].strip(), "' alias? (y/N)"]))
|
strings[position - 1], "' alias? (y/N)"]))
|
||||||
selected.append(strings[position - 1].split()[0].strip())
|
selected.append(strings[position - 1])
|
||||||
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 = GetTreeList(False, expanded)
|
strings = conf.sections()
|
||||||
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))
|
||||||
|
@ -771,78 +444,29 @@ 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].split()[0].strip())
|
selected.append(strings[position - 1])
|
||||||
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([
|
||||||
"Enter specific command to execute with selected ",
|
"Enter specific command to execute with selected ",
|
||||||
"alias/aliases:"]
|
"alias/aliases:"]
|
||||||
), "", normal_text, highlight_text)
|
), "", normal_text, highlight_text)
|
||||||
try:
|
command_string = command_textpad.edit(CursesTextpadConfirm)
|
||||||
command_string = command_textpad.edit(CursesTextpadConfirm)
|
CursesConnect(screen, selected,
|
||||||
Connect(selected, command_string.replace("\n", "").rstrip(),
|
command_string.replace("\n", "").rstrip())
|
||||||
False, screen)
|
if (key_pressed == ord('k') or key_pressed == ord('K') or
|
||||||
except CursesTextpadEsc:
|
key_pressed == (curses.KEY_F7)) and row_num != 0:
|
||||||
command_string = ""
|
curses.endwin()
|
||||||
curses.curs_set(0)
|
HoldConnection(strings[position - 1])
|
||||||
if (key_pressed == ord('t') or key_pressed == ord(
|
if (key_pressed == ord("\n") or key_pressed == (
|
||||||
'T') or key_pressed == curses.KEY_F11) and row_num != 0:
|
curses.KEY_F9)) and row_num != 0:
|
||||||
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].split()[0].strip())
|
selected.append(strings[position - 1])
|
||||||
curses.curs_set(1)
|
CursesConnect(screen, selected)
|
||||||
command_textpad = CursesTextpad(screen, 3, width - 8,
|
|
||||||
(height // 2) - 1, 4,
|
|
||||||
"".join([
|
|
||||||
"Enter specific command to execute with selected ",
|
|
||||||
"alias/aliases:"]
|
|
||||||
), "", normal_text, highlight_text)
|
|
||||||
try:
|
|
||||||
command_string = command_textpad.edit(CursesTextpadConfirm)
|
|
||||||
Connect(selected, command_string.replace("\n", "").rstrip(),
|
|
||||||
True, screen)
|
|
||||||
except CursesTextpadEsc:
|
|
||||||
command_string = ""
|
|
||||||
curses.curs_set(0)
|
|
||||||
if (key_pressed == ord('k') or key_pressed == ord('K') or
|
|
||||||
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()
|
|
||||||
HoldConnection(strings[position - 1].split()[0].strip())
|
|
||||||
if (key_pressed == ord("\n") or key_pressed == (
|
|
||||||
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 = []
|
|
||||||
for i in range(1, row_num + 1):
|
|
||||||
if selected_strings[i] == "*":
|
|
||||||
selected.append(strings[i - 1])
|
|
||||||
if not len(selected) > 0:
|
|
||||||
selected.append(strings[position - 1].split()[0].strip())
|
|
||||||
Connect(selected, False, False, screen)
|
|
||||||
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] == ' ':
|
||||||
|
@ -866,8 +490,7 @@ def CursesMain():
|
||||||
page = page + 1
|
page = page + 1
|
||||||
position = 1 + (max_row * (page - 1))
|
position = 1 + (max_row * (page - 1))
|
||||||
if key_pressed == curses.KEY_DOWN or key_pressed == ord(
|
if key_pressed == curses.KEY_DOWN or key_pressed == ord(
|
||||||
'x') or key_pressed == ord('X') or key_pressed == ord(
|
'x') or key_pressed == ord('X'):
|
||||||
's') or key_pressed == ord('S'):
|
|
||||||
if page == 1:
|
if page == 1:
|
||||||
if position < i:
|
if position < i:
|
||||||
position = position + 1
|
position = position + 1
|
||||||
|
@ -885,8 +508,7 @@ def CursesMain():
|
||||||
page = page + 1
|
page = page + 1
|
||||||
position = 1 + (max_row * (page - 1))
|
position = 1 + (max_row * (page - 1))
|
||||||
if key_pressed == curses.KEY_UP or key_pressed == ord(
|
if key_pressed == curses.KEY_UP or key_pressed == ord(
|
||||||
'z') or key_pressed == ord('Z') or key_pressed == ord(
|
'z') or key_pressed == ord('Z'):
|
||||||
'w') or key_pressed == ord('W'):
|
|
||||||
if page == 1:
|
if page == 1:
|
||||||
if position > 1:
|
if position > 1:
|
||||||
position = position - 1
|
position = position - 1
|
||||||
|
@ -906,12 +528,6 @@ 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)
|
||||||
|
@ -921,7 +537,14 @@ 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:
|
||||||
exec_string = ["[", selected_strings[i], "] ", stringsfull[i - 1]]
|
if conf.has_option(strings[i - 1], "password"):
|
||||||
|
password = " [password]"
|
||||||
|
else:
|
||||||
|
password = ""
|
||||||
|
exec_string = ["[", selected_strings[i], "] ", str(i), " ",
|
||||||
|
strings[i - 1], " (", (conf.get(strings[i - 1],
|
||||||
|
"exec_string") if conf.has_option(strings[i - 1],
|
||||||
|
"exec_string") else ""), ")", 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)
|
||||||
|
@ -942,29 +565,10 @@ if __name__ == "__main__":
|
||||||
except NameError:
|
except NameError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
if not path.exists(conf_dir):
|
|
||||||
try:
|
|
||||||
from os import makedirs
|
|
||||||
makedirs(conf_dir)
|
|
||||||
except:
|
|
||||||
print("Can't make dir " + conf_dir)
|
|
||||||
exit()
|
|
||||||
|
|
||||||
conf = configparser.RawConfigParser()
|
conf = configparser.RawConfigParser()
|
||||||
|
|
||||||
if not path.exists(conf_file):
|
if not path.exists(conf_file):
|
||||||
try:
|
open(conf_file, 'w')
|
||||||
open(conf_file, 'w')
|
conf.read(conf_file)
|
||||||
except:
|
|
||||||
print("Can't make file at " + conf_dir)
|
|
||||||
exit()
|
|
||||||
|
|
||||||
try:
|
|
||||||
conf.read(conf_file)
|
|
||||||
except:
|
|
||||||
print("Error: can't read config file " + conf_file)
|
|
||||||
exit()
|
|
||||||
|
|
||||||
if len(argv) > 1:
|
if len(argv) > 1:
|
||||||
try:
|
try:
|
||||||
CMDOptions()
|
CMDOptions()
|
||||||
|
@ -973,10 +577,6 @@ if __name__ == "__main__":
|
||||||
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()
|
||||||
except IOError:
|
|
||||||
print("Error: can't use your config file, please check permissionss of " + conf_file)
|
|
||||||
exit()
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
CursesMain()
|
CursesMain()
|
||||||
|
@ -988,6 +588,3 @@ if __name__ == "__main__":
|
||||||
except curses.error:
|
except curses.error:
|
||||||
CursesExit("".join(["Error: can't show some curses element, maybe ",
|
CursesExit("".join(["Error: can't show some curses element, maybe ",
|
||||||
"your terminal is too small"]))
|
"your terminal is too small"]))
|
||||||
except IOError:
|
|
||||||
CursesExit("".join(["Error: can't use your config file, please ",
|
|
||||||
"check permissionss of ", conf_file]))
|
|
||||||
|
|
Loading…
Reference in New Issue