#!/usr/bin/python # Client program version = "0.1" # # MISSING FEATURES # # FEA1 : Multiple servers handling # FEA2 : md5 file identification based # FEA3 : REGISTER checks md5 and date # FEA5 : Defining and implementing command line option # FEA6 : Objectify the application # FEA7 : Support links and directory # FEA8 : Support bandwitdh limitation # FEA9 : Limit listening interface # FEA10 : Configuration for log cleaner # FEA12 : rigths handling # FEA13 : Secure udp stream # FEA14 : Plugable file treatment # FEA16 : Support for small changes only such as rigths or mtime # FEA17 : Recursively files and directories stucture # FEA18 : alteration detection and automatic trigger transfert # import sys import string import os import time import md5 from datetime import datetime from stat import * from socket import * from DefaultConfigParser import DefaultConfigParser config = DefaultConfigParser() config.read("/etc/puftp.ini") # Set the misc parameters host = config.get("Client", "udpAddress", '') port = config.get("Client", "udpPort", 12000) bufferSize = config.get("Data", "bufferSize", 1024) allowBroadcast=config.get("Client", "allowUdpBroadcast", 1) messageTimeout=config.get("Client", "messageTimeout",4) bannerMessage=config.get("Client", "bannerMessage", "Welcome to puftp v%s" % version) verbose=config.get("Client", "verbose", 1) maxResent=config.get("Client", "maxResent",3) registered_servers=[] ip=gethostbyaddr(gethostname())[2][0] def uniq(alist): # Fastest order preserving set = {} return [set.setdefault(e,e) for e in alist if e not in set] def getNowEpoch(): return str(time.mktime(datetime.now().timetuple())).split('.')[0] def handleError( request ): if verbose: print "*** ERROR;%s;%s" % (getNowEpoch(), request) def handleAckRegister( request ): try: type, serverIp, serverDate, fileName, remoteStatus = request.split(';') except: print "### Error while reading request (%s)" % type return handleError(request) if remoteStatus == "KO": registered_servers.append(serverIp) if verbose: print "*** Server '%s' added in the registered server list for '%s'" % (serverIp, fileName) else: if verbose: print "*** Server %s : nothing to do for '%s'" %(serverIp, fileName) def handleAckCheck( request ): try: type, serverIp, serverDate, fileName, remoteStatus, missings = request.split(';', 5) except: print "### Error while reading request (%s)" % type return handleError( request ) lMissings=[] if remoteStatus == "KO": for i in missings.split(';'): lMissings.append(int(i)) if verbose: print "*** Server '%s' missed some data index : %s for '%s'" %(serverIp, lMissings, fileName) else: if verbose: print "*** Server %s is OK: nothing to do for '%s'" %(serverIp, fileName) return lMissings def handleAckCommit( request ): try: type, serverIp, serverDate, fileName, remoteStatus = request.split(';') except: print "### Error while reading request (%s)" % type return handleError( request ) if remoteStatus == "KO": if verbose: print "*** Server '%s' failed committing file '%s' " %(serverIp, fileName) else: if verbose: print "*** Server %s have commit '%s' sucessfuly" %(serverIp, fileName) registered_servers.remove(serverIp) def handleServerRequest( request ): handlers = { "ACK_REG": handleAckRegister, "ACK_CHECK": handleAckCheck, "ACK_COMMIT": handleAckCommit } if verbose: print "*** HANDLING '%s' request" % request.split(';', 1)[0] return handlers.get(request.split(';', 1)[0], handleError)(request) addr = (host,port) # Create socket UDPSock = socket(AF_INET,SOCK_DGRAM) UDPSock.setsockopt(SOL_SOCKET, SO_BROADCAST, allowBroadcast) UDPSock.settimeout(messageTimeout) #def_msg = bannerMessage #print "\n",def_msg localFilename=sys.argv[1] if not os.path.exists(localFilename): print "%s doen't exists locally " % localFilename os._exit (1) # registered phase statinfo=os.stat(localFilename) localSize=statinfo[ST_SIZE] localLastModified=statinfo[ST_MTIME] registeredData="REGISTER;%s;%s;%s" % (localFilename, localSize, localLastModified) if verbose: print "--> Sending registered message '",registeredData,"'....." if(UDPSock.sendto(registeredData, addr)): try: while (1): reqAck,addrServ = UDPSock.recvfrom(bufferSize) if reqAck: if verbose: print "<-- Server response received'", reqAck.strip(),"'" handleServerRequest(reqAck) except : if verbose: print "*** No more server registration" if len(registered_servers)==0: if verbose: print "No registered server" os._exit(0) for serverIp in registered_servers: if verbose: print "*** %s registered as destination server for '%s'" % (serverIp, sys.argv[1]) f = open(localFilename, 'r') nbDataReq=localSize/(bufferSize-47)+1 if verbose: print "*** %d requests needed to transfer %s(%d)" % (nbDataReq, localFilename, localSize) listTransfertIndex=range(0, nbDataReq) if verbose: print "*** request to send : %s" % (listTransfertIndex) #Transfer data phase while len(listTransfertIndex)>0 and maxResent>0: for i in listTransfertIndex : chunckDatas="DATA;%024d;%16s;" % (i, localFilename) if verbose: print "*** MetaData effective size : %d" % len(chunckDatas) metaSize= len(chunckDatas) dataSize=bufferSize-metaSize if verbose: print "*** reading %d bytes of data in the file. %d bytes of meta data" % (dataSize, metaSize) f.seek(i*dataSize) dataFile=f.read( dataSize ) if verbose: print "*** %d bytes reading file '%s'" % (len(dataFile), localFilename) sendSize=UDPSock.sendto("%s%s" % (chunckDatas, dataFile), addr) if verbose: print "--> Sending %d bytes : %s ...." % (sendSize, chunckDatas) listTransfertIndex=[] checkData="CHECK;%s;%s;%s;%d" % (localFilename, localSize, localLastModified, nbDataReq) if verbose: print "--> Sending checked message '", checkData,"'....." if(UDPSock.sendto(checkData, addr)): try: while (1): reqAck,addrServ = UDPSock.recvfrom(bufferSize) if reqAck: if verbose: print "<-- Server response received'", reqAck.strip(),"'" listTransfertIndex+=handleServerRequest(reqAck) except : if verbose: print "*** No more server check response" listTransfertIndex=uniq(listTransfertIndex) # building the missing index maxResent-=1 if verbose: print "*** %d tries left" % maxResent if verbose: print "*** Following index missed by servers : %s" % listTransfertIndex f.close() # commit phase commitData="COMMIT;%s;%s;%s" % (localFilename, localSize, localLastModified) if verbose: print "--> Sending commit message '", commitData,"'....." if(UDPSock.sendto( commitData, addr)): try: while (1): reqAck,addrServ = UDPSock.recvfrom(bufferSize) if reqAck: if verbose: print "<-- Server response received'", reqAck.strip(),"'" handleServerRequest(reqAck) except : if verbose: print "*** No more server commit ack" if len(registered_servers)==0: if verbose: print "*** All servers have commited '%s'" %localFilename elif verbose: print "*** %s servers have failed commiting '%s'" % (registered_servers, localFilename) # # Close socket UDPSock.close()