Django

Code

Changeset 6605

Show
Ignore:
Timestamp:
10/26/07 14:52:04 (11 months ago)
Author:
jacob
Message:

i18n security fix. Details will be posted shortly to the Django mailing lists and the official weblog.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/0.91-bugfixes/django/utils/translation.py

    r2132 r6605  
    11"translation helper functions" 
    22 
    3 import os, re, sys 
     3import locale 
     4import os 
     5import re 
     6import sys 
    47import gettext as gettext_module 
    58from cStringIO import StringIO 
     
    2629_default = None 
    2730 
    28 # This is a cache for accept-header to translation object mappings to prevent 
    29 # the accept parser to run multiple times for one user
     31# This is a cache for normalised accept-header languages to prevent multiple 
     32# file lookups when checking the same locale on repeated requests
    3033_accepted = {} 
    3134 
    32 def to_locale(language): 
     35# Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9. 
     36accept_language_re = re.compile(r''' 
     37        ([A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*|\*)   # "en", "en-au", "x-y-z", "*" 
     38        (?:;q=(0(?:\.\d{,3})?|1(?:.0{,3})?))?   # Optional "q=1.00", "q=0.8" 
     39        (?:\s*,\s*|$)                            # Multiple accepts per header. 
     40        ''', re.VERBOSE) 
     41 
     42def to_locale(language, to_lower=False): 
    3343    "Turns a language name (en-us) into a locale name (en_US)." 
    3444    p = language.find('-') 
    3545    if p >= 0: 
    36         return language[:p].lower()+'_'+language[p+1:].upper() 
     46        if to_lower: 
     47            return language[:p].lower()+'_'+language[p+1:].lower() 
     48        else: 
     49            return language[:p].lower()+'_'+language[p+1:].upper() 
    3750    else: 
    3851        return language.lower() 
     
    298311            return lang_code 
    299312 
    300     lang_code = request.COOKIES.get('django_language', None
    301     if lang_code in supported and lang_code is not None and check_for_language(lang_code): 
     313    lang_code = request.COOKIES.get('django_language'
     314    if lang_code and lang_code in supported and check_for_language(lang_code): 
    302315        return lang_code 
    303316 
    304     accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None) 
    305     if accept is not None: 
    306  
    307         t = _accepted.get(accept, None) 
    308         if t is not None: 
    309             return t 
    310  
    311         def _parsed(el): 
    312             p = el.find(';q=') 
    313             if p >= 0: 
    314                 lang = el[:p].strip() 
    315                 order = int(float(el[p+3:].strip())*100) 
    316             else: 
    317                 lang = el 
    318                 order = 100 
    319             p = lang.find('-') 
    320             if p >= 0: 
    321                 mainlang = lang[:p] 
    322             else: 
    323                 mainlang = lang 
    324             return (lang, mainlang, order) 
    325  
    326         langs = [_parsed(el) for el in accept.split(',')] 
    327         langs.sort(lambda a,b: -1*cmp(a[2], b[2])) 
    328  
    329         for lang, mainlang, order in langs: 
    330             if lang in supported or mainlang in supported: 
    331                 langfile = gettext_module.find('django', globalpath, [to_locale(lang)]) 
    332                 if langfile: 
    333                     # reconstruct the actual language from the language 
    334                     # filename, because otherwise we might incorrectly 
    335                     # report de_DE if we only have de available, but 
    336                     # did find de_DE because of language normalization 
    337                     lang = langfile[len(globalpath):].split(os.path.sep)[1] 
    338                     _accepted[accept] = lang 
    339                     return lang 
     317    accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '') 
     318    for lang, unused in parse_accept_lang_header(accept): 
     319        if lang == '*': 
     320            break 
     321 
     322        # We have a very restricted form for our language files (no encoding 
     323        # specifier, since they all must be UTF-8 and only one possible 
     324        # language each time. So we avoid the overhead of gettext.find() and 
     325        # look up the MO file manually. 
     326 
     327        normalized = locale.locale_alias.get(to_locale(lang, True)) 
     328        if not normalized: 
     329            continue 
     330 
     331        # Remove the default encoding from locale_alias 
     332        normalized = normalized.split('.')[0] 
     333 
     334        if normalized in _accepted: 
     335            # We've seen this locale before and have an MO file for it, so no 
     336            # need to check again. 
     337            return _accepted[normalized] 
     338 
     339        for lang in (normalized, normalized.split('_')[0]): 
     340            if lang not in supported: 
     341                continue 
     342            langfile = os.path.join(globalpath, lang, 'LC_MESSAGES', 
     343                    'django.mo') 
     344            if os.path.exists(langfile): 
     345                _accepted[normalized] = lang 
     346            return lang 
    340347 
    341348    return settings.LANGUAGE_CODE 
     
    458465                out.write(blankout(t.contents, 'X')) 
    459466    return out.getvalue() 
     467 
     468def parse_accept_lang_header(lang_string): 
     469    """ 
     470    Parses the lang_string, which is the body of an HTTP Accept-Language 
     471    header, and returns a list of (lang, q-value), ordered by 'q' values. 
     472 
     473    Any format errors in lang_string results in an empty list being returned. 
     474    """ 
     475    result = [] 
     476    pieces = accept_language_re.split(lang_string) 
     477    if pieces[-1]: 
     478        return [] 
     479    for i in range(0, len(pieces) - 1, 3): 
     480        first, lang, priority = pieces[i : i + 3] 
     481        if first: 
     482            return [] 
     483        priority = priority and float(priority) or 1.0 
     484        result.append((lang, priority)) 
     485    result.sort(lambda x, y: -cmp(x[1], y[1])) 
     486    return result 
  • django/branches/0.91-bugfixes/setup.py

    r1907 r6605  
    66setup( 
    77    name = "Django", 
    8     version = "0.91", 
     8    version = "0.91.1", 
    99    url = 'http://www.djangoproject.com/', 
    1010    author = 'Lawrence Journal-World',