Python:SimpleFTP

From Devicenull's Code

Jump to: navigation, search

This is a simple wrapper around the Python:ReliableFTP class that provides some common FTP operations

import urlparse, os, sys, re
from ftplib import FTP
from reliableftp import ReliableFTP
 
class FTPFileListProtocol:
# stolen from twisted, who knows what licenses I am breaking
    fileLinePattern = re.compile(
        r'^(?P<filetype>.)(?P<perms>.{9})\s+(?P<nlinks>\d*)\s*'
        r'(?P<owner>\S+)\s+(?P<group>\S+)\s+(?P<size>\d+)\s+'
        r'(?P<date>...\s+\d+\s+[\d:]+)\s+(?P<filename>([^ ]|\\ )*?)'
        r'( -> (?P<linktarget>[^\r]*))?\r?$'
    )
    delimiter = '\n'
    def parseDirectoryLine(self, line):
        """Return a dictionary of fields, or None if line cannot be parsed.
 
        @param line: line of text expected to contain a directory entry
        @type line: str
 
        @return: dict
        """
        match = self.fileLinePattern.match(line)
        if match is None:
            return None
        else:
            d = match.groupdict()
            d['filename'] = d['filename'].replace(r'\ ', ' ')
            d['nlinks'] = int(d['nlinks'])
            d['size'] = int(d['size'])
            if d['linktarget']:
                d['linktarget'] = d['linktarget'].replace(r'\ ', ' ')
            return d
 
valid_commands = ['S','O','A']
 
class SimpleFTP:
    def __init__(self,url):
        u = urlparse.urlparse(url)
        if u[0] != 'ftp':
            print "This isn't a ftp URL"
            exit
 
        fullhost = u[1].split('@',1)
 
        user = fullhost[0].split(':',1)[0]
        password = fullhost[0].split(':',1)[1]
 
        host = fullhost[1]
 
        self.ftp = ReliableFTP(host,user,password)
        #self.ftp.set_debuglevel(1)
 
    def ls(self,dest_dir=""):
        orig_dir = self.ftp.pwd()
        if dest_dir != "":
            self.ftp.cwd(dest_dir)
 
        lines = []
        def appendlines(curline):
            lines.append(curline)
        files = self.ftp.retrlines("LIST",appendlines)        
        files = []
        flist = FTPFileListProtocol()
        for c in lines:
            d = flist.parseDirectoryLine(c)
            if d == None:
                continue
            cur_item = d['filename']
            cur_type = d['filetype'][0]
            files.append((cur_type,cur_item))
 
        self.ftp.cwd(orig_dir)
        return files
 
    # This callback occurs when a file already exists.. the default method is to ask the user
    # Must return (S)kip, (O)verwrite, (A)bort    
    @staticmethod
    def onExists(filename):
        print "File %s already exists" % (filename)
        inp = raw_input("What should I do? Skip, Overwrite, Abort (S|O|A) ")
        while (valid_commands.count(inp) == 0):
            inp = raw_input("What should I do? Skip, Overwrite, Abort (S|O|A) ")    
        return inp
 
    def uploadDirectory(self,source_dir,dest_dir,onExistsHandler=onExists):
        orig_dir = self.ftp.pwd()
        src_len = len(source_dir)
        for root, dirs, files in os.walk(source_dir):
            current_src_dir =  root[src_len:].replace("\\","/")
            #print "R: %s D: %s F: %s" % (root, dirs, files)
            if (len(root) == src_len):
                #print 'In root directory'
                self.ftp.cwd(dest_dir)
            else:
                #print 'In directory: %s' % current_src_dir
                self.ftp.cwd("%s%s" % (dest_dir,current_src_dir))
 
            for cur_dir in dirs:
                try:
                    self.ftp.mkd(cur_dir)
                except:
                    pass
            file_list = self.ls()
            for cur_file in files:
                exists = 0
                for cur_type, filename in file_list:
                    if (filename == cur_file):
                        exists = 1
                        break
                inp = ""
                if exists == 1:                        
                    inp = onExistsHandler("%s/%s" % (current_src_dir,cur_file))
 
                if inp == 'A':
                    print 'Aborting..'
                    sys.exit(1)
                elif inp == 'O' or exists == 0:                      
                    f = open("%s/%s" % (root,cur_file),"rb")
                    if cur_file[0] == '\\' or cur_file[0] == '/':
                        cur_file = cur_file[1:]
                    self.ftp.storbinary("STOR %s" % cur_file,f)
                    f.close()
 
        self.ftp.cwd(orig_dir)
 
    def uploadFile(self,dest_dir,dest_name,file_obj):
        orig_dir = self.ftp.pwd()
        self.ftp.cwd(dest_dir)
        self.ftp.storbinary("STOR %s" % dest_name,file_obj)
        self.ftp.cwd(orig_dir)
 
    def lookForFile(self,filename,ignoremasks=[]):
        return self.lookForObject(filename,ignoremasks,'F')
 
    def lookForDirectory(self,filename,ignoremasks=[]):
        return self.lookForObject(filename,ignoremasks,'D')    
 
    # Generic version of lookForWhatever, 'F' for file, 'D' for directory
    def lookForObject(self,filename,ignoremasks,obj_type='F'):
        try:
            orig_dir = self.ftp.pwd()
            res = ""
            files = self.ls()
            directories = []
            for cur_type, cur_item in files:
                if obj_type == 'F':
                    if cur_type == 'd':
                        directories.append(cur_item)          
                    else:
                        #print '* File: %s' % cur_item
                        if cur_item == filename:
                            skip = 0
                            for c in ignoremasks:
                                if orig_dir.find(c) != -1:
                                    skip = 1
                            if skip == 0:
                                location = self.ftp.pwd()
                                self.ftp.cwd(orig_dir)
                                return location
                else:
                    if cur_type == 'd':
                        if cur_item == filename:
                            skip = 0
                            for c in ignoremasks:
                                if orig_dir.find(c) != -1:
                                    skip = 1
                            if skip == 0:
                                location = self.ftp.pwd()
                                self.ftp.cwd(orig_dir)
                                return location     
 
            # ok, it wasn't in this directory, keep looking!
            for c in directories:
            	skip = 0
            	for ig in ignoremasks:
            		if c.find(ig) != -1:
            			skip = 1
            	if skip == 1:
            		continue
                print 'Entering directory %s' % (c)
                self.ftp.cwd(c)
                res = self.lookForObject(filename,ignoremasks,obj_type)
                self.ftp.cwd(orig_dir)
                if res != "":
                    return res
 
            self.ftp.cwd(orig_dir)
            return "" 
        except Exception, e:
            print 'Original directory: %s' % orig_dir
            print 'Current directory: %s' % self.ftp.pwd()
            raise        
 
    def downloadFile(self,src_dir,src_file,dest_obj):
        orig_dir = self.ftp.pwd()
        self.ftp.cwd(src_dir)
        self.ftp.retrbinary("RETR %s" % src_file, dest_obj.write)
        self.ftp.cwd(orig_dir)