#
# ftp_update.py		Version 1.1
#
# Copyright by Michael Neumann; 30.1.1999
# neumann@s-direktnet.de
# http://www.s-direktnet.de/homepages/neumann/index.htm
#
# Python ist kostenlos unter http://www.python.org erhältlich.
# 
# Dieses Programm stellt Änderungen in einem speziellen Verzeichnis fest, und
# aktualisiert ein Verzeichnis auf einem FTP-Server dementsprechend, daß beide
# den gleichen Inhalt haben.
# Wenn Sie beispielsweise Ihre Homepage in einem Verzeichnis auf ihrem Computer
# editieren und möchten, daß im Internet dasselbe zu sehen ist, können Sie einfach
# dieses Programm dafür benutzen.
# 



Aufruf = """
         1.) python.exe ftp_update.py -n filename
             Erzeugt ein neues Projekt . Sie werden automatisch
             aufgefordert, bestimmte Angaben zu machen.

         2.) python.exe ftp_update.py filename
	     Die Aktualisierung des FTP-Servers wird durch die Angaben des
             Projekts  durchgeführt.

         3.) python.exe ftp_update.py -nof filename
             Diese Option wird benötigt wenn sich schon etwas auf dem FTP-Server 
             befindet, und somit COMPUTER-Ordner und FTP-Server übereinstimmen.
             Es wird nur das Projekt aktualisiert und nichts übertragen."""


ProjektDateiAufbau = """
	Computer-Dir
	Ftp-Dir
	LogFile
	Host
	User
	Passwd
	Account	
	
	Data
"""





# eingebundene Module
import os, sys, string
from ftplib import FTP


on = 1
off = 0





################################################################################################
# generiert eine CRC-Prüfsumme der übergebenen Datei
################################################################################################

def crc(path):
	import sys, zlib
	buffer_size = 1024			
	f=open(path,'rb')
	fbuf = f.read(buffer_size)
	crc_val = zlib.crc32(fbuf)
	while len(fbuf)>0:
		fbuf = f.read(buffer_size)
		crc_val = zlib.crc32(fbuf,crc_val)
	f.close
	return crc_val	


################################################################################################
# File-Funktionen
################################################################################################

def writeln(fileptr, text):
	fileptr.write(text+'\n')

def readln(fileptr):
	return fileptr.readline()[:-1]


################################################################################################
# Utilities für den FTP-Zugriff
################################################################################################


ftp_handle = 0		# das erzeugte FTP-Objekt
ftp_level  = 0		# gibt die Höhe des Pfades an

def connect(host, user, passwd, acct):
	"Nimmt Verbindung mit dem FTP-Server auf"

	global ftp_handle
	ftp_level = 0
	ftp_handle = FTP(host,user,passwd,acct)



def disconnect():
	"Trennt Verbindung mit dem FTP-Server (nicht aber telefon!)"

	global ftp_handle
	ftp_handle.quit()




def create_dir(name):
	"erzeugt z.B. bei a\\b\\c\\ oder a\\b\\c\\x das verzeichnis c"
	"kann nur immer das letzte erzeugen, nicht mehrere auf einmal"
	
	global ftp_handle
	ftp_handle.mkd(_beforedir(name))
	


def delete_dir(name):
	"wie create_dir nur das es das verzeichnis löscht"
	
	global ftp_handle
	ftp_handle.rmd(_beforedir(name))


def copy_file(file,name):
	"file stellt den kompletten filenamen dar, name ist der name auf dem server mit verz"

	global ftp_handle
	name = _todir(name)
	f=open(file,'rb')
	ftp_handle.storbinary('STOR '+name, f, 1024)	
	f.close()



def delete_file(name):
	"löscht name auf dem server"
	
	global ftp_handle
	name = _todir(name)
	ftp_handle.sendcmd('DELE '+name)



def _cd(dir):
	"wechselt das Remote-Verzeichnis (nur ein Verzeichnis aufeinmal)"

	global ftp_level, ftp_handle
	if dir=='..': ftp_level = ftp_level - 1
	else:	      ftp_level = ftp_level + 1
	ftp_handle.cwd(dir)


def _home():
	"kehrt ins Home-Remote-Verzeichnis zurück"

	global ftp_level
	while ftp_level>0: _cd('..')


def _todir(dir):
	"wechselt in ein beliebiges Remote-Verzeichnis, Verzeichnis muß mit \\ enden"
	"es kann auch ein filename mit angegeben werden! z.B. a\\b wechselt in a, wie a\\"

	_home()
	a = string.split(dir,'\\')
	for i in a[:-1]: _cd(i)
	return a[-1]


def _beforedir(dir):
	"fast wie _todir nur ein verzeichnis weniger"
	"gibt das verzeichnis danach an"

	_home()
	a = string.split(dir,'\\')
	for i in a[:-2]: _cd(i)
	return a[-2]
			

################################################################################################
# Utilities für Listen (kürzesten String bzw. längsten aus einer Liste finden)
################################################################################################

def find_shortest(a):
	"sucht den kürzesten String aus der Liste 'a' heraus und gibt ihn zurück"
	if len(a)==0: return ''
	shortest = a[0]
	for i in a:
		if len(i)<len(shortest): shortest = i
	return shortest

def find_shortest2(a):
	"sucht den kürzesten String aus der doppel-Liste 'a' heraus und gibt ihn zurück"
	if len(a)==0: return ['',0]
	shortest = a[0][0]
	ptr = a[0]
	for i in a:
		if len(i[0])<len(shortest): 
			shortest = i[0]
			ptr = i	
	return [shortest,ptr]


def find_longest(a):
	"sucht den längsten String aus der Liste 'a' heraus und gibt ihn zurück"
	if len(a)==0: return ''
	longest = a[0]
	for i in a:
		if len(i)>len(longest): longest = i
	return longest
		
	
def find_longest2(a):
	"sucht den längsten String aus der doppel-Liste 'a' heraus und gibt ihn zurück"
	if len(a)==0: return ['',0]
	longest = a[0][0]
	ptr = a[0]
	for i in a:
		if len(i[0])>len(longest): 
			longest = i[0]
			ptr = i	
	return [longest,ptr]


################################################################################################
################################################################################################










def ende():
	raw_input("Ende ")
	sys.exit()




# Option -n
def new_project(filename):
	print "Verzeichnisse enden mit \\ !"
	f = open(filename,"w")
	a = raw_input("Computer-Verzeichnis: ")
	writeln(f,a)
	a = raw_input("FTP-Verzeichnis: ")
	writeln(f,a)
	a = raw_input("Logfile: ")
	writeln(f,a)
	print "Bei folgende Angaben wenn nicht zutreffend bitte nichts eingeben"
	a = raw_input("Host: ")
	writeln(f,a)
	a = raw_input("User: ")
	writeln(f,a)
	a = raw_input("Passwort: ")
	writeln(f,a)
	a = raw_input("Account: ")
	writeln(f,a)
	f.close()











#
# Listen
#
COMPdirList=[]
COMPfileList=[]
FTPdirList=[]
FTPfileList=[]

# enthält später das aktuelle Projektfile und wird dann nach erfolgreicher FTP-Übertragung
# geschrieben
changed_proj=[]



def check_dirs(srcdir, path):
	global changed_proj,COMPdirList,COMPfileList

	l = os.listdir(srcdir+path)
	for i in l:
		if os.path.isdir(srcdir+path+i):
			print srcdir+path+i+'\\'
			changed_proj.append(path+i+'\\')
			COMPdirList.append(path+i+'\\')
                        check_dirs(srcdir,path+i+'\\')
		else:			
			print srcdir+path+i
			changed_proj.append(path+i)
			crcsum = crc(srcdir+path+i)
			changed_proj.append(str(crcsum))
			COMPfileList.append([path+i,crcsum])



	

def update(filename,ftp):
	global COMPdirList, COMPfileList, FTPdirList, FTPfileList, changed_proj

	f = open(filename,'r')		# Projektdatei öffnen
	
	computer_dir = readln(f)
	ftp_dir = readln(f)
	logfile = readln(f)
	host = readln(f)
	user = readln(f)
	passwd = readln(f)
	account = readln(f)
	readln(f)			# leerzeile
	

	#
	# die Daten des Projektfiles werden ausgelesen
	#

	ln = readln(f)
	while len(ln)!=0:

		# 'ln' ist ein Verzeichnis
		if ln[-1]=='\\': FTPdirList.append(ln)

		# 'ln' ist eine Datei
		else: FTPfileList.append([ln,int(readln(f))])

		ln=readln(f)

	f.close()
	
	
	#
	# das Computer-Verzeichnis wird auf Veränderungen geprüft und das
	# aktualisierte Projekt in changed_proj gespeichert.
	#
		
	changed_proj.append(computer_dir)
	changed_proj.append(ftp_dir)
	changed_proj.append(logfile)
	changed_proj.append(host)
	changed_proj.append(user)
	changed_proj.append(passwd)
	changed_proj.append(account)
	changed_proj.append("")

	check_dirs(computer_dir,'')
	


	#
	# nun werden alle gleichen Einträge gelöscht
	#


	## Zuerst Files
	
	for i in FTPfileList[:]:
		try:
			COMPfileList.remove(i)
			FTPfileList.remove(i)
		except ValueError: pass

	for i in COMPfileList[:]:
		try:
			FTPfileList.remove(i)
			COMPfileList.remove(i)
		except ValueError: pass


	## Jetzt die Directories

	for i in FTPdirList[:]:
		try:
			COMPdirList.remove(i)
			FTPdirList.remove(i)
		except ValueError: pass

	for i in COMPdirList[:]:
		try:
			FTPdirList.remove(i)
			COMPdirList.remove(i)
		except ValueError: pass


	
	######################################################################
	# hier wird nun der FTP-Server upgedatet
	#
	# 1.) Files vom FTP-Server löschen
	# 2.) Directories vom FTP-Server löschen
	# 3.) Directories auf dem FTP-Server erstellen
	# 4.) Files auf den FTP-Server kopieren
	#
	######################################################################

	f=open(logfile,'w')
	print ''
	
	if len(COMPdirList)==0 and len(COMPfileList)==0 and len(FTPdirList)==0 and len(FTPfileList)==0:
		print 'Nothing to change'
		writeln(f,'Nothing to change')
		f.close()
		ende()
	
	try:
		if ftp==on: 
			connect(host, user, passwd, account)
			print 'Connected...'
			writeln(f,'Connected...')
			
		# 1.) Files vom FTP-Server löschen
		a = find_longest2(FTPfileList)
		while a[0]!='':
			# a[0] löschen
			print 'delete_file '+ftp_dir + a[0]
			writeln(f,'delete_file '+ftp_dir+a[0])
			try:
				if ftp==on: delete_file(ftp_dir+a[0])
			except:
				print 'could not delete file <'+ftp_dir + a[0] +'>'
				writeln(f,'could not delete file <'+ftp_dir + a[0] +'>')
			FTPfileList.remove(a[1])
			a = find_longest2(FTPfileList)
			
			
		# 2.) Directories vom FTP-Server löschen
		a = find_longest(FTPdirList)
		while a!='':
			print 'delete_dir '+ftp_dir+a
			writeln(f,'delete_dir '+ftp_dir+a)
			try:
				if ftp==on: delete_dir(ftp_dir+a)
			except:
				print 'could not delete directory <'+ftp_dir + a +'>'
				writeln(f,'could not delete directory <'+ftp_dir + a +'>')
			FTPdirList.remove(a)
			a = find_longest(FTPdirList)
		
		
		# 3.) Directories auf dem FTP-Server erstellen
		a = find_shortest(COMPdirList)
		while a!='':
			print 'create_dir '+ftp_dir+a
			writeln(f,'create_dir '+ftp_dir+a)
			try:
				if ftp==on: create_dir(ftp_dir+a)
			except:
				print 'could not create directory <'+ftp_dir + a +'>'
				writeln(f,'could not create directory <'+ftp_dir + a +'>')
			COMPdirList.remove(a)
			a = find_shortest(COMPdirList)
	
	
		# 4.) Files auf den FTP-Server kopieren
		a = find_shortest2(COMPfileList)
		while a[0]!='':
			print 'copy_file '+computer_dir+a[0]+' -> '+ftp_dir + a[0]
			writeln(f,'copy_file '+computer_dir+a[0]+' -> '+ftp_dir + a[0])
			try:
				if ftp==on: copy_file(computer_dir+a[0],ftp_dir+a[0])
			except:
				print 'could not copy file '+computer_dir+a[0]+' -> '+ftp_dir + a[0]
				writeln(f,'could not copy file '+computer_dir+a[0]+' -> '+ftp_dir + a[0])				
			COMPfileList.remove(a[1])
			a = find_shortest2(COMPfileList)
		
	
		
		if ftp==on: 
			disconnect()
			print 'Disconnected'			
			writeln(f,'Disconnected')
			print 'Bitte Ihre Telefonverbindung trennen, wenn das nicht automatisch geht'
		f.close()

	except:
		print 'Ein Fehler ist aufgetreten! Die Projektdatei wurde nicht upgedated!'
		print 'Computer und FTP-Server können unterschiedliche Inhalte haben!'
		f.close()
		ende()

	f = open(filename,"w")
	for i in changed_proj:
		writeln(f,i)
	print '--- Success ---'
	ende()
	
	
	
	
	



#### main ####
if len(sys.argv)==3:
	if sys.argv[1] == '-n':
		new_project(sys.argv[2])
		ende()
	elif sys.argv[1] == '-nof':
		update(sys.argv[2],off)
		ende()
	else: 
		print "!!!! Falscher Parameter !!!!"
		print
		print Aufruf
		ende()
elif len(sys.argv)==2:
	update(sys.argv[1],on)
	ende()
else:
	print "!!!! Falsche Anzahl an Parametern !!!!"
	print
	print Aufruf
	ende()




#################################################################################
# Copyright (c) 1999 by Michael Neumann
#################################################################################