def instance_open(request): ''' Records an email open ''' instance_id = request.GET.get('instance', None) recipient_id = request.GET.get('recipient', None) mac = request.GET.get('mac', None) if recipient_id and mac and instance_id is not None: try: instance_id = int(instance_id) recipient_id = int(recipient_id) except ValueError: # corrupted pass else: if mac == calc_open_mac(recipient_id, instance_id): try: recipient = Recipient.objects.get(id=recipient_id) instance = Instance.objects.get(id=instance_id) InstanceOpen.objects.get(recipient=recipient, instance=instance) except Recipient.DoesNotExist: # strange log.error('bad recipient') pass except Instance.DoesNotExist: # also strange log.error('bad instance') pass except InstanceOpen.DoesNotExist: instance_open = InstanceOpen(recipient=recipient, instance=instance) instance_open.save() log.debug('open saved') return HttpResponse(settings.DOT, content_type='image/png')
def _test_open_tracking(self, instance): ''' Test the open tracking. Must be called in the context of an email instance. ''' client = Client() response = client.get('?'.join([ reverse('manager-email-open'), urllib.urlencode({ 'recipient':self.recipient.pk, 'instance' :instance.pk, 'mac' :calc_open_mac(self.recipient.pk, instance.pk) }) ])) self.assertTrue(response.status_code == 200) opens = InstanceOpen.objects.all() self.assertTrue(opens.count() == 1)
def _test_open_tracking(self, instance): ''' Test the open tracking. Must be called in the context of an email instance. ''' client = Client() response = client.get('?'.join([ reverse('manager-email-open'), urllib.parse.urlencode({ 'recipient': self.recipient.pk, 'instance': instance.pk, 'mac': calc_open_mac(self.recipient.pk, instance.pk) }) ])) self.assertTrue(response.status_code == 200) opens = InstanceOpen.objects.all() self.assertTrue(opens.count() == 1)
def run(self): amazon = None reconnect = False reconnect_counter = 0 error = False error_counter = 0 rate_limit_counter = 0 while True: if recipient_details_queue.empty(): log.debug('%s queue empty, exiting.' % self.name) break if sender_stop.is_set(): log.debug('%s receieved termination signal.' % self.name) while not recipient_details_queue.empty(): recipient_details_queue.get() recipient_details_queue.task_done() break recipient_details = recipient_details_queue.get() try: if amazon is None or reconnect: try: amazon = smtplib.SMTP_SSL(settings.AMAZON_SMTP['host'], settings.AMAZON_SMTP['port']) amazon.login(settings.AMAZON_SMTP['username'], settings.AMAZON_SMTP['password']) except: if reconnect_counter == SendingThread._AMAZON_RECONNECT_THRESHOLD: log.debug('%s, reached reconnect threshold, exiting') raise reconnect_counter += 1 reconnect = True continue else: reconnect = False # Customize the email for this recipient customized_html = recipient_details.instance.sent_html # Replace template placeholders delimiter = recipient_details.instance.email.replace_delimiter for placeholder in placeholders: replacement = '' if placeholder.lower() != 'unsubscribe': if recipient_attributes[recipient_details.recipient.pk][placeholder] is None: log.error('Recipient %s is missing attribute %s' % (str(recipient_details.recipient), placeholder)) else: replacement = recipient_attributes[recipient_details.recipient.pk][placeholder] customized_html = customized_html.replace(delimiter + placeholder + delimiter, replacement) # URL Tracking if recipient_details.instance.urls_tracked: for url in tracking_urls: tracking_url = '?'.join([ settings.PROJECT_URL + reverse('manager-email-redirect'), urllib.urlencode({ 'instance' :recipient_details.instance.pk, 'recipient' :recipient_details.recipient.pk, 'url' :urllib.quote(url.name), 'position' :url.position, # The mac uniquely identifies the recipient and acts as a secure integrity check 'mac' :calc_url_mac(url.name, url.position, recipient_details.recipient.pk, recipient_details.instance.pk) }) ]) customized_html = customized_html.replace( 'href="' + url.name + '"', 'href="' + tracking_url + '"', 1 ) # Open Tracking if recipient_details.instance.opens_tracked: customized_html += '<img src="%s" />' % '?'.join([ settings.PROJECT_URL + reverse('manager-email-open'), urllib.urlencode({ 'recipient':recipient_details.recipient.pk, 'instance' :recipient_details.instance.pk, 'mac' :calc_open_mac(recipient_details.recipient.pk, recipient_details.instance.pk) }) ]) # Unsubscribe link customized_html = re.sub( re.escape(delimiter) + 'UNSUBSCRIBE' + re.escape(delimiter), '<a href="%s" style="color:blue;text-decoration:none;">unsubscribe</a>' % recipient_details.recipient.unsubscribe_url, customized_html) # Construct the message msg = MIMEMultipart('alternative') msg['subject'] = subject msg['From'] = display_from msg['To'] = recipient_details.recipient.email_address msg.attach(MIMEText(customized_html, 'html', _charset='us-ascii')) if text is not None: msg.attach(MIMEText(text, 'plain', _charset='us-ascii' )) log.debug('thread: %s, email: %s' % (self.name, recipient_details.recipient.email_address)) try: amazon.sendmail(real_from, recipient_details.recipient.email_address, msg.as_string()) except smtplib.SMTPResponseException, e: if e.smtp_error.find('Maximum sending rate exceeded') >= 0: recipient_details_queue.put(recipient_details) log.debug('thread %s, maximum sending rate exceeded, sleeping for a bit') time.sleep(float(1) + random.random()) else: recipient_details.exception_msg = str(e) except smtplib.SMTPServerDisconnected: # Connection error log.debug('thread %s, connection error, sleeping for a bit') time.sleep(float(1) + random.random()) recipient_details_queue.put(recipient_details) reconnect = True else: recipient_details.when = datetime.now() finally: recipient_details.save() except Exception, e: if error_counter == SendingThread._ERROR_THRESHOLD: recipient_details_queue.task_done() log.debug('%s, reached error threshold, exiting') with recipient_details_queue.mutex: recipient_details_queue.queue.clear() return error_counter += 1 log.exception('%s exception' % self.name)