#!/usr/bin/env python
import sys,os,re,getopt,subprocess,urllib2
from Carbon.Launch import LSFindApplicationForInfo
from Carbon.CoreFoundation import kCFURLPOSIXPathStyle
#===============================================================================================
kPreferenceUTI = "ch.ruedi.tools.checkVersions"
kBestVersionsURLBase = "http://www.toposmm.ch/RuediSite/versions"
kToolVersion = "1.9.5"
#===============================================================================================
debug = 0
filterPattern = ""
sendEMailPrefix = ""
execute = 1
configurePreferences = 0
ignoreNoVersionInfo = 0
requiredInstallation = 0
loadAppInstaller = 0
showAppOldInfo = 0
showResultsWithDialog = 0
FinderAvailable = 0
undefinedVersions = 0
validVersions = 0
triggerAutoUpdate = 0
verbose = 0
#===============================================================================================
def HabenRequiredFormat(CheckItemKind, BinaryItems):
	print(CheckItemKind+" has wrong items: " + str(BinaryItems))
	print("Expected are at least 3 values: " + CheckItemKind + " uti name [info-Item]")
	print("")
	sys.exit(2)
#===============================================================================================
def	DirectCmd(ShellCmd):
	p =	subprocess.Popen(ShellCmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
	return p.stdout.read().rstrip()
#===============================================================================================
def	RunShellCmd(ShellCmd):
	if debug: print(ShellCmd)
	if not execute: return
	#
	return DirectCmd(ShellCmd)
#===============================================================================================
def	AskPrefs(AppPreferenceFile,TokenToFind):
	answer = ""
	try:
		answer = DirectCmd("defaults read \"" + AppPreferenceFile + "\" " + TokenToFind)
	except:
		answer = ""
	return answer
#===============================================================================================
def	AskBundlePrefs(BundleDirectory, TokenToFind, BundleSubDirectory = "Contents", BundleInfoPropertyFile = "Info"):
	return AskPrefs(BundleDirectory + "/" + BundleSubDirectory + "/" + BundleInfoPropertyFile,TokenToFind)
#===============================================================================================
def	AskUTIPrefs(TokenToFind):
	return AskPrefs(kPreferenceUTI,TokenToFind)
#===============================================================================================
def	SetUTIPrefs(TokenToWrite,ValueToWrite):
	writeDone = 0
	try:
		answer = DirectCmd("defaults write " + kPreferenceUTI + " " + TokenToWrite + " " + ValueToWrite)
		writeDone = 1
	except:
		writeDone = 0
	return writeDone
#===============================================================================================
def	SendEMail(Messages):
	import smtplib
	import base64
	# create message preferences
	#
	preferenceFile = os.getenv("HOME")+"/Library/Preferences/"+kPreferenceUTI+".plist"
	if not os.path.isfile(preferenceFile): 
		print(">>> ")
		print(">>>  mail-configuration " + preferenceFile + " is missing")
		print(">>> ")
		return 0
	fromAddress = AskUTIPrefs("FromAddress")
	toAddress = AskUTIPrefs("ToAddress")
	servername = AskUTIPrefs("SMTPServer")
	username = AskUTIPrefs("SMTPUsername")
	passwordEncoded = AskUTIPrefs("SMTPPassword")
	serverPort = AskUTIPrefs("SMTPPort")
	if servername == "" or toAddress == "": 
		print(">>> ")
		print(">>> mail-configuration not found in " + kPreferenceUTI)
		print(">>> ")
		return 0
	#
	# build message
	#
	mailSubject = "checkVersions " + kToolVersion + " " + hostname
	mailContent = "From: %s\n" % fromAddress
	mailContent = mailContent + "To: %s\n" % toAddress
	mailContent = mailContent + "Subject: "+mailSubject+"\n\n"
	for zeile in Messages: mailContent = mailContent + zeile + "\n"
	#
	if debug: print(mailContent)
	if not execute: return 0
	#
	# send message (and decode password on the fly)
	#
	sendmailPort = 25
	if serverPort != "":
		try:
			sendmailPort = int(serverPort)
		except:
			sendmailPort = 25
	elif passwordEncoded != "":
		sendmailPort = 587
	#
	sentByEMail = 0
	try:
		server = smtplib.SMTP(servername,sendmailPort)
		if debug: server.set_debuglevel(3)
		if username != "" and passwordEncoded != "" : server.login(username,base64.b64decode(passwordEncoded))
		server.sendmail(fromAddress,toAddress,mailContent)
		server.quit()
		sentByEMail = 1
		if verbose: print("results sent to " + toAddress)
	except:
		sentByEMail = 0
	return sentByEMail
#===============================================================================================
def	ItemNotFound(NameTitle,NameIdentifier):
	if not showResultsWithDialog:
		print(">>>")
		print(">>> Required " + NameTitle + " \"" + NameIdentifier + "\" not found")
		print(">>>")
#===============================================================================================
def	FindApp(AppIdentifier,RequiredInstallation):
	kLSUnknownCreator = '\x00\x00\x00\x00'
	try:
		(fsRef, cfURL) = LSFindApplicationForInfo(kLSUnknownCreator, AppIdentifier, None)
		return fsRef.as_pathname()
	except:
		if RequiredInstallation:
			ItemNotFound("application",AppIdentifier)
	return ""
#===============================================================================================
def	FindBinaryDir(PathDir, Name, Extension,RequiredInstallation):
	baseDirList = []
	baseDirList.append("")
	baseDirList.append(os.getenv("HOME")+"/")
	for prefixPath in baseDirList:
		bcInfo = prefixPath + PathDir + "/" + Name + "." + Extension
		if os.path.isdir(bcInfo): return bcInfo
	#
	if RequiredInstallation:
		ItemNotFound("directory",Name + "." + Extension)
	return ""
#===============================================================================================
def	FindBinaryFile(PrefixPath, Name, Extension,RequiredInstallation):
	if PrefixPath == "":
		bcInfo = Name
	else:
		bcInfo = PrefixPath + "/" + Name + "." + Extension
	if not os.path.isfile(bcInfo):
		if RequiredInstallation:
			ItemNotFound("file",bcInfo)
		return ""
	return bcInfo
#===============================================================================================
def	GetFullURL(VersionsURLFile,AppInfoURL):
	if "/" in AppInfoURL: return AppInfoURL
	(nurPfad,n) = os.path.split(VersionsURLFile)
	return nurPfad+"/"+AppInfoURL
#===============================================================================================
def	UniqueItems(ListofValues):
	uniqueList = []
	for singleValue in ListofValues:
		if not singleValue in uniqueList:
			uniqueList.append(singleValue)
	return uniqueList
#===============================================================================================
def uniqueSchemaNames(ListIndex):  
	checked = [] 
	for e in alleSchemas[ListIndex]: 
			if e in checked: 
				print (e + " exists already")
			else:
				checked.append(e)
	return len(checked) == len(alleSchemas[ListIndex])
#===============================================================================================
def	ExecuteURL(VersionsURL, AppInfoURL, SaveLocation = ""):
	# nur URL zulassen
	if ":" not in AppInfoURL: return
	#
	urlToLoad = GetFullURL(VersionsURL, AppInfoURL)
	# python-Skripte werden so kopiert
	if SaveLocation != "":
		try:
			f = urllib2.urlopen(urlToLoad)
			data = f.read()
			with open(SaveLocation, "w") as fd:
				fd.write(data)
			if verbose: print("Installing "+urlToLoad+" into "+SaveLocation + " ...")
		except:
			print("--------------------------------")
			print(urlToLoad+" is not available")
			print("--------------------------------")
	else:
		RunShellCmd("open " +urlToLoad)
#===============================================================================================
def	ExecuteInfoItems(VersionsURL, InfoURL, LoadURL, LoadAction, SaveLocation = ""):
	#
	if showAppOldInfo and InfoURL != "": ExecuteURL(VersionsURL,InfoURL)
	if loadAppInstaller and LoadURL != "": 
		if "auto-update" in LoadAction.lower():
			ExecuteURL(VersionsURL, LoadURL, SaveLocation)
		else:
			ExecuteURL(VersionsURL, LoadURL)
#===============================================================================================
def	AskURL(VersionsURL, AppIdentifier):
	try:
		f = urllib2.urlopen(VersionsURL)
	except:
		print("")
		print(VersionsURL+" is not available")
		print("")
		sys.exit(2)
	alleZeilen = f.readlines()
	anzahlZeilen = len(alleZeilen)
	i = 0
	#
	# hier wird der Dict definiert
	#
	appInfo = {'version':"", 'versionDetail':"", 'info':"", 'load': "", 'loadAction': "", 'updater': "", 'title':""}
	#
	# immer 2 Zeilen werden angeschaut
	# 
	while (i < anzahlZeilen):
		z = alleZeilen[i]
		zweiteZeile = ""
		if (i < anzahlZeilen-1): zweiteZeile = alleZeilen[i+1].rstrip()
		i=i+1
		ersteZeile = z.rstrip()
		if len(ersteZeile) > 0 and ersteZeile[0] != '#':
			tokens = ersteZeile.split()
			#print tokens
			if len(tokens) > 1:
				# compare always with lowercase
				requestAppIdentifier = tokens[0].lower().replace("%20"," ")
				if requestAppIdentifier == AppIdentifier.lower():
					if debug: print ("AskURL: "+requestAppIdentifier + " =?= " + AppIdentifier.lower())
					appInfo['version'] = tokens[1].replace("%20"," ")
					if zweiteZeile != "<reserved>":	
						validHints = 0
						for keyWord in [":",".htm",".rss"]:
							if keyWord in zweiteZeile: validHints = validHints + 1
						if validHints > 0:
							infoAndLoad = zweiteZeile.split()
							appInfo['info'] = infoAndLoad[0]
							if len(infoAndLoad) > 1: appInfo['load'] = infoAndLoad[1]
							if len(infoAndLoad) > 2: appInfo['loadAction'] = infoAndLoad[2]
						else:
							print("")
							print(tokens[0] + " : " + zweiteZeile + " seems not to be an URL,feed or similar")
							print("")
					if len(tokens) > 2: 
						# wir akzeptieren keywords mit comment,updater,title:
						for einzelToken in tokens[2:]:
							if "comment:" in einzelToken:
								tok = einzelToken.split("comment:")
								if len(tok[1]) > 0: appInfo['versionDetail'] = tok[1].replace("%20"," ")
							elif "title:" in einzelToken:
								tok = einzelToken.split("title:")
								if len(tok[1]) > 0: appInfo['title'] = tok[1].replace("%20"," ")
							elif "updater:" in einzelToken:
								tok = einzelToken.split("updater:")
								if len(tok[1]) > 0: appInfo['updater'] = tok[1].replace("%20"," ")
							else:
								appInfo['versionDetail'] = einzelToken.replace("%20"," ")
					return appInfo
	return appInfo
#
#===============================================================================================
def	ScanVersionURL(ThisVersionFile):
	global checkURL_List,checkBinaries
	#
	try:
		fv = urllib2.urlopen(ThisVersionFile)
	except:
		print("")
		print(ThisVersionFile+" is not available")
		print("")
		return "failed"
	if verbose: 
		print("# Use URL "+ThisVersionFile)
		print("# ====================================================================")
	(versionFileBaseURL,versionFileName) = os.path.split(ThisVersionFile)
	if versionFileBaseURL == "": versionFileBaseURL = "file:"
	for zeile in fv:
		zeile = zeile.rstrip()
		if len(zeile) > 0 and zeile[0] != '#':
			tokens = zeile.split()
			#print(tokens)
			if len(tokens) > 1:
				# tokens sind case-sensitive:
				#
				# "check" bringt weitere Soll-Versionen:
				# die Endung in effVersionFile sollte ".best" oder ".txt" sein,
				# aber dies ist nur eine Konvention und nicht explizit verlangt
				#
				if tokens[0] == "check":
					if "/" not in tokens[1]: # ein relativer URL
						if versionFileBaseURL == "file:": versionFileBaseURL = versionFileBaseURL + "."
						effVersionFile = versionFileBaseURL+"/"+tokens[1]
					else:
						effVersionFile = tokens[1]
			    # wichtig: ganzer URL-Pfad nur addieren, falls noch nicht vorhanden
					if effVersionFile not in checkURL_List: 
						checkURL_List.append(effVersionFile)
						if verbose: print("# Validate versions with URL "+effVersionFile)
				#
				# "include" bringt weitere Haben-Versionen:
				#
				# die Endung in effVersionFile sollte ".rcvk" sein,
				# aber dies ist nur eine Konvention und nicht explizit verlangt
				#
				elif tokens[0] == "include":
					if "/" not in tokens[1]: # ein relativer URL
						effVersionFile = versionFileBaseURL+"/"+tokens[1]
					else:
						effVersionFile = tokens[1]
					if verbose: print("# Include versions from URL "+effVersionFile)
					ScanVersionURL(effVersionFile)
				#
				# 
				elif tokens[0] in ["app","bundle","fw","prefpane","sys","tool"]:
					# Syntax:  app [infoItem [required]]
					# falls len==1, werden alle items ausgelassen (nur das Argument-item wird genommen)
					addThisValues = 0
					if filterPattern != "":
						if filterPattern in zeile: addThisValues = 1
					else:
						addThisValues = 1
			    # wichtig: einzelnes Kriterium nur addieren, falls noch nicht vorhanden
					if addThisValues and tokens not in checkBinaries: 
						checkBinaries.append(tokens)
				else:
					print("Unknown Key: \""+tokens[0]+"\"")
			else:
				print("No enough keywords: \""+zeile+"\"")
	return ""				
#===============================================================================================
def	PrepareHTMLZeile(CheckURLPath,AppAndVersion,HabenVersion,SollVersion,TheInfoURL,TheLoadURL,UpdaterAction):
	if TheInfoURL != "":
		if not "/" in TheInfoURL:
			effURL = CheckURLPath + "/" + TheInfoURL
		else:
			effURL = TheInfoURL
		td1 = "<a href =\""+effURL+"\">" + AppAndVersion + "</a>"
	else:
		td1 = AppAndVersion
	if TheLoadURL != "":
		if not "/" in TheLoadURL:
			effURL = CheckURLPath + "/" + TheLoadURL
		else:
			effURL = TheLoadURL
		td3 = "<a href =\""+effURL+"\">" + SollVersion + "</a>"
	else:
		if UpdaterAction != "":
			td3 = SollVersion + " " + UpdaterAction
		else:
			td3 = SollVersion
	return "<tr><td>"+td1+"</td><td>"+HabenVersion+"</td><td>"+td3+"</td></tr>"
#===============================================================================================
def	AutoConfigure():
	value = AskUTIPrefs("bestVersionsURLBase")
	if value == "": SetUTIPrefs("bestVersionsURLBase",kBestVersionsURLBase)
	value = AskUTIPrefs("bestVersionsURL")
	if value == "": 
		hostname = DirectCmd("uname -n")
		SetUTIPrefs("bestVersionsURL",hostname+".rcvk")
	value = AskUTIPrefs("showDialogDetails")
	if value == "": SetUTIPrefs("showDialogDetails","url")
	value = AskUTIPrefs("PrefsVersion")
	if value == "": SetUTIPrefs("PrefsVersion","1")

#===============================================================================================
def	CompareVersions(HabenVersion,SollVersion):
	hv = HabenVersion.replace("=",".")
	sv = SollVersion.replace("=",".")
	habenItems = hv.split(".")
	sollItems = sv.split(".")
	versionAnders = 0
	
	if debug: print "RBD CompareVersions: haben:",HabenVersion,"==",SollVersion,":soll"
	comparison = "equal"
	if len(habenItems) > 0:
		for i in range(min(len(habenItems),len(sollItems))):
			# unbrauchbare meta-character eliminieren:
			haben = habenItems[i].replace("_","").replace(":","")
			soll = sollItems[i].replace("_","").replace(":","")
			try:
				if int(haben) > int(soll): comparison = "new"
				if int(haben) < int(soll): comparison = "old"
				if debug: print "RBD CompareVersions:       ",habenItems[i],"==",sollItems[i],":",comparison
				if comparison != "equal": return comparison
			except:
				for ic in range(min(len(haben),len(soll))):
					if ord(haben[ic]) > ord(soll[ic]): comparison = "new"
					if ord(haben[ic]) < ord(soll[ic]): comparison = "old"
					if debug: print "RBD CompareVersions  ASCII: ",ord(haben[ic]),"==",ord(soll[ic]),":",comparison
					if comparison != "equal": return comparison
	if len(habenItems) > len(sollItems): comparison = "new"
	if len(habenItems) < len(sollItems): comparison = "old"
	return comparison
#===============================================================================================
def	strbool(Value):
	if Value: return "true"
	return "false"
#===============================================================================================
def Usage():
	print("Usage: " + sys.argv[0] + " options versionCheck-URL")
	print("")
	print("Show outdated applications according to the required version-information defined by the versionCheck-URL")
	print("")
	print("Parameters:")
	print(" versionCheck-URL can be a local file-URL, e.g. file:sample.rcvk")
	print("                  or a true url, e.g. http://wwww.sample.com/sample.rcvk")
	print("Options:")
	print(" --auto:	     trigger auto-updates, default="+strbool(triggerAutoUpdate))
	print(" --configure: configure only preferences and exit")
	print(" -d:	         show debug-info")
	print(" --dialog:	   results will be shown with dialog, default="+strbool(showResultsWithDialog))
	print(" -e:	         send results as email")
	print(" --email:	   send results as email")
	print(" --filter pattern:	 filter from URL-data")
	print(" -h:	         show usage")
	print(" --ignore:	   ignore if expected version info could not be found, default="+strbool(ignoreNoVersionInfo))
	print(" --load:	     for outdated items: get/load installer, default="+strbool(loadAppInstaller))
	print(" --info:	     for outdated items: show info, default="+strbool(showAppOldInfo))
	print(" --missed:	   show any missing applications, default="+strbool(requiredInstallation))
	print(" -n:	         dont execute shell commands, default="+strbool(execute))
	print(" --undefined: show undefined version info, default="+strbool(undefinedVersions))
	print(" -v:	         verbose, default="+strbool(verbose))
	print(" --valid:     show current and too new version info, default="+strbool(validVersions))
	print(" --version:	 show version of the tool")
	print("")
	print("")
	print("v"+kToolVersion)
	sys.exit(2)
#===============================================================================================
hostname = DirectCmd("uname -n")
try:
	opts, args = getopt.getopt(sys.argv[1:], "deghnv", ["auto","configure","dialog","email","filter=","ignore","info","load","missed","undefined","valid","version"])
except getopt.GetoptError:
	Usage()
for o, a in opts:
	if o == "--auto":
		triggerAutoUpdate = 1
	elif o == "--ignore":
		ignoreNoVersionInfo = 1
	elif o == "-d":
		debug=1
	elif o == "--configure":
		configurePreferences = 1
	elif o == "--dialog":
		showResultsWithDialog = 1
	elif o == "-e" or o == "--email" :
		sendEMailPrefix = hostname + "\t" + os.getenv("USER")
	elif o == "--filter" :
		filterPattern = a
	elif o == "--load" :
		loadAppInstaller = 1
	elif o == "-h":
		Usage()
	elif o == "--info" :
		showAppOldInfo = 1
	elif o == "--missed":
		requiredInstallation = 1
	elif o == "--undefined":
		undefinedVersions = 1
	elif o == "--valid":
		validVersions = 1
	elif o == "-n":
		execute = 0
	elif o == "-v":
		verbose = 1
	elif o == "--version":
		print(kToolVersion)
		sys.exit()
#===============================================================================================
#
if configurePreferences:
	AutoConfigure()
	sys.exit()
#
checkURL_List = []
checkBinaries = []
infoResults = []

if (len(args) > 0):
	checkVersionFile = args[0]
else:
	checkVersionName = AskUTIPrefs("bestVersionsURL")
	if checkVersionName == "": checkVersionName = hostname + ".rcvk"
	checkVersionFile = AskUTIPrefs("bestVersionsURLBase") + "/" + checkVersionName
#
# checkVersionFile ist ein URL, lokal oder remote
#
#DEBUG print ("checkVersionFile:"+checkVersionFile)
if ":" not in checkVersionFile:
	print("")
	print(checkVersionFile + " is not an URL")
	print("")
	Usage()
	
if sendEMailPrefix != "":
	if "file:" in checkVersionFile:
		(prefix,namext) = checkVersionFile.split(":")
	else:
		(npath,namext) = os.path.split(checkVersionFile)
	(nurname,nurext) = os.path.splitext(namext)
	sendEMailPrefix = nurname + "\t" + sendEMailPrefix + "\t"
#
if validVersions: print("")
status = ScanVersionURL(checkVersionFile)
if status == "failed":
	#
	# gibt es eine alternate URL-Base (d.h. mit 2. Prioritaet)
	#
	checkVersionBase = AskUTIPrefs("bestVersionsURLBase2")
	if checkVersionBase != "": 
		checkVersionFile = checkVersionBase + "/" + checkVersionName
		if verbose: print("# Validate versions with alternate URL-Base "+checkVersionBase)
		status = ScanVersionURL(checkVersionFile)
	if status == "failed": sys.exit()
	#
if validVersions: print("")
#
# Reihenfolge kehren, d.h.
# die 1. Zeile im Textfile wird zuletzt abgesucht,
# die letzte Zeile im Textfile wird zuerst abgesucht
#
checkURL_List.reverse()				
#
#
psUsername = os.getenv("USER")
cmd = "ps -U "+psUsername+" | grep Finder | wc -l"
FinderAvailable = int(DirectCmd(cmd))
#
# falls wir eingeloggt sind, ist FinderAvailable==2, sonst ==1
#
# hier brauchen wir den Finder:
if FinderAvailable < 2: 
	showResultsWithDialog = 0
	triggerAutoUpdate = 0
#
htmlViewerFile = ""
if showResultsWithDialog:
	htmlViewerFile = os.getenv("HOME")+"/Library/Caches/CheckVersions.html"
	htmlViewer = open(htmlViewerFile,"w")
	htmlViewer.write("<html><head><title>Check Versions</title></head><body>\n")
	htmlViewer.write("<h1>Check software versions v"+kToolVersion +"</h1>\n")
	htmlViewer.write("<hr align=left>\n")
#
#
for oneBinary in checkBinaries:
	infoItem = "CFBundleShortVersionString"
	infoItemToken = 0
	toolName = ""
	versionCmd = ""
	habenVersion = ""
	checkItemKind = oneBinary[0].lower()
	#
	# %20 ist ein Platzhalter fuer ein Blank im Namen
	#
	einzelneUTIOrName = oneBinary[1].replace("%20"," ")
	#DEBUG print "einzelneUTIOrName:",einzelneUTIOrName
	#
	# alle Arte von Anwendungen
	#
	if checkItemKind == "app":
		if len(oneBinary) > 2:
			infoItem = oneBinary[2]
			if len(oneBinary) > 3:
				#
				# req: required installation
				# opt: optional installation
				#
				if "req" in oneBinary[3].lower(): requiredInstallation = 1
				if "opt" in oneBinary[3].lower(): requiredInstallation = 0
		binaryFullPath = FindApp(einzelneUTIOrName,requiredInstallation)
	#
	# irgendwelche Bundles, typischerweise mit absolutem Pfad
	#
	elif checkItemKind == "bundle":
		binaryFullPath = oneBinary[2].replace("%20"," ")
		if len(oneBinary) > 3:
			infoItem = oneBinary[3]
			if len(oneBinary) > 4:
				#
				# req: required installation
				# opt: optional installation
				#
				if "req" in oneBinary[4].lower(): requiredInstallation = 1
				if "opt" in oneBinary[4].lower(): requiredInstallation = 0
		if requiredInstallation and not os.path.isdir(binaryFullPath):
			ItemNotFound("bundle",binaryFullPath)
	#
	# Frameworks
	#
	elif checkItemKind == "fw":
		if len(oneBinary) < 3: HabenRequiredFormat(checkItemKind, oneBinary)
		fwName = oneBinary[2].replace("%20"," ")
		if len(oneBinary) > 3: infoItem = oneBinary[3]
		if len(oneBinary) > 4: infoItemToken = int(oneBinary[4])
		binaryFullPath = FindBinaryDir("/Library/Frameworks",fwName,"framework",requiredInstallation)
	#
	# Preference-Panes
	#
	elif checkItemKind == "prefpane":
		if len(oneBinary) < 3: HabenRequiredFormat(checkItemKind, oneBinary)
		prefpaneName = oneBinary[2].replace("%20"," ")
		if len(oneBinary) > 3: infoItem = oneBinary[3]
		binaryFullPath = FindBinaryDir("/Library/PreferencePanes",prefpaneName,"prefPane",requiredInstallation)
	#
	# System-spezifisch
	#
	elif checkItemKind == "sys":
		#
		# welches System, welche Architektur
		#
		sysArch = RunShellCmd("sysctl -n hw.machine")
		sysKind = RunShellCmd("sw_vers -productName")
		activeSystemUTI = "sys.osx"
		# 32- oder 64-bit kernels testen
		if "i386" in sysArch or "x86_64" in sysArch:
			activeSystemUTI = activeSystemUTI + ".intel"
		else:
			activeSystemUTI = activeSystemUTI + ".ppc"
		if "Server" in sysKind:
			activeSystemUTI = activeSystemUTI + ".server"
		else:
			activeSystemUTI = activeSystemUTI + ".client"
		# nur das passende System auswerten:
		# print "sysArch:",sysArch," sysKind: ",sysKind
		# print "UTI-Vergleich:",einzelneUTIOrName," == ",activeSystemUTI
		if einzelneUTIOrName == activeSystemUTI:
			# / represents the system
			binaryFullPath = "/"
			versionCmd = oneBinary[2].replace("%20"," ")
		else:
			binaryFullPath = ""
	#
	# tool als Sammelbegriff fuer eine einzelne Datei
	#
	elif checkItemKind == "tool":
		if len(oneBinary) < 3: HabenRequiredFormat(checkItemKind, oneBinary)
		# print "versionCmd:",versionCmd
		toolName = oneBinary[2].replace("%20"," ")
		if len(oneBinary) > 3: 
			versionCmd = toolName + " " + oneBinary[3].replace("%20"," ")
		else:
			versionCmd = toolName + " --version"
		binaryFullPath = FindBinaryFile("",toolName,"",requiredInstallation)
		#
		# den Namen mit der effektiven UTI ersetzen:
		#
		#DEBUG print(checkItemKind + ":" + binaryFullPath)
	
	#binaryFullPath = binaryFullPath.replace("%20"," ")
	if debug: print(checkItemKind + " UTI or Name: \"" + einzelneUTIOrName + "\" Pfad: " + binaryFullPath)
	if binaryFullPath != "":
		if checkItemKind == "sys":
			habenVersion = RunShellCmd(versionCmd)
		elif checkItemKind == "tool":
			habenVersion = RunShellCmd(versionCmd)
		elif checkItemKind == "fw":
			habenVersion = AskBundlePrefs(binaryFullPath,infoItem,"Versions/Current/Resources")
		else:
			#
			# nur bei einem App-Bundle  der anderem Bundle finden wir die Version heraus:
			#
			if os.path.isdir(binaryFullPath):
				habenVersion = AskBundlePrefs(binaryFullPath,infoItem)
			else:
				habenVersion = "undefined"
		if infoItemToken > 0: 
			tok = habenVersion.split()
			if len(tok) >= infoItemToken: habenVersion = tok[infoItemToken-1]
		outDelimiter = "\t"
		if showResultsWithDialog: outDelimiter = " "
		appAndVersion = einzelneUTIOrName+outDelimiter+habenVersion
		# 
		# alle URLs werden in der Reihenfolge abgesucht,
		# der 1. Treffer gilt, d.h. die Reihenfolge muss sein 1. neue_URL ==> N. alte_URL
		sollVersionFound = 0
		for checkURL in checkURL_List:
			(checkURLPath,n) = os.path.split(checkURL)
			appInfo = AskURL(checkURL,einzelneUTIOrName)
			sollVersion = appInfo['version']
			sollVersionDetail = appInfo['versionDetail']
			infoURL = appInfo['info']
			loadURL = appInfo['load']
			loadAction = appInfo['loadAction']
			updater = appInfo['updater']
			#if debug: print (checkURL,einzelneUTIOrName,infoURL)
			#
			#v1.7 anstelle der ev. kruden UTI einen besseren Titel verwenden:
			if appInfo['title'] != "": appAndVersion = appInfo['title']+outDelimiter+habenVersion
			installInfo = ""
			if sollVersionDetail != "": appAndVersion = appAndVersion + " (" + sollVersionDetail+ ")"
			if sollVersion != "":
				sollVersionFound = sollVersionFound + 1
				if not showResultsWithDialog:
					if infoURL != "": 
						installInfo = installInfo + " (info"
						if loadURL != "": installInfo = installInfo + ",load"
						installInfo = installInfo + ")"
				#
				# falls die sollVersion nur mit a.b angegeben ist, und die habenVersion mit a.b.c gefunden wird,
				# wird habenVersion a.b.c auf a.b verkuerzt
				#
				if debug:
					print(checkItemKind + " " + einzelneUTIOrName + " \""+habenVersion+"\" == \""+sollVersion+"\"")
				#
				versionEvaluation = ""
				versionsCompared = CompareVersions(habenVersion,sollVersion)
				#DEBUG print "habenVersion:",habenVersion," sollVersion:",sollVersion," :",versionsCompared
				#	
				# 4 Varianten:
				#	
				# 1. haben-Version ist nicht definiert
				#	
				if habenVersion == "undefined":
					if undefinedVersions:	versionEvaluation = (appAndVersion+" is undefined")
				#	
				# 2. Versionen sind gleich
				#	
				elif versionsCompared == "equal":
					if validVersions:	
						if showResultsWithDialog:
							htmlZeile = PrepareHTMLZeile(checkURLPath,appAndVersion,habenVersion,sollVersion,infoURL,loadURL,updater)
							if debug: print htmlZeile
							infoResults.append(htmlZeile+"\n")
						else:
							versionEvaluation = (appAndVersion+" is up-to-date")
				#
				# 3. Applikation/Tool ist veraltet gegenueber der Soll-Version
				#	
				elif versionsCompared == "old":
					sollVersionInfo = ""
					if sendEMailPrefix == "": 
						if "Application Update" in updater and triggerAutoUpdate: 
							ut = updater.split()
							if len(ut) > 2:
								specificUpdater = ut[2]
								if "/" in specificUpdater:
									# wir haben einen spezifischen Updater als absoluten Pfad
									RunShellCmd("open -a " + specificUpdater)
								else:
									# wir haben einen spezifischen Updater als UTI
									RunShellCmd("open -b " + specificUpdater)
							else:
								RunShellCmd("open -b " + einzelneUTIOrName)
						if showResultsWithDialog:
							htmlZeile = PrepareHTMLZeile(checkURLPath,appAndVersion,habenVersion,sollVersion,infoURL,loadURL,updater)
							if debug: print htmlZeile
							infoResults.append(htmlZeile)
						else:
							if sollVersionDetail != "":
								versionEvaluation = (appAndVersion+" is outdated: "+sollVersion + " (" + sollVersionDetail + ")" + installInfo)
							else:
								versionEvaluation = (appAndVersion+" is outdated: "+sollVersion + installInfo)
						#
						# falls wir eingeloggt sind, ist FinderAvailable==1 sonst == 0
						#
						if not showResultsWithDialog:
							if showAppOldInfo and infoURL != "": versionEvaluation = versionEvaluation +("\nopen " + GetFullURL(checkURL,infoURL))
							if loadAppInstaller and FinderAvailable == 2 and "Application Update" in updater: 
								RunShellCmd("open -b " +einzelneUTIOrName)
							elif loadAppInstaller and loadURL != "": 
								if loadAction == "auto-update":
									ExecuteInfoItems(checkURL,infoURL,loadURL,loadAction,binaryFullPath)
								else:
									(p,nameOnly) = os.path.split(loadURL)
									versionEvaluation = versionEvaluation +("\ncurl -o /Users/Shared/" + nameOnly + " " + GetFullURL(checkURL,loadURL))
					else:
						installInfo = ""
						for zi in [infoURL,loadURL]:
							if len(zi) > 0:	
								if "/" not in zi: zi = checkURLPath+"/"+zi
								installInfo = installInfo + outDelimiter + zi
						#
						if sollVersionDetail != "":
							versionEvaluation = (appAndVersion+"\toutdated\t"+sollVersion + "\t" + sollVersionDetail + installInfo)
						else:
							versionEvaluation = (appAndVersion+"\toutdated\t"+sollVersion + installInfo)
				#
				# 4. Applikation/Tool ist zu neu gegenueber der Soll-Version
				#
				else: # versionsCompared == "new"
					if validVersions:	versionEvaluation = (appAndVersion+" is too new (" + sollVersion + ") " + installInfo)
					
				if versionEvaluation != "":
					if len(sendEMailPrefix) > 0:
						infoResults.append(sendEMailPrefix + versionEvaluation)
					else:
						infoResults.append(versionEvaluation)
		if not sollVersionFound:
			if not ignoreNoVersionInfo: print(appAndVersion+" has no version requirements for "+einzelneUTIOrName)
				#print(AskBundlePrefs(binaryFullPath,""))
#
# doppelte Resultate jetz noch wegschmeissen
#
infoResults = UniqueItems(infoResults)
#
if showResultsWithDialog:
	if len(infoResults) > 0:
		# nur HTML-zeigen wenn notwendig
		htmlViewer.write("<table border=\"1\">\n")
		htmlViewer.write("<tr><th>Name</th><th>Installed</th><th>Better Version</th></tr>\n")
		for zeile in infoResults: htmlViewer.write(zeile+"\n")
		htmlViewer.write("</table></body></html>\n")
		htmlViewer.close()
		print(str(len(infoResults))+" outdated items found")
		RunShellCmd("open "+ htmlViewerFile)
elif len(infoResults) > 0:
	listItems = 1
	if sendEMailPrefix != "" and SendEMail(infoResults): listItems = 0
	if listItems:	
		for zeile in infoResults: print(zeile)



