Ejemplo n.º 1
0
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)
Ejemplo n.º 2
0
	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)
Ejemplo n.º 3
0
            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)