def redirect(request): ''' Redirects based on URL and records URL click ''' instance_id = request.GET.get('instance', None) url_string = request.GET.get('url', None) position = request.GET.get('position', None) recipient_id = request.GET.get('recipient', None) mac = request.GET.get('mac', None) if not url_string: pass # Where do we go? else: url_string = urllib.unquote(url_string) # No matter what happens, make sure the redirection works try: if position and recipient_id and mac and instance_id: try: position = int(position) recipient_id = int(recipient_id) instance_id = int(instance_id) except ValueError: log.error('value error') pass else: if mac == calc_url_mac(url_string, position, recipient_id, instance_id): try: recipient = Recipient.objects.get(id=recipient_id) instance = Instance.objects.get(id=instance_id) url = URL.objects.get(name=url_string, position=position, instance=instance) except URL.DoesNotExist: # This should have been created # in the mailer-process command # when this email was sent pass except Recipient.DoesNotExist: # strange log.error('bad recipient') pass except Instance.DoesNotExist: # also strange log.error('bad instance') pass else: url_click = URLClick(recipient=recipient, url=url) url_click.save() log.debug('url click saved') else: log.error('wrong mac') else: log.error('something none') except Exception, e: log.error(str(e)) pass return HttpResponseRedirect(url_string)
def _test_url_tracking(self, instance): ''' Test the URL tracking. Must be called in the context of an email instance. ''' # Is the URL Tracking working? urls = URL.objects.all() if urls.count() == 0: raise AssertionError( 'There must be at least 1 URL is the email content') else: client = Client() # Make sure this is valid URL for redirection test_url = None for url in urls: try: HttpResponseRedirect(url.name) except SuspiciousOperation: continue else: test_url = url if test_url is None: raise AssertionError('No valid test URL found.') response = client.get('?'.join([ reverse('manager-email-redirect'), urllib.parse.urlencode({ 'instance': instance.pk, 'recipient': self.recipient.pk, 'url': urllib.parse.quote(test_url.name), 'position': test_url.position, 'mac': calc_url_mac(test_url.name, test_url.position, self.recipient.pk, instance.pk) }) ])) self.assertTrue(response.status_code == 302) clicks = URLClick.objects.all() self.assertTrue(clicks.count() == 1)
def _test_url_tracking(self, instance): ''' Test the URL tracking. Must be called in the context of an email instance. ''' # Is the URL Tracking working? urls = URL.objects.all() if urls.count() == 0: raise AssertionError('There must be at least 1 URL is the email content') else: client = Client() # Make sure this is valid URL for redirection test_url = None for url in urls: try: HttpResponseRedirect(url.name) except SuspiciousOperation: continue else: test_url = url if test_url is None: raise AssertionError('No valid test URL found.') response = client.get('?'.join([ reverse('manager-email-redirect'), urllib.urlencode({ 'instance' :instance.pk, 'recipient' :self.recipient.pk, 'url' :urllib.quote(test_url.name), 'position' :test_url.position, 'mac' :calc_url_mac(test_url.name, test_url.position, self.recipient.pk, instance.pk) }) ])) self.assertTrue(response.status_code == 302) clicks = URLClick.objects.all() self.assertTrue(clicks.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)