]>
git.ipfire.org Git - ipfire.org.git/blob - src/backend/messages.py
4 import email
.mime
.multipart
10 import tornado
.template
13 from .decorators
import *
15 class Messages(misc
.Object
):
18 return Queue(self
.backend
)
21 def template_loader(self
):
23 Creates a new template loader
25 templates_dir
= os
.path
.join(self
.settings
.get("templates_dir"), "messages")
27 return tornado
.template
.Loader(templates_dir
, autoescape
=None)
30 return email
.utils
.make_msgid("ipfire", domain
="ipfire.org")
33 def bounce_email_address(self
):
34 return self
.settings
.get("bounce_email_address")
36 def send(self
, recipients
, message
, priority
=None, headers
={}):
37 # Convert message from string
38 if not isinstance(message
, email
.message
.Message
):
39 message
= email
.message_from_string(message
)
41 # Add a message ID if non exsist
42 if not "Message-Id" in message
and not "Message-ID" in message
:
43 message
.add_header("Message-Id", self
.make_msgid())
48 message
.replace_header(k
, v
)
50 message
.add_header(k
, v
)
52 # Add date if the message doesn't have one already
53 if "Date" not in message
:
54 message
.add_header("Date", email
.utils
.formatdate())
56 # Send any errors to the bounce address
57 if self
.bounce_email_address
:
58 message
.add_header("Errors-To", "<%s>" % self
.bounce_email_address
)
61 self
._send
(recipients
, message
.as_string(), priority
=priority
)
63 def send_template(self
, template_name
, recipients
,
64 sender
=None, priority
=None, headers
={}, **kwargs
):
66 Send a message based on the given template
68 # Create the required namespace to render the message
71 "backend" : self
.backend
,
73 namespace
.update(kwargs
)
75 # Create a MIMEMultipart message.
76 message
= email
.mime
.multipart
.MIMEMultipart()
78 for extension
, mime_type
in (("txt", "plain"), ("html", "html")):
80 t
= self
.template_loader
.load("%s.%s" % (template_name
, extension
))
86 message_part
= t
.generate(**namespace
)
88 # Reset the rendered template when it could not be rendered
90 self
.template_loader
.reset()
93 # Parse the message and extract the header
94 message_part
= email
.message_from_string(message_part
.decode())
95 for k
, v
in list(message_part
.items()):
97 message
.replace_header(k
, v
)
99 message
.add_header(k
, v
)
101 message_body
= message_part
.get_payload()
103 # Wrap texts to 120 characters per line
104 if mime_type
== "plain":
105 message_body
= wrap(message_body
, 120)
107 # Create a MIMEText object out of it
108 message_part
= email
.mime
.text
.MIMEText(message_body
, mime_type
, "utf-8")
110 # Attach the parts to the mime container.
111 # According to RFC2046, the last part of a multipart message
113 alternative
.attach(message_part
)
115 # Add alternative section to outer message
116 message
.attach(alternative
)
119 self
.send(recipients
, message
, priority
=priority
, headers
=headers
)
121 # In debug mode, re-compile the templates with every request
122 if self
.backend
.debug
:
123 self
.template_loader
.reset()
126 class Queue(misc
.Object
):
129 return self
.db
.query("SELECT * FROM messages \
130 WHERE time_sent IS NULL \
131 ORDER BY priority DESC, time_created ASC")
133 @tornado.gen
.coroutine
136 for message
in self
.messages
:
137 self
._sendmail
(message
)
139 logging
.debug("All messages sent")
141 def _sendmail(self
, message
):
143 Delivers the given message to sendmail.
146 # Parse the message from what is in the database
147 msg
= email
.message_from_string(message
.message
)
149 logging
.info("Sending a message %s to: %s" % (
150 msg
.get("Subject"), ", ".join(message
.envelope_recipients
)
153 # Make sendmail command line
155 "/usr/sbin/sendmail",
157 # Don't treat a single line with . as end of input
161 "-f", msg
.get("From"),
164 # Envelope Recipients
165 cmd
+= message
.envelope_recipients
167 # Run sendmail and pipe the email in
168 p
= subprocess
.Popen(cmd
, bufsize
=0, close_fds
=True,
169 stdin
=subprocess
.PIPE
, stdout
=subprocess
.PIPE
, stderr
=subprocess
.STDOUT
)
171 stdout
, stderr
= p
.communicate(message
.message
.encode("utf-8"))
173 # Wait until sendmail has finished
177 self
.db
.execute("UPDATE messages SET error_message = %s \
178 WHERE id = %s", stdout
, message
.id)
180 logging
.error("Could not send mail: %s" % stdout
)
182 # Raise all exceptions
187 # After the email has been successfully sent, we mark it as such
188 self
.db
.execute("UPDATE messages SET time_sent = NOW() \
189 WHERE id = %s", message
.id)
191 @tornado.gen
.coroutine
193 logging
.debug("Cleaning up message queue")
195 self
.db
.execute("DELETE FROM messages \
196 WHERE time_sent IS NOT NULL AND time_sent >= NOW() + '1 day'::interval")
199 def wrap(text
, width
):
202 for paragraph
in text
.split("\n\n"):
203 paragraph
= textwrap
.wrap(paragraph
, width
,
204 break_long_words
=False, replace_whitespace
=False)
207 s
.append("\n".join(paragraph
))
209 return "\n\n".join(s
)