Merge github.com:web2py/web2py
Conflicts: gluon/custom_import.py
This commit is contained in:
@@ -63,6 +63,7 @@ app:
|
||||
python2.5 -c 'import compileall; compileall.compile_dir("gluon/")'
|
||||
#python web2py.py -S welcome -R __exit__.py
|
||||
find gluon -path '*.pyc' -exec cp {} ../web2py_osx/site-packages/{} \;
|
||||
cd ../web2py_osx/site-packages/; unzip ../site-packages.zip
|
||||
cd ../web2py_osx/site-packages/; zip -r ../site-packages.zip *
|
||||
mv ../web2py_osx/site-packages.zip ../web2py_osx/web2py/web2py.app/Contents/Resources/lib/python2.5
|
||||
cp README.markdown ../web2py_osx/web2py/web2py.app/Contents/Resources
|
||||
@@ -86,6 +87,7 @@ win:
|
||||
echo 'did you uncomment import_all in gluon/main.py?'
|
||||
python2.5 -c 'import compileall; compileall.compile_dir("gluon/")'
|
||||
find gluon -path '*.pyc' -exec cp {} ../web2py_win/library/{} \;
|
||||
cd ../web2py_win/library/; unzip ../library.zip
|
||||
cd ../web2py_win/library/; zip -r ../library.zip *
|
||||
mv ../web2py_win/library.zip ../web2py_win/web2py
|
||||
cp README.markdown ../web2py_win/web2py/
|
||||
|
||||
@@ -1 +1 @@
|
||||
Version 2.00.0 (2012-06-05 20:02:32) dev
|
||||
Version 2.00.0 (2012-06-12 08:25:19) dev
|
||||
|
||||
@@ -25,8 +25,7 @@ try:
|
||||
except:
|
||||
hosts = (http_host, )
|
||||
|
||||
if request.env.http_x_forwarded_for or request.env.wsgi_url_scheme\
|
||||
in ['https', 'HTTPS']:
|
||||
if request.env.http_x_forwarded_for or request.is_https:
|
||||
session.secure()
|
||||
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"):
|
||||
raise HTTP(200, T('appadmin is disabled because insecure channel'))
|
||||
|
||||
@@ -104,8 +104,8 @@ function web2py_ajax_page(method, action, data, target) {
|
||||
|
||||
function web2py_component(action, target, timeout, times){
|
||||
jQuery(function(){
|
||||
var element = $("#" + target).get(0);
|
||||
var statement = "$('#" + target + "').get(0).reload();";
|
||||
var element = jQuery("#" + target).get(0);
|
||||
var statement = "jQuery('#" + target + "').get(0).reload();";
|
||||
element.reload = function (){
|
||||
// Continue if times is Infinity or
|
||||
// the times limit is not reached
|
||||
|
||||
@@ -25,8 +25,7 @@ try:
|
||||
except:
|
||||
hosts = (http_host, )
|
||||
|
||||
if request.env.http_x_forwarded_for or request.env.wsgi_url_scheme\
|
||||
in ['https', 'HTTPS']:
|
||||
if request.env.http_x_forwarded_for or request.is_https:
|
||||
session.secure()
|
||||
elif (remote_addr not in hosts) and (remote_addr != "127.0.0.1"):
|
||||
raise HTTP(200, T('appadmin is disabled because insecure channel'))
|
||||
|
||||
@@ -104,8 +104,8 @@ function web2py_ajax_page(method, action, data, target) {
|
||||
|
||||
function web2py_component(action, target, timeout, times){
|
||||
jQuery(function(){
|
||||
var element = $("#" + target).get(0);
|
||||
var statement = "$('#" + target + "').get(0).reload();";
|
||||
var element = jQuery("#" + target).get(0);
|
||||
var statement = "jQuery('#" + target + "').get(0).reload();";
|
||||
element.reload = function (){
|
||||
// Continue if times is Infinity or
|
||||
// the times limit is not reached
|
||||
|
||||
@@ -104,8 +104,8 @@ function web2py_ajax_page(method, action, data, target) {
|
||||
|
||||
function web2py_component(action, target, timeout, times){
|
||||
jQuery(function(){
|
||||
var element = $("#" + target).get(0);
|
||||
var statement = "$('#" + target + "').get(0).reload();";
|
||||
var element = jQuery("#" + target).get(0);
|
||||
var statement = "jQuery('#" + target + "').get(0).reload();";
|
||||
element.reload = function (){
|
||||
// Continue if times is Infinity or
|
||||
// the times limit is not reached
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<span class="icon-bar"></span>
|
||||
</a>
|
||||
<a class="brand" href="#">web2py™ </a>
|
||||
<div id="navbar">{{='auth' in globals() and auth.navbar(separators=(' ',' ',''))}}</div>
|
||||
<div id="navbar">{{='auth' in globals() and auth.navbar(separators=(' ',' ','')) or ''}}</div>
|
||||
<div class="nav-collapse">
|
||||
{{is_mobile=request.user_agent().is_mobile}}
|
||||
{{if response.menu:}}
|
||||
|
||||
@@ -0,0 +1,502 @@
|
||||
"""Simple AES cipher implementation in pure Python following PEP-272 API
|
||||
|
||||
Homepage: https://bitbucket.org/intgr/pyaes/
|
||||
|
||||
The goal of this module is to be as fast as reasonable in Python while still
|
||||
being Pythonic and readable/understandable. It is licensed under the permissive
|
||||
MIT license.
|
||||
|
||||
Hopefully the code is readable and commented enough that it can serve as an
|
||||
introduction to the AES cipher for Python coders. In fact, it should go along
|
||||
well with the Stick Figure Guide to AES:
|
||||
http://www.moserware.com/2009/09/stick-figure-guide-to-advanced.html
|
||||
|
||||
Contrary to intuition, this implementation numbers the 4x4 matrices from top to
|
||||
bottom for efficiency reasons::
|
||||
|
||||
0 4 8 12
|
||||
1 5 9 13
|
||||
2 6 10 14
|
||||
3 7 11 15
|
||||
|
||||
Effectively it's the transposition of what you'd expect. This actually makes
|
||||
the code simpler -- except the ShiftRows step, but hopefully the explanation
|
||||
there clears it up.
|
||||
|
||||
"""
|
||||
|
||||
####
|
||||
# Copyright (c) 2010 Marti Raudsepp <marti@juffo.org>
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
####
|
||||
|
||||
|
||||
from array import array
|
||||
|
||||
# Globals mandated by PEP 272:
|
||||
# http://www.python.org/dev/peps/pep-0272/
|
||||
MODE_ECB = 1
|
||||
MODE_CBC = 2
|
||||
#MODE_CTR = 6
|
||||
|
||||
block_size = 16
|
||||
key_size = None
|
||||
|
||||
def new(key, mode=MODE_CBC, IV=None):
|
||||
if mode == MODE_ECB:
|
||||
return ECBMode(AES(key))
|
||||
elif mode == MODE_CBC:
|
||||
if IV is None:
|
||||
raise ValueError, "CBC mode needs an IV value!"
|
||||
|
||||
return CBCMode(AES(key), IV)
|
||||
else:
|
||||
raise NotImplementedError
|
||||
|
||||
#### AES cipher implementation
|
||||
|
||||
class AES(object):
|
||||
block_size = 16
|
||||
|
||||
def __init__(self, key):
|
||||
self.setkey(key)
|
||||
|
||||
def setkey(self, key):
|
||||
"""Sets the key and performs key expansion."""
|
||||
|
||||
self.key = key
|
||||
self.key_size = len(key)
|
||||
|
||||
if self.key_size == 16:
|
||||
self.rounds = 10
|
||||
elif self.key_size == 24:
|
||||
self.rounds = 12
|
||||
elif self.key_size == 32:
|
||||
self.rounds = 14
|
||||
else:
|
||||
raise ValueError, "Key length must be 16, 24 or 32 bytes"
|
||||
|
||||
self.expand_key()
|
||||
|
||||
def expand_key(self):
|
||||
"""Performs AES key expansion on self.key and stores in self.exkey"""
|
||||
|
||||
# The key schedule specifies how parts of the key are fed into the
|
||||
# cipher's round functions. "Key expansion" means performing this
|
||||
# schedule in advance. Almost all implementations do this.
|
||||
#
|
||||
# Here's a description of AES key schedule:
|
||||
# http://en.wikipedia.org/wiki/Rijndael_key_schedule
|
||||
|
||||
# The expanded key starts with the actual key itself
|
||||
exkey = array('B', self.key)
|
||||
|
||||
# extra key expansion steps
|
||||
if self.key_size == 16:
|
||||
extra_cnt = 0
|
||||
elif self.key_size == 24:
|
||||
extra_cnt = 2
|
||||
else:
|
||||
extra_cnt = 3
|
||||
|
||||
# 4-byte temporary variable for key expansion
|
||||
word = exkey[-4:]
|
||||
# Each expansion cycle uses 'i' once for Rcon table lookup
|
||||
for i in xrange(1, 11):
|
||||
|
||||
#### key schedule core:
|
||||
# left-rotate by 1 byte
|
||||
word = word[1:4] + word[0:1]
|
||||
|
||||
# apply S-box to all bytes
|
||||
for j in xrange(4):
|
||||
word[j] = aes_sbox[word[j]]
|
||||
|
||||
# apply the Rcon table to the leftmost byte
|
||||
word[0] = word[0] ^ aes_Rcon[i]
|
||||
#### end key schedule core
|
||||
|
||||
for z in xrange(4):
|
||||
for j in xrange(4):
|
||||
# mix in bytes from the last subkey
|
||||
word[j] ^= exkey[-self.key_size + j]
|
||||
exkey.extend(word)
|
||||
|
||||
# Last key expansion cycle always finishes here
|
||||
if len(exkey) >= (self.rounds+1) * self.block_size:
|
||||
break
|
||||
|
||||
# Special substitution step for 256-bit key
|
||||
if self.key_size == 32:
|
||||
for j in xrange(4):
|
||||
# mix in bytes from the last subkey XORed with S-box of
|
||||
# current word bytes
|
||||
word[j] = aes_sbox[word[j]] ^ exkey[-self.key_size + j]
|
||||
exkey.extend(word)
|
||||
|
||||
# Twice for 192-bit key, thrice for 256-bit key
|
||||
for z in xrange(extra_cnt):
|
||||
for j in xrange(4):
|
||||
# mix in bytes from the last subkey
|
||||
word[j] ^= exkey[-self.key_size + j]
|
||||
exkey.extend(word)
|
||||
|
||||
self.exkey = exkey
|
||||
|
||||
def add_round_key(self, block, round):
|
||||
"""AddRoundKey step in AES. This is where the key is mixed into plaintext"""
|
||||
|
||||
offset = round * 16
|
||||
exkey = self.exkey
|
||||
|
||||
for i in xrange(16):
|
||||
block[i] ^= exkey[offset + i]
|
||||
|
||||
#print 'AddRoundKey:', block
|
||||
|
||||
def sub_bytes(self, block, sbox):
|
||||
"""SubBytes step, apply S-box to all bytes
|
||||
|
||||
Depending on whether encrypting or decrypting, a different sbox array
|
||||
is passed in.
|
||||
"""
|
||||
|
||||
for i in xrange(16):
|
||||
block[i] = sbox[block[i]]
|
||||
|
||||
#print 'SubBytes :', block
|
||||
|
||||
def shift_rows(self, b):
|
||||
"""ShiftRows step. Shifts 2nd row to left by 1, 3rd row by 2, 4th row by 3
|
||||
|
||||
Since we're performing this on a transposed matrix, cells are numbered
|
||||
from top to bottom::
|
||||
|
||||
0 4 8 12 -> 0 4 8 12 -- 1st row doesn't change
|
||||
1 5 9 13 -> 5 9 13 1 -- row shifted to left by 1 (wraps around)
|
||||
2 6 10 14 -> 10 14 2 6 -- shifted by 2
|
||||
3 7 11 15 -> 15 3 7 11 -- shifted by 3
|
||||
"""
|
||||
|
||||
b[1], b[5], b[ 9], b[13] = b[ 5], b[ 9], b[13], b[ 1]
|
||||
b[2], b[6], b[10], b[14] = b[10], b[14], b[ 2], b[ 6]
|
||||
b[3], b[7], b[11], b[15] = b[15], b[ 3], b[ 7], b[11]
|
||||
|
||||
#print 'ShiftRows :', b
|
||||
|
||||
def shift_rows_inv(self, b):
|
||||
"""Similar to shift_rows above, but performed in inverse for decryption."""
|
||||
|
||||
b[ 5], b[ 9], b[13], b[ 1] = b[1], b[5], b[ 9], b[13]
|
||||
b[10], b[14], b[ 2], b[ 6] = b[2], b[6], b[10], b[14]
|
||||
b[15], b[ 3], b[ 7], b[11] = b[3], b[7], b[11], b[15]
|
||||
|
||||
#print 'ShiftRows :', b
|
||||
|
||||
def mix_columns(self, block):
|
||||
"""MixColumns step. Mixes the values in each column"""
|
||||
|
||||
# Cache global multiplication tables (see below)
|
||||
mul_by_2 = gf_mul_by_2
|
||||
mul_by_3 = gf_mul_by_3
|
||||
|
||||
# Since we're dealing with a transposed matrix, columns are already
|
||||
# sequential
|
||||
for i in xrange(4):
|
||||
col = i * 4
|
||||
|
||||
#v0, v1, v2, v3 = block[col : col+4]
|
||||
v0, v1, v2, v3 = (block[col], block[col + 1], block[col + 2],
|
||||
block[col + 3])
|
||||
|
||||
block[col ] = mul_by_2[v0] ^ v3 ^ v2 ^ mul_by_3[v1]
|
||||
block[col+1] = mul_by_2[v1] ^ v0 ^ v3 ^ mul_by_3[v2]
|
||||
block[col+2] = mul_by_2[v2] ^ v1 ^ v0 ^ mul_by_3[v3]
|
||||
block[col+3] = mul_by_2[v3] ^ v2 ^ v1 ^ mul_by_3[v0]
|
||||
|
||||
#print 'MixColumns :', block
|
||||
|
||||
def mix_columns_inv(self, block):
|
||||
"""Similar to mix_columns above, but performed in inverse for decryption."""
|
||||
|
||||
# Cache global multiplication tables (see below)
|
||||
mul_9 = gf_mul_by_9
|
||||
mul_11 = gf_mul_by_11
|
||||
mul_13 = gf_mul_by_13
|
||||
mul_14 = gf_mul_by_14
|
||||
|
||||
# Since we're dealing with a transposed matrix, columns are already
|
||||
# sequential
|
||||
for i in xrange(4):
|
||||
col = i * 4
|
||||
|
||||
v0, v1, v2, v3 = (block[col], block[col + 1], block[col + 2],
|
||||
block[col + 3])
|
||||
#v0, v1, v2, v3 = block[col:col+4]
|
||||
|
||||
block[col ] = mul_14[v0] ^ mul_9[v3] ^ mul_13[v2] ^ mul_11[v1]
|
||||
block[col+1] = mul_14[v1] ^ mul_9[v0] ^ mul_13[v3] ^ mul_11[v2]
|
||||
block[col+2] = mul_14[v2] ^ mul_9[v1] ^ mul_13[v0] ^ mul_11[v3]
|
||||
block[col+3] = mul_14[v3] ^ mul_9[v2] ^ mul_13[v1] ^ mul_11[v0]
|
||||
|
||||
#print 'MixColumns :', block
|
||||
|
||||
def encrypt_block(self, block):
|
||||
"""Encrypts a single block. This is the main AES function"""
|
||||
|
||||
# For efficiency reasons, the state between steps is transmitted via a
|
||||
# mutable array, not returned.
|
||||
self.add_round_key(block, 0)
|
||||
|
||||
for round in xrange(1, self.rounds):
|
||||
self.sub_bytes(block, aes_sbox)
|
||||
self.shift_rows(block)
|
||||
self.mix_columns(block)
|
||||
self.add_round_key(block, round)
|
||||
|
||||
self.sub_bytes(block, aes_sbox)
|
||||
self.shift_rows(block)
|
||||
# no mix_columns step in the last round
|
||||
self.add_round_key(block, self.rounds)
|
||||
|
||||
def decrypt_block(self, block):
|
||||
"""Decrypts a single block. This is the main AES decryption function"""
|
||||
|
||||
# For efficiency reasons, the state between steps is transmitted via a
|
||||
# mutable array, not returned.
|
||||
self.add_round_key(block, self.rounds)
|
||||
|
||||
# count rounds down from 15 ... 1
|
||||
for round in xrange(self.rounds-1, 0, -1):
|
||||
self.shift_rows_inv(block)
|
||||
self.sub_bytes(block, aes_inv_sbox)
|
||||
self.add_round_key(block, round)
|
||||
self.mix_columns_inv(block)
|
||||
|
||||
self.shift_rows_inv(block)
|
||||
self.sub_bytes(block, aes_inv_sbox)
|
||||
self.add_round_key(block, 0)
|
||||
# no mix_columns step in the last round
|
||||
|
||||
|
||||
#### ECB mode implementation
|
||||
|
||||
class ECBMode(object):
|
||||
"""Electronic CodeBook (ECB) mode encryption.
|
||||
|
||||
Basically this mode applies the cipher function to each block individually;
|
||||
no feedback is done. NB! This is insecure for almost all purposes
|
||||
"""
|
||||
|
||||
def __init__(self, cipher):
|
||||
self.cipher = cipher
|
||||
self.block_size = cipher.block_size
|
||||
|
||||
def ecb(self, data, block_func):
|
||||
"""Perform ECB mode with the given function"""
|
||||
|
||||
if len(data) % self.block_size != 0:
|
||||
raise ValueError, "Plaintext length must be multiple of 16"
|
||||
|
||||
block_size = self.block_size
|
||||
data = array('B', data)
|
||||
|
||||
for offset in xrange(0, len(data), block_size):
|
||||
block = data[offset : offset+block_size]
|
||||
block_func(block)
|
||||
data[offset : offset+block_size] = block
|
||||
|
||||
return data.tostring()
|
||||
|
||||
def encrypt(self, data):
|
||||
"""Encrypt data in ECB mode"""
|
||||
|
||||
return self.ecb(data, self.cipher.encrypt_block)
|
||||
|
||||
def decrypt(self, data):
|
||||
"""Decrypt data in ECB mode"""
|
||||
|
||||
return self.ecb(data, self.cipher.decrypt_block)
|
||||
|
||||
#### CBC mode
|
||||
|
||||
class CBCMode(object):
|
||||
"""Cipher Block Chaining (CBC) mode encryption. This mode avoids content leaks.
|
||||
|
||||
In CBC encryption, each plaintext block is XORed with the ciphertext block
|
||||
preceding it; decryption is simply the inverse.
|
||||
"""
|
||||
|
||||
# A better explanation of CBC can be found here:
|
||||
# http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
|
||||
|
||||
def __init__(self, cipher, IV):
|
||||
self.cipher = cipher
|
||||
self.block_size = cipher.block_size
|
||||
self.IV = array('B', IV)
|
||||
|
||||
def encrypt(self, data):
|
||||
"""Encrypt data in CBC mode"""
|
||||
|
||||
block_size = self.block_size
|
||||
if len(data) % block_size != 0:
|
||||
raise ValueError, "Plaintext length must be multiple of 16"
|
||||
|
||||
data = array('B', data)
|
||||
IV = self.IV
|
||||
|
||||
for offset in xrange(0, len(data), block_size):
|
||||
block = data[offset : offset+block_size]
|
||||
|
||||
# Perform CBC chaining
|
||||
for i in xrange(block_size):
|
||||
block[i] ^= IV[i]
|
||||
|
||||
self.cipher.encrypt_block(block)
|
||||
data[offset : offset+block_size] = block
|
||||
IV = block
|
||||
|
||||
self.IV = IV
|
||||
return data.tostring()
|
||||
|
||||
def decrypt(self, data):
|
||||
"""Decrypt data in CBC mode"""
|
||||
|
||||
block_size = self.block_size
|
||||
if len(data) % block_size != 0:
|
||||
raise ValueError, "Ciphertext length must be multiple of 16"
|
||||
|
||||
data = array('B', data)
|
||||
IV = self.IV
|
||||
|
||||
for offset in xrange(0, len(data), block_size):
|
||||
ctext = data[offset : offset+block_size]
|
||||
block = ctext[:]
|
||||
self.cipher.decrypt_block(block)
|
||||
|
||||
# Perform CBC chaining
|
||||
#for i in xrange(block_size):
|
||||
# data[offset + i] ^= IV[i]
|
||||
for i in xrange(block_size):
|
||||
block[i] ^= IV[i]
|
||||
data[offset : offset+block_size] = block
|
||||
|
||||
IV = ctext
|
||||
#data[offset : offset+block_size] = block
|
||||
|
||||
self.IV = IV
|
||||
return data.tostring()
|
||||
|
||||
####
|
||||
|
||||
def galois_multiply(a, b):
|
||||
"""Galois Field multiplicaiton for AES"""
|
||||
p = 0
|
||||
while b:
|
||||
if b & 1:
|
||||
p ^= a
|
||||
a <<= 1
|
||||
if a & 0x100:
|
||||
a ^= 0x1b
|
||||
b >>= 1
|
||||
|
||||
return p & 0xff
|
||||
|
||||
# Precompute the multiplication tables for encryption
|
||||
gf_mul_by_2 = array('B', [galois_multiply(x, 2) for x in range(256)])
|
||||
gf_mul_by_3 = array('B', [galois_multiply(x, 3) for x in range(256)])
|
||||
# ... for decryption
|
||||
gf_mul_by_9 = array('B', [galois_multiply(x, 9) for x in range(256)])
|
||||
gf_mul_by_11 = array('B', [galois_multiply(x, 11) for x in range(256)])
|
||||
gf_mul_by_13 = array('B', [galois_multiply(x, 13) for x in range(256)])
|
||||
gf_mul_by_14 = array('B', [galois_multiply(x, 14) for x in range(256)])
|
||||
|
||||
####
|
||||
|
||||
# The S-box is a 256-element array, that maps a single byte value to another
|
||||
# byte value. Since it's designed to be reversible, each value occurs only once
|
||||
# in the S-box
|
||||
#
|
||||
# More information: http://en.wikipedia.org/wiki/Rijndael_S-box
|
||||
|
||||
aes_sbox = array('B',
|
||||
'637c777bf26b6fc53001672bfed7ab76'
|
||||
'ca82c97dfa5947f0add4a2af9ca472c0'
|
||||
'b7fd9326363ff7cc34a5e5f171d83115'
|
||||
'04c723c31896059a071280e2eb27b275'
|
||||
'09832c1a1b6e5aa0523bd6b329e32f84'
|
||||
'53d100ed20fcb15b6acbbe394a4c58cf'
|
||||
'd0efaafb434d338545f9027f503c9fa8'
|
||||
'51a3408f929d38f5bcb6da2110fff3d2'
|
||||
'cd0c13ec5f974417c4a77e3d645d1973'
|
||||
'60814fdc222a908846eeb814de5e0bdb'
|
||||
'e0323a0a4906245cc2d3ac629195e479'
|
||||
'e7c8376d8dd54ea96c56f4ea657aae08'
|
||||
'ba78252e1ca6b4c6e8dd741f4bbd8b8a'
|
||||
'703eb5664803f60e613557b986c11d9e'
|
||||
'e1f8981169d98e949b1e87e9ce5528df'
|
||||
'8ca1890dbfe6426841992d0fb054bb16'.decode('hex')
|
||||
)
|
||||
|
||||
# This is the inverse of the above. In other words:
|
||||
# aes_inv_sbox[aes_sbox[val]] == val
|
||||
|
||||
aes_inv_sbox = array('B',
|
||||
'52096ad53036a538bf40a39e81f3d7fb'
|
||||
'7ce339829b2fff87348e4344c4dee9cb'
|
||||
'547b9432a6c2233dee4c950b42fac34e'
|
||||
'082ea16628d924b2765ba2496d8bd125'
|
||||
'72f8f66486689816d4a45ccc5d65b692'
|
||||
'6c704850fdedb9da5e154657a78d9d84'
|
||||
'90d8ab008cbcd30af7e45805b8b34506'
|
||||
'd02c1e8fca3f0f02c1afbd0301138a6b'
|
||||
'3a9111414f67dcea97f2cfcef0b4e673'
|
||||
'96ac7422e7ad3585e2f937e81c75df6e'
|
||||
'47f11a711d29c5896fb7620eaa18be1b'
|
||||
'fc563e4bc6d279209adbc0fe78cd5af4'
|
||||
'1fdda8338807c731b11210592780ec5f'
|
||||
'60517fa919b54a0d2de57a9f93c99cef'
|
||||
'a0e03b4dae2af5b0c8ebbb3c83539961'
|
||||
'172b047eba77d626e169146355210c7d'.decode('hex')
|
||||
)
|
||||
|
||||
# The Rcon table is used in AES's key schedule (key expansion)
|
||||
# It's a pre-computed table of exponentation of 2 in AES's finite field
|
||||
#
|
||||
# More information: http://en.wikipedia.org/wiki/Rijndael_key_schedule
|
||||
|
||||
aes_Rcon = array('B',
|
||||
'8d01020408102040801b366cd8ab4d9a'
|
||||
'2f5ebc63c697356ad4b37dfaefc59139'
|
||||
'72e4d3bd61c29f254a943366cc831d3a'
|
||||
'74e8cb8d01020408102040801b366cd8'
|
||||
'ab4d9a2f5ebc63c697356ad4b37dfaef'
|
||||
'c5913972e4d3bd61c29f254a943366cc'
|
||||
'831d3a74e8cb8d01020408102040801b'
|
||||
'366cd8ab4d9a2f5ebc63c697356ad4b3'
|
||||
'7dfaefc5913972e4d3bd61c29f254a94'
|
||||
'3366cc831d3a74e8cb8d010204081020'
|
||||
'40801b366cd8ab4d9a2f5ebc63c69735'
|
||||
'6ad4b37dfaefc5913972e4d3bd61c29f'
|
||||
'254a943366cc831d3a74e8cb8d010204'
|
||||
'08102040801b366cd8ab4d9a2f5ebc63'
|
||||
'c697356ad4b37dfaefc5913972e4d3bd'
|
||||
'61c29f254a943366cc831d3a74e8cb'.decode('hex')
|
||||
)
|
||||
@@ -313,7 +313,7 @@ class _Web2pyImporter(_BaseImporter):
|
||||
try:
|
||||
result = result or new_mod.__dict__[name]
|
||||
except KeyError, e:
|
||||
raise ImportError, ('Cannot import module %s' % str(e),)
|
||||
raise ImportError, 'Cannot import module %s' % str(e)
|
||||
prefix += "." + name
|
||||
return result
|
||||
|
||||
|
||||
+27
-11
@@ -4080,9 +4080,21 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
||||
if use_common_filters(query):
|
||||
query = self.common_filter(query,[tablename])
|
||||
|
||||
if len(self.db[tablename].fields) == len(fields):
|
||||
#getting all fields, not a projection query
|
||||
projection = None
|
||||
else:
|
||||
projection = [f.name for f in fields]
|
||||
|
||||
#tableobj is a GAE Model class (or subclass)
|
||||
tableobj = self.db[tablename]._tableobj
|
||||
items = tableobj.all()
|
||||
filters = self.expand(query)
|
||||
|
||||
#projection's can't include 'id'. it will be added to the result later
|
||||
query_projection = [p for p in projection if p != 'id'] if projection \
|
||||
else None
|
||||
items = gae.Query(tableobj, projection=query_projection)
|
||||
|
||||
for filter in filters:
|
||||
if filter.name=='__key__' and filter.op=='>' and filter.value==0:
|
||||
continue
|
||||
@@ -4090,17 +4102,23 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
||||
if filter.value==0:
|
||||
items = []
|
||||
elif isinstance(filter.value, Key):
|
||||
#key qeuries return a class instance, can't use projection
|
||||
# extra values will be ignored in post-processing later
|
||||
item = tableobj.get(filter.value)
|
||||
items = (item and [item]) or []
|
||||
else:
|
||||
#key qeuries return a class instance, can't use projection
|
||||
# extra values will be ignored in post-processing later
|
||||
item = tableobj.get_by_id(filter.value)
|
||||
items = (item and [item]) or []
|
||||
elif isinstance(items,list): # i.e. there is a single record!
|
||||
items = [i for i in items if filter.apply(getattr(item,filter.name),
|
||||
filter.value)]
|
||||
items = [i for i in items if filter.apply(
|
||||
getattr(item,filter.name),filter.value)]
|
||||
else:
|
||||
if filter.name=='__key__': items.order('__key__')
|
||||
items = items.filter('%s %s' % (filter.name,filter.op),filter.value)
|
||||
if filter.name=='__key__':
|
||||
items.order('__key__')
|
||||
items = items.filter('%s %s' % (filter.name,filter.op),
|
||||
filter.value)
|
||||
if not isinstance(items,list):
|
||||
if attributes.get('left', None):
|
||||
raise SyntaxError, 'Set: no left join in appengine'
|
||||
@@ -4120,9 +4138,8 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
||||
if attributes.get('limitby', None):
|
||||
(lmin, lmax) = attributes['limitby']
|
||||
(limit, offset) = (lmax - lmin, lmin)
|
||||
items = items.fetch(limit, offset=offset)
|
||||
fields = self.db[tablename].fields
|
||||
return (items, tablename, fields)
|
||||
items = items.fetch(limit,offset=offset)
|
||||
return (items, tablename, projection or self.db[tablename].fields)
|
||||
|
||||
def select(self,query,fields,attributes):
|
||||
(items, tablename, fields) = self.select_raw(query,fields,attributes)
|
||||
@@ -4134,7 +4151,6 @@ class GoogleDatastoreAdapter(NoSQLAdapter):
|
||||
processor = attributes.get('processor',self.parse)
|
||||
return processor(rows,fields,colnames,False)
|
||||
|
||||
|
||||
def count(self,query,distinct=None):
|
||||
if distinct:
|
||||
raise RuntimeError, "COUNT DISTINCT not supported"
|
||||
@@ -4569,7 +4585,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
return expression
|
||||
|
||||
def _select(self,query,fields,attributes):
|
||||
from pymongo import son
|
||||
from pymongo.son import SON
|
||||
|
||||
for key in set(attributes.keys())-set(('limitby','orderby')):
|
||||
raise SyntaxError, 'invalid select attribute: %s' % key
|
||||
@@ -4598,7 +4614,7 @@ class MongoDBAdapter(NoSQLAdapter):
|
||||
else:
|
||||
limitby_skip = limitby_limit = 0
|
||||
|
||||
mongofields_dict = son.SON()
|
||||
mongofields_dict = SON()
|
||||
mongoqry_dict = {}
|
||||
for item in fields:
|
||||
if isinstance(item,SQLALL):
|
||||
|
||||
+56
-6
@@ -36,6 +36,13 @@ import os
|
||||
import sys
|
||||
import traceback
|
||||
import threading
|
||||
import hmac
|
||||
import base64
|
||||
|
||||
try:
|
||||
from Crypto.Cipher import AES
|
||||
except ImportError:
|
||||
from contrib import aes as AES
|
||||
|
||||
try:
|
||||
from gluon.contrib.minify import minify
|
||||
@@ -409,6 +416,7 @@ class Session(Storage):
|
||||
migrate=True,
|
||||
separate = None,
|
||||
check_client=False,
|
||||
cookie_key=None,
|
||||
):
|
||||
"""
|
||||
separate can be separate=lambda(session_name): session_name[-2:]
|
||||
@@ -421,9 +429,30 @@ class Session(Storage):
|
||||
if not masterapp:
|
||||
masterapp = request.application
|
||||
response.session_id_name = 'session_id_%s' % masterapp.lower()
|
||||
|
||||
# Load session data from cookie
|
||||
|
||||
if cookie_key:
|
||||
response.session_cookie_key = cookie_key
|
||||
response.session_cookie_key2 = hashlib.md5(cookie_key).digest()
|
||||
cookie_name = request.application.lower()+'_session_data'
|
||||
response.session_cookie_name = cookie_name
|
||||
if cookie_data in request.cookies:
|
||||
cookie_value = request.cookies[cookie_name].value
|
||||
cookie_parts = cookie_value.split(":")
|
||||
enc = cookie_parts[2]
|
||||
cipher = AES.new(cookie_key)
|
||||
decrypted = cipher.decrypt(base64.b64decode(enc)).rstrip('{')
|
||||
check = hmac.new(response.session_cookie_key2,enc).hexdigest()
|
||||
if cookie_parts[0] == check:
|
||||
session_data = cPickle.loads(decrypted)
|
||||
self.update(session_data)
|
||||
else:
|
||||
return
|
||||
|
||||
if not db:
|
||||
if global_settings.db_sessions is True or masterapp in global_settings.db_sessions:
|
||||
if global_settings.db_sessions is True \
|
||||
or masterapp in global_settings.db_sessions:
|
||||
return
|
||||
response.session_new = False
|
||||
client = request.client and request.client.replace(':', '.')
|
||||
@@ -441,11 +470,13 @@ class Session(Storage):
|
||||
response.session_file = \
|
||||
open(response.session_filename, 'rb+')
|
||||
try:
|
||||
portalocker.lock(response.session_file,portalocker.LOCK_EX)
|
||||
portalocker.lock(response.session_file,
|
||||
portalocker.LOCK_EX)
|
||||
response.session_locked = True
|
||||
self.update(cPickle.load(response.session_file))
|
||||
response.session_file.seek(0)
|
||||
oc = response.session_filename.split('/')[-1].split('-')[0]
|
||||
oc = response.session_filename.split('/')[-1]\
|
||||
.split('-')[0]
|
||||
if check_client and client!=oc:
|
||||
raise Exception, "cookie attack"
|
||||
finally:
|
||||
@@ -459,7 +490,8 @@ class Session(Storage):
|
||||
response.session_id = '%s-%s' % (client, uuid)
|
||||
if separate:
|
||||
prefix = separate(response.session_id)
|
||||
response.session_id = '%s/%s' % (prefix,response.session_id)
|
||||
response.session_id = '%s/%s' % \
|
||||
(prefix,response.session_id)
|
||||
response.session_filename = \
|
||||
os.path.join(up(request.folder), masterapp,
|
||||
'sessions', response.session_id)
|
||||
@@ -492,16 +524,25 @@ class Session(Storage):
|
||||
migrate=table_migrate,
|
||||
)
|
||||
try:
|
||||
|
||||
# Get session data out of the database
|
||||
|
||||
# Key comes from the cookie
|
||||
key = request.cookies[response.session_id_name].value
|
||||
(record_id, unique_key) = key.split(':')
|
||||
if record_id == '0':
|
||||
raise Exception, 'record_id == 0'
|
||||
# Select from database.
|
||||
rows = db(table.id == record_id).select()
|
||||
|
||||
# Make sure the session data exists in the database
|
||||
if len(rows) == 0 or rows[0].unique_key != unique_key:
|
||||
raise Exception, 'No record'
|
||||
|
||||
# rows[0].update_record(locked=True)
|
||||
|
||||
|
||||
# Unpickle the data
|
||||
session_data = cPickle.loads(rows[0].session_data)
|
||||
self.update(session_data)
|
||||
except Exception:
|
||||
@@ -540,8 +581,18 @@ class Session(Storage):
|
||||
self._close(response)
|
||||
self._forget = True
|
||||
|
||||
def _try_store_in_db(self, request, response):
|
||||
def _try_store_in_cookie(self, request, response):
|
||||
pad = lambda s: s + (32 - len(s) % 32) * '{'
|
||||
data = cPickle.dumps(dict(self))
|
||||
cipher = AES.new(response.session_cookie_key)
|
||||
encrypted_data = base64.b64encode(cipher.encrypt(pad(data)))
|
||||
signature = hmac.new(response.session_cookie_key2,encrypted_data)\
|
||||
.hexdigest()
|
||||
value = signature+':'+encrypted_data
|
||||
response.cookies[response.session_cookie_name] = value
|
||||
response.cookies[response.session_cookie_name]['path'] = '/'
|
||||
|
||||
def _try_store_in_db(self, request, response):
|
||||
# don't save if file-based sessions, no session id, or session being forgotten
|
||||
if not response.session_db or not response.session_id or self._forget:
|
||||
return
|
||||
@@ -618,4 +669,3 @@ class Session(Storage):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
+5
-1
@@ -224,7 +224,11 @@ class cronlauncher(threading.Thread):
|
||||
|
||||
def run(self):
|
||||
import subprocess
|
||||
proc = subprocess.Popen(self.cmd,
|
||||
if isinstance(self.cmd, (list,tuple)):
|
||||
cmd = self.cmd
|
||||
else:
|
||||
cmd = self.cmd.split()
|
||||
proc = subprocess.Popen(cmd,
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
|
||||
+2
-2
@@ -361,7 +361,7 @@ class Scheduler(MetaScheduler):
|
||||
Field('enabled','boolean',default=True),
|
||||
Field('start_time','datetime',default=now),
|
||||
Field('next_run_time','datetime',default=now),
|
||||
Field('stop_time','datetime',default=now+datetime.timedelta(days=1)),
|
||||
Field('stop_time','datetime',default=None),
|
||||
Field('repeats','integer',default=1,comment="0=unlimted"),
|
||||
Field('period','integer',default=60,comment='seconds'),
|
||||
Field('timeout','integer',default=60,comment='seconds'),
|
||||
@@ -542,7 +542,7 @@ class Scheduler(MetaScheduler):
|
||||
all_available = db(ts.status.belongs((QUEUED,ASSIGNED)))\
|
||||
((ts.times_run<ts.repeats)|(ts.repeats==0))\
|
||||
(ts.start_time<=now)\
|
||||
(ts.stop_time>now)\
|
||||
((ts.stop_time==None) | (ts.stop_time>now))\
|
||||
(ts.next_run_time<=now)\
|
||||
(ts.enabled==True)\
|
||||
(ts.group_name.belongs(self.group_names)) #\
|
||||
|
||||
@@ -1130,23 +1130,6 @@ class SQLFORM(FORM):
|
||||
hideerror=hideerror,
|
||||
)
|
||||
|
||||
if not ret and self.record and self.errors:
|
||||
### if there are errors in update mode
|
||||
# and some errors refers to an already uploaded file
|
||||
# delete error if
|
||||
# - user not trying to upload a new file
|
||||
# - there is existing file and user is not trying to delete it
|
||||
# this is because removing the file may not pass validation
|
||||
for key in self.errors.keys():
|
||||
if key in self.table \
|
||||
and self.table[key].type == 'upload' \
|
||||
and request_vars.get(key, None) in (None, '') \
|
||||
and self.record[key] \
|
||||
and not key + UploadWidget.ID_DELETE_SUFFIX in request_vars:
|
||||
del self.errors[key]
|
||||
if not self.errors:
|
||||
ret = True
|
||||
|
||||
self.deleted = \
|
||||
request_vars.get(self.FIELDNAME_REQUEST_DELETE, False)
|
||||
|
||||
|
||||
+1
-1
@@ -1513,7 +1513,7 @@ class Auth(object):
|
||||
*settings.extra_fields.get(settings.table_cas_name,[]),
|
||||
**dict(
|
||||
migrate=self.__get_migrate(
|
||||
settings.table_event_name, migrate),
|
||||
settings.table_cas_name, migrate),
|
||||
fake_migrate=fake_migrate))
|
||||
table.user_id.requires = IS_IN_DB(db, '%s.id' % \
|
||||
settings.table_user_name,
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@ if remove_msft_dlls:
|
||||
os.unlink(os.path.join('dist',f))
|
||||
except:
|
||||
print "unable to delete dist/"+f
|
||||
sys.exit(1)
|
||||
#sys.exit(1)
|
||||
|
||||
|
||||
#Should we include applications?
|
||||
|
||||
Reference in New Issue
Block a user