Keep Isso modular, not monolithic. Make it easy to integrate a web interface or add XMPP notifications. This refactorization includes minor bugfixes and changes: * CORS middleware did not work properly due to wrong unit tests * more type checks on JSON input * new detection for origin and public url, closes #28 * new activation and delete url (no redirect for old urls, but you can convert the old urls: copy hash after `/activate/` (or delete) and open `/id/<id of comment>/activate/<hash>` * move crypto.py to utils/ With this commit, SMTP is no longer automatically configured: add `notify = smtp` to the `[general]` section to use SMTP.pull/38/head
parent
a442b8e0ee
commit
6e85c54a2e
@ -0,0 +1,17 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
|
||||
class Signal(object):
|
||||
|
||||
def __init__(self, *subscriber):
|
||||
self.subscriptions = defaultdict(list)
|
||||
|
||||
for sub in subscriber:
|
||||
for signal, func in sub:
|
||||
self.subscriptions[signal].append(func)
|
||||
|
||||
def __call__(self, origin, *args, **kwargs):
|
||||
for subscriber in self.subscriptions[origin]:
|
||||
subscriber(*args, **kwargs)
|
@ -0,0 +1,160 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import time
|
||||
import json
|
||||
|
||||
import socket
|
||||
import smtplib
|
||||
|
||||
from email.header import Header
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger("isso")
|
||||
|
||||
try:
|
||||
import uwsgi
|
||||
except ImportError:
|
||||
uwsgi = None
|
||||
|
||||
from isso.compat import PY2K
|
||||
from isso import local
|
||||
|
||||
if PY2K:
|
||||
from thread import start_new_thread
|
||||
else:
|
||||
from _thread import start_new_thread
|
||||
|
||||
|
||||
class SMTP(object):
|
||||
|
||||
def __init__(self, isso):
|
||||
|
||||
self.isso = isso
|
||||
self.conf = isso.conf.section("smtp")
|
||||
|
||||
# test SMTP connectivity
|
||||
try:
|
||||
with self:
|
||||
logger.info("connected to SMTP server")
|
||||
except (socket.error, smtplib.SMTPException):
|
||||
logger.warn("unable to connect to SMTP server")
|
||||
|
||||
if uwsgi:
|
||||
def spooler(args):
|
||||
try:
|
||||
self._sendmail(args["subject"].decode("utf-8"),
|
||||
args["body"].decode("utf-8"))
|
||||
except smtplib.SMTPConnectError:
|
||||
return uwsgi.SPOOL_RETRY
|
||||
else:
|
||||
return uwsgi.SPOOL_OK
|
||||
|
||||
uwsgi.spooler = spooler
|
||||
|
||||
def __enter__(self):
|
||||
klass = (smtplib.SMTP_SSL if self.conf.getboolean('ssl') else smtplib.SMTP)
|
||||
self.client = klass(host=self.conf.get('host'), port=self.conf.getint('port'))
|
||||
|
||||
if self.conf.get('username') and self.conf.get('password'):
|
||||
self.client.login(self.conf.get('username'),
|
||||
self.conf.get('password'))
|
||||
|
||||
return self.client
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.client.quit()
|
||||
|
||||
def __iter__(self):
|
||||
yield "comments.new:after-save", self.notify
|
||||
|
||||
def format(self, thread, comment):
|
||||
|
||||
permalink = local("origin") + thread["uri"] + "#isso-%i" % comment["id"]
|
||||
|
||||
rv = []
|
||||
rv.append("%s schrieb:" % (comment["author"] or "Jemand"))
|
||||
rv.append("")
|
||||
rv.append(comment["text"])
|
||||
rv.append("")
|
||||
|
||||
if comment["website"]:
|
||||
rv.append("Webseite des Kommentators: %s" % comment["website"])
|
||||
|
||||
rv.append("IP Adresse: %s" % comment["remote_addr"])
|
||||
rv.append("Link zum Kommentar: %s" % permalink)
|
||||
rv.append("")
|
||||
|
||||
uri = local("host") + "/id/%i" % comment["id"]
|
||||
key = self.isso.sign(comment["id"])
|
||||
|
||||
rv.append("---")
|
||||
rv.append("Kommentar löschen: %s" % uri + "/delete/" + key)
|
||||
|
||||
if comment["mode"] == 2:
|
||||
rv.append("Kommentar freischalten: %s" % uri + "/activate/" + key)
|
||||
|
||||
return u'\n'.join(rv)
|
||||
|
||||
def notify(self, thread, comment):
|
||||
|
||||
body = self.format(thread, comment)
|
||||
|
||||
if uwsgi:
|
||||
uwsgi.spool({b"subject": thread["title"].encode("utf-8"),
|
||||
b"body": body.encode("utf-8")})
|
||||
else:
|
||||
start_new_thread(self._retry, (thread["title"], body))
|
||||
|
||||
def _sendmail(self, subject, body):
|
||||
|
||||
from_addr = self.conf.get("from")
|
||||
to_addr = self.conf.get("to")
|
||||
|
||||
msg = MIMEText(body, 'plain', 'utf-8')
|
||||
msg['From'] = "Ich schrei sonst! <%s>" % from_addr
|
||||
msg['To'] = to_addr
|
||||
msg['Subject'] = Header(subject, 'utf-8')
|
||||
|
||||
with self as con:
|
||||
con.sendmail(from_addr, to_addr, msg.as_string())
|
||||
|
||||
def _retry(self, subject, body):
|
||||
for x in range(5):
|
||||
try:
|
||||
self._sendmail(subject, body)
|
||||
except smtplib.SMTPConnectError:
|
||||
time.sleep(60)
|
||||
else:
|
||||
break
|
||||
|
||||
|
||||
class Stdout(object):
|
||||
|
||||
def __init__(self, conf):
|
||||
pass
|
||||
|
||||
def __iter__(self):
|
||||
|
||||
yield "comments.new:new-thread", self._new_thread
|
||||
yield "comments.new:finish", self._new_comment
|
||||
yield "comments.edit", self._edit_comment
|
||||
yield "comments.delete", self._delete_comment
|
||||
yield "comments.activate", self._activate_comment
|
||||
|
||||
def _new_thread(self, thread):
|
||||
logger.info("new thread %(id)s: %(title)s" % thread)
|
||||
|
||||
def _new_comment(self, thread, comment):
|
||||
logger.info("comment created: %s", json.dumps(comment))
|
||||
|
||||
def _edit_comment(self, comment):
|
||||
logger.info('comment %i edited: %s', comment["id"], json.dumps(comment))
|
||||
|
||||
def _delete_comment(self, id):
|
||||
logger.info('comment %i deleted', id)
|
||||
|
||||
def _activate_comment(self, id):
|
||||
logger.info("comment %s activated" % id)
|
@ -1,83 +0,0 @@
|
||||
# -*- encoding: utf-8 -*-
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from smtplib import SMTP, SMTP_SSL
|
||||
from email.header import Header
|
||||
from email.mime.text import MIMEText
|
||||
|
||||
|
||||
def format(comment, permalink, remote_addr, deletion_key, activation_key=None):
|
||||
|
||||
rv = []
|
||||
rv.append("%s schrieb:" % (comment["author"] or "Jemand"))
|
||||
rv.append("")
|
||||
rv.append(comment["text"])
|
||||
rv.append("")
|
||||
|
||||
if comment["website"]:
|
||||
rv.append("Webseite des Kommentators: %s" % comment["website"])
|
||||
|
||||
rv.append("IP Adresse: %s" % remote_addr)
|
||||
rv.append("Link zum Kommentar: %s" % permalink)
|
||||
|
||||
rv.append("")
|
||||
rv.append("---")
|
||||
rv.append("Kommentar löschen: %s" % deletion_key)
|
||||
|
||||
if activation_key:
|
||||
rv.append("Kommentar freischalten: %s" % activation_key)
|
||||
|
||||
return u'\n'.join(rv)
|
||||
|
||||
|
||||
class Connection(object):
|
||||
"""
|
||||
Establish connection to SMTP server, optional with authentication, and
|
||||
close connection afterwards.
|
||||
"""
|
||||
|
||||
def __init__(self, conf):
|
||||
self.conf = conf
|
||||
|
||||
def __enter__(self):
|
||||
self.server = (SMTP_SSL if self.conf.getboolean('smtp', 'ssl') else SMTP)(
|
||||
host=self.conf.get('smtp', 'host'), port=self.conf.getint('smtp', 'port'))
|
||||
|
||||
if self.conf.get('smtp', 'username') and self.conf.get('smtp', 'password'):
|
||||
self.server.login(self.conf.get('smtp', 'username'),
|
||||
self.conf.get('smtp', 'password'))
|
||||
|
||||
return self.server
|
||||
|
||||
def __exit__(self, exc_type, exc_value, traceback):
|
||||
self.server.quit()
|
||||
|
||||
|
||||
class SMTPMailer(object):
|
||||
|
||||
def __init__(self, conf):
|
||||
|
||||
self.conf = conf
|
||||
self.from_addr = conf.get('smtp', 'from')
|
||||
self.to_addr = conf.get('smtp', 'to')
|
||||
|
||||
# test SMTP connectivity
|
||||
with Connection(self.conf):
|
||||
pass
|
||||
|
||||
def sendmail(self, subject, body):
|
||||
|
||||
msg = MIMEText(body, 'plain', 'utf-8')
|
||||
msg['From'] = "Ich schrei sonst! <%s>" % self.from_addr
|
||||
msg['To'] = self.to_addr
|
||||
msg['Subject'] = Header(subject, 'utf-8')
|
||||
|
||||
with Connection(self.conf) as con:
|
||||
con.sendmail(self.from_addr, self.to_addr, msg.as_string())
|
||||
|
||||
|
||||
class NullMailer(object):
|
||||
|
||||
def sendmail(self, subject, body):
|
||||
pass
|
Loading…
Reference in new issue