<commit_before>import uuid
from datetime import datetime, timedelta
import time
import random
from networking import *
import re
import sys
import smtplib
from email.mime.text import MIMEText

class DARN:
	VERSION = "0.1"
	SEND_PINGS=1
	LOG_DEBUG=0

	def log(self, severity, message):
		hostname = "unknown"
		if hasattr(self, 'config') and 'hostname' in self.config:
			hostname = self.config['hostname']
		print "%s: DARN[%s][%s]: %s" % (datetime.now(), hostname, severity, message)

	def info(self, message):
		self.log("info", message)

	def debug(self, message):
		if DARN.LOG_DEBUG:
			self.log("debug", message)

	"""Create a DARN object. Read config from given file. """
	def __init__(self, configfile):
		self.info("Initialising DARN version " + DARN.VERSION)
		self.configfile = configfile
		self.net = DARNetworking()
		self.running = False
		self.node_configs = {}
		self.expected_pongs = set()
		self.error_seq = 1
		self.error_events = []
		self.config_version = None
		self.hosts = {}
		self.reload()

	def split_hostname(self, node):
		m = re.match(r'^(.+?)(?::(\d+))?$', node)
		return (m.group(1), int(m.group(2)))

	def host(self, node):
		if node not in self.hosts:
			(hostname, port) = self.split_hostname(node)
			self.hosts[node] = host = DARNHost(self.new_outgoing_connection, self.data_from_identified_host)
			host.setHost(hostname, port)
			host.connect()
			return host
		return self.hosts[node]

	def find_peer_from_darnhost(self, host):
		for peer in self.hosts:
			if self.hosts[peer] == host:
				return peer
		return None

	def new_outgoing_connection(self, host):
		host.send_priority({'hostname': self.config['hostname']})

	def data_from_unidentified_host(self, host, data):
		self.debug("DARN Host connected to me: %s and sent: %s" % (host, data))
		if 'hostname' not in data:
			host.destroy()
			return
		(hostname, port) = self.split_hostname(data['hostname'])
		host.setHost(hostname, port)
		if data['hostname'] in self.hosts:
			host.merge(self.hosts[data['hostname']])
		else:
			host.change_callbacks(self.new_outgoing_connection, self.data_from_identified_host)
			self.hosts[data['hostname']] = host

	def data_from_identified_host(self, host, data):
		self.debug("DARN Host Data from identified host %s: %s" % (host, data))

		if 'type' not in data:
			host.destroy()
			return

		peer = self.find_peer_from_darnhost(host)

		if peer is None:
			self.info("Failed to match data from host %s to a peer" % host);
			return

		if data['type'] == "config":
			self.info("Noted configuration for identified host: %s" % peer)
			self.node_configs[peer] = data;
		elif data['type'] == "ping":
			self.debug("Received ping from friend node %s" % peer)
			config_version = data['config_version']
			pong_packet = {
				'type': 'pong',
				'ttl': 15,
			}
			self.host(peer).send(pong_packet)
			if config_version != self.config_version:
				self.info("Friend node %s has older config of mine (version %s), pushing new config version %s"
					% (peer, config_version, self.config_version))
				self.push_config_to_node(peer)
		elif data['type'] == "pong":
			self.debug("Received pong from friend node %s" % peer)
			if peer in self.expected_pongs:
				self.expected_pongs.remove(peer)
		elif data['type'] == "error":
			self.info("Received error from friend node %s" % peer)
			self.receive_error_event(peer, data)
		elif data['type'] == "signoff":
			self.info("Received signoff event from friend node %s, success=%s: %s" % (peer, data['success'], data['message']))
			self.process_error_event_signoff(peer, data['id'], data['success'])
		else:
			self.info("Received unknown packet type %s from friend node %s" % (data['type'], peer))

	def stop(self):
		self.info("Stopping")
		self.running = False

	"""Start the DARN daemon. This call blocks until stop() is called. """
	def run(self):
		if self.running:
			return
		self.info("Starting")
		self.running = True
		self.net.add_timer(0, self.check_nodes)
		# This method blocks until there are no more timers to run
		self.net.run()

	"""
	Start checking all nodes. This generates a list of 'ping' calls to the
	networking layer. If no succesful pong comes back for a given node,
	an error event is generated. This is checked asynchronously, so this
	call does not block.
	"""
	def check_nodes(self):
		self.debug("About to check friend nodes")
		if not self.running:
			return
		if not DARN.SEND_PINGS:
			return
		for node in self.config['nodes']:
			node_config_version = 0;
			if node['hostname'] in self.node_configs:
				node_config_version = self.node_configs[node['hostname']]['config_version']
			ping_packet = {
				'type': 'ping',
				'ttl': 15,
				'config_version': node_config_version,
			}
			self.expected_pongs.add(node['hostname'])
			self.debug("Sending ping to friend node %s, config version %d" % (node['hostname'], node_config_version))
			self.host(node['hostname']).send(ping_packet)
		self.net.add_timer(10, self.check_timeouts)
		self.net.add_timer(15, self.check_nodes)

	def handle_error_event(self, event, callback):
		victim_config = self.node_configs[event['victim']]
		if not 'email' in victim_config['config']:
			callback(False, "Cannot send e-mail regarding failure of victim %s: no e-mail address known" % event['victim'])
		email = victim_config['config']['email']
		msg = MIMEText("%s: Error event report:\n%s\n" % (datetime.now(), event))

		if not 'smtp' in self.config or not 'sender' in self.config['smtp'] or not 'host' in self.config['smtp']:
			callback(False, "Cannot send e-mail regarding failure of victim %s: no valid smtp configuration" % event['victim'])

		msg['Subject'] = "DARN! Error event report"
		msg['From'] = self.config['smtp']['sender']
		msg['To'] = email
		email_succeeded = None
		email_error = None
		try:
			s = smtplib.SMTP(self.config['smtp']['host'])
			recipients_failed = s.sendmail(self.config['smtp']['sender'], [email], msg.as_string())
			s.quit()
			if len(recipients_failed) == 0:
				email_succeeded = False<commit_msg>Fix SMTP result checking<commit_after>import uuid
from datetime import datetime, timedelta
import time
import random
from networking import *
import re
import sys
import smtplib
from email.mime.text import MIMEText

class DARN:
	VERSION = "0.1"
	SEND_PINGS=1
	LOG_DEBUG=0

	def log(self, severity, message):
		hostname = "unknown"
		if hasattr(self, 'config') and 'hostname' in self.config:
			hostname = self.config['hostname']
		print "%s: DARN[%s][%s]: %s" % (datetime.now(), hostname, severity, message)

	def info(self, message):
		self.log("info", message)

	def debug(self, message):
		if DARN.LOG_DEBUG:
			self.log("debug", message)

	"""Create a DARN object. Read config from given file. """
	def __init__(self, configfile):
		self.info("Initialising DARN version " + DARN.VERSION)
		self.configfile = configfile
		self.net = DARNetworking()
		self.running = False
		self.node_configs = {}
		self.expected_pongs = set()
		self.error_seq = 1
		self.error_events = []
		self.config_version = None
		self.hosts = {}
		self.reload()

	def split_hostname(self, node):
		m = re.match(r'^(.+?)(?::(\d+))?$', node)
		return (m.group(1), int(m.group(2)))

	def host(self, node):
		if node not in self.hosts:
			(hostname, port) = self.split_hostname(node)
			self.hosts[node] = host = DARNHost(self.new_outgoing_connection, self.data_from_identified_host)
			host.setHost(hostname, port)
			host.connect()
			return host
		return self.hosts[node]

	def find_peer_from_darnhost(self, host):
		for peer in self.hosts:
			if self.hosts[peer] == host:
				return peer
		return None

	def new_outgoing_connection(self, host):
		host.send_priority({'hostname': self.config['hostname']})

	def data_from_unidentified_host(self, host, data):
		self.debug("DARN Host connected to me: %s and sent: %s" % (host, data))
		if 'hostname' not in data:
			host.destroy()
			return
		(hostname, port) = self.split_hostname(data['hostname'])
		host.setHost(hostname, port)
		if data['hostname'] in self.hosts:
			host.merge(self.hosts[data['hostname']])
		else:
			host.change_callbacks(self.new_outgoing_connection, self.data_from_identified_host)
			self.hosts[data['hostname']] = host

	def data_from_identified_host(self, host, data):
		self.debug("DARN Host Data from identified host %s: %s" % (host, data))

		if 'type' not in data:
			host.destroy()
			return

		peer = self.find_peer_from_darnhost(host)

		if peer is None:
			self.info("Failed to match data from host %s to a peer" % host);
			return

		if data['type'] == "config":
			self.info("Noted configuration for identified host: %s" % peer)
			self.node_configs[peer] = data;
		elif data['type'] == "ping":
			self.debug("Received ping from friend node %s" % peer)
			config_version = data['config_version']
			pong_packet = {
				'type': 'pong',
				'ttl': 15,
			}
			self.host(peer).send(pong_packet)
			if config_version != self.config_version:
				self.info("Friend node %s has older config of mine (version %s), pushing new config version %s"
					% (peer, config_version, self.config_version))
				self.push_config_to_node(peer)
		elif data['type'] == "pong":
			self.debug("Received pong from friend node %s" % peer)
			if peer in self.expected_pongs:
				self.expected_pongs.remove(peer)
		elif data['type'] == "error":
			self.info("Received error from friend node %s" % peer)
			self.receive_error_event(peer, data)
		elif data['type'] == "signoff":
			self.info("Received signoff event from friend node %s, success=%s: %s" % (peer, data['success'], data['message']))
			self.process_error_event_signoff(peer, data['id'], data['success'])
		else:
			self.info("Received unknown packet type %s from friend node %s" % (data['type'], peer))

	def stop(self):
		self.info("Stopping")
		self.running = False

	"""Start the DARN daemon. This call blocks until stop() is called. """
	def run(self):
		if self.running:
			return
		self.info("Starting")
		self.running = True
		self.net.add_timer(0, self.check_nodes)
		# This method blocks until there are no more timers to run
		self.net.run()

	"""
	Start checking all nodes. This generates a list of 'ping' calls to the
	networking layer. If no succesful pong comes back for a given node,
	an error event is generated. This is checked asynchronously, so this
	call does not block.
	"""
	def check_nodes(self):
		self.debug("About to check friend nodes")
		if not self.running:
			return
		if not DARN.SEND_PINGS:
			return
		for node in self.config['nodes']:
			node_config_version = 0;
			if node['hostname'] in self.node_configs:
				node_config_version = self.node_configs[node['hostname']]['config_version']
			ping_packet = {
				'type': 'ping',
				'ttl': 15,
				'config_version': node_config_version,
			}
			self.expected_pongs.add(node['hostname'])
			self.debug("Sending ping to friend node %s, config version %d" % (node['hostname'], node_config_version))
			self.host(node['hostname']).send(ping_packet)
		self.net.add_timer(10, self.check_timeouts)
		self.net.add_timer(15, self.check_nodes)

	def handle_error_event(self, event, callback):
		victim_config = self.node_configs[event['victim']]
		if not 'email' in victim_config['config']:
			callback(False, "Cannot send e-mail regarding failure of victim %s: no e-mail address known" % event['victim'])
		email = victim_config['config']['email']
		msg = MIMEText("%s: Error event report:\n%s\n" % (datetime.now(), event))

		if not 'smtp' in self.config or not 'sender' in self.config['smtp'] or not 'host' in self.config['smtp']:
			callback(False, "Cannot send e-mail regarding failure of victim %s: no valid smtp configuration" % event['victim'])

		msg['Subject'] = "DARN! Error event report"
		msg['From'] = self.config['smtp']['sender']
		msg['To'] = email
		email_succeeded = None
		email_error = None
		try:
			s = smtplib.SMTP(self.config['smtp']['host'])
			recipients_failed = s.sendmail(self.config['smtp']['sender'], [email], msg.as_string())
			s.quit()
			if len(recipients_failed) > 0:
				email_succeeded = False
{'fraction_non_alphanumeric': 0.1415110073382255, 'fraction_numerical': 0.003002001334222815, 'mean_word_length': 4.3683974932855865, 'pattern_counts': {'":': 10, '<': 3, '<?xml version=': 0, '>': 4, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '427474', 'n_tokens_mistral': 4565, 'n_tokens_neox': 4056, 'n_words': 1106}