You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by lg...@apache.org on 2019/10/16 11:36:29 UTC

[mina-sshd] branch SSHD-914 updated (e5db956 -> 4aa88fe)

This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a change to branch SSHD-914
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git.


 discard e5db956  [SSHD-914] WIP
     new 4aa88fe  [SSHD-914] WIP

This update added new revisions after undoing existing revisions.
That is to say, some revisions that were in the old version of the
branch are not in the new version.  This situation occurs
when a user --force pushes a change and generates a repository
containing something like this:

 * -- * -- B -- O -- O -- O   (e5db956)
            \
             N -- N -- N   refs/heads/SSHD-914 (4aa88fe)

You should already have received notification emails for all of the O
revisions, and so the following emails describe only the N revisions
from the common base, B.

Any revisions marked "omit" are not gone; other references still
refer to them.  Any revisions marked "discard" are gone forever.

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/python/sftpclient.py | 50 +++++++++++++++++++++++++++---------------------
 1 file changed, 28 insertions(+), 22 deletions(-)


[mina-sshd] 01/01: [SSHD-914] WIP

Posted by lg...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

lgoldstein pushed a commit to branch SSHD-914
in repository https://gitbox.apache.org/repos/asf/mina-sshd.git

commit 4aa88fed7e20e2400b7a140bddfe0560dbb450a0
Author: Lyor Goldstein <lg...@apache.org>
AuthorDate: Wed Oct 16 14:17:46 2019 +0300

    [SSHD-914] WIP
---
 .gitignore               |   3 +
 src/python/sftpclient.py | 386 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 389 insertions(+)

diff --git a/.gitignore b/.gitignore
index 485a2b4..39cdffd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,9 @@ Servers/
 RemoteSystemsTempFiles/
 .classpath
 .project
+# Puthon related files
+.pydevproject
+*.pyc
 .checkstyle
 .eclipse-pmd
 .pmd
diff --git a/src/python/sftpclient.py b/src/python/sftpclient.py
new file mode 100644
index 0000000..fcb89a5
--- /dev/null
+++ b/src/python/sftpclient.py
@@ -0,0 +1,386 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+'''
+Simple wrapper for Paramiko SFTP client (see http://www.paramiko.org/)
+'''
+
+import getpass
+import json
+import os
+import signal
+import sys
+
+import paramiko
+
+
+VERSION='1.0'
+
+# -----------------------------------------------------------------------------------------
+
+def die(msg=None,rc=1):
+    """
+    Cleanly exits the program with an error message
+    """
+
+    if msg:
+        print(msg)
+
+    sys.exit(rc)
+
+# ----------------------------------------------------------------------------
+
+def isEmpty(s):
+    if (s is None) or (len(s) <= 0):
+        return True
+    else:
+        return False
+
+# ----------------------------------------------------------------------------
+
+def isNumberString(value):
+    """
+    Checks if value is a string that has only digits - possibly with leading '+' or '-'
+    """
+    if not value:
+        return False
+
+    sign = value[0]
+    if (sign == '+') or (sign == '-'):
+        if len(value) <= 1:
+            return False
+
+        absValue = value[1:]
+        return absValue.isdigit()
+    else:
+        if len(value) <= 0:
+            return False
+        else:
+            return value.isdigit()
+
+def isNumberValue(value):
+    return isinstance(value, (int, float))
+
+# ----------------------------------------------------------------------------
+
+def isFloatingPointString(value):
+    """
+    Checks if value is a string that has only digits - possibly with leading '+' or '-' - AND a single dot
+    """
+    if isEmpty(value):
+        return False
+
+    sign = value[0]
+    if (sign == '+') or (sign == '-'):
+        if len(value) <= 1:
+            return False
+
+        absValue = value[1:]
+    else:
+        absValue = value
+
+    dotPos = absValue.find('.')
+    # Must have a dot and it cannot be the last character
+    if (dotPos < 0) or (dotPos == (len(absValue) - 1)):
+        return False
+
+    # Must have EXACTLY one dot
+    dotCount = absValue.count('.')
+    if dotCount != 1:
+        return False
+
+    # Make sure both sides of the dot are integer numbers
+    intPart = absValue[0:dotPos]
+    if not isNumberString(intPart):
+        return False
+
+    facPart = absValue[dotPos + 1:]
+    # Do not allow 123.-5
+    sign = facPart[0]
+    if (sign == '+') or (sign == '-'):
+        return False
+
+    if not isNumberString(facPart):
+        return False
+
+    return True
+
+# ----------------------------------------------------------------------------
+
+def normalizeValue(value):
+    """
+    Checks if value is 'True', 'False' or all numeric and converts it accordingly
+    Otherwise it just returns it
+
+    Args:
+        value (str) - String value
+    """
+
+    if not value:
+        return value
+
+    loCase = value.lower()
+    if loCase == "none":
+        return None
+    elif loCase == "true":
+        return True
+    elif loCase == "false":
+        return False
+    elif isNumberString(loCase):
+        return int(loCase)
+    else:
+        return value
+
+# ----------------------------------------------------------------------------
+
+def parseCommandLineArguments(args):
+    """
+    Parses an array of arguments having the format: --name=value. If
+    only --name is provided then it is assumed to a TRUE boolean value.
+    If the value is all digits, then it is assumed to be a number.
+
+    If the same key is specified more than once, then a list of
+    the accumulated values is created. The result is a dictionary
+    with the names as the keys and value as their mapped values
+
+    Args:
+        args (str[]) - The command line arguments to parse
+    """
+
+    valsMap = {}
+    if len(args) <= 0:
+        return valsMap
+
+    for item in args:
+        if not item.startswith("--"):
+            raise Exception("Missing option identifier: %s" % item)
+
+        propPair = item[2:]     # strip the prefix
+        sepPos = propPair.find('=')
+
+        if sepPos == 0:
+            raise Exception("Missing name: %s" % item)
+        if sepPos >= (len(propPair) - 1):
+            raise Exception("Missing value: %s" % item)
+
+        propName = propPair
+        propValue = None
+        if sepPos < 0:
+            propValue = True
+        else:
+            propName = propPair[0:sepPos]
+            propValue = normalizeValue(propPair[sepPos + 1:])
+
+        if propName in valsMap:
+            curValue = valsMap[propName]
+            if not isinstance(curValue, list):
+                curValue = [ curValue ]
+            curValue.append(propValue)
+            valsMap[propName] = curValue
+        else:
+            valsMap[propName] = propValue
+
+    return valsMap
+
+# ----------------------------------------------------------------------------
+
+def resolvePathVariables(path):
+    """
+    Expands ~/xxx and ${XXX} variables
+    """
+    if isEmpty(path):
+        return path
+
+    path = os.path.expanduser(path)
+    path = os.path.expandvars(path)
+    return path
+
+# ----------------------------------------------------------------------------
+
+def _decode_list(data):
+    # can happen for internal sub-lists of objects
+    if isinstance(data, dict):
+        return _decode_dict(data)
+
+    rv = []
+    for item in data:
+        if isinstance(item, list):
+            item = _decode_list(item)
+        elif isinstance(item, dict):
+            item = _decode_dict(item)
+        rv.append(item)
+    return rv
+
+# ----------------------------------------------------------------------------
+
+def _decode_dict(data):
+    # can happen for internal sub-lists of objects
+    if isinstance(data, list):
+        return _decode_list(data)
+
+    rv = {}
+    for key, value in data.items():
+        if isinstance(value, list):
+            value = _decode_list(value)
+        elif isinstance(value, dict):
+            value = _decode_dict(value)
+        rv[key] = value
+
+    return rv
+
+# ----------------------------------------------------------------------------
+
+def loadJsonFile(configFile):
+    if isEmpty(configFile):
+        return {}
+
+    with open(configFile) as config_file:
+        return json.load(config_file, object_hook=_decode_dict);
+
+# ----------------------------------------------------------------------------
+
+def createSftpClient(args):
+    host = args.get("host", "localhost")
+    port = args.get("port", 22)
+    username = args.get("username", None)
+    if isEmpty(username):
+        username = getpass.getuser()
+    password = args.get("password", None)
+    keyfile = args.get("keyFile", None)
+    keytype = args.get("keyType", "RSA")
+
+    sftp = None
+    transport = None
+    try:
+        key = None
+        if keyfile is not None:
+            # Get private key used to authenticate user.
+            if keytype == 'DSA':
+                # The private key is a DSA type key.
+                key = paramiko.DSSKey.from_private_key_file(keyfile)
+            else:
+                # The private key is a RSA type key.
+                key = paramiko.RSAKey.from_private_key(keyfile)
+
+        # Create Transport object using supplied method of authentication.
+        transport = paramiko.Transport((host, port))
+        transport.connect(None, username, password, key)
+ 
+        sftp = paramiko.SFTPClient.from_transport(transport)
+        return sftp
+    except Exception as e:
+        print('An error occurred creating SFTP client: %s: %s' % (e.__class__, e))
+
+        if sftp is not None:
+            try:
+                sftp.close()
+            except Exception as err:
+                print('Failed to close SFTP client: %s: %s' % (err.__class__, err))
+
+        if transport is not None:
+            try:
+                transport.close()
+            except Exception as err:
+                print('Failed to close transport: %s: %s' % (err.__class__, err))
+
+        raise e
+    
+# =========================================================================================
+
+def doList(sftp, curdir, argsList):
+    dirPath = curdir;
+    if not isEmpty(argsList):
+        dirPath = argsList.pop(0)
+        dirPath = dirPath.strip()
+        dirPath = os.path.join(curdir, dirPath)
+
+    # Also available: listdir_attr, listdir
+    dirlist = sftp.listdir_iter(path=dirPath)
+    for row in dirlist:
+        # see https://docs.paramiko.org/en/2.6/api/sftp.html#paramiko.sftp_attr.SFTPAttributes
+        print("    %s" % str(row))
+
+def doChdir(sftp, homedir, curdir, argsList):
+    dirPath = homedir
+    if not isEmpty(argsList):
+        dirPath = argsList.pop(0)
+        dirPath = dirPath.strip()
+        dirPath = os.path.join(curdir, dirPath)
+    sftp.chdir(dirPath)
+
+# ----------------------------------------------------------------------------
+
+# see https://github.com/paramiko/paramiko/blob/master/demos/demo_sftp.py
+# see https://docs.paramiko.org/en/2.6/api/sftp.html
+def doSftp(sftp):
+    homedir = sftp.normalize('.')
+    sftp.chdir(homedir)
+    
+    while True:
+        curdir = sftp.getcwd()
+        sys.stdout.write("%s > " % curdir)
+        l = sys.stdin.readline()
+        l = l.strip()
+    
+        if isEmpty(l):
+            continue
+        
+        argsList = l.split(' ')
+        op = argsList.pop(0)
+        
+        if (op == "quit") or (op == "exit") or (op == "bye"):
+            break
+        elif (op == "ls") or (op == "list"):
+            doList(sftp, curdir, argsList)
+        elif (op == "cd"):
+            doChdir(sftp, homedir, curdir, argsList)
+        else:
+            print("Unknown command: %s" % l)
+
+def doMain(args):
+    sftp = createSftpClient(args)
+    try:
+        doSftp(sftp);
+    except Exception as e:
+        print('An error occurred using the SFTP client: %s: %s' % (e.__class__, e))
+        raise e
+    finally:
+        sftp.close()
+
+#
+# Usage: python3 sftpclient.py --arg1=value1 --arg2=value2 ...
+#
+# Where available arguments are:
+#
+#    * host - default=localhost
+#    * port - default=22
+#    * username - the login user - default=currently logged in user
+#    * password - the password - can be omitted if key file used
+#    * keyFile - path to key file
+#    * keyType - type of key in file (RSA/DSA) - default=RSA
+def main(args):
+    if len(args) > 0:
+        subArgs = parseCommandLineArguments(args)
+    else:
+        subArgs = {}
+    doMain(subArgs)
+    sys.exit(0)
+    
+# ----------------------------------------------------------------------------
+
+def signal_handler(signal, frame):
+    die('Exit due to Control+C')
+
+if __name__ == "__main__":
+    pyVersion = sys.version_info
+    if pyVersion.major != 3:
+        die("Major Python version must be 3.x: %s" % str(pyVersion))
+    if pyVersion.minor < 0:
+        print("Warning: minor Python version %s should be at least 3.0+" % str(pyVersion))
+
+    signal.signal(signal.SIGINT, signal_handler)
+    if os.name == 'nt':
+        print("Use Ctrl+Break to stop the script")
+    else:
+        print("Use Ctrl+C to stop the script")
+    main(sys.argv[1:])
\ No newline at end of file