diff --git a/config.py.example b/config.py.example
index cf7d0ba3b8551363673e6ccccd1221a1b2ab6371..3ae558dbd9bedd9ee2a87f2401cc2ecb89a1e998 100644
--- a/config.py.example
+++ b/config.py.example
@@ -31,3 +31,9 @@ ERROR_PAGE = 'static/500.html'
 RWTH_IP_RANGES = ['134.130.0.0/16', '137.226.0.0/16', '134.61.0.0/16', '192.35.229.0/24', '2a00:8a60::/32']
 FSMPI_IP_RANGES = ['137.226.35.192/29', '137.226.75.0/27', '137.226.127.32/27', '137.226.231.192/26', '134.130.102.0/26' ]
 DISABLE_SCHEDULER = False
+#MAIL_SERVER = 'mail.fsmpi.rwth-aachen.de'
+MAIL_FROM = 'Video AG-Website <videoag-it@lists.fsmpi.rwth-aachen.de>'
+MAIL_SUFFIX = 'fsmpi.rwth-aachen.de'
+MAIL_SERVER = 'mail.fsmpi.rwth-aachen.de'
+MAIL_DEFAULT = 'Video AG <videoag@fsmpi.rwth-aachen.de>'
+MAIL_ADMINS = 'videoag-it@lists.fsmpi.rwth-aachen.de'
diff --git a/db_schema.sql b/db_schema.sql
index b5f8a4dc1f02c11c1eccc25de5fffc72d3b06096..8a6bc103a8dd53bc10f9cec0d4b6987e3539a430 100644
--- a/db_schema.sql
+++ b/db_schema.sql
@@ -205,7 +205,8 @@ CREATE TABLE IF NOT EXISTS `users` (
   `fsacc` varchar(32) NOT NULL,
   `last_login` datetime DEFAULT NULL,
   `calendar_key` varchar(40) NOT NULL,
-  `rfc6238` varchar(20) NOT NULL
+  `rfc6238` varchar(20) NOT NULL,
+  `mail_notifications` INTEGER NOT NULL DEFAULT '1'
 );
 CREATE TABLE IF NOT EXISTS `videos_data` (
 `id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
@@ -292,6 +293,12 @@ CREATE TABLE IF NOT EXISTS `jobs` (
   `status` text NOT NULL DEFAULT '{}'
 );
 
+CREATE TABLE IF NOT EXISTS `responsible` (
+`id` INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
+  `course_id` INTEGER NOT NULL,
+  `user_id` INTEGER NOT NULL
+);
+
 CREATE VIEW IF NOT EXISTS `courses` AS select * from `courses_data` where (not(`courses_data`.`deleted`));
 CREATE VIEW IF NOT EXISTS `lectures` AS select `lectures_data`.* from `lectures_data` join `courses_data` on (`courses_data`.`id` = `course_id`) where (not(`lectures_data`.`deleted` or `courses_data`.`deleted`));
 CREATE VIEW IF NOT EXISTS `videos` AS select `videos_data`.* from `videos_data` join `lectures_data` on (`lectures_data`.`id` = `lecture_id`) join `courses_data` on (`courses_data`.`id` = `course_id`) where (not(`videos_data`.`deleted` or `lectures_data`.`deleted` or `courses_data`.`deleted`));
diff --git a/mail.py b/mail.py
new file mode 100644
index 0000000000000000000000000000000000000000..35bd774c6466efe53e8db78116e11fd51f0659b1
--- /dev/null
+++ b/mail.py
@@ -0,0 +1,67 @@
+from server import *
+
+from email.message import EmailMessage
+import smtplib
+import traceback
+
+def send_message(msgtype, recipients, **kwargs):
+	msg = EmailMessage()
+	msg['From'] = config['MAIL_FROM']
+	msg['To'] = ', '.join([r.replace(',', '') for r in recipients])
+	cc = kwargs.pop('cc', [])
+	if cc:
+		msg['Cc'] = ', '.join([r.replace(',', '') for r in cc])
+	try:
+		msg['Subject'] = render_template('mails/'+msgtype+'.subject', **kwargs)
+		msg.set_content(render_template('mails/'+msgtype+'.body', **kwargs))
+	except:
+		traceback.print_exc()
+		return
+	if 'MAIL_SERVER' not in config:
+		return
+	s = smtplib.SMTP(config['MAIL_SERVER'])
+	s.send_message(msg)
+	s.quit()
+
+def notify_users(msgtype, uids, **kwargs):
+	recipients = []
+	exclude = kwargs.pop('exclude_uids', [])
+	for uid in uids:
+		user = query('SELECT * FROM users WHERE id = ?', uid)
+		if user[0]['id'] in exclude:
+			continue
+		if not user or not user[0]['fsacc'] or not user[0]['mail_notifications']:
+			continue
+		if msgtype in user[0] and not user[0][msgtype]:
+			continue
+		if user[0]['realname']:
+			recipients.append('%s <%s@%s>'%(user[0]['realname'], user[0]['fsacc'],
+						config['MAIL_SUFFIX']))
+		else:
+			recipients.append('%s@%s'%(user[0]['fsacc'], config['MAIL_SUFFIX']))
+	if recipients:
+		if kwargs.pop('importend', False):
+			kwargs['cc'] = [config['MAIL_DEFAULT']]
+	else:	
+		if kwargs.pop('importend', False):
+			recipients = [config['MAIL_DEFAULT']]
+		else:
+			return
+	send_message(msgtype, recipients, **kwargs)
+
+def notify_mods(msgtype, course_id, **kwargs):
+	users = query('SELECT * FROM responsible WHERE course_id = ?', course_id)
+	uids = []
+	for user in users:
+		if msgtype in user and not user[msgtype]:
+			continue
+		uids.append(user['user_id'])
+	notify_users(msgtype, uids, **kwargs)
+
+def notify_admins(msgtype, **kwargs):
+	try:
+		send_message(msgtype, [config['MAIL_ADMINS']], **kwargs)
+	except:
+		traceback.print_exc()
+		pass
+
diff --git a/server.py b/server.py
index 5e2cda4204721be0576e81d0c7dc474de00aabb1..60740ee12054f93bf7457b2ae2fc649c4087ac2d 100644
--- a/server.py
+++ b/server.py
@@ -46,6 +46,7 @@ from db import query, modify, show, searchquery
 from ldap import ldapauth
 from legacy import legacy_index
 from scheduler import sched_func
+from mail import notify_mods, notify_admins
 
 mod_endpoints = []
 
@@ -123,6 +124,7 @@ def handle_not_found(e=None):
 @app.errorhandler(Exception)
 def handle_internal_error(e):
 	traceback.print_exc()
+	notify_admins('exception', traceback=traceback.format_exc())
 	return render_template('500.html'), 500
 
 @sched_func(5*60, firstdelay=0)
diff --git a/templates/mails/exception.body b/templates/mails/exception.body
new file mode 100644
index 0000000000000000000000000000000000000000..e0ebfe5bcc4345b0331cb1613e16b283441b65bf
--- /dev/null
+++ b/templates/mails/exception.body
@@ -0,0 +1,6 @@
+{{ request.method }} {{ request.url }}
+
+Endpoint: {{ request.endpoint }}
+Hostname: {{ gethostname() }}
+
+{{ traceback }}
diff --git a/templates/mails/exception.subject b/templates/mails/exception.subject
new file mode 100644
index 0000000000000000000000000000000000000000..6ee488a121a78ba9b12d989dbc39afd655ebce24
--- /dev/null
+++ b/templates/mails/exception.subject
@@ -0,0 +1 @@
+Exception in endpoint "{{ request.endpoint }}"