Fixed #7919 -- md5 and sha modules are deprecated since Python 2.5, use hashlib module when available. Patch from Karen Tracey.

git-svn-id: http://code.djangoproject.com/svn/django/trunk@8193 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Gary Wilson Jr 2008-08-02 05:56:57 +00:00
parent 8a58f2216c
commit c85c8f8891
17 changed files with 138 additions and 117 deletions

View File

@ -1,3 +1,8 @@
import base64
import cPickle as pickle
import datetime
import re
from django import http, template from django import http, template
from django.contrib.admin import ModelAdmin from django.contrib.admin import ModelAdmin
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
@ -9,11 +14,7 @@ from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy, ugettext as _ from django.utils.translation import ugettext_lazy, ugettext as _
from django.views.decorators.cache import never_cache from django.views.decorators.cache import never_cache
from django.conf import settings from django.conf import settings
import base64 from django.utils.hashcompat import md5_constructor
import cPickle as pickle
import datetime
import md5
import re
ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
LOGIN_FORM_KEY = 'this_is_the_login_form' LOGIN_FORM_KEY = 'this_is_the_login_form'
@ -29,14 +30,14 @@ class NotRegistered(Exception):
def _encode_post_data(post_data): def _encode_post_data(post_data):
from django.conf import settings from django.conf import settings
pickled = pickle.dumps(post_data) pickled = pickle.dumps(post_data)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5) return base64.encodestring(pickled + pickled_md5)
def _decode_post_data(encoded_data): def _decode_post_data(encoded_data):
from django.conf import settings from django.conf import settings
encoded_data = base64.decodestring(encoded_data) encoded_data = base64.decodestring(encoded_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
raise SuspiciousOperation, "User may have tampered with session cookie." raise SuspiciousOperation, "User may have tampered with session cookie."
return pickle.loads(pickled) return pickle.loads(pickled)
@ -48,10 +49,10 @@ class AdminSite(object):
register() method, and the root() method can then be used as a Django view function register() method, and the root() method can then be used as a Django view function
that presents a full admin interface for the collection of registered models. that presents a full admin interface for the collection of registered models.
""" """
index_template = None index_template = None
login_template = None login_template = None
def __init__(self): def __init__(self):
self._registry = {} # model_class class -> admin_class instance self._registry = {} # model_class class -> admin_class instance
@ -117,23 +118,23 @@ class AdminSite(object):
return request.user.is_authenticated() and request.user.is_staff return request.user.is_authenticated() and request.user.is_staff
def root(self, request, url): def root(self, request, url):
""" """
Handles main URL routing for the admin app. Handles main URL routing for the admin app.
`url` is the remainder of the URL -- e.g. 'comments/comment/'. `url` is the remainder of the URL -- e.g. 'comments/comment/'.
""" """
if request.method == 'GET' and not request.path.endswith('/'): if request.method == 'GET' and not request.path.endswith('/'):
return http.HttpResponseRedirect(request.path + '/') return http.HttpResponseRedirect(request.path + '/')
# Figure out the admin base URL path and stash it for later use # Figure out the admin base URL path and stash it for later use
self.root_path = re.sub(re.escape(url) + '$', '', request.path) self.root_path = re.sub(re.escape(url) + '$', '', request.path)
url = url.rstrip('/') # Trim trailing slash, if it exists. url = url.rstrip('/') # Trim trailing slash, if it exists.
# The 'logout' view doesn't require that the person is logged in. # The 'logout' view doesn't require that the person is logged in.
if url == 'logout': if url == 'logout':
return self.logout(request) return self.logout(request)
# Check permission to continue or display login form. # Check permission to continue or display login form.
if not self.has_permission(request): if not self.has_permission(request):
return self.login(request) return self.login(request)
@ -154,7 +155,7 @@ class AdminSite(object):
match = USER_CHANGE_PASSWORD_URL_RE.match(url) match = USER_CHANGE_PASSWORD_URL_RE.match(url)
if match: if match:
return self.user_change_password(request, match.group(1)) return self.user_change_password(request, match.group(1))
if '/' in url: if '/' in url:
return self.model_page(request, *url.split('/', 2)) return self.model_page(request, *url.split('/', 2))
@ -320,14 +321,14 @@ class AdminSite(object):
# Sort the models alphabetically within each app. # Sort the models alphabetically within each app.
for app in app_list: for app in app_list:
app['models'].sort(lambda x, y: cmp(x['name'], y['name'])) app['models'].sort(lambda x, y: cmp(x['name'], y['name']))
context = { context = {
'title': _('Site administration'), 'title': _('Site administration'),
'app_list': app_list, 'app_list': app_list,
'root_path': self.root_path, 'root_path': self.root_path,
} }
context.update(extra_context or {}) context.update(extra_context or {})
return render_to_response(self.index_template or 'admin/index.html', context, return render_to_response(self.index_template or 'admin/index.html', context,
context_instance=template.RequestContext(request) context_instance=template.RequestContext(request)
) )
index = never_cache(index) index = never_cache(index)
@ -342,7 +343,7 @@ class AdminSite(object):
post_data = _encode_post_data(request.POST) post_data = _encode_post_data(request.POST)
else: else:
post_data = _encode_post_data({}) post_data = _encode_post_data({})
context = { context = {
'title': _('Log in'), 'title': _('Log in'),
'app_path': request.path, 'app_path': request.path,

View File

@ -1,5 +1,4 @@
import base64 import base64
import md5
import cPickle as pickle import cPickle as pickle
try: try:
from functools import wraps from functools import wraps
@ -12,6 +11,7 @@ from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.utils.translation import ugettext_lazy, ugettext as _ from django.utils.translation import ugettext_lazy, ugettext as _
from django.utils.hashcompat import md5_constructor
ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.") ERROR_MESSAGE = ugettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
LOGIN_FORM_KEY = 'this_is_the_login_form' LOGIN_FORM_KEY = 'this_is_the_login_form'
@ -35,13 +35,13 @@ def _display_login_form(request, error_message=''):
def _encode_post_data(post_data): def _encode_post_data(post_data):
pickled = pickle.dumps(post_data) pickled = pickle.dumps(post_data)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5) return base64.encodestring(pickled + pickled_md5)
def _decode_post_data(encoded_data): def _decode_post_data(encoded_data):
encoded_data = base64.decodestring(encoded_data) encoded_data = base64.decodestring(encoded_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
raise SuspiciousOperation, "User may have tampered with session cookie." raise SuspiciousOperation, "User may have tampered with session cookie."
return pickle.loads(pickled) return pickle.loads(pickled)
@ -87,7 +87,7 @@ def staff_member_required(view_func):
if len(users) == 1: if len(users) == 1:
message = _("Your e-mail address is not your username. Try '%s' instead.") % users[0].username message = _("Your e-mail address is not your username. Try '%s' instead.") % users[0].username
else: else:
# Either we cannot find the user, or if more than 1 # Either we cannot find the user, or if more than 1
# we cannot guess which user is the correct one. # we cannot guess which user is the correct one.
message = _("Usernames cannot contain the '@' character.") message = _("Usernames cannot contain the '@' character.")
return _display_login_form(request, message) return _display_login_form(request, message)

View File

@ -50,17 +50,17 @@ class PasswordResetTokenGenerator(object):
# last_login will also change), we produce a hash that will be # last_login will also change), we produce a hash that will be
# invalid as soon as it is used. # invalid as soon as it is used.
# We limit the hash to 20 chars to keep URL short # We limit the hash to 20 chars to keep URL short
import sha from django.utils.hashcompat import sha_constructor
hash = sha.new(settings.SECRET_KEY + unicode(user.id) + hash = sha_constructor(settings.SECRET_KEY + unicode(user.id) +
user.password + unicode(user.last_login) + user.password + unicode(user.last_login) +
unicode(timestamp)).hexdigest()[::2] unicode(timestamp)).hexdigest()[::2]
return "%s-%s" % (ts_b36, hash) return "%s-%s" % (ts_b36, hash)
def _num_days(self, dt): def _num_days(self, dt):
return (dt - date(2001,1,1)).days return (dt - date(2001,1,1)).days
def _today(self): def _today(self):
# Used for mocking in tests # Used for mocking in tests
return date.today() return date.today()
default_token_generator = PasswordResetTokenGenerator() default_token_generator = PasswordResetTokenGenerator()

View File

@ -29,8 +29,8 @@ class CommentManager(models.Manager):
'pa,ra') and target (something like 'lcom.eventtimes:5157'). Used to 'pa,ra') and target (something like 'lcom.eventtimes:5157'). Used to
validate that submitted form options have not been tampered-with. validate that submitted form options have not been tampered-with.
""" """
import md5 from django.utils.hashcompat import md5_constructor
return md5.new(options + photo_options + rating_options + target + settings.SECRET_KEY).hexdigest() return md5_constructor(options + photo_options + rating_options + target + settings.SECRET_KEY).hexdigest()
def get_rating_options(self, rating_string): def get_rating_options(self, rating_string):
""" """

View File

@ -2,44 +2,45 @@
Cross Site Request Forgery Middleware. Cross Site Request Forgery Middleware.
This module provides a middleware that implements protection This module provides a middleware that implements protection
against request forgeries from other sites. against request forgeries from other sites.
""" """
from django.conf import settings
from django.http import HttpResponseForbidden
from django.utils.safestring import mark_safe
import md5
import re import re
import itertools import itertools
from django.conf import settings
from django.http import HttpResponseForbidden
from django.utils.hashcompat import md5_constructor
from django.utils.safestring import mark_safe
_ERROR_MSG = mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>') _ERROR_MSG = mark_safe('<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><body><h1>403 Forbidden</h1><p>Cross Site Request Forgery detected. Request aborted.</p></body></html>')
_POST_FORM_RE = \ _POST_FORM_RE = \
re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE) re.compile(r'(<form\W[^>]*\bmethod=(\'|"|)POST(\'|"|)\b[^>]*>)', re.IGNORECASE)
_HTML_TYPES = ('text/html', 'application/xhtml+xml') _HTML_TYPES = ('text/html', 'application/xhtml+xml')
def _make_token(session_id): def _make_token(session_id):
return md5.new(settings.SECRET_KEY + session_id).hexdigest() return md5_constructor(settings.SECRET_KEY + session_id).hexdigest()
class CsrfMiddleware(object): class CsrfMiddleware(object):
"""Django middleware that adds protection against Cross Site """Django middleware that adds protection against Cross Site
Request Forgeries by adding hidden form fields to POST forms and Request Forgeries by adding hidden form fields to POST forms and
checking requests for the correct value. checking requests for the correct value.
In the list of middlewares, SessionMiddleware is required, and must come In the list of middlewares, SessionMiddleware is required, and must come
after this middleware. CsrfMiddleWare must come after compression after this middleware. CsrfMiddleWare must come after compression
middleware. middleware.
If a session ID cookie is present, it is hashed with the SECRET_KEY If a session ID cookie is present, it is hashed with the SECRET_KEY
setting to create an authentication token. This token is added to all setting to create an authentication token. This token is added to all
outgoing POST forms and is expected on all incoming POST requests that outgoing POST forms and is expected on all incoming POST requests that
have a session ID cookie. have a session ID cookie.
If you are setting cookies directly, instead of using Django's session If you are setting cookies directly, instead of using Django's session
framework, this middleware will not work. framework, this middleware will not work.
""" """
def process_request(self, request): def process_request(self, request):
if request.method == 'POST': if request.method == 'POST':
try: try:
@ -54,10 +55,10 @@ class CsrfMiddleware(object):
request_csrf_token = request.POST['csrfmiddlewaretoken'] request_csrf_token = request.POST['csrfmiddlewaretoken']
except KeyError: except KeyError:
return HttpResponseForbidden(_ERROR_MSG) return HttpResponseForbidden(_ERROR_MSG)
if request_csrf_token != csrf_token: if request_csrf_token != csrf_token:
return HttpResponseForbidden(_ERROR_MSG) return HttpResponseForbidden(_ERROR_MSG)
return None return None
def process_response(self, request, response): def process_response(self, request, response):
@ -66,7 +67,7 @@ class CsrfMiddleware(object):
cookie = response.cookies[settings.SESSION_COOKIE_NAME] cookie = response.cookies[settings.SESSION_COOKIE_NAME]
csrf_token = _make_token(cookie.value) csrf_token = _make_token(cookie.value)
except KeyError: except KeyError:
# No outgoing cookie to set session, but # No outgoing cookie to set session, but
# a session might already exist. # a session might already exist.
try: try:
session_id = request.COOKIES[settings.SESSION_COOKIE_NAME] session_id = request.COOKIES[settings.SESSION_COOKIE_NAME]
@ -74,12 +75,12 @@ class CsrfMiddleware(object):
except KeyError: except KeyError:
# no incoming or outgoing cookie # no incoming or outgoing cookie
pass pass
if csrf_token is not None and \ if csrf_token is not None and \
response['Content-Type'].split(';')[0] in _HTML_TYPES: response['Content-Type'].split(';')[0] in _HTML_TYPES:
# ensure we don't add the 'id' attribute twice (HTML validity) # ensure we don't add the 'id' attribute twice (HTML validity)
idattributes = itertools.chain(("id='csrfmiddlewaretoken'",), idattributes = itertools.chain(("id='csrfmiddlewaretoken'",),
itertools.repeat('')) itertools.repeat(''))
def add_csrf_field(match): def add_csrf_field(match):
"""Returns the matched <form> tag plus the added <input> element""" """Returns the matched <form> tag plus the added <input> element"""

View File

@ -2,12 +2,13 @@
Formtools Preview application. Formtools Preview application.
""" """
import cPickle as pickle
from django.conf import settings from django.conf import settings
from django.http import Http404 from django.http import Http404
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template.context import RequestContext from django.template.context import RequestContext
import cPickle as pickle from django.utils.hashcompat import md5_constructor
import md5
AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter. AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
@ -109,7 +110,7 @@ class FormPreview(object):
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires # Use HIGHEST_PROTOCOL because it's the most efficient. It requires
# Python 2.3, but Django requires 2.3 anyway, so that's OK. # Python 2.3, but Django requires 2.3 anyway, so that's OK.
pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
return md5.new(pickled).hexdigest() return md5_constructor(pickled).hexdigest()
def failed_hash(self, request): def failed_hash(self, request):
"Returns an HttpResponse in the case of an invalid security hash." "Returns an HttpResponse in the case of an invalid security hash."

View File

@ -4,13 +4,14 @@ step and storing the form's state as HTML hidden fields so that no state is
stored on the server side. stored on the server side.
""" """
import cPickle as pickle
from django import forms from django import forms
from django.conf import settings from django.conf import settings
from django.http import Http404 from django.http import Http404
from django.shortcuts import render_to_response from django.shortcuts import render_to_response
from django.template.context import RequestContext from django.template.context import RequestContext
import cPickle as pickle from django.utils.hashcompat import md5_constructor
import md5
class FormWizard(object): class FormWizard(object):
# Dictionary of extra template context variables. # Dictionary of extra template context variables.
@ -150,7 +151,7 @@ class FormWizard(object):
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires # Use HIGHEST_PROTOCOL because it's the most efficient. It requires
# Python 2.3, but Django requires 2.3 anyway, so that's OK. # Python 2.3, but Django requires 2.3 anyway, so that's OK.
pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
return md5.new(pickled).hexdigest() return md5_constructor(pickled).hexdigest()
def determine_step(self, request, *args, **kwargs): def determine_step(self, request, *args, **kwargs):
""" """

View File

@ -1,5 +1,4 @@
import base64 import base64
import md5
import os import os
import random import random
import sys import sys
@ -12,6 +11,7 @@ except ImportError:
from django.conf import settings from django.conf import settings
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
from django.utils.hashcompat import md5_constructor
class SessionBase(object): class SessionBase(object):
@ -73,13 +73,13 @@ class SessionBase(object):
def encode(self, session_dict): def encode(self, session_dict):
"Returns the given session dictionary pickled and encoded as a string." "Returns the given session dictionary pickled and encoded as a string."
pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL) pickled = pickle.dumps(session_dict, pickle.HIGHEST_PROTOCOL)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5) return base64.encodestring(pickled + pickled_md5)
def decode(self, session_data): def decode(self, session_data):
encoded_data = base64.decodestring(session_data) encoded_data = base64.decodestring(session_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
raise SuspiciousOperation("User tampered with session cookie.") raise SuspiciousOperation("User tampered with session cookie.")
try: try:
return pickle.loads(pickled) return pickle.loads(pickled)
@ -117,8 +117,8 @@ class SessionBase(object):
# No getpid() in Jython, for example # No getpid() in Jython, for example
pid = 1 pid = 1
while 1: while 1:
session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), session_key = md5_constructor("%s%s%s%s" % (random.randint(0, sys.maxint - 1),
pid, time.time(), settings.SECRET_KEY)).hexdigest() pid, time.time(), settings.SECRET_KEY)).hexdigest()
if not self.exists(session_key): if not self.exists(session_key):
break break
return session_key return session_key

View File

@ -1,10 +1,10 @@
import base64 import base64
import md5
import cPickle as pickle import cPickle as pickle
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
from django.utils.hashcompat import md5_constructor
class SessionManager(models.Manager): class SessionManager(models.Manager):
@ -13,7 +13,7 @@ class SessionManager(models.Manager):
Returns the given session dictionary pickled and encoded as a string. Returns the given session dictionary pickled and encoded as a string.
""" """
pickled = pickle.dumps(session_dict) pickled = pickle.dumps(session_dict)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest() pickled_md5 = md5_constructor(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5) return base64.encodestring(pickled + pickled_md5)
def save(self, session_key, session_dict, expire_date): def save(self, session_key, session_dict, expire_date):
@ -56,7 +56,7 @@ class Session(models.Model):
def get_decoded(self): def get_decoded(self):
encoded_data = base64.decodestring(self.session_data) encoded_data = base64.decodestring(self.session_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:] pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check: if md5_constructor(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
raise SuspiciousOperation, "User tampered with session cookie." raise SuspiciousOperation, "User tampered with session cookie."
try: try:

View File

@ -1,29 +1,31 @@
"File-based cache backend" "File-based cache backend"
import md5 import os
import os, time import time
try: try:
import cPickle as pickle import cPickle as pickle
except ImportError: except ImportError:
import pickle import pickle
from django.core.cache.backends.base import BaseCache from django.core.cache.backends.base import BaseCache
from django.utils.hashcompat import md5_constructor
class CacheClass(BaseCache): class CacheClass(BaseCache):
def __init__(self, dir, params): def __init__(self, dir, params):
BaseCache.__init__(self, params) BaseCache.__init__(self, params)
max_entries = params.get('max_entries', 300) max_entries = params.get('max_entries', 300)
try: try:
self._max_entries = int(max_entries) self._max_entries = int(max_entries)
except (ValueError, TypeError): except (ValueError, TypeError):
self._max_entries = 300 self._max_entries = 300
cull_frequency = params.get('cull_frequency', 3) cull_frequency = params.get('cull_frequency', 3)
try: try:
self._cull_frequency = int(cull_frequency) self._cull_frequency = int(cull_frequency)
except (ValueError, TypeError): except (ValueError, TypeError):
self._cull_frequency = 3 self._cull_frequency = 3
self._dir = dir self._dir = dir
if not os.path.exists(self._dir): if not os.path.exists(self._dir):
self._createdir() self._createdir()
@ -31,7 +33,7 @@ class CacheClass(BaseCache):
def add(self, key, value, timeout=None): def add(self, key, value, timeout=None):
if self.has_key(key): if self.has_key(key):
return None return None
self.set(key, value, timeout) self.set(key, value, timeout)
def get(self, key, default=None): def get(self, key, default=None):
@ -52,12 +54,12 @@ class CacheClass(BaseCache):
def set(self, key, value, timeout=None): def set(self, key, value, timeout=None):
fname = self._key_to_file(key) fname = self._key_to_file(key)
dirname = os.path.dirname(fname) dirname = os.path.dirname(fname)
if timeout is None: if timeout is None:
timeout = self.default_timeout timeout = self.default_timeout
self._cull() self._cull()
try: try:
if not os.path.exists(dirname): if not os.path.exists(dirname):
os.makedirs(dirname) os.makedirs(dirname)
@ -103,12 +105,12 @@ class CacheClass(BaseCache):
def _cull(self): def _cull(self):
if int(self._num_entries) < self._max_entries: if int(self._num_entries) < self._max_entries:
return return
try: try:
filelist = os.listdir(self._dir) filelist = os.listdir(self._dir)
except (IOError, OSError): except (IOError, OSError):
return return
if self._cull_frequency == 0: if self._cull_frequency == 0:
doomed = filelist doomed = filelist
else: else:
@ -133,11 +135,11 @@ class CacheClass(BaseCache):
Convert the filename into an md5 string. We'll turn the first couple Convert the filename into an md5 string. We'll turn the first couple
bits of the path into directory prefixes to be nice to filesystems bits of the path into directory prefixes to be nice to filesystems
that have problems with large numbers of files in a directory. that have problems with large numbers of files in a directory.
Thus, a cache key of "foo" gets turnned into a file named Thus, a cache key of "foo" gets turnned into a file named
``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``. ``{cache-dir}ac/bd/18db4cc2f85cedef654fccc4a4d8``.
""" """
path = md5.new(key.encode('utf-8')).hexdigest() path = md5_constructor(key.encode('utf-8')).hexdigest()
path = os.path.join(path[:2], path[2:4], path[4:]) path = os.path.join(path[:2], path[2:4], path[4:])
return os.path.join(self._dir, path) return os.path.join(self._dir, path)
@ -147,4 +149,3 @@ class CacheClass(BaseCache):
count += len(files) count += len(files)
return count return count
_num_entries = property(_get_num_entries) _num_entries = property(_get_num_entries)

View File

@ -1,7 +1,8 @@
import datetime import datetime
import md5
from time import time from time import time
from django.utils.hashcompat import md5_constructor
try: try:
import decimal import decimal
except ImportError: except ImportError:
@ -114,7 +115,7 @@ def truncate_name(name, length=None):
if length is None or len(name) <= length: if length is None or len(name) <= length:
return name return name
hash = md5.md5(name).hexdigest()[:4] hash = md5_constructor(name).hexdigest()[:4]
return '%s%s' % (name[:length-4], hash) return '%s%s' % (name[:length-4], hash)

View File

@ -1,4 +1,3 @@
import md5
import re import re
from django.conf import settings from django.conf import settings
@ -6,6 +5,7 @@ from django import http
from django.core.mail import mail_managers from django.core.mail import mail_managers
from django.utils.http import urlquote from django.utils.http import urlquote
from django.core import urlresolvers from django.core import urlresolvers
from django.utils.hashcompat import md5_constructor
class CommonMiddleware(object): class CommonMiddleware(object):
""" """
@ -21,7 +21,7 @@ class CommonMiddleware(object):
slash, and it is not found in urlpatterns, a new URL is formed by slash, and it is not found in urlpatterns, a new URL is formed by
appending a slash at the end. If this new URL is found in appending a slash at the end. If this new URL is found in
urlpatterns, then an HTTP-redirect is returned to this new URL; urlpatterns, then an HTTP-redirect is returned to this new URL;
otherwise the initial URL is processed as usual. otherwise the initial URL is processed as usual.
- ETags: If the USE_ETAGS setting is set, ETags will be calculated from - ETags: If the USE_ETAGS setting is set, ETags will be calculated from
the entire page content and Not Modified responses will be returned the entire page content and Not Modified responses will be returned
@ -108,7 +108,7 @@ class CommonMiddleware(object):
if response.has_header('ETag'): if response.has_header('ETag'):
etag = response['ETag'] etag = response['ETag']
else: else:
etag = '"%s"' % md5.new(response.content).hexdigest() etag = '"%s"' % md5_constructor(response.content).hexdigest()
if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag: if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
cookies = response.cookies cookies = response.cookies
response = http.HttpResponseNotModified() response = http.HttpResponseNotModified()

View File

@ -17,7 +17,6 @@ An example: i18n middleware would need to distinguish caches by the
"Accept-language" header. "Accept-language" header.
""" """
import md5
import re import re
import time import time
try: try:
@ -29,6 +28,7 @@ from django.conf import settings
from django.core.cache import cache from django.core.cache import cache
from django.utils.encoding import smart_str, iri_to_uri from django.utils.encoding import smart_str, iri_to_uri
from django.utils.http import http_date from django.utils.http import http_date
from django.utils.hashcompat import md5_constructor
cc_delim_re = re.compile(r'\s*,\s*') cc_delim_re = re.compile(r'\s*,\s*')
@ -104,7 +104,7 @@ def patch_response_headers(response, cache_timeout=None):
if cache_timeout < 0: if cache_timeout < 0:
cache_timeout = 0 # Can't have max-age negative cache_timeout = 0 # Can't have max-age negative
if not response.has_header('ETag'): if not response.has_header('ETag'):
response['ETag'] = '"%s"' % md5.new(response.content).hexdigest() response['ETag'] = '"%s"' % md5_constructor(response.content).hexdigest()
if not response.has_header('Last-Modified'): if not response.has_header('Last-Modified'):
response['Last-Modified'] = http_date() response['Last-Modified'] = http_date()
if not response.has_header('Expires'): if not response.has_header('Expires'):
@ -138,7 +138,7 @@ def patch_vary_headers(response, newheaders):
def _generate_cache_key(request, headerlist, key_prefix): def _generate_cache_key(request, headerlist, key_prefix):
"""Returns a cache key from the headers given in the header list.""" """Returns a cache key from the headers given in the header list."""
ctx = md5.new() ctx = md5_constructor()
for header in headerlist: for header in headerlist:
value = request.META.get(header, None) value = request.META.get(header, None)
if value is not None: if value is not None:

View File

@ -0,0 +1,16 @@
"""
The md5 and sha modules are deprecated since Python 2.5, replaced by the
hashlib module containing both hash algorithms. Here, we provide a common
interface to the md5 and sha constructors, preferring the hashlib module when
available.
"""
try:
import hashlib
md5_constructor = hashlib.md5
sha_constructor = hashlib.sha1
except ImportError:
import md5
md5_constructor = md5.new
import sha
sha_constructor = sha.new

View File

@ -3,11 +3,17 @@
# Unit tests for cache framework # Unit tests for cache framework
# Uses whatever cache backend is set in the test settings file. # Uses whatever cache backend is set in the test settings file.
import os
import shutil
import tempfile
import time import time
import unittest import unittest
from django.core.cache import cache from django.core.cache import cache
from django.utils.cache import patch_vary_headers from django.core.cache.backends.filebased import CacheClass as FileCache
from django.http import HttpResponse from django.http import HttpResponse
from django.utils.cache import patch_vary_headers
from django.utils.hashcompat import md5_constructor
# functions/classes for complex data type tests # functions/classes for complex data type tests
def f(): def f():
@ -27,7 +33,7 @@ class Cache(unittest.TestCase):
cache.add("addkey1", "value") cache.add("addkey1", "value")
cache.add("addkey1", "newvalue") cache.add("addkey1", "newvalue")
self.assertEqual(cache.get("addkey1"), "value") self.assertEqual(cache.get("addkey1"), "value")
def test_non_existent(self): def test_non_existent(self):
# get with non-existent keys # get with non-existent keys
self.assertEqual(cache.get("does_not_exist"), None) self.assertEqual(cache.get("does_not_exist"), None)
@ -80,9 +86,9 @@ class Cache(unittest.TestCase):
cache.set('expire2', 'very quickly', 1) cache.set('expire2', 'very quickly', 1)
cache.set('expire3', 'very quickly', 1) cache.set('expire3', 'very quickly', 1)
time.sleep(2) time.sleep(2)
self.assertEqual(cache.get("expire1"), None) self.assertEqual(cache.get("expire1"), None)
cache.add("expire2", "newvalue") cache.add("expire2", "newvalue")
self.assertEqual(cache.get("expire2"), "newvalue") self.assertEqual(cache.get("expire2"), "newvalue")
self.assertEqual(cache.has_key("expire3"), False) self.assertEqual(cache.has_key("expire3"), False)
@ -98,11 +104,6 @@ class Cache(unittest.TestCase):
cache.set(key, value) cache.set(key, value)
self.assertEqual(cache.get(key), value) self.assertEqual(cache.get(key), value)
import os
import md5
import shutil
import tempfile
from django.core.cache.backends.filebased import CacheClass as FileCache
class FileBasedCacheTests(unittest.TestCase): class FileBasedCacheTests(unittest.TestCase):
""" """
@ -112,23 +113,23 @@ class FileBasedCacheTests(unittest.TestCase):
self.dirname = tempfile.mktemp() self.dirname = tempfile.mktemp()
os.mkdir(self.dirname) os.mkdir(self.dirname)
self.cache = FileCache(self.dirname, {}) self.cache = FileCache(self.dirname, {})
def tearDown(self): def tearDown(self):
shutil.rmtree(self.dirname) shutil.rmtree(self.dirname)
def test_hashing(self): def test_hashing(self):
"""Test that keys are hashed into subdirectories correctly""" """Test that keys are hashed into subdirectories correctly"""
self.cache.set("foo", "bar") self.cache.set("foo", "bar")
keyhash = md5.new("foo").hexdigest() keyhash = md5_constructor("foo").hexdigest()
keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
self.assert_(os.path.exists(keypath)) self.assert_(os.path.exists(keypath))
def test_subdirectory_removal(self): def test_subdirectory_removal(self):
""" """
Make sure that the created subdirectories are correctly removed when empty. Make sure that the created subdirectories are correctly removed when empty.
""" """
self.cache.set("foo", "bar") self.cache.set("foo", "bar")
keyhash = md5.new("foo").hexdigest() keyhash = md5_constructor("foo").hexdigest()
keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:]) keypath = os.path.join(self.dirname, keyhash[:2], keyhash[2:4], keyhash[4:])
self.assert_(os.path.exists(keypath)) self.assert_(os.path.exists(keypath))
@ -139,9 +140,9 @@ class FileBasedCacheTests(unittest.TestCase):
class CacheUtils(unittest.TestCase): class CacheUtils(unittest.TestCase):
"""TestCase for django.utils.cache functions.""" """TestCase for django.utils.cache functions."""
def test_patch_vary_headers(self): def test_patch_vary_headers(self):
headers = ( headers = (
# Initial vary, new headers, resulting vary. # Initial vary, new headers, resulting vary.
(None, ('Accept-Encoding',), 'Accept-Encoding'), (None, ('Accept-Encoding',), 'Accept-Encoding'),
('Accept-Encoding', ('accept-encoding',), 'Accept-Encoding'), ('Accept-Encoding', ('accept-encoding',), 'Accept-Encoding'),

View File

@ -1,6 +1,5 @@
import os import os
import errno import errno
import sha
import shutil import shutil
import unittest import unittest
@ -8,6 +7,7 @@ from django.core.files import temp as tempfile
from django.core.files.uploadedfile import SimpleUploadedFile from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase, client from django.test import TestCase, client
from django.utils import simplejson from django.utils import simplejson
from django.utils.hashcompat import sha_constructor
from models import FileModel, UPLOAD_ROOT, UPLOAD_TO from models import FileModel, UPLOAD_ROOT, UPLOAD_TO
@ -45,10 +45,10 @@ class FileUploadTests(TestCase):
for key in post_data.keys(): for key in post_data.keys():
try: try:
post_data[key + '_hash'] = sha.new(post_data[key].read()).hexdigest() post_data[key + '_hash'] = sha_constructor(post_data[key].read()).hexdigest()
post_data[key].seek(0) post_data[key].seek(0)
except AttributeError: except AttributeError:
post_data[key + '_hash'] = sha.new(post_data[key]).hexdigest() post_data[key + '_hash'] = sha_constructor(post_data[key]).hexdigest()
response = self.client.post('/file_uploads/verify/', post_data) response = self.client.post('/file_uploads/verify/', post_data)

View File

@ -1,12 +1,10 @@
""" """
Regression tests for the Test Client, especially the customized assertions. Regression tests for the Test Client, especially the customized assertions.
""" """
from django.test import Client, TestCase from django.test import Client, TestCase
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.core.exceptions import SuspiciousOperation from django.core.exceptions import SuspiciousOperation
import os
import sha
class AssertContainsTests(TestCase): class AssertContainsTests(TestCase):
def test_contains(self): def test_contains(self):
@ -24,7 +22,7 @@ class AssertContainsTests(TestCase):
self.assertNotContains(response, 'once') self.assertNotContains(response, 'once')
except AssertionError, e: except AssertionError, e:
self.assertEquals(str(e), "Response should not contain 'once'") self.assertEquals(str(e), "Response should not contain 'once'")
try: try:
self.assertContains(response, 'never', 1) self.assertContains(response, 'never', 1)
except AssertionError, e: except AssertionError, e:
@ -287,7 +285,7 @@ class URLEscapingTests(TestCase):
class ExceptionTests(TestCase): class ExceptionTests(TestCase):
fixtures = ['testdata.json'] fixtures = ['testdata.json']
def test_exception_cleared(self): def test_exception_cleared(self):
"#5836 - A stale user exception isn't re-raised by the test client." "#5836 - A stale user exception isn't re-raised by the test client."
@ -300,7 +298,7 @@ class ExceptionTests(TestCase):
pass pass
# At this point, an exception has been raised, and should be cleared. # At this point, an exception has been raised, and should be cleared.
# This next operation should be successful; if it isn't we have a problem. # This next operation should be successful; if it isn't we have a problem.
login = self.client.login(username='staff', password='password') login = self.client.login(username='staff', password='password')
self.failUnless(login, 'Could not log in') self.failUnless(login, 'Could not log in')