def refuse(self, m, mylist, reason): deepdbg('Refuse mail for %s' % unicode(mylist)) msgfrom = mylist.list_address_extended('owner') msgto = m['From'] reason = u"\n\nYour message to the list %s has been refused\n\n" % unicode(mylist) + reason # create mail mail = MIMEMultipart() mail['Subject'] = u"Your message to the list %s has been refused " % unicode(mylist) mail['From'] = msgfrom mail['To'] = msgto mail.attach(MIMEText(reason, 'plain', 'utf8')) # error msg mail.attach(m) # previous mail # ugly hack we need to add headers at the TOP # items = m.items() # for key,val in items: m.__delitem__(key) # delete them all # for key,val in items: m.add_header(key,val) # re-add them all # mail.attach(m) # add original mail # send it try: connection = SMTPConnection() # we use the django one, configured with stuff from settings connection.open() if not connection.connection: raise SMTPException, "Fail to create SMTPConnection.connection" connection.connection.sendmail( mylist.list_address_extended('owner'), # from, will be set in Return-Path [ msgto ], # to mail.as_string()) # content except SMTPException, detail: err('refuse: SMTP Error while sending the email: %s'%detail) return # TODO : prevent loops if smtp fails : the mail stays in incoming and will be tried again in loop
def send_all(block_size=500): """ Send all non-deferred messages in the queue. A lock file is used to ensure that this process can not be started again while it is already running. The ``block_size`` argument allows for queued messages to be iterated in blocks, allowing new prioritised messages to be inserted during iteration of a large number of queued messages. """ lock = FileLock(LOCK_PATH) logger.debug("Acquiring lock...") try: # lockfile has a bug dealing with a negative LOCK_WAIT_TIMEOUT (which # is the default if it's not provided) systems which use a LinkFileLock # so ensure that it is never a negative number. lock.acquire(LOCK_WAIT_TIMEOUT and max(0, LOCK_WAIT_TIMEOUT)) except AlreadyLocked: logger.debug("Lock already in place. Exiting.") return except LockTimeout: logger.debug("Waiting for the lock timed out. Exiting.") return logger.debug("Lock acquired.") start_time = time.time() sent = deferred = skipped = 0 connection = None try: connection = SMTPConnection() blacklist = models.Blacklist.objects.values_list("email", flat=True) connection.open() for message in _message_queue(block_size): result = send_message(message, smtp_connection=connection, blacklist=blacklist) if result == constants.RESULT_SENT: sent += 1 elif result == constants.RESULT_FAILED: deferred += 1 elif result == constants.RESULT_SKIPPED: skipped += 1 connection.close() finally: logger.debug("Releasing lock...") lock.release() logger.debug("Lock released.") logger.debug("") if sent or deferred or skipped: log = logger.warning else: log = logger.info log("%s sent, %s deferred, %s skipped." % (sent, deferred, skipped)) logger.debug("Completed in %.2f seconds." % (time.time() - start_time))
def forward_mail_to_subscribers(self, mail, subscribers, mylist): # actually forward it try: connection = SMTPConnection() # we use the django one, configured with stuff from settings connection.open() if not connection.connection: raise SMTPException, "Fail to create SMTPConnection.connection" connection.connection.sendmail( mylist.list_address_extended('owner'), # from, will be set in Return-Path subscribers, # to mail.as_string()) # content except SMTPException, detail: err('SMTP Error while sending the email: %s'%detail) return # TODO : prevent loops if smtp fails : the mail stays in incoming and will be tried again in loop
def forward_mail_to_subscribers(self, mail, subscribers, mylist): # actually forward it try: connection = SMTPConnection( ) # we use the django one, configured with stuff from settings connection.open() if not connection.connection: raise SMTPException, "Fail to create SMTPConnection.connection" connection.connection.sendmail( mylist.list_address_extended( 'owner'), # from, will be set in Return-Path subscribers, # to mail.as_string()) # content except SMTPException, detail: err('SMTP Error while sending the email: %s' % detail) return # TODO : prevent loops if smtp fails : the mail stays in incoming and will be tried again in loop
def send_message(queued_message, smtp_connection=None, blacklist=None, log=True): """ Send a queued message, returning a response code as to the action taken. The response codes can be found in ``django_mailer.constants``. The response will be either ``RESULT_SKIPPED`` for a blacklisted email, ``RESULT_FAILED`` for a deferred message or ``RESULT_SENT`` for a successful sent message. To allow optimizations if multiple messages are to be sent, an SMTP connection can be provided and a list of blacklisted email addresses. Otherwise an SMTP connection will be opened to send this message and the email recipient address checked against the ``Blacklist`` table. If the message recipient is blacklisted, the message will be removed from the queue without being sent. Otherwise, the message is attempted to be sent with an SMTP failure resulting in the message being flagged as deferred so it can be tried again later. By default, a log is created as to the action. Either way, the original message is not deleted. """ message = queued_message.message if smtp_connection is None: smtp_connection = SMTPConnection() opened_connection = False if blacklist is None: blacklisted = models.Blacklist.objects.filter(email=message.to_address) else: blacklisted = message.to_address in blacklist log_message = '' if blacklisted: logger.info("Not sending to blacklisted email: %s" % message.to_address.encode("utf-8")) queued_message.delete() result = constants.RESULT_SKIPPED else: try: logger.info("Sending message to %s: %s" % (message.to_address.encode("utf-8"), message.subject.encode("utf-8"))) opened_connection = smtp_connection.open() smtp_connection.connection.sendmail(message.from_address, [message.to_address], message.encoded_message) queued_message.delete() result = constants.RESULT_SENT except (SocketError, smtplib.SMTPSenderRefused, smtplib.SMTPRecipientsRefused, smtplib.SMTPAuthenticationError), err: queued_message.defer() logger.warning("Message to %s deferred due to failure: %s" % (message.to_address.encode("utf-8"), err)) log_message = unicode(err) result = constants.RESULT_FAILED
def refuse(self, m, mylist, reason): deepdbg('Refuse mail for %s' % unicode(mylist)) msgfrom = mylist.list_address_extended('owner') msgto = m['From'] reason = u"\n\nYour message to the list %s has been refused\n\n" % unicode( mylist) + reason # create mail mail = MIMEMultipart() mail[ 'Subject'] = u"Your message to the list %s has been refused " % unicode( mylist) mail['From'] = msgfrom mail['To'] = msgto mail.attach(MIMEText(reason, 'plain', 'utf8')) # error msg mail.attach(m) # previous mail # ugly hack we need to add headers at the TOP # items = m.items() # for key,val in items: m.__delitem__(key) # delete them all # for key,val in items: m.add_header(key,val) # re-add them all # mail.attach(m) # add original mail # send it try: connection = SMTPConnection( ) # we use the django one, configured with stuff from settings connection.open() if not connection.connection: raise SMTPException, "Fail to create SMTPConnection.connection" connection.connection.sendmail( mylist.list_address_extended( 'owner'), # from, will be set in Return-Path [msgto], # to mail.as_string()) # content except SMTPException, detail: err('refuse: SMTP Error while sending the email: %s' % detail) return # TODO : prevent loops if smtp fails : the mail stays in incoming and will be tried again in loop
def send_invites(): count = 0 try: count = sys.argv[1] except: pass invites = Email_Invite.objects.filter(invited=False).order_by('created_date')[:count] messages = [ ] subject = 'Welcome to the Portrit preview!' html_content = '<p>We are happy to announce the launch of the Portrit preview</p>' \ '<a href="http://portrit.com/">' \ '<img src="http://portrit.com/site_media/img/invite/invite.jpg"/>' \ '</a>' \ '<p>Please go to <a href="http://portrit.com">portrit.com</a> and login with the email address this message was sent to.</p>' \ '<p>With great power comes great responsibilty. With this in mind, we ask that you help us with any feedback you may have.<br>You can email us at <a href="mailto:[email protected]">[email protected]</a> or use the built in feedback control at the top of Portrit.</p>' \ '<br><br>' \ '<p style="color: #aeaeae; text-align: center; font-size: 12px;">You are receiving this email because you asked to receive an invite to portrit.com<br>' \ 'Note: This email address is for distribution purposes only. Please do not reply to this email because your message will not be received.</p>' from_email = '*****@*****.**' from django.core.mail import SMTPConnection from django.core import mail connection = SMTPConnection() # Manually open the connection connection.open() for invite in invites: email = mail.EmailMessage(subject, html_content, from_email, [invite.email], connection=connection) email.content_subtype = "html" messages.append(email) invite.invited = True invite.save() connection.send_messages(messages) connection.close()
def handle_noargs(self, **options): current_site = Site.objects.get_current() # use low level SMTP object to improve performance smtp = SMTPConnection() smtp.open() notices_count = 0 for user in User.objects.filter(email__isnull=False): notices = {} for notice in Notice.objects.notices_for(user).order_by('-notice_type__default', 'notice_type'): if should_send(user, notice.notice_type, DIGEST_MEDIUM): if notice.notice_type.display in notices: notices[notice.notice_type.display].append(notice) else: notices[notice.notice_type.display] = [notice] notices_count += 1 notice.archive() if notices: context = Context({'notices': notices, 'user': user}) body = render_to_string ('notification/digest_email_body.html', context_instance=context) subject = render_to_string ('notification/digest_email_subject.txt', {'count': notices_count}) msg = EmailMessage(subject, body, settings.DEFAULT_FROM_EMAIL, [user.email]) msg.content_subtype = "html" # Main content is now text/html smtp._send(msg) smtp.close()
class SendingOutCommand(NoArgsCommand): subscriber_base = None # Subscriber.rsd or whatever from_email = 'The Daily Gazette <*****@*****.**>' WAIT_TIME = 60 def set_content(self, dummy_request): raise NotImplemented def get_subject(self): today = datetime.date.today() return today.strftime("%%A, %%B %d, %%Y" % today.day) def get_sent_str(self): return datetime.date.today().strftime('%Y-%m-%d') def contents_for_subscriber(self, subscriber): return (self.text_content, self.html_content) def send_to_subscriber(self, subscriber): subj = self.subject frm = self.from_email text_content, html_content = self.contents_for_subscriber(subscriber) if subscriber.plain_text: msg = EmailMessage(subj, text_content, frm, [subscriber.email]) else: msg = EmailMessage(subj, html_content, frm, [subscriber.email]) msg.content_subtype = "html" # msg = EmailMessage(self.subject, text_content, self.from_email, [subscriber.email]) # if not subscriber.plain_text: # msg.multipart_subtype = 'alternative' # msg.attach(content=html_content, mimetype='text/html') self.connection.send_messages([msg]) # if it didn't just crash, sending was successful :) subscriber.last_sent = self.sent_str subscriber.save() def renew_connection(self, subscriber, retries=5, sleep_time_base=2): # close the old one self.connection.fail_silently = True self.connection.close() # get a new one for i in range(retries): try: self.connection = SMTPConnection() self.connection.open() except socket.timeout: self.add_error(subscriber, 'timeout while opening connection') time.sleep(sleep_time_base ** i) else: return def try_sending_to_subscriber(self, subscriber, repeat_index=0, max_repeats=2): if repeat_index >= max_repeats: return try: self.send_to_subscriber(subscriber) except smtplib.SMTPRecipientsRefused: self.add_error(subscriber, 'recipient refused') except smtplib.SMTPServerDisconnected: self.add_error(subscriber, 'server disconnected') self.renew_connection(subscriber) self.try_sending_to_subscriber(subscriber, repeat_index + 1, max_repeats) except socket.sslerror: self.add_error(subscriber, 'ssl error') # probably timeout self.renew_connection(subscriber) self.try_sending_to_subscriber(subscriber, repeat_index + 1, max_repeats) except socket.timeout: self.add_error(subscriber, 'timeout') self.renew_connection(subscriber) self.try_sending_to_subscriber(subscriber, repeat_index + 1, max_repeats) except smtplib.SMTPException: self.add_error(subscriber, 'smtp error') except Exception: self.add_error(subscriber, 'generic error') def add_error(self, subscriber, key): self.errors.setdefault(subscriber.email, {}) self.errors[subscriber.email].setdefault(key, []) exc_type, exc_val, exc_trace = sys.exc_info() self.errors[subscriber.email][key].append({ 'type': exc_type, 'val': exc_val, 'traceback': ''.join(traceback.format_exception(exc_type, exc_val, exc_trace)), 'time': datetime.datetime.now() }) def handle_noargs(self, **options): dummy_request = HttpRequest() dummy_request.GET['for_email'] = 'true' self.set_content(dummy_request) self.subject = self.get_subject() self.sent_str = self.get_sent_str() self.connection = SMTPConnection() self.errors = {} print 'starting: ' + datetime.datetime.now().strftime("%c") # try up to four times, if necessary for i in range(4): not_sent = self.subscriber_base.exclude(last_sent=self.sent_str) if len(not_sent): # intentionally resolve the query if i != 0: print datetime.datetime.now().strftime('%c'), print '- sleeping before retrying %s emails...' % len(not_sent) time.sleep(self.WAIT_TIME) for subscriber in not_sent: self.try_sending_to_subscriber(subscriber) # any that *still* didn't work? error_output = [] for sub in self.subscriber_base.exclude(last_sent=self.sent_str): errors = [ '%d %s' % (len(data), kind) for kind, data in self.errors[sub.email].items() ] error_output.append('errors with %s: %s' % (sub.email, '\t'.join(errors))) if error_output: print '\n', '\n'.join(error_output), '\n' mail_admins('Errors sending out', "Got the following errors while sending the email:\n" + '\n'.join(error_output) + '\n\nMore information is available by loading the pickled log file.' ) if self.errors: print 'more info available in the pickle file' pickle.dump(self.errors, sys.stderr) print 'finished: ' + datetime.datetime.now().strftime("%c")
class SendingOutCommand(NoArgsCommand): subscriber_base = None # Subscriber.rsd or whatever from_email = 'The Daily Gazette <*****@*****.**>' WAIT_TIME = 60 def set_content(self, dummy_request): raise NotImplemented def get_subject(self): today = datetime.date.today() return today.strftime("%%A, %%B %d, %%Y" % today.day) def get_sent_str(self): return datetime.date.today().strftime('%Y-%m-%d') def contents_for_subscriber(self, subscriber): return (self.text_content, self.html_content) def send_to_subscriber(self, subscriber): subj = self.subject frm = self.from_email text_content, html_content = self.contents_for_subscriber(subscriber) if subscriber.plain_text: msg = EmailMessage(subj, text_content, frm, [subscriber.email]) else: msg = EmailMessage(subj, html_content, frm, [subscriber.email]) msg.content_subtype = "html" # msg = EmailMessage(self.subject, text_content, self.from_email, [subscriber.email]) # if not subscriber.plain_text: # msg.multipart_subtype = 'alternative' # msg.attach(content=html_content, mimetype='text/html') self.connection.send_messages([msg]) # if it didn't just crash, sending was successful :) subscriber.last_sent = self.sent_str subscriber.save() def renew_connection(self, subscriber, retries=5, sleep_time_base=2): # close the old one self.connection.fail_silently = True self.connection.close() # get a new one for i in range(retries): try: self.connection = SMTPConnection() self.connection.open() except socket.timeout: self.add_error(subscriber, 'timeout while opening connection') time.sleep(sleep_time_base**i) else: return def try_sending_to_subscriber(self, subscriber, repeat_index=0, max_repeats=2): if repeat_index >= max_repeats: return try: self.send_to_subscriber(subscriber) except smtplib.SMTPRecipientsRefused: self.add_error(subscriber, 'recipient refused') except smtplib.SMTPServerDisconnected: self.add_error(subscriber, 'server disconnected') self.renew_connection(subscriber) self.try_sending_to_subscriber(subscriber, repeat_index + 1, max_repeats) except socket.sslerror: self.add_error(subscriber, 'ssl error') # probably timeout self.renew_connection(subscriber) self.try_sending_to_subscriber(subscriber, repeat_index + 1, max_repeats) except socket.timeout: self.add_error(subscriber, 'timeout') self.renew_connection(subscriber) self.try_sending_to_subscriber(subscriber, repeat_index + 1, max_repeats) except smtplib.SMTPException: self.add_error(subscriber, 'smtp error') except Exception: self.add_error(subscriber, 'generic error') def add_error(self, subscriber, key): self.errors.setdefault(subscriber.email, {}) self.errors[subscriber.email].setdefault(key, []) exc_type, exc_val, exc_trace = sys.exc_info() self.errors[subscriber.email][key].append({ 'type': exc_type, 'val': exc_val, 'traceback': ''.join(traceback.format_exception(exc_type, exc_val, exc_trace)), 'time': datetime.datetime.now() }) def handle_noargs(self, **options): dummy_request = HttpRequest() dummy_request.GET['for_email'] = 'true' self.set_content(dummy_request) self.subject = self.get_subject() self.sent_str = self.get_sent_str() self.connection = SMTPConnection() self.errors = {} print 'starting: ' + datetime.datetime.now().strftime("%c") # try up to four times, if necessary for i in range(4): not_sent = self.subscriber_base.exclude(last_sent=self.sent_str) if len(not_sent): # intentionally resolve the query if i != 0: print datetime.datetime.now().strftime('%c'), print '- sleeping before retrying %s emails...' % len( not_sent) time.sleep(self.WAIT_TIME) for subscriber in not_sent: self.try_sending_to_subscriber(subscriber) # any that *still* didn't work? error_output = [] for sub in self.subscriber_base.exclude(last_sent=self.sent_str): errors = [ '%d %s' % (len(data), kind) for kind, data in self.errors[sub.email].items() ] error_output.append('errors with %s: %s' % (sub.email, '\t'.join(errors))) if error_output: print '\n', '\n'.join(error_output), '\n' mail_admins( 'Errors sending out', "Got the following errors while sending the email:\n" + '\n'.join(error_output) + '\n\nMore information is available by loading the pickled log file.' ) if self.errors: print 'more info available in the pickle file' pickle.dump(self.errors, sys.stderr) print 'finished: ' + datetime.datetime.now().strftime("%c")