8000 Stable/0.95.x by dhaval-trootech · Pull Request #16823 · django/django · GitHub
[go: up one dir, main page]

Skip to content

Stable/0.95.x #16823

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

8000
Closed
wants to merge 15 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion django/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = (0, 95, None)
VERSION = (0, 95.4, None)
12 changes: 11 additions & 1 deletion django/bin/compile-messages.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,17 @@ def compile_messages():
if f.endswith('.po'):
sys.stderr.write('processing file %s in %s\n' % (f, dirpath))
pf = os.path.splitext(os.path.join(dirpath, f))[0]
cmd = 'msgfmt -o "%s.mo" "%s.po"' % (pf, pf)
# Store the names of the .mo and .po files in an environment
# variable, rather than doing a string replacement into the
# command, so that we can take advantage of shell quoting, to
# quote any malicious characters/escaping.
# See http://cyberelk.net/tim/articles/cmdline/ar01s02.html
os.environ['djangocompilemo'] = pf + '.mo'
os.environ['djangocompilepo'] = pf + '.po'
if sys.platform == 'win32': # Different shell-variable syntax
cmd = 'msgfmt -o "%djangocompilemo%" "%djangocompilepo%"'
else:
cmd = 'msgfmt -o "$djangocompilemo" "$djangocompilepo"'
os.system(cmd)

if __name__ == "__main__":
Expand Down
1 change: 0 additions & 1 deletion django/contrib/admin/templates/admin/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
<div class="form-row">
<label for="id_password">{% trans 'Password:' %}</label> <input type="password" name="password" id="id_password" />
<input type="hidden" name="this_is_the_login_form" value="1" />
<input type="hidden" name="post_data" value="{{ post_data }}" /> {% comment %}<span class="help">{% trans 'Have you <a href="/password_reset/">forgotten your password</a>?' %}</span>{% endcomment %}
</div>
<div class="submit-row">
<label>&nbsp;</label><input type="submit" value="{% trans 'Log in' %}" />
Expand Down
45 changes: 5 additions & 40 deletions django/contrib/admin/views/decorators.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,43 +3,21 @@
from django.contrib.auth.models import User
from django.contrib.auth import authenticate, login
from django.shortcuts import render_to_response
from django.utils.html import escape
from django.utils.translation import gettext_lazy
import base64, datetime, md5
import cPickle as pickle
import base64, datetime

ERROR_MESSAGE = gettext_lazy("Please enter a correct username and password. Note that both fields are case-sensitive.")
LOGIN_FORM_KEY = 'this_is_the_login_form'

def _display_login_form(request, error_message=''):
request.session.set_test_cookie()
if request.POST and request.POST.has_key('post_data'):
# User has failed login BUT has previously saved post data.
post_data = request.POST['post_data']
elif request.POST:
# User's session must have expired; save their post data.
post_data = _encode_post_data(request.POST)
else:
post_data = _encode_post_data({})
return render_to_response('admin/login.html', {
'title': _('Log in'),
'app_path': request.path,
'post_data': post_data,
'app_path': escape(request.path),
'error_message': error_message
}, context_instance=template.RequestContext(request))

def _encode_post_data(post_data):
pickled = pickle.dumps(post_data)
pickled_md5 = md5.new(pickled + settings.SECRET_KEY).hexdigest()
return base64.encodestring(pickled + pickled_md5)

def _decode_post_data(encoded_data):
encoded_data = base64.decodestring(encoded_data)
pickled, tamper_check = encoded_data[:-32], encoded_data[-32:]
if md5.new(pickled + settings.SECRET_KEY).hexdigest() != tamper_check:
from django.core.exceptions import SuspiciousOperation
raise SuspiciousOperation, "User may have tampered with session cookie."
return pickle.loads(pickled)

def staff_member_required(view_func):
"""
Decorator for views that checks that the user is logged in and is a staff
Expand All @@ -48,18 +26,14 @@ def staff_member_required(view_func):
def _checklogin(request, *args, **kwargs):
if request.user.is_authenticated() and request.user.is_staff:
# The user is valid. Continue to the admin page.
if request.POST.has_key('post_data'):
# User must have re-authenticated through a different window
# or tab.
request.POST = _decode_post_data(request.POST['post_data'])
return view_func(request, *args, **kwargs)

assert hasattr(request, 'session'), "The Django admin requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."

# If this isn't already the login page, display it.
if not request.POST.has_key(LOGIN_FORM_KEY):
if request.POST:
message = _("Please log in again, because your session has expired. Don't worry: Your submission has been saved.")
message = _("Please log in again, because your session has expired.")
else:
message = ""
return _display_login_form(request, message)
Expand Down Expand Up @@ -92,16 +66,7 @@ def _checklogin(request, *args, **kwargs):
# TODO: set last_login with an event.
user.last_login = datetime.datetime.now()
user.save()
if request.POST.has_key('post_data'):
post_data = _decode_post_data(request.POST['post_data'])
if post_data and not post_data.has_key(LOGIN_FORM_KEY):
# overwrite request.POST with the saved post_data, and continue
request.POST = post_data
request.user = user
return view_func(request, *args, **kwargs)
else:
request.session.delete_test_cookie()
return http.HttpResponseRedirect(request.path)
return http.HttpResponseRedirect(request.path)
else:
return _display_login_form(request, ERROR_MESSAGE)

Expand Down
9 changes: 3 additions & 6 deletions django/contrib/auth/middleware.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
class LazyUser(object):
def __init__(self):
self._user = None

def __get__(self, request, obj_type=None):
if self._user is None:
if not hasattr(request, '_cached_user'):
from django.contrib.auth import get_user
self._user = get_user(request)
return self._user
request._cached_user = get_user(request)
return request._cached_user

class AuthenticationMiddleware(object):
def process_request(self, request):
Expand Down
4 changes: 3 additions & 1 deletion django/core/servers/fastcgi.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ def runfastcgi(argset):
wsgi_opts = {}
else:
return fastcgi_help("ERROR: Implementation must be one of prefork or thread.")


wsgi_opts['debug'] = False # Turn off flup tracebacks

# Prep up and go
from django.core.handlers.wsgi import WSGIHandler

Expand Down
27 changes: 18 additions & 9 deletions django/db/models/query.py
935E
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.db import backend, connection, transaction
from django.db.models.fields import DateField, FieldDoesNotExist
from django.db.models.fields.generic import GenericRelation
from django.db.models import signals
from django.dispatch import dispatcher
from django.utils.datastructures import SortedDict
Expand Down Expand Up @@ -925,18 +926,26 @@ def delete_objects(seen_objs):

pk_list = [pk for pk,instance in seen_objs[cls]]
for related in cls._meta.get_all_related_many_to_many_objects():
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(related.field.m2m_db_table()),
qn(related.field.m2m_reverse_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
if not isinstance(related.field, GenericRelation):
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(related.field.m2m_db_table()),
qn(related.field.m2m_reverse_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
for f in cls._meta.many_to_many:
if isinstance(f, GenericRelation):
from django.contrib.contenttypes.models import ContentType
query_extra = 'AND %s=%%s' % f.rel.to._meta.get_field(f.content_type_field_name).column
args_extra = [ContentType.objects.get_for_model(cls).id]
else:
query_extra = ''
args_extra = []
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
cursor.execute("DELETE FROM %s WHERE %s IN (%s)" % \
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])),
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE])
(qn(f.m2m_db_table()), qn(f.m2m_column_name()),
','.join(['%s' for pk in pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE]])) + query_extra,
pk_list[offset:offset+GET_ITERATOR_CHUNK_SIZE] + args_extra)
for field in cls._meta.fields:
if field.rel and field.null and field.rel.to in seen_objs:
for offset in range(0, len(pk_list), GET_ITERATOR_CHUNK_SIZE):
Expand Down
114 changes: 71 additions & 43 deletions django/utils/translation/trans_real.py
10000
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
"Translation helper functions"

import os, re, sys
import locale
import os
import re
import sys
import gettext as gettext_module
from cStringIO import StringIO
from django.utils.functional import lazy
Expand All @@ -25,15 +28,25 @@ def currentThread():
# The default translation is based on the settings file.
_default = None

# This is a cache for accept-header to translation object mappings to prevent
# the accept parser to run multiple times for one user.
# This is a cache for normalised accept-header languages to prevent multiple
# file lookups when checking the same locale on repeated requests.
_accepted = {}

def to_locale(language):
# Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9.
accept_language_re = re.compile(r'''
([A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*|\*) # "en", "en-au", "x-y-z", "*"
(?:;q=(0(?:\.\d{,3})?|1(?:.0{,3})?))? # Optional "q=1.00", "q=0.8"
(?:\s*,\s*|$) # Multiple accepts per header.
''', re.VERBOSE)

def to_locale(language, to_lower=False):
"Turns a language name (en-us) into a locale name (en_US)."
p = language.find('-')
if p >= 0:
return language[:p].lower()+'_'+language[p+1:].upper()
if to_lower:
return language[:p].lower()+'_'+language[p+1:].lower()
else:
return language[:p].lower()+'_'+language[p+1:].upper()
else:
return language.lower()

Expand Down Expand Up @@ -309,46 +322,40 @@ def get_language_from_request(request):
if lang_code in supported and lang_code is not None and check_for_language(lang_code):
return lang_code

lang_code = request.COOKIES.get('django_language', None)
if lang_code in supported and lang_code is not None and check_for_language(lang_code):
lang_code = request.COOKIES.get('django_language')
if lang_code and lang_code in supported and check_for_language(lang_code):
return lang_code

accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None)
if accept is not None:

t = _accepted.get(accept, None)
if t is not None:
return t

def _parsed(el):
p = el.find(';q=')
if p >= 0:
lang = el[:p].strip()
order = int(float(el[p+3:].strip())*100)
else:
lang = el
order = 100
p = lang.find('-')
if p >= 0:
mainlang = lang[:p]
else:
mainlang = lang
return (lang, mainlang, order)

langs = [_parsed(el) for el in accept.split(',')]
langs.sort(lambda a,b: -1*cmp(a[2], b[2]))

for lang, mainlang, order in langs:
if lang in supported or mainlang in supported:
langfile = gettext_module.find('django', globalpath, [to_locale(lang)])
if langfile:
# reconstruct the actual language from the language
# filename, because otherwise we might incorrectly
# report de_DE if we only have de available, but
# did find de_DE because of language normalization
lang = langfile[len(globalpath):].split(os.path.sep)[1]
_accepted[accept] = lang
return lang
accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
for lang, unused in parse_accept_lang_header(accept):
if lang == '*':
break

# We have a very restricted form for our language files (no encoding
# specifier, since they all must be UTF-8 and only one possible
# language each time. So we avoid the overhead of gettext.find() and
# look up the MO file manually.

normalized = locale.locale_alias.get(to_locale(lang, True))
if not normalized:
continue

# Remove the default encoding from locale_alias
normalized = normalized.split('.')[0]

if normalized in _accepted:
# We've seen this locale before and have an MO file for it, so no
# need to check again.
return _accepted[normalized]

for lang in (normalized, normalized.split('_')[0]):
if lang not in supported:
continue
langfile = os.path.join(globalpath, lang, 'LC_MESSAGES',
'django.mo')
if os.path.exists(langfile):
_accepted[normalized] = lang
return lang

return settings.LANGUAGE_CODE

Expand Down Expand Up @@ -494,3 +501,24 @@ def string_concat(*strings):
return ''.join([str(el) for el in strings])

string_concat = lazy(string_concat, str)

def parse_accept_lang_header(lang_string):
"""
Parses the lang_string, which is the body of an HTTP Accept-Language
header, and returns a list of (lang, q-value), ordered by 'q' values.
Any format errors in lang_string results in an empty list being returned.
"""
result = []
pieces = accept_language_re.split(lang_string)
if pieces[-1]:
return []
for i in range(0, len(pieces) - 1, 3):
first, lang, priority = pieces[i : i + 3]
if first:
return []
priority = priority and float(priority) or 1.0
result.append((lang, priority))
result.sort(lambda x, y: -cmp(x[1], y[1]))
return result

36 changes: 30 additions & 6 deletions docs/release_notes_0.95.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
=================================
Django version 0.95 release notes
=================================
===================================
Django version 0.95.2 release notes
===================================


Welcome to the Django 0.95 release.
Welcome to the Django 0.95.2 release.

This represents a significant advance in Django development since the 0.91
release in January 2006. The details of every change in this release would be
Expand Down Expand Up @@ -91,6 +90,31 @@ easy checklist_ for reference when undertaking the porting operation.
.. _Removing The Magic: http://code.djangoproject.com/wiki/RemovingTheMagic
.. _checklist: http://code.djangoproject.com/wiki/MagicRemovalCheatSheet1

Changes since the 0.95 release
==============================

This release contains fixes for several bugs discovered after the
initial release of Django 0.95; these include:

* A patch for a small security vulnerability in the script
Django's internationalization system uses to compile translation
files.

* A fix for a bug in Django's authentication middleware which
could cause apparent "caching" of a logged-in user.

* A patch which disables debugging mode in the flup FastCGI
package Django uses to launch its FastCGI server, which prevents
tracebacks from bubbling up during production use.

* A security fix to the i18n framework which could allow an
attacker to send extremely large strings in the Accept-Language
header and cause a denial of service by filling available memory.

Because these problems weren't discovered and fixed until after the
0.95 release, it's recommended that you use this release rather than
the original 0.95.

Problem reports and getting help
================================

Expand Down Expand Up @@ -118,5 +142,5 @@ available at any hour of the day -- to help, or just to chat.
Thanks for using Django!

The Django Team
July 2006
January 2007

2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

setup(
name = "Django",
version = "0.95",
version = "0.95.4",
url = 'http://www.djangoproject.com/',
author = 'Lawrence Journal-World',
author_email = 'holovaty@gmail.com',
Expand Down
Loading
0