Source

filesnake / filesnake / networking / client.py

Full commit
'''
This defines theprincipal client
'''
from twisted.spread import pb
from twisted.internet import reactor,protocol,defer
from twisted.internet.protocol import ClientCreator
from ..helpers.path import get_size
from .fileprotocol import FileClientProtocol
import os

class SendingClient(object):
    """
    This is a client Object that handles the sending of the resource
    in general, the file or the dir
    """
    
    def __init__(self, name, host, port):
        self.name = name
        self.factory = pb.PBClientFactory()
        self._connection = reactor.connectTCP(host, port , self.factory)

    def disconnect(self):
        self._connection.disconnect()
        
    def request_session(self, path, size):
        '''
        Request sending a file or a folder
        
        returns a deferred in case we want to chain the errback
        '''
        basename = os.path.basename(path)
        size = get_size(path)     
        
        d = self.factory.getRootObject()
        d.addCallback(call_remote("request_session",self.name, basename, size))
        return d
    
    @defer.inlineCallbacks
    def get_directives(self):
        root = yield self.factory.getRootObject()
        directives = yield root.callRemote("get_directives")
        defer.returnValue(directives)

    @defer.inlineCallbacks
    def send_file(self, secret, path, prefix = "", monitor = None):
        '''
        Send a file over network
        '''
        # Getting info about the file service
        self.file_service = yield self.get_directives()
        
        # same hostname as the perspective broker one
        host = self._connection.getDestination().host
        port = self.file_service
        
        # Creating a single use client
        c = ClientCreator(reactor, FileClientProtocol)
        proto = yield c.connectTCP(host, port)
        
        # Check authorization
        auth = yield proto.authorize(secret)
        
        if not auth:
            return
        
        # Sending the file descriptor and the destination
        basename = os.path.basename(path)
        dest = os.path.join(prefix,basename)
        fd = open(path, "rb")
        
        # Returning a deferred
        proto.send_resource(dest, fd, monitor)
        
    @defer.inlineCallbacks
    def send_folder(self, secret, path,  monitor = None):
        root = yield self.factory.getRootObject()

        folder_maker = yield root.callRemote("get_folder_maker", secret)
        # Should produce and pack the monitor
        
        # Making dir structure
        
        # Toplevel Folder, tr is trash, a variable to let yield go
        toplevel = os.path.basename(path)
        tr = yield folder_maker.callRemote("mkdir", toplevel)
        
        for root, dirs, files in os.walk(path):
            for direc in dirs:
                # Getting relative path
                # if I have files like /home/user/folder/file.txt
                # this becomes folder/file.txt
                relative_dir = os.path.relpath(
                    os.path.join(root,direc),
                    path)
                # tr = yield
                folder_maker.callRemote("mkdir", os.path.join(toplevel,relative_dir))
                
            for f in files:
                # Should send files under the current relative dir
                
                prefix = os.path.join(toplevel,
                                      os.path.relpath(root, path))
                prefix = os.path.normpath(prefix) # Cleaning relpath ./
                
                filepath = os.path.join(root, f)
                #XXX I'll chain them only if it's necessary
                # This forces the sending to happen
                # tr = yield
                self.send_file(secret, filepath, prefix = prefix, monitor=monitor)

        
def call_remote( name, *args):
    '''
    it's a little closure
    '''
    def callback(root, ):
        return root.callRemote(name, *args)
    return callback