summaryrefslogtreecommitdiffstats
path: root/sftp_syncfiles
blob: 35569fd398e517a72ca6a04e2e80935aa763225a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#!/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)