from twisted.internet.protocol import DatagramProtocol
from twisted.internet import wxreactor, task, threads
from buzhug import TS_Base
from PythonCard import model, dialog
import smtplib, time, re, os.path, pickle, syslogConf, sys
import socket
from email.mime.text import MIMEText

#import variables from syslog.conf
configs = pickle.load(open("syslog.conf", 'r'))
import syslogConf
guiList = []
db = TS_Base(configs["dbName"]).create(('srcIP', str), ('hostName', str), ('time', str), ('priority', int), ('facility', str), ('message', str), mode="open")
highQueue = ""
medQueue = ""

class MainWindow(model.Background):
	def on_initialize(self, event):
		global guiList
		global configs
		guiList = self.components.MultiColumnList
		guiList.SetColumnWidth(0, 130)
		guiList.SetColumnWidth(1, 145)
		guiList.SetColumnWidth(2, 100)
		guiList.SetColumnWidth(3, 50)
		guiList.SetColumnWidth(4, 175)
		guiList.SetColumnWidth(5, 445)
		self.confWindow = model.childWindow(self, syslogConf.ConfWindow)
	
	def on_menuFileConfig_select(self, event):
		self.confWindow.visible = True
		
	def on_menuFileClear_select(self, event):
		guiList.items = []
	
	def on_menuLogsSave_select(self, event):
		wildcard = "LOG file *.log)|*.log|All Files (*.*)|*.*"
		result = dialog.saveFileDialog(wildcard=wildcard)
		dbPurge(result.paths[0])
	
	def on_menuLogsLoadDatabase_select(self, event):
		guiList.items = []
		for record in db:
			cleaned = []
			for item in record:
				cleaned.append(item.strip("-").rstrip())
			index = guiList.InsertStringItem(0, cleaned[4])
			guiList.SetStringItem(index, 1, cleaned[3])
			guiList.SetStringItem(index, 2, cleaned[2])
			guiList.SetStringItem(index, 3, cleaned[6].split("-")[1])
			guiList.SetStringItem(index, 4, cleaned[6])
			guiList.SetStringItem(index, 5, cleaned[7].split("\\n")[0])
			
	def on_menuLogsLoadFile_select(self, sevent):
		guiList.items = []
		wildcard = "LOG file (*.log)|*.log|All Files (*.*)|*.*"
		selFile = dialog.openFileDialog(wildcard=wildcard)
		if selFile.accepted is True:
			logFile = open(selFile.paths[0], "r")
			for line in logFile:
				sysList = line.split("; ", 5)
				guiList.InsertStringItem(0, sysList[2])
				guiList.SetStringItem(0, 1, sysList[1])
				guiList.SetStringItem(0, 2, sysList[0])
				guiList.SetStringItem(0, 3, sysList[3])
				guiList.SetStringItem(0, 4, sysList[4])
				guiList.SetStringItem(0, 5, sysList[5])
				#db.insert(sysList[0], sysList[1], sysList[2], int(sysList[3]), sysList[4], sysList[5])
			logFile.close()
	
	def on_menuAboutAbout_select(self, event):
		result = dialog.alertDialog(self, "Syslog Server v. 1.2\nDeveloped by William Huba\nc. 2009", "About")
		

class SyslogInput(DatagramProtocol):
	def datagramReceived(self, data, (host, port)):
		sysList = data.split(": ")
		sysList.append(int(sysList[2].split("-")[1]))
		#Reverse DNS lookup runs deferred, call processData when done
		defHost = threads.deferToThread(self.getHostName, host)
		defHost.addCallback(self.processData, (host, sysList))
	
	def getHostName(self, host):
		try:
			return socket.gethostbyaddr(host)[0].rstrip(".ciscocomcalo")
		except:
			return "Unknown"
			
	def processData(self, hostName, (host, sysList)):
		global guiList
		global highQueue
		global medQueue
		#add to database
		db.insert(host, hostName, sysList[1], sysList[4], sysList[2], sysList[3].rstrip())
		#print to list in gui
		index = guiList.InsertStringItem(0, sysList[1])
		guiList.SetStringItem(index, 1, hostName)
		guiList.SetStringItem(index, 2, host)
		guiList.SetStringItem(index, 3, str(sysList[4]))
		guiList.SetStringItem(index, 4, sysList[2])
		guiList.SetStringItem(index, 5, sysList[3])

		#notification for high and medium priority
		sysMsg = "Time: " + sysList[1] + "\nSourced from: " + hostName + " (" + host + ")\nFacility: " + sysList[2] + "\nMessage: " + sysList[3] + "\n\n\n"
		if sysList[4] <= configs["minPriHigh"]:
			highQueue += sysMsg
		elif sysList[4] <= configs["minPriMed"]:
			medQueue += sysMsg

def emailAlert(priority):
	global highQueue
	global medQueue
	if priority == "high" and not highQueue == "":
		msg = MIMEText(highQueue)
		msg['Subject'] = configs["emailSubj"]
		msg['From'] = configs["emailFrom"]
		msg['To'] = configs["emailToHigh"]
		mailServ = smtplib.SMTP(configs["relay"])
		mailServ.sendmail(configs["emailFrom"], [configs["emailToHigh"]], msg.as_string())
		mailServ.quit()
		highQueue = ""
	elif priority == "medium" and medQueue != "":
		msg = MIMEText(medQueue)
		msg['Subject'] = configs["emailSubj"]
		msg['From'] = configs["emailFrom"]
		msg['To'] = configs["emailToMed"]
		mailServ = smtplib.SMTP(configs["relay"])
		mailServ.sendmail(configs["emailFrom"], [configs["emailToMed"]], msg.as_string())
		mailServ.quit()
		medQueue = ""
	else:
		return

def dbPurge(fName):
	#writes all db entries to a text file and wipes db
	file = open(fName, "w")
	for record in db:
		list = re.split(" [a-zA-Z]+:", str(record).strip("<>"))
		del list[0]
		file.write("; ".join([i for i in list]))
		file.write("\n")
		db.delete(record)
	file.close()
	db.cleanup()
	
def autoClean():
	if time.localtime().tm_wday == configs["cleanDay"] and time.localtime().tm_hour == configs["cleanHour"]:
		dbPurge(os.path.join("Logs", time.strftime("%y%b%d.log")))
		
		
if __name__ == '__main__':
	wxreactor.install()
	from twisted.internet import reactor
	app = model.Application(MainWindow)
	cleanLoop = task.LoopingCall(autoClean)
	highEmailLoop = task.LoopingCall(emailAlert, "high")
	medEmailLoop = task.LoopingCall(emailAlert, "medium")
	reactor.registerWxApp(app)
	reactor.listenUDP(514, SyslogInput())
	cleanLoop.start(2700.0)
	highEmailLoop.start(float(configs["highTime"])*60)
	medEmailLoop.start(float(configs["medTime"])*60)
	reactor.run()
	db.close()

