@ -14,6 +14,11 @@ from email.utils import formatdate
from email . header import Header
from email . mime . text import MIMEText
try :
from urllib . parse import quote
except ImportError :
from urllib import quote
import logging
logger = logging . getLogger ( " isso " )
@ -31,37 +36,10 @@ else:
from _thread import start_new_thread
class SMTP ( object ) :
def __init__ ( self , isso ) :
self . isso = isso
self . conf = isso . conf . section ( " smtp " )
gh = isso . conf . get ( " general " , " host " )
if type ( gh ) == str :
self . general_host = gh
#if gh is not a string then gh is a list
else :
self . general_host = gh [ 0 ]
# test SMTP connectivity
try :
with self :
logger . info ( " connected to SMTP server " )
except ( socket . error , smtplib . SMTPException ) :
logger . exception ( " unable to connect to SMTP server " )
class SMTPConnection ( object ) :
if uwsgi :
def spooler ( args ) :
try :
self . _sendmail ( args [ b " 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 __init__ ( self , conf ) :
self . conf = conf
def __enter__ ( self ) :
klass = ( smtplib . SMTP_SSL if self . conf . get (
@ -91,10 +69,40 @@ class SMTP(object):
def __exit__ ( self , exc_type , exc_value , traceback ) :
self . client . quit ( )
class SMTP ( object ) :
def __init__ ( self , isso ) :
self . isso = isso
self . conf = isso . conf . section ( " smtp " )
self . public_endpoint = isso . conf . get ( " server " , " public-endpoint " ) or local ( " host " )
self . admin_notify = any ( ( n in ( " smtp " , " SMTP " ) ) for n in isso . conf . getlist ( " general " , " notify " ) )
self . reply_notify = isso . conf . getboolean ( " general " , " reply-notifications " )
# test SMTP connectivity
try :
with SMTPConnection ( self . conf ) :
logger . info ( " connected to SMTP server " )
except ( socket . error , smtplib . SMTPException ) :
logger . exception ( " unable to connect to SMTP server " )
if uwsgi :
def spooler ( args ) :
try :
self . _sendmail ( args [ b " 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 __iter__ ( self ) :
yield " comments.new:after-save " , self . notify
yield " comments.new:after-save " , self . notify_new
yield " comments.activate " , self . notify_activated
def format ( self , thread , comment ) :
def format ( self , thread , comment , parent_comment , recipient = None , admin = False ):
rv = io . StringIO ( )
@ -107,40 +115,74 @@ class SMTP(object):
rv . write ( comment [ " text " ] + " \n " )
rv . write ( " \n " )
if comment [ " website " ] :
rv . write ( " User ' s URL: %s \n " % comment [ " website " ] )
if admin :
if comment [ " website " ] :
rv . write ( " User ' s URL: %s \n " % comment [ " website " ] )
rv . write ( " IP address: %s \n " % comment [ " remote_addr " ] )
rv . write ( " IP address: %s \n " % comment [ " remote_addr " ] )
rv . write ( " Link to comment: %s \n " %
( local ( " origin " ) + thread [ " uri " ] + " #isso- %i " % comment [ " id " ] ) )
rv . write ( " \n " )
rv . write ( " --- \n " )
uri = self . general_host + " /id/ %i " % comment [ " id " ]
key = self . isso . sign ( comment [ " id " ] )
if admin :
uri = self . public_endpoint + " /id/ %i " % comment [ " id " ]
key = self . isso . sign ( comment [ " id " ] )
rv . write ( " --- \n " )
rv . write ( " Delete comment: %s \n " % ( uri + " /delete/ " + key ) )
rv . write ( " Delete comment: %s \n " % ( uri + " /delete/ " + key ) )
if comment [ " mode " ] == 2 :
rv . write ( " Activate comment: %s \n " % ( uri + " /activate/ " + key ) )
if comment [ " mode " ] == 2 :
rv . write ( " Activate comment: %s \n " % ( uri + " /activate/ " + key ) )
rv . seek ( 0 )
return rv . read ( )
else :
uri = self . public_endpoint + " /id/ %i " % parent_comment [ " id " ]
key = self . isso . sign ( ( ' unsubscribe ' , recipient ) )
def notify ( self , thread , comment ) :
rv . write ( " Unsubscribe from this conversation: %s \n " % ( uri + " /unsubscribe/ " + quote ( recipient ) + " / " + key ) )
body = self . format ( thread , comment )
rv . seek ( 0 )
return rv . read ( )
def notify_new ( self , thread , comment ) :
if self . admin_notify :
body = self . format ( thread , comment , None , admin = True )
self . sendmail ( thread [ " title " ] , body , thread , comment )
if comment [ " mode " ] == 1 :
self . notify_users ( thread , comment )
def notify_activated ( self , thread , comment ) :
self . notify_users ( thread , comment )
def notify_users ( self , thread , comment ) :
if self . reply_notify and " parent " in comment and comment [ " parent " ] is not None :
# Notify interested authors that a new comment is posted
notified = [ ]
parent_comment = self . isso . db . comments . get ( comment [ " parent " ] )
comments_to_notify = [ parent_comment ] if parent_comment is not None else [ ]
comments_to_notify + = self . isso . db . comments . fetch ( thread [ " uri " ] , mode = 1 , parent = comment [ " parent " ] )
for comment_to_notify in comments_to_notify :
email = comment_to_notify [ " email " ]
if " email " in comment_to_notify and comment_to_notify [ " notification " ] and email not in notified \
and comment_to_notify [ " id " ] != comment [ " id " ] and email != comment [ " email " ] :
body = self . format ( thread , comment , parent_comment , email , admin = False )
subject = " Re: New comment posted on %s " % thread [ " title " ]
self . sendmail ( subject , body , thread , comment , to = email )
notified . append ( email )
def sendmail ( self , subject , body , thread , comment , to = None ) :
if uwsgi :
uwsgi . spool ( { b " subject " : thread [ " title " ] . encode ( " utf-8 " ) ,
b " body " : body . encode ( " utf-8 " ) } )
uwsgi . spool ( { b " subject " : subject . encode ( " utf-8 " ) ,
b " body " : body . encode ( " utf-8 " ) ,
b " to " : to } )
else :
start_new_thread ( self . _retry , ( thread [ " title " ] , body ) )
start_new_thread ( self . _retry , ( subject, body , to ) )
def _sendmail ( self , subject , body ) :
def _sendmail ( self , subject , body , to = None ):
from_addr = self . conf . get ( " from " )
to_addr = self . conf . get ( " to " )
to_addr = to or self . conf . get ( " to " )
msg = MIMEText ( body , ' plain ' , ' utf-8 ' )
msg [ ' From ' ] = from_addr
@ -148,13 +190,13 @@ class SMTP(object):
msg [ ' Date ' ] = formatdate ( localtime = True )
msg [ ' Subject ' ] = Header ( subject , ' utf-8 ' )
with self as con :
with SMTPConnection ( self . conf ) as con :
con . sendmail ( from_addr , to_addr , msg . as_string ( ) )
def _retry ( self , subject , body ):
def _retry ( self , subject , body , to ):
for x in range ( 5 ) :
try :
self . _sendmail ( subject , body )
self . _sendmail ( subject , body , to )
except smtplib . SMTPConnectError :
time . sleep ( 60 )
else :
@ -187,5 +229,5 @@ class Stdout(object):
def _delete_comment ( self , id ) :
logger . info ( ' comment %i deleted ' , id )
def _activate_comment ( self , id ) :
logger . info ( " comment % s activated " % i d)
def _activate_comment ( self , thread , comment ) :
logger . info ( " comment % (id)s activated " % threa d)