101 lines
3.8 KiB
Python
Executable File
101 lines
3.8 KiB
Python
Executable File
#!/usr/bin/python3
|
|
import argparse
|
|
import cryptography
|
|
import glob
|
|
import os
|
|
import paramiko
|
|
import warnings
|
|
warnings.simplefilter("ignore", cryptography.utils.CryptographyDeprecationWarning)
|
|
|
|
|
|
def connection(hostname, username, password=None, keyfile=None):
|
|
if not keyfile:
|
|
keyfile = os.path.expanduser('~/.ssh/id_rsa')
|
|
key = paramiko.RSAKey.from_private_key_file(keyfile)
|
|
client = paramiko.SSHClient()
|
|
client.load_system_host_keys()
|
|
client.connect(hostname, username='csiraut', password=password, pkey=key)
|
|
return paramiko.SFTPClient.from_transport(client.get_transport())
|
|
|
|
|
|
def validate_local_path(path):
|
|
if not os.path.exists(path) or not os.path.isdir(path):
|
|
raise(Exception('Directory %s does not exist' % path))
|
|
|
|
|
|
def validate_remote_path(sftp, path):
|
|
lstat = sftp.lstat(path)
|
|
mode = oct(lstat.st_mode)[5]
|
|
if mode not in ['5', '7']:
|
|
raise(Exception('Remote path is not a folder or not writable'))
|
|
|
|
|
|
def parse_location(loc):
|
|
hostname, username, path = None, None, loc
|
|
if '@' in loc and ':' in loc:
|
|
username, remaning = loc.split('@')
|
|
hostname, path = remaning.split(':')
|
|
return hostname, username, path
|
|
|
|
|
|
def get(hostname, username, remotepath, localpath, password=None, keyfile=None):
|
|
validate_local_path(localpath)
|
|
sftp = connection(hostname, username, password=password, keyfile=keyfile)
|
|
validate_remote_path(sftp, remotepath)
|
|
|
|
dirlist = sftp.listdir_attr(remotepath)
|
|
filelist = [x.filename for x in dirlist if oct(x.st_mode)[5] not in ('5', '7')]
|
|
for f in filelist:
|
|
if args.verbose:
|
|
print('syncing %s to %s' % (remotepath, localpath))
|
|
remotefilepath = '%s/%s' % (remotepath, f)
|
|
localfilepath = '%s/%s' % (localpath, f)
|
|
sftp.get(remotefilepath, localfilepath)
|
|
if args.delete:
|
|
if args.verbose:
|
|
print('removing %s' % remotefilepath)
|
|
sftp.remove(remotefilepath)
|
|
|
|
|
|
def put(hostname, username, remotepath, localpath, password=None, keyfile=None):
|
|
validate_local_path(localpath)
|
|
sftp = connection(hostname, username, password=password, keyfile=keyfile)
|
|
validate_remote_path(sftp, remotepath)
|
|
|
|
filelist = [f for f in glob.glob('%s/*' % localpath) if not os.path.isdir(f)]
|
|
for f in filelist:
|
|
remotefilepath = '%s/%s' % (remotepath, f.split('/')[-1])
|
|
if args.verbose:
|
|
print('syncing %s to %s' % (f, remotefilepath))
|
|
sftp.put(f, remotefilepath)
|
|
if args.delete:
|
|
if args.verbose:
|
|
print('removing %s' % f)
|
|
os.remove(f)
|
|
|
|
|
|
def main(args):
|
|
orig = parse_location(args.origin)
|
|
dest = parse_location(args.destination)
|
|
if (orig[0] and dest[0]) or (not orig[1] and not dest[1]) or \
|
|
((orig[1] and '@' not in orig[1]) and (dest[1] and '@' not in dest[1])):
|
|
raise(Exception('Please provide one remote location like this: user@server:/path/to/direction'))
|
|
if orig[1]:
|
|
action, hostname, username, remotepath, localpath = 'get', orig[0], orig[1], orig[2], dest[2]
|
|
else:
|
|
action, hostname, username, remotepath, localpath = 'put', dest[0], dest[1], dest[2], orig[2]
|
|
|
|
globals()[action](hostname, username, remotepath, localpath, args.password, args.keyfile)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description='sftp.py — secure file transfer program')
|
|
parser.add_argument('origin', help='directory holding files to sync')
|
|
parser.add_argument('destination', help='target directory')
|
|
parser.add_argument('--keyfile')
|
|
parser.add_argument('--password')
|
|
parser.add_argument('--delete', action='store_true', help='remove source file(s) when transfered')
|
|
parser.add_argument('--verbose', action='store_true')
|
|
args = parser.parse_args()
|
|
main(args)
|