""" Provides functions & Fields for validating credit card numbers Thanks to David Shaw for the Luhn Checksum code http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/172845) """ import re from django import newforms as forms import datetime def ValidateLuhnChecksum(number_as_string): """ checks to make sure that the card passes a luhn mod-10 checksum """ sum = 0 num_digits = len(number_as_string) oddeven = num_digits & 1 for i in range(0, num_digits): digit = int(number_as_string[i]) if not (( i & 1 ) ^ oddeven ): digit = digit * 2 if digit > 9: digit = digit - 9 sum = sum + digit return ( (sum % 10) == 0 ) # Regex for valid card numbers CC_PATTERNS = { 'mastercard': '^5[12345]([0-9]{14})$', 'visa': '^4([0-9]{12,15})$', } def ValidateCharacters(number): """ Checks to make sure string only contains valid characters """ return re.compile('^[0-9 ]*$').match(number) != None def StripToNumbers(number): """ remove spaces from the number """ if ValidateCharacters(number): result = '' rx = re.compile('^[0-9]$') for d in number: if rx.match(d): result += d return result else: raise Exception('Number has invalid digits') def ValidateDigits(type, number): """ Checks to make sure that the Digits match the CC pattern """ regex = CC_PATTERNS.get(type.lower(), False) if regex: return re.compile(regex).match(number) != None else: return False def ValidateCreditCard(type, number): """ Check that a credit card number matches the type and validates the Luhn Checksum """ type = type.strip().lower() if ValidateCharacters(number): number = StripToNumbers(number) if CC_PATTERNS.has_key(type): return ValidateDigits(type, number) return ValidateLuhnChecksum(number) return False class CreditCardNumberField(forms.CharField): """ A newforms widget for a creditcard number """ def clean(self, value): value = forms.CharField.clean(self, value) if not ValidateCharacters(value): raise forms.ValidationError('Can only contain numbers and spaces.') value = StripToNumbers(value) if not ValidateLuhnChecksum(value): raise forms.ValidationError('Not a valid credit card number.') return value class CreditCardExpiryField(forms.CharField): """ A newforms widget for a creditcard expiry date """ def clean(self, value): value = forms.CharField.clean(self, value.strip()) # Just check MM/YY Pattern r = re.compile('^([0-9][0-9])/([0-9][0-9])$') m = r.match(value) if m == None: raise forms.ValidationError('Must be in the format MM/YY. i.e. "11/10" for Nov 2010.') # Check that the month is 1-12 month = int(m.groups()[0]) if month < 1 or month > 12: raise forms.ValidationError('Month must be in the range 1 - 12.') # Check that the year is not too far into the future year = int(m.groups()[1]) curr_year = datetime.datetime.now().year % 100 max_year = curr_year + 10 if year > max_year or year < curr_year: raise forms.ValidationError('Year must be in the range %s - %s.' % (str(curr_year).zfill(2), str(max_year).zfill(2),)) return value # An example Form based on ModelForm. class PaymentForm(forms.ModelForm): cc_number = creditcards.CreditCardNumberField(required=False) cc_expiry = creditcards.CreditCardExpiryField() class Meta: model = Payment """ This function checks that the card number matches the card type. If you don't want to do this, comment out this function. """ def clean(self): if self.cleaned_data: if len(self.cleaned_data.items()) == len(self.fields): if self.cleaned_data['method'] == 'cc': the_type = self.cleaned_data.get('cc_type', '') number = self.cleaned_data.get('cc_number', '') if not ValidateDigits(the_type, number): raise forms.ValidationError('Card Number is not a valid ' + the_type.upper() + ' card number.') if not self.instance.is_payment_valid(): raise forms.ValidationError('Credit card payment could not be processed. Reason is %s. Check that card details are correct and try again. If you still receive this error, check with your financial institution.' % (self.instance.gateway_resptxt)) return self.cleaned_data
HTML is the method of choice for those wishing to send emails with rich text, layout and graphics. Often it is desirable to embed the graphics within the message so recipients can display the message directly, without further downloads.
Some mail agents don't support HTML or their users prefer to receive plain text messages. Senders of HTML messages should include a plain text message as an alternate for these users.
This recipe sends a short HTML message with a single embedded image and an alternate plain text message.
Some mail agents don't support HTML or their users prefer to receive plain text messages. Senders of HTML messages should include a plain text message as an alternate for these users.
This recipe sends a short HTML message with a single embedded image and an alternate plain text message.
Python |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | # Send an HTML email with an embedded image and a plain text message for
# email clients that don't want to display the HTML.
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
from email.MIMEImage import MIMEImage
# Define these once; use them twice!
strFrom = 'from@example.com'
strTo = 'to@example.com'
# Create the root message and fill in the from, to, and subject headers
msgRoot = MIMEMultipart('related')
msgRoot['Subject'] = 'test message'
msgRoot['From'] = strFrom
msgRoot['To'] = strTo
msgRoot.preamble = 'This is a multi-part message in MIME format.'
# Encapsulate the plain and HTML versions of the message body in an
# 'alternative' part, so message agents can decide which they want to display.
msgAlternative = MIMEMultipart('alternative')
msgRoot.attach(msgAlternative)
msgText = MIMEText('This is the alternative plain text message.')
msgAlternative.attach(msgText)
# We reference the image in the IMG SRC attribute by the ID we give it below
msgText = MIMEText('<b>Some <i>HTML</i> text</b> and an image.<br><img src="cid:image1"><br>Nifty!', 'html')
msgAlternative.attach(msgText)
# This example assumes the image is in the current directory
fp = open('test.jpg', 'rb')
msgImage = MIMEImage(fp.read())
fp.close()
# Define the image's ID as referenced above
msgImage.add_header('Content-ID', '<image1>')
msgRoot.attach(msgImage)
# Send the email (this example assumes SMTP authentication is required)
import smtplib
smtp = smtplib.SMTP()
smtp.connect('smtp.example.com')
smtp.login('exampleuser', 'examplepass')
smtp.sendmail(strFrom, strTo, msgRoot.as_string())
smtp.quit()
|
Discussion
While the practice of embedding images within the message provides a better experience for many users than linking to web-hosted images does it is important to consider the impact this has on message size and consequently on message download time.
An alternative implementation for sending HTML messages is provided in this recipe http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/67083, though it doesn't address embedding images and was written before the email package was incorporated into Python. The email package and its MIME-handling capabilities significantly reduce the amount of code required.