]> git.lizzy.rs Git - cheatdb.git/blob - app/maillogger.py
Fix profile picture bugs
[cheatdb.git] / app / maillogger.py
1 import logging
2 from enum import Enum
3 from app.tasks.emails import sendEmailRaw
4
5 def _has_newline(line):
6         """Used by has_bad_header to check for \\r or \\n"""
7         if line and ("\r" in line or "\n" in line):
8                 return True
9         return False
10
11 def _is_bad_subject(subject):
12         """Copied from: flask_mail.py class Message def has_bad_headers"""
13         if _has_newline(subject):
14                 for linenum, line in enumerate(subject.split("\r\n")):
15                         if not line:
16                                 return True
17                         if linenum > 0 and line[0] not in "\t ":
18                                 return True
19                         if _has_newline(line):
20                                 return True
21                         if len(line.strip()) == 0:
22                                 return True
23         return False
24
25
26 class FlaskMailSubjectFormatter(logging.Formatter):
27         def format(self, record):
28                 record.message = record.getMessage()
29                 if self.usesTime():
30                         record.asctime = self.formatTime(record, self.datefmt)
31                 s = self.formatMessage(record)
32                 return s
33
34 class FlaskMailTextFormatter(logging.Formatter):
35         pass
36
37 # TODO: hier nog niet tevreden over (vooral logger.error(..., exc_info, stack_info))
38 class FlaskMailHTMLFormatter(logging.Formatter):
39         pre_template = "<h1>%s</h1><pre>%s</pre>"
40         def formatException(self, exc_info):
41                 formatted_exception = logging.Handler.formatException(self, exc_info)
42                 return FlaskMailHTMLFormatter.pre_template % ("Exception information", formatted_exception)
43         def formatStack(self, stack_info):
44                 return FlaskMailHTMLFormatter.pre_template % ("<h1>Stack information</h1><pre>%s</pre>", stack_info)
45
46
47 # see: https://github.com/python/cpython/blob/3.6/Lib/logging/__init__.py (class Handler)
48
49 class FlaskMailHandler(logging.Handler):
50         def __init__(self, mailer, subject_template, level=logging.NOTSET):
51                 logging.Handler.__init__(self, level)
52                 self.mailer = mailer
53                 self.send_to = mailer.app.config["MAIL_UTILS_ERROR_SEND_TO"]
54                 self.subject_template = subject_template
55                 self.html_formatter = None
56
57         def setFormatter(self, text_fmt, html_fmt=None):
58                 """
59                 Set the formatters for this handler. Provide at least one formatter.
60                 When no text_fmt is provided, no text-part is created for the email body.
61                 """
62                 assert (text_fmt, html_fmt) != (None, None), "At least one formatter should be provided"
63                 if type(text_fmt)==str:
64                         text_fmt = FlaskMailTextFormatter(text_fmt)
65                 self.formatter = text_fmt
66                 if type(html_fmt)==str:
67                         html_fmt = FlaskMailHTMLFormatter(html_fmt)
68                 self.html_formatter = html_fmt
69
70         def getSubject(self, record):
71                 fmt = FlaskMailSubjectFormatter(self.subject_template)
72                 subject = fmt.format(record)
73                 #Since templates can cause header problems, and we rather have a incomplete email then an error, we fix this
74                 if _is_bad_subject(subject):
75                         subject="FlaskMailHandler log-entry from %s [original subject is replaced, because it would result in a bad header]" % self.mailer.app.name
76                 return subject
77
78         def emit(self, record):
79                 text = self.format(record)                              if self.formatter         else None
80                 html = self.html_formatter.format(record) if self.html_formatter else None
81                 sendEmailRaw.delay(self.send_to, self.getSubject(record), text, html)
82
83
84 def register_mail_error_handler(app, mailer):
85         subject_template = "ContentDB crashed (%(module)s > %(funcName)s)"
86         text_template = """
87 Message type: %(levelname)s
88 Location:        %(pathname)s:%(lineno)d
89 Module:    %(module)s
90 Function:        %(funcName)s
91 Time:            %(asctime)s
92 Message:
93 %(message)s"""
94         html_template = """
95 <style>th { text-align: right}</style><table>
96 <tr><th>Message type:</th><td>%(levelname)s</td></tr>
97 <tr>    <th>Location:</th><td>%(pathname)s:%(lineno)d</td></tr>
98 <tr>      <th>Module:</th><td>%(module)s</td></tr>
99 <tr>    <th>Function:</th><td>%(funcName)s</td></tr>
100 <tr>            <th>Time:</th><td>%(asctime)s</td></tr>
101 </table>
102 <h2>Message</h2>
103 <pre>%(message)s</pre>"""
104
105         import logging
106         mail_handler = FlaskMailHandler(mailer, subject_template)
107         mail_handler.setLevel(logging.ERROR)
108         mail_handler.setFormatter(text_template, html_template)
109         app.logger.addHandler(mail_handler)