many pep8 improvements
This commit is contained in:
1 parent
b716df1a05
commit
4b2ba185ae
117 files changed
+7160
-5670
No files matched your search
@@ -21,6 +21,7 @@ import urllib
|
||||
|
||||
_known_tuple_types = {}
|
||||
|
||||
|
||||
class NamedTupleBase(tuple):
|
||||
"""Base class for named tuples with the __new__ operator set, named tuples
|
||||
yielded by the namedtuple() function will subclass this and add
|
||||
@@ -29,7 +30,7 @@ class NamedTupleBase(tuple):
|
||||
"""Create a new instance of this fielded tuple"""
|
||||
# May need to unpack named field values here
|
||||
if kws:
|
||||
values = list(args) + [None]*(len(cls._fields) - len(args))
|
||||
values = list(args) + [None] * (len(cls._fields) - len(args))
|
||||
fields = dict((val, idx) for idx, val in enumerate(cls._fields))
|
||||
for kw, val in kws.iteritems():
|
||||
assert kw in kws, "%r not in field list" % kw
|
||||
@@ -37,6 +38,7 @@ class NamedTupleBase(tuple):
|
||||
args = tuple(values)
|
||||
return tuple.__new__(cls, args)
|
||||
|
||||
|
||||
def namedtuple(typename, fieldnames):
|
||||
"""
|
||||
>>> import namedtuples
|
||||
@@ -75,24 +77,26 @@ def namedtuple(typename, fieldnames):
|
||||
# Done
|
||||
return new_tuple_type
|
||||
|
||||
|
||||
class AIM:
|
||||
|
||||
class AIMError(Exception):
|
||||
def __init__(self, value):
|
||||
self.parameter = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.parameter)
|
||||
|
||||
def __init__(self, login, transkey, testmode=False):
|
||||
if str(login).strip() == '' or login == None:
|
||||
if str(login).strip() == '' or login is None:
|
||||
raise AIM.AIMError('No login name provided')
|
||||
if str(transkey).strip() == '' or transkey == None:
|
||||
if str(transkey).strip() == '' or transkey is None:
|
||||
raise AIM.AIMError('No transaction key provided')
|
||||
if testmode != True and testmode != False:
|
||||
raise AIM.AIMError('Invalid value for testmode. Must be True or False. "{0}" given.'.format(testmode))
|
||||
|
||||
self.testmode = testmode
|
||||
self.proxy = None;
|
||||
self.proxy = None
|
||||
self.delimiter = '|'
|
||||
self.results = []
|
||||
self.error = True
|
||||
@@ -117,8 +121,9 @@ class AIM:
|
||||
else:
|
||||
url = 'https://secure.authorize.net/gateway/transact.dll'
|
||||
|
||||
if self.proxy == None:
|
||||
self.results += str(urllib.urlopen(url, encoded_args).read()).split(self.delimiter)
|
||||
if self.proxy is None:
|
||||
self.results += str(urllib.urlopen(
|
||||
url, encoded_args).read()).split(self.delimiter)
|
||||
else:
|
||||
opener = urllib.FancyURLopener(self.proxy)
|
||||
opened = opener.open(url, encoded_args)
|
||||
@@ -147,36 +152,37 @@ class AIM:
|
||||
raise AIM.AIMError(self.response.ResponseText)
|
||||
|
||||
def setTransaction(self, creditcard, expiration, total, cvv=None, tax=None, invoice=None):
|
||||
if str(creditcard).strip() == '' or creditcard == None:
|
||||
if str(creditcard).strip() == '' or creditcard is None:
|
||||
raise AIM.AIMError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
|
||||
if str(expiration).strip() == '' or expiration == None:
|
||||
if str(expiration).strip() == '' or expiration is None:
|
||||
raise AIM.AIMError('No expiration number to setTransaction(): {0}'.format(expiration))
|
||||
if str(total).strip() == '' or total == None:
|
||||
if str(total).strip() == '' or total is None:
|
||||
raise AIM.AIMError('No total amount passed to setTransaction(): {0}'.format(total))
|
||||
|
||||
self.setParameter('x_card_num', creditcard)
|
||||
self.setParameter('x_exp_date', expiration)
|
||||
self.setParameter('x_amount', total)
|
||||
if cvv != None:
|
||||
if cvv is not None:
|
||||
self.setParameter('x_card_code', cvv)
|
||||
if tax != None:
|
||||
if tax is not None:
|
||||
self.setParameter('x_tax', tax)
|
||||
if invoice != None:
|
||||
if invoice is not None:
|
||||
self.setParameter('x_invoice_num', invoice)
|
||||
|
||||
def setTransactionType(self, transtype=None):
|
||||
types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE', 'CREDIT', 'CAPTURE_ONLY', 'VOID']
|
||||
types = ['AUTH_CAPTURE', 'AUTH_ONLY', 'PRIOR_AUTH_CAPTURE',
|
||||
'CREDIT', 'CAPTURE_ONLY', 'VOID']
|
||||
if transtype.upper() not in types:
|
||||
raise AIM.AIMError('Incorrect Transaction Type passed to setTransactionType(): {0}'.format(transtype))
|
||||
self.setParameter('x_type', transtype.upper())
|
||||
|
||||
def setProxy(self, proxy=None):
|
||||
if str(proxy).strip() == '' or proxy == None:
|
||||
if str(proxy).strip() == '' or proxy is None:
|
||||
raise AIM.AIMError('No proxy passed to setProxy()')
|
||||
self.proxy = {'http': str(proxy).strip()}
|
||||
|
||||
def setParameter(self, key=None, value=None):
|
||||
if key != None and value != None and str(key).strip() != '' and str(value).strip() != '':
|
||||
if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '':
|
||||
self.parameters[key] = str(value).strip()
|
||||
else:
|
||||
raise AIM.AIMError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
|
||||
@@ -194,10 +200,11 @@ class AIM:
|
||||
responses = ['', 'Approved', 'Declined', 'Error']
|
||||
return responses[int(self.results[0])]
|
||||
|
||||
def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None,
|
||||
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ',testmode=True):
|
||||
payment = AIM(login,transkey,testmode)
|
||||
expiration = expiration.replace('/','')
|
||||
|
||||
def process(creditcard, expiration, total, cvv=None, tax=None, invoice=None,
|
||||
login='cnpdev4289', transkey='SR2P8g4jdEn7vFLQ', testmode=True):
|
||||
payment = AIM(login, transkey, testmode)
|
||||
expiration = expiration.replace('/', '')
|
||||
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
|
||||
try:
|
||||
payment.process()
|
||||
@@ -205,6 +212,7 @@ def process(creditcard,expiration,total,cvv=None,tax=None,invoice=None,
|
||||
except AIM.AIMError:
|
||||
return False
|
||||
|
||||
|
||||
def test():
|
||||
import socket
|
||||
import sys
|
||||
@@ -215,12 +223,14 @@ def test():
|
||||
total = '1.00'
|
||||
cvv = '123'
|
||||
tax = '0.00'
|
||||
invoice = str(time())[4:10] # get a random invoice number
|
||||
invoice = str(time())[4:10] # get a random invoice number
|
||||
|
||||
try:
|
||||
payment = AIM('cnpdev4289', 'SR2P8g4jdEn7vFLQ', True)
|
||||
payment.setTransaction(creditcard, expiration, total, cvv, tax, invoice)
|
||||
payment.setParameter('x_duplicate_window', 180) # three minutes duplicate windows
|
||||
payment.setTransaction(
|
||||
creditcard, expiration, total, cvv, tax, invoice)
|
||||
payment.setParameter(
|
||||
'x_duplicate_window', 180) # three minutes duplicate windows
|
||||
payment.setParameter('x_cust_id', '1324') # customer ID
|
||||
payment.setParameter('x_first_name', 'John')
|
||||
payment.setParameter('x_last_name', 'Conde')
|
||||
@@ -232,7 +242,8 @@ def test():
|
||||
payment.setParameter('x_country', 'US')
|
||||
payment.setParameter('x_phone', '800-555-1234')
|
||||
payment.setParameter('x_description', 'Test Transaction')
|
||||
payment.setParameter('x_customer_ip', socket.gethostbyname(socket.gethostname()))
|
||||
payment.setParameter(
|
||||
'x_customer_ip', socket.gethostbyname(socket.gethostname()))
|
||||
payment.setParameter('x_email', 'john@example.com')
|
||||
payment.setParameter('x_email_customer', False)
|
||||
payment.process()
|
||||
@@ -251,9 +262,9 @@ def test():
|
||||
except AIM.AIMError, e:
|
||||
print "Exception thrown:", e
|
||||
print 'An error occured'
|
||||
print 'approved',payment.isApproved()
|
||||
print 'declined',payment.isDeclined()
|
||||
print 'error',payment.isError()
|
||||
print 'approved', payment.isApproved()
|
||||
print 'declined', payment.isDeclined()
|
||||
print 'error', payment.isError()
|
||||
|
||||
if __name__=='__main__':
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
@@ -15,25 +15,27 @@ __all__ = ['DowCommerce']
|
||||
from operator import itemgetter
|
||||
import urllib
|
||||
|
||||
|
||||
class DowCommerce:
|
||||
|
||||
class DowCommerceError(Exception):
|
||||
def __init__(self, value):
|
||||
self.parameter = value
|
||||
|
||||
def __str__(self):
|
||||
return str(self.parameter)
|
||||
|
||||
def __init__(self, username=None, password=None, demomode=False):
|
||||
if not demomode:
|
||||
if str(username).strip() == '' or username == None:
|
||||
if str(username).strip() == '' or username is None:
|
||||
raise DowCommerce.DowCommerceError('No username provided')
|
||||
if str(password).strip() == '' or password == None:
|
||||
if str(password).strip() == '' or password is None:
|
||||
raise DowCommerce.DowCommerceError('No password provided')
|
||||
else:
|
||||
username = 'demo'
|
||||
password = 'password'
|
||||
|
||||
self.proxy = None;
|
||||
self.proxy = None
|
||||
self.delimiter = '&'
|
||||
self.results = {}
|
||||
self.error = True
|
||||
@@ -45,11 +47,11 @@ class DowCommerce:
|
||||
self.setParameter('username', username)
|
||||
self.setParameter('password', password)
|
||||
|
||||
|
||||
def process(self):
|
||||
encoded_args = urllib.urlencode(self.parameters)
|
||||
if self.proxy == None:
|
||||
results = str(urllib.urlopen(self.url, encoded_args).read()).split(self.delimiter)
|
||||
if self.proxy is None:
|
||||
results = str(urllib.urlopen(
|
||||
self.url, encoded_args).read()).split(self.delimiter)
|
||||
else:
|
||||
opener = urllib.FancyURLopener(self.proxy)
|
||||
opened = opener.open(self.url, encoded_args)
|
||||
@@ -59,7 +61,7 @@ class DowCommerce:
|
||||
opened.close()
|
||||
|
||||
for result in results:
|
||||
(key,val) = result.split('=')
|
||||
(key, val) = result.split('=')
|
||||
self.results[key] = val
|
||||
|
||||
if self.results['response'] == '1':
|
||||
@@ -80,17 +82,18 @@ class DowCommerce:
|
||||
self.declined = False
|
||||
raise DowCommerce.DowCommerceError(self.results)
|
||||
|
||||
def setTransaction(self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
|
||||
ipaddress=None, tax=None, shipping=None,
|
||||
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None,
|
||||
country=None, phone=None, fax=None, emailaddress=None, website=None,
|
||||
shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None,
|
||||
shipping_city=None, shipping_state=None, shipping_zipcode = None, shipping_country=None, shipping_emailaddress=None):
|
||||
if str(creditcard).strip() == '' or creditcard == None:
|
||||
def setTransaction(
|
||||
self, creditcard, expiration, total, cvv=None, orderid=None, orderdescription=None,
|
||||
ipaddress=None, tax=None, shipping=None,
|
||||
firstname=None, lastname=None, company=None, address1=None, address2=None, city=None, state=None, zipcode=None,
|
||||
country=None, phone=None, fax=None, emailaddress=None, website=None,
|
||||
shipping_firstname=None, shipping_lastname=None, shipping_company=None, shipping_address1=None, shipping_address2=None,
|
||||
shipping_city=None, shipping_state=None, shipping_zipcode=None, shipping_country=None, shipping_emailaddress=None):
|
||||
if str(creditcard).strip() == '' or creditcard is None:
|
||||
raise DowCommerce.DowCommerceError('No credit card number passed to setTransaction(): {0}'.format(creditcard))
|
||||
if str(expiration).strip() == '' or expiration == None:
|
||||
if str(expiration).strip() == '' or expiration is None:
|
||||
raise DowCommerce.DowCommerceError('No expiration number passed to setTransaction(): {0}'.format(expiration))
|
||||
if str(total).strip() == '' or total == None:
|
||||
if str(total).strip() == '' or total is None:
|
||||
raise DowCommerce.DowCommerceError('No total amount passed to setTransaction(): {0}'.format(total))
|
||||
|
||||
self.setParameter('ccnumber', creditcard)
|
||||
@@ -165,12 +168,12 @@ class DowCommerce:
|
||||
self.setParameter('type', transtype.lower())
|
||||
|
||||
def setProxy(self, proxy=None):
|
||||
if str(proxy).strip() == '' or proxy == None:
|
||||
if str(proxy).strip() == '' or proxy is None:
|
||||
raise DowCommerce.DowCommerceError('No proxy passed to setProxy()')
|
||||
self.proxy = {'http': str(proxy).strip()}
|
||||
|
||||
def setParameter(self, key=None, value=None):
|
||||
if key != None and value != None and str(key).strip() != '' and str(value).strip() != '':
|
||||
if key is not None and value is not None and str(key).strip() != '' and str(value).strip() != '':
|
||||
self.parameters[key] = str(value).strip()
|
||||
else:
|
||||
raise DowCommerce.DowCommerceError('Incorrect parameters passed to setParameter(): {0}:{1}'.format(key, value))
|
||||
@@ -194,6 +197,7 @@ class DowCommerce:
|
||||
def getResponseText(self):
|
||||
return self.results['responsetext']
|
||||
|
||||
|
||||
def test():
|
||||
import socket
|
||||
import sys
|
||||
@@ -212,13 +216,14 @@ def test():
|
||||
total = '1.00'
|
||||
cvv = '999'
|
||||
tax = '0.00'
|
||||
orderid = str(time())[4:10] # get a random invoice number
|
||||
orderid = str(time())[4:10] # get a random invoice number
|
||||
|
||||
try:
|
||||
payment = DowCommerce(demomode=True)
|
||||
payment.setTransaction(creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction',
|
||||
firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA',
|
||||
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
|
||||
payment.setTransaction(
|
||||
creditcard, expiration, total, cvv=cvv, tax=tax, orderid=orderid, orderdescription='Test Transaction',
|
||||
firstname='John', lastname='Doe', company='Acme', address1='123 Min Street', city='Hometown', state='VA',
|
||||
zipcode='12345', country='US', phone='888-555-1212', emailaddress='john@noemail.local', ipaddress='192.168.1.1')
|
||||
|
||||
payment.process()
|
||||
if payment.isApproved():
|
||||
@@ -231,9 +236,9 @@ def test():
|
||||
except DowCommerce.DowCommerceError, e:
|
||||
print "Exception thrown:", e
|
||||
print 'An error occured'
|
||||
print 'approved',payment.isApproved()
|
||||
print 'declined',payment.isDeclined()
|
||||
print 'error',payment.isError()
|
||||
print 'approved', payment.isApproved()
|
||||
print 'declined', payment.isDeclined()
|
||||
print 'error', payment.isError()
|
||||
|
||||
if __name__=='__main__':
|
||||
if __name__ == '__main__':
|
||||
test()
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
+29
-16
@@ -42,7 +42,9 @@ revision3.com
|
||||
viddler.com
|
||||
"""
|
||||
|
||||
import re, cgi, sys
|
||||
import re
|
||||
import cgi
|
||||
import sys
|
||||
from simplejson import loads
|
||||
import urllib
|
||||
import uuid
|
||||
@@ -75,23 +77,28 @@ EMBED_MAPS = [
|
||||
'http://revision3.com/api/oembed/'),
|
||||
(re.compile('http://\S+.viddler.com/\S+'),
|
||||
'http://lab.viddler.com/services/oembed/'),
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
def image(url):
|
||||
return '<img src="%s" style="max-width:100%%"/>' % url
|
||||
|
||||
|
||||
def audio(url):
|
||||
return '<audio controls="controls" style="max-width:100%%"><source src="%s" /></audio>' % url
|
||||
|
||||
|
||||
def video(url):
|
||||
return '<video controls="controls" style="max-width:100%%"><source src="%s" /></video>' % url
|
||||
|
||||
|
||||
def googledoc_viewer(url):
|
||||
return '<iframe src="http://docs.google.com/viewer?url=%s&embedded=true" style="max-width:100%%"></iframe>' % urllib.quote(url)
|
||||
|
||||
|
||||
def web2py_component(url):
|
||||
code = str(uuid.uuid4())
|
||||
return '<div id="%s"></div><script>\nweb2py_component("%s","%s");\n</script>' % (code,url,code)
|
||||
return '<div id="%s"></div><script>\nweb2py_component("%s","%s");\n</script>' % (code, url, code)
|
||||
|
||||
EXTENSION_MAPS = {
|
||||
'png': image,
|
||||
@@ -126,33 +133,37 @@ EXTENSION_MAPS = {
|
||||
'xps': googledoc_viewer,
|
||||
}
|
||||
|
||||
|
||||
class VimeoURLOpener(urllib.FancyURLopener):
|
||||
"Vimeo blocks the urllib user agent for some reason"
|
||||
version = "Mozilla/4.0"
|
||||
urllib._urlopener = VimeoURLOpener()
|
||||
|
||||
|
||||
def oembed(url):
|
||||
for k,v in EMBED_MAPS:
|
||||
for k, v in EMBED_MAPS:
|
||||
if k.match(url):
|
||||
oembed = v+'?format=json&url='+cgi.escape(url)
|
||||
oembed = v + '?format=json&url=' + cgi.escape(url)
|
||||
try:
|
||||
data = urllib.urlopen(oembed).read()
|
||||
print data
|
||||
return loads(data) # json!
|
||||
return loads(data) # json!
|
||||
except:
|
||||
pass
|
||||
return {}
|
||||
|
||||
|
||||
def extension(url):
|
||||
return url.split('?')[0].split('.')[-1].lower()
|
||||
|
||||
def expand_one(url,cdict):
|
||||
|
||||
def expand_one(url, cdict):
|
||||
# try ombed but first check in cache
|
||||
if cdict and url in cdict:
|
||||
r = cdict[url]
|
||||
else:
|
||||
r = oembed(url)
|
||||
if isinstance(cdict,dict):
|
||||
if isinstance(cdict, dict):
|
||||
cdict[url] = r
|
||||
# if oembed service
|
||||
if 'html' in r:
|
||||
@@ -170,21 +181,23 @@ def expand_one(url,cdict):
|
||||
# else regular link
|
||||
return '<a href="%(u)s">%(u)s</a>' % dict(u=url)
|
||||
|
||||
def expand_html(html,cdict=None):
|
||||
|
||||
def expand_html(html, cdict=None):
|
||||
if not have_soup:
|
||||
raise RuntimeError, "Missing BeautifulSoup"
|
||||
raise RuntimeError("Missing BeautifulSoup")
|
||||
soup = BeautifulSoup(html)
|
||||
comments = soup.findAll(text=lambda text:isinstance(text, Comment))
|
||||
comments = soup.findAll(text=lambda text: isinstance(text, Comment))
|
||||
[comment.extract() for comment in comments]
|
||||
for txt in soup.findAll(text=True):
|
||||
if not txt.parent.name in ('a','script','pre','code','embed','object','audio','video'):
|
||||
if not txt.parent.name in ('a', 'script', 'pre', 'code', 'embed', 'object', 'audio', 'video'):
|
||||
ntxt = regex_link.sub(
|
||||
lambda match: expand_one(match.group(0),cdict), txt)
|
||||
lambda match: expand_one(match.group(0), cdict), txt)
|
||||
txt.replaceWith(BeautifulSoup(ntxt))
|
||||
return str(soup)
|
||||
|
||||
|
||||
def test():
|
||||
example="""
|
||||
example = """
|
||||
<h3>Fringilla nisi parturient nullam</h3>
|
||||
<p>http://www.youtube.com/watch?v=IWBFiI5RrA0</p>
|
||||
<p>http://www.web2py.com/examples/static/images/logo_bw.png</p>
|
||||
@@ -198,8 +211,8 @@ laoreet tortor.</p>
|
||||
"""
|
||||
return expand_html(example)
|
||||
|
||||
if __name__=="__main__":
|
||||
if len(sys.argv)>1:
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
print expand_html(open(sys.argv[1]).read())
|
||||
else:
|
||||
print test()
|
||||
@@ -12,6 +12,7 @@ cache.ram=cache.disk=MemcacheClient(request)
|
||||
import time
|
||||
from google.appengine.api.memcache import Client
|
||||
|
||||
|
||||
class MemcacheClient(object):
|
||||
|
||||
client = Client()
|
||||
@@ -24,12 +25,12 @@ class MemcacheClient(object):
|
||||
key,
|
||||
f,
|
||||
time_expire=300,
|
||||
):
|
||||
):
|
||||
key = '%s/%s' % (self.request.application, key)
|
||||
dt = time_expire
|
||||
value = None
|
||||
obj = self.client.get(key)
|
||||
if obj and (dt == None or obj[0] > time.time() - dt):
|
||||
if obj and (dt is None or obj[0] > time.time() - dt):
|
||||
value = obj[1]
|
||||
elif f is None:
|
||||
if obj:
|
||||
@@ -47,21 +48,21 @@ class MemcacheClient(object):
|
||||
self.client.set(key, (time.time(), value))
|
||||
return value
|
||||
|
||||
def clear(self, key = None):
|
||||
def clear(self, key=None):
|
||||
if key:
|
||||
key = '%s/%s' % (self.request.application, key)
|
||||
self.client.delete(key)
|
||||
else:
|
||||
self.client.flush_all()
|
||||
|
||||
def delete(self,*a,**b):
|
||||
return self.client.delete(*a,**b)
|
||||
def delete(self, *a, **b):
|
||||
return self.client.delete(*a, **b)
|
||||
|
||||
def get(self,*a,**b):
|
||||
return self.client.delete(*a,**b)
|
||||
def get(self, *a, **b):
|
||||
return self.client.delete(*a, **b)
|
||||
|
||||
def set(self,*a,**b):
|
||||
return self.client.delete(*a,**b)
|
||||
def set(self, *a, **b):
|
||||
return self.client.delete(*a, **b)
|
||||
|
||||
def flush_all(self,*a,**b):
|
||||
return self.client.delete(*a,**b)
|
||||
def flush_all(self, *a, **b):
|
||||
return self.client.delete(*a, **b)
|
||||
@@ -51,7 +51,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
|
||||
:param exponent: rate of exponential back-off.
|
||||
"""
|
||||
|
||||
import time, logging
|
||||
import time
|
||||
import logging
|
||||
from google.appengine.api import apiproxy_stub_map
|
||||
from google.appengine.runtime import apiproxy_errors
|
||||
from google.appengine.datastore import datastore_pb
|
||||
@@ -60,8 +61,8 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
|
||||
interval = float(interval)
|
||||
exponent = float(exponent)
|
||||
wrapped = apiproxy_stub_map.MakeSyncCall
|
||||
errors = {datastore_pb.Error.TIMEOUT:'Timeout',
|
||||
datastore_pb.Error.CONCURRENT_TRANSACTION:'TransactionFailedError'}
|
||||
errors = {datastore_pb.Error.TIMEOUT: 'Timeout',
|
||||
datastore_pb.Error.CONCURRENT_TRANSACTION: 'TransactionFailedError'}
|
||||
|
||||
def wrapper(*args, **kwargs):
|
||||
count = 0.0
|
||||
@@ -70,10 +71,12 @@ def autoretry_datastore_timeouts(attempts=5.0, interval=0.1, exponent=2.0):
|
||||
return wrapped(*args, **kwargs)
|
||||
except apiproxy_errors.ApplicationError, err:
|
||||
errno = err.application_error
|
||||
if errno not in errors: raise
|
||||
if errno not in errors:
|
||||
raise
|
||||
sleep = (exponent ** count) * interval
|
||||
count += 1.0
|
||||
if count > attempts: raise
|
||||
if count > attempts:
|
||||
raise
|
||||
msg = "Datastore %s: retry #%d in %s seconds.\n%s"
|
||||
vals = ''
|
||||
if count == 1.0:
|
||||
|
||||
+25
-16
@@ -12,6 +12,7 @@ from gluon.sanitizer import sanitize
|
||||
from gluon.contrib.markmin.markmin2latex import markmin2latex
|
||||
from gluon.contrib.markmin.markmin2pdf import markmin2pdf
|
||||
|
||||
|
||||
def wrapper(f):
|
||||
def g(data):
|
||||
try:
|
||||
@@ -25,39 +26,47 @@ def wrapper(f):
|
||||
raise HTTP(405, '%s error' % e)
|
||||
return g
|
||||
|
||||
|
||||
def latex_from_html(html):
|
||||
markmin=TAG(html).element('body').flatten(markmin_serializer)
|
||||
markmin = TAG(html).element('body').flatten(markmin_serializer)
|
||||
return XML(markmin2latex(markmin))
|
||||
|
||||
|
||||
def pdflatex_from_html(html):
|
||||
if os.system('which pdflatex > /dev/null')==0:
|
||||
markmin=TAG(html).element('body').flatten(markmin_serializer)
|
||||
out,warnings,errors=markmin2pdf(markmin)
|
||||
if os.system('which pdflatex > /dev/null') == 0:
|
||||
markmin = TAG(html).element('body').flatten(markmin_serializer)
|
||||
out, warnings, errors = markmin2pdf(markmin)
|
||||
if errors:
|
||||
current.response.headers['Content-Type']='text/html'
|
||||
raise HTTP(405,HTML(BODY(H1('errors'),
|
||||
UL(*errors),
|
||||
H1('warnings'),
|
||||
UL(*warnings))).xml())
|
||||
current.response.headers['Content-Type'] = 'text/html'
|
||||
raise HTTP(405, HTML(BODY(H1('errors'),
|
||||
UL(*errors),
|
||||
H1('warnings'),
|
||||
UL(*warnings))).xml())
|
||||
else:
|
||||
return XML(out)
|
||||
|
||||
|
||||
def pyfpdf_from_html(html):
|
||||
request = current.request
|
||||
|
||||
def image_map(path):
|
||||
if path.startswith('/%s/static/' % request.application):
|
||||
return os.path.join(request.folder,path.split('/',2)[2])
|
||||
return 'http%s://%s%s' % (request.is_https and 's' or '',request.env.http_host, path)
|
||||
class MyFPDF(FPDF, HTMLMixin): pass
|
||||
pdf=MyFPDF()
|
||||
return os.path.join(request.folder, path.split('/', 2)[2])
|
||||
return 'http%s://%s%s' % (request.is_https and 's' or '', request.env.http_host, path)
|
||||
|
||||
class MyFPDF(FPDF, HTMLMixin):
|
||||
pass
|
||||
pdf = MyFPDF()
|
||||
pdf.add_page()
|
||||
html = sanitize(html, escape=False) #### should have better list of allowed tags
|
||||
pdf.write_html(html,image_map=image_map)
|
||||
html = sanitize(
|
||||
html, escape=False) # should have better list of allowed tags
|
||||
pdf.write_html(html, image_map=image_map)
|
||||
return XML(pdf.output(dest='S'))
|
||||
|
||||
|
||||
def pdf_from_html(html):
|
||||
# try use latex and pdflatex
|
||||
if os.system('which pdflatex > /dev/null')==0:
|
||||
if os.system('which pdflatex > /dev/null') == 0:
|
||||
return pdflatex_from_html(html)
|
||||
else:
|
||||
return pyfpdf_from_html(html)
|
||||
@@ -1,5 +1,6 @@
|
||||
from gluon import XML
|
||||
|
||||
|
||||
def button(merchant_id="123456789012345",
|
||||
products=[dict(name="shoes",
|
||||
quantity=1,
|
||||
@@ -8,8 +9,8 @@ def button(merchant_id="123456789012345",
|
||||
description="running shoes black")]):
|
||||
t = '<input name="item_%(key)s_%(k)s" type="hidden" value="%(value)s"/>'
|
||||
list_products = ''
|
||||
for k,product in enumerate(products):
|
||||
for key,value in product.items():
|
||||
list_products += t % dict(k=k+1,key=key,value=value)
|
||||
for k, product in enumerate(products):
|
||||
for key, value in product.items():
|
||||
list_products += t % dict(k=k + 1, key=key, value=value)
|
||||
button = '<form action="https://checkout.google.com/api/checkout/v2/checkoutForm/Merchant/%s" id="BB_BuyButtonForm" method="post" name="BB_BuyButtonForm" target="_top">%s<input name="_charset_" type="hidden" value="utf-8"/><input alt="" src="https://checkout.google.com/buttons/buy.gif?merchant_id=%s&w=117&h=48&style=white&variant=text&loc=en_US" type="image"/></form>' % (merchant_id, list_products, merchant_id)
|
||||
return XML(button)
|
||||
@@ -1,5 +1,5 @@
|
||||
# this file exists for backward compatibility
|
||||
|
||||
__all__ = ['DAL','Field','drivers','gae']
|
||||
__all__ = ['DAL', 'Field', 'drivers', 'gae']
|
||||
|
||||
from gluon.dal import DAL, Field, Table, Query, Set, Expression, Row, Rows, drivers, BaseAdapter, SQLField, SQLTable, SQLXorable, SQLQuery, SQLSet, SQLRows, SQLStorage, SQLDB, GQLDB, SQLALL, SQLCustomType, gae
|
||||
@@ -22,6 +22,7 @@
|
||||
#########################################################################
|
||||
from gluon import current
|
||||
|
||||
|
||||
class RESIZE(object):
|
||||
def __init__(self, nx=160, ny=80, error_message=' image resize'):
|
||||
(self.nx, self.ny, self.error_message) = (nx, ny, error_message)
|
||||
@@ -43,6 +44,7 @@ class RESIZE(object):
|
||||
else:
|
||||
return (value, None)
|
||||
|
||||
|
||||
def THUMB(image, nx=120, ny=120, gae=False, name='thumb'):
|
||||
if image:
|
||||
if not gae:
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ def basic_auth(server="http://127.0.0.1"):
|
||||
"""
|
||||
|
||||
def basic_login_aux(username,
|
||||
password,
|
||||
server=server):
|
||||
key = base64.b64encode(username+':'+password)
|
||||
password,
|
||||
server=server):
|
||||
key = base64.b64encode(username + ':' + password)
|
||||
headers = {'Authorization': 'Basic ' + key}
|
||||
request = urllib2.Request(server, None, headers)
|
||||
try:
|
||||
@@ -22,5 +22,3 @@ def basic_auth(server="http://127.0.0.1"):
|
||||
except (urllib2.URLError, urllib2.HTTPError):
|
||||
return False
|
||||
return basic_login_aux
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ from gluon.storage import Storage
|
||||
from gluon.tools import fetch
|
||||
import gluon.contrib.simplejson as json
|
||||
|
||||
|
||||
class BrowserID(object):
|
||||
"""
|
||||
from gluon.contrib.login_methods.browserid_account import BrowserID
|
||||
@@ -34,17 +35,17 @@ class BrowserID(object):
|
||||
"""
|
||||
|
||||
def __init__(self,
|
||||
request,
|
||||
audience = "",
|
||||
assertion_post_url = "",
|
||||
prompt = "BrowserID Login",
|
||||
issuer = "browserid.org",
|
||||
verify_url = "https://browserid.org/verify",
|
||||
browserid_js = "https://browserid.org/include.js",
|
||||
browserid_button = "https://browserid.org/i/sign_in_red.png",
|
||||
crypto_js = "https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js",
|
||||
on_login_failure = None,
|
||||
):
|
||||
request,
|
||||
audience="",
|
||||
assertion_post_url="",
|
||||
prompt="BrowserID Login",
|
||||
issuer="browserid.org",
|
||||
verify_url="https://browserid.org/verify",
|
||||
browserid_js="https://browserid.org/include.js",
|
||||
browserid_button="https://browserid.org/i/sign_in_red.png",
|
||||
crypto_js="https://crypto-js.googlecode.com/files/2.2.0-crypto-md5.js",
|
||||
on_login_failure=None,
|
||||
):
|
||||
|
||||
self.request = request
|
||||
self.audience = audience
|
||||
@@ -67,13 +68,13 @@ class BrowserID(object):
|
||||
if request.vars.assertion:
|
||||
audience = self.audience
|
||||
issuer = self.issuer
|
||||
assertion = XML(request.vars.assertion,sanitize=True)
|
||||
verify_data = {'assertion':assertion,'audience':audience}
|
||||
auth_info_json = fetch(self.verify_url,data=verify_data)
|
||||
assertion = XML(request.vars.assertion, sanitize=True)
|
||||
verify_data = {'assertion': assertion, 'audience': audience}
|
||||
auth_info_json = fetch(self.verify_url, data=verify_data)
|
||||
j = json.loads(auth_info_json)
|
||||
epoch_time = int(time.time()*1000) # we need 13 digit epoch time
|
||||
epoch_time = int(time.time() * 1000) # we need 13 digit epoch time
|
||||
if j["status"] == "okay" and j["audience"] == audience and j['issuer'] == issuer and j['expires'] >= epoch_time:
|
||||
return dict(email = j['email'])
|
||||
return dict(email=j['email'])
|
||||
elif self.on_login_failure:
|
||||
redirect('http://google.com')
|
||||
else:
|
||||
@@ -83,9 +84,8 @@ class BrowserID(object):
|
||||
def login_form(self):
|
||||
request = self.request
|
||||
onclick = "javascript:navigator.id.getVerifiedEmail(gotVerifiedEmail) ; return false"
|
||||
form = DIV(SCRIPT(_src=self.browserid_js,_type="text/javascript"),
|
||||
SCRIPT(_src=self.crypto_js,_type="text/javascript"),
|
||||
A(IMG(_src=self.browserid_button,_alt=self.prompt),_href="#",_onclick=onclick,_class="browserid",_title="Login With BrowserID"),
|
||||
SCRIPT(self.asertion_js))
|
||||
form = DIV(SCRIPT(_src=self.browserid_js, _type="text/javascript"),
|
||||
SCRIPT(_src=self.crypto_js, _type="text/javascript"),
|
||||
A(IMG(_src=self.browserid_button, _alt=self.prompt), _href="#", _onclick=onclick, _class="browserid", _title="Login With BrowserID"),
|
||||
SCRIPT(self.asertion_js))
|
||||
return form
|
||||
|
||||
@@ -11,7 +11,8 @@ Tinkered by Szabolcs Gyuris < szimszo n @ o regpreshaz dot eu>
|
||||
|
||||
from gluon import current, redirect
|
||||
|
||||
class CasAuth( object ):
|
||||
|
||||
class CasAuth(object):
|
||||
"""
|
||||
Login will be done via Web2py's CAS application, instead of web2py's
|
||||
login form.
|
||||
@@ -39,101 +40,105 @@ class CasAuth( object ):
|
||||
user's username.
|
||||
|
||||
"""
|
||||
def __init__(self, g=None, ### g for backward compatibility ###
|
||||
urlbase = "https://web2py.com/cas/cas",
|
||||
actions=['login','validate','logout'],
|
||||
maps=dict(username=lambda v:v.get('username',v['user']),
|
||||
email=lambda v:v.get('email',None),
|
||||
user_id=lambda v:v['user']),
|
||||
casversion = 1,
|
||||
casusername = 'cas:user'
|
||||
def __init__(self, g=None, # g for backward compatibility ###
|
||||
urlbase="https://web2py.com/cas/cas",
|
||||
actions=['login', 'validate', 'logout'],
|
||||
maps=dict(username=lambda v: v.get('username', v['user']),
|
||||
email=lambda v: v.get('email', None),
|
||||
user_id=lambda v: v['user']),
|
||||
casversion=1,
|
||||
casusername='cas:user'
|
||||
):
|
||||
self.urlbase=urlbase
|
||||
self.cas_login_url="%s/%s"%(self.urlbase,actions[0])
|
||||
self.cas_check_url="%s/%s"%(self.urlbase,actions[1])
|
||||
self.cas_logout_url="%s/%s"%(self.urlbase,actions[2])
|
||||
self.maps=maps
|
||||
self.urlbase = urlbase
|
||||
self.cas_login_url = "%s/%s" % (self.urlbase, actions[0])
|
||||
self.cas_check_url = "%s/%s" % (self.urlbase, actions[1])
|
||||
self.cas_logout_url = "%s/%s" % (self.urlbase, actions[2])
|
||||
self.maps = maps
|
||||
self.casversion = casversion
|
||||
self.casusername = casusername
|
||||
http_host=current.request.env.http_x_forwarded_host
|
||||
if not http_host: http_host=current.request.env.http_host
|
||||
if current.request.env.wsgi_url_scheme in [ 'https', 'HTTPS' ]:
|
||||
http_host = current.request.env.http_x_forwarded_host
|
||||
if not http_host:
|
||||
http_host = current.request.env.http_host
|
||||
if current.request.env.wsgi_url_scheme in ['https', 'HTTPS']:
|
||||
scheme = 'https'
|
||||
else:
|
||||
scheme = 'http'
|
||||
self.cas_my_url='%s://%s%s'%( scheme, http_host, current.request.env.path_info )
|
||||
self.cas_my_url = '%s://%s%s' % (
|
||||
scheme, http_host, current.request.env.path_info)
|
||||
|
||||
def login_url( self, next = "/" ):
|
||||
current.session.token=self._CAS_login()
|
||||
def login_url(self, next="/"):
|
||||
current.session.token = self._CAS_login()
|
||||
return next
|
||||
def logout_url( self, next = "/" ):
|
||||
current.session.token=None
|
||||
current.session.auth=None
|
||||
|
||||
def logout_url(self, next="/"):
|
||||
current.session.token = None
|
||||
current.session.auth = None
|
||||
self._CAS_logout()
|
||||
return next
|
||||
def get_user( self ):
|
||||
user=current.session.token
|
||||
|
||||
def get_user(self):
|
||||
user = current.session.token
|
||||
if user:
|
||||
d = {'source':'web2py cas'}
|
||||
d = {'source': 'web2py cas'}
|
||||
for key in self.maps:
|
||||
d[key]=self.maps[key](user)
|
||||
d[key] = self.maps[key](user)
|
||||
return d
|
||||
return None
|
||||
def _CAS_login( self ):
|
||||
|
||||
def _CAS_login(self):
|
||||
"""
|
||||
exposed as CAS.login(request)
|
||||
returns a token on success, None on failed authentication
|
||||
"""
|
||||
import urllib
|
||||
self.ticket=current.request.vars.ticket
|
||||
self.ticket = current.request.vars.ticket
|
||||
if not current.request.vars.ticket:
|
||||
redirect( "%s?service=%s"% (self.cas_login_url,
|
||||
redirect("%s?service=%s" % (self.cas_login_url,
|
||||
self.cas_my_url))
|
||||
else:
|
||||
url="%s?service=%s&ticket=%s" % (self.cas_check_url,
|
||||
self.cas_my_url,
|
||||
self.ticket )
|
||||
data=urllib.urlopen( url ).read()
|
||||
url = "%s?service=%s&ticket=%s" % (self.cas_check_url,
|
||||
self.cas_my_url,
|
||||
self.ticket)
|
||||
data = urllib.urlopen(url).read()
|
||||
if data.startswith('yes') or data.startswith('no'):
|
||||
data = data.split('\n')
|
||||
if data[0]=='yes':
|
||||
if ':' in data[1]: # for Compatibility with Custom CAS
|
||||
if data[0] == 'yes':
|
||||
if ':' in data[1]: # for Compatibility with Custom CAS
|
||||
items = data[1].split(':')
|
||||
a = items[0]
|
||||
b = len(items)>1 and items[1] or a
|
||||
c = len(items)>2 and items[2] or b
|
||||
b = len(items) > 1 and items[1] or a
|
||||
c = len(items) > 2 and items[2] or b
|
||||
else:
|
||||
a = b = c = data[1]
|
||||
return dict(user=a,email=b,username=c)
|
||||
return dict(user=a, email=b, username=c)
|
||||
return None
|
||||
import xml.dom.minidom as dom
|
||||
import xml.parsers.expat as expat
|
||||
try:
|
||||
dxml=dom.parseString(data)
|
||||
envelop = dxml.getElementsByTagName("cas:authenticationSuccess")
|
||||
if len(envelop)>0:
|
||||
dxml = dom.parseString(data)
|
||||
envelop = dxml.getElementsByTagName(
|
||||
"cas:authenticationSuccess")
|
||||
if len(envelop) > 0:
|
||||
res = dict()
|
||||
for x in envelop[0].childNodes:
|
||||
if x.nodeName.startswith('cas:') and len(x.childNodes):
|
||||
key = x.nodeName[4:].encode('utf8')
|
||||
value = x.childNodes[0].nodeValue.encode('utf8')
|
||||
if not key in res:
|
||||
res[key]=value
|
||||
res[key] = value
|
||||
else:
|
||||
if not isinstance(res[key],list):
|
||||
res[key]=[res[key]]
|
||||
if not isinstance(res[key], list):
|
||||
res[key] = [res[key]]
|
||||
res[key].append(value)
|
||||
return res
|
||||
except expat.ExpatError: pass
|
||||
return None # fallback
|
||||
except expat.ExpatError:
|
||||
pass
|
||||
return None # fallback
|
||||
|
||||
|
||||
def _CAS_logout( self ):
|
||||
def _CAS_logout(self):
|
||||
"""
|
||||
exposed CAS.logout()
|
||||
redirects to the CAS logout page
|
||||
"""
|
||||
import urllib
|
||||
redirect("%s?service=%s" % (self.cas_logout_url,self.cas_my_url))
|
||||
|
||||
|
||||
redirect("%s?service=%s" % (self.cas_logout_url, self.cas_my_url))
|
||||
@@ -18,11 +18,13 @@ from gluon.tools import fetch
|
||||
from gluon.storage import Storage
|
||||
import gluon.contrib.simplejson as json
|
||||
|
||||
|
||||
class DropboxAccount(object):
|
||||
|
||||
"""
|
||||
from gluon.contrib.login_methods.dropbox_account import DropboxAccount
|
||||
auth.settings.actions_disabled=['register','change_password','request_reset_password']
|
||||
auth.settings.actions_disabled=['register','change_password',
|
||||
'request_reset_password']
|
||||
auth.settings.login_form = DropboxAccount(request,
|
||||
key="...",
|
||||
secret="...",
|
||||
@@ -34,47 +36,45 @@ class DropboxAccount(object):
|
||||
|
||||
def __init__(self,
|
||||
request,
|
||||
key = "",
|
||||
secret = "",
|
||||
key="",
|
||||
secret="",
|
||||
access_type="app_folder",
|
||||
login_url = "",
|
||||
login_url="",
|
||||
on_login_failure=None,
|
||||
):
|
||||
|
||||
self.request=request
|
||||
self.key=key
|
||||
self.secret=secret
|
||||
self.access_type=access_type
|
||||
|
||||
self.request = request
|
||||
self.key = key
|
||||
self.secret = secret
|
||||
self.access_type = access_type
|
||||
self.login_url = login_url
|
||||
self.on_login_failure = on_login_failure
|
||||
self.sess = session.DropboxSession(
|
||||
self.key,self.secret,self.access_type)
|
||||
|
||||
self.key, self.secret, self.access_type)
|
||||
|
||||
def get_user(self):
|
||||
request = self.request
|
||||
if not current.session.dropbox_request_token:
|
||||
return None
|
||||
elif not current.session.dropbox_access_token:
|
||||
|
||||
request_token = current.session.dropbox_request_token
|
||||
self.sess.set_request_token(request_token[0],request_token[1])
|
||||
|
||||
request_token = current.session.dropbox_request_token
|
||||
self.sess.set_request_token(request_token[0], request_token[1])
|
||||
access_token = self.sess.obtain_access_token(self.sess.token)
|
||||
current.session.dropbox_access_token = \
|
||||
(access_token.key,access_token.secret)
|
||||
(access_token.key, access_token.secret)
|
||||
else:
|
||||
access_token = current.session.dropbox_access_token
|
||||
self.sess.set_token(access_token[0],access_token[1])
|
||||
self.sess.set_token(access_token[0], access_token[1])
|
||||
|
||||
|
||||
user = Storage()
|
||||
self.client = client.DropboxClient(self.sess)
|
||||
data = self.client.account_info()
|
||||
display_name = data.get('display_name','').split(' ',1)
|
||||
user = dict(email = data.get('email',None),
|
||||
first_name = display_name[0],
|
||||
last_name = display_name[-1],
|
||||
registration_id = data.get('uid',None))
|
||||
display_name = data.get('display_name', '').split(' ', 1)
|
||||
user = dict(email=data.get('email', None),
|
||||
first_name=display_name[0],
|
||||
last_name=display_name[-1],
|
||||
registration_id=data.get('uid', None))
|
||||
if not user['registration_id'] and self.on_login_failure:
|
||||
redirect(self.on_login_failure)
|
||||
return user
|
||||
@@ -83,7 +83,7 @@ class DropboxAccount(object):
|
||||
|
||||
request_token = self.sess.obtain_request_token()
|
||||
current.session.dropbox_request_token = \
|
||||
(request_token.key,request_token.secret)
|
||||
(request_token.key, request_token.secret)
|
||||
dropbox_url = self.sess.build_authorize_url(request_token,
|
||||
self.login_url)
|
||||
redirect(dropbox_url)
|
||||
@@ -93,29 +93,32 @@ class DropboxAccount(object):
|
||||
_style="width:400px;height:240px;")
|
||||
return form
|
||||
|
||||
def logout_url(self, next = "/"):
|
||||
current.session.dropbox_request_token=None
|
||||
current.session.auth=None
|
||||
def logout_url(self, next="/"):
|
||||
current.session.dropbox_request_token = None
|
||||
current.session.auth = None
|
||||
redirect('https://www.dropbox.com/logout')
|
||||
return next
|
||||
def put(self,filename,file):
|
||||
return json.loads(self.client.put_file(filename,file))['bytes']
|
||||
def get(self,filename,file):
|
||||
|
||||
def put(self, filename, file):
|
||||
return json.loads(self.client.put_file(filename, file))['bytes']
|
||||
|
||||
def get(self, filename, file):
|
||||
return self.client.get_file(filename)
|
||||
def dir(self,path):
|
||||
|
||||
def dir(self, path):
|
||||
return json.loads(self.client.metadata(path))
|
||||
|
||||
def use_dropbox(auth,filename='private/dropbox.key',**kwargs):
|
||||
path = os.path.join(current.request.folder,filename)
|
||||
|
||||
def use_dropbox(auth, filename='private/dropbox.key', **kwargs):
|
||||
path = os.path.join(current.request.folder, filename)
|
||||
if os.path.exists(path):
|
||||
request = current.request
|
||||
key,secret,access_type = open(path,'r').read().strip().split(':')
|
||||
key, secret, access_type = open(path, 'r').read().strip().split(':')
|
||||
host = current.request.env.http_host
|
||||
login_url = "http://%s/%s/default/user/login" % \
|
||||
(host,request.application)
|
||||
(host, request.application)
|
||||
auth.settings.actions_disabled = \
|
||||
['register','change_password','request_reset_password']
|
||||
['register', 'change_password', 'request_reset_password']
|
||||
auth.settings.login_form = DropboxAccount(
|
||||
request,key=key,secret=secret,access_type=access_type,
|
||||
login_url = login_url,**kwargs)
|
||||
|
||||
request, key=key, secret=secret, access_type=access_type,
|
||||
login_url=login_url, **kwargs)
|
||||
@@ -1,6 +1,7 @@
|
||||
import smtplib
|
||||
import logging
|
||||
|
||||
|
||||
def email_auth(server="smtp.gmail.com:587",
|
||||
domain="@gmail.com",
|
||||
tls_mode=None):
|
||||
@@ -17,9 +18,9 @@ def email_auth(server="smtp.gmail.com:587",
|
||||
domain=domain,
|
||||
tls_mode=tls_mode):
|
||||
if domain:
|
||||
if not isinstance(domain,(list,tuple)):
|
||||
domain=[str(domain)]
|
||||
if not [d for d in domain if email[-len(d):]==d]:
|
||||
if not isinstance(domain, (list, tuple)):
|
||||
domain = [str(domain)]
|
||||
if not [d for d in domain if email[-len(d):] == d]:
|
||||
return False
|
||||
(host, port) = server.split(':')
|
||||
if tls_mode is None: # then auto detect
|
||||
@@ -43,4 +44,3 @@ def email_auth(server="smtp.gmail.com:587",
|
||||
pass
|
||||
return False
|
||||
return email_auth_aux
|
||||
|
||||
@@ -8,6 +8,7 @@ So user can choose the built-in login or extended login methods.
|
||||
|
||||
from gluon import current, DIV
|
||||
|
||||
|
||||
class ExtendedLoginForm(object):
|
||||
"""
|
||||
Put extended_login_form under web2py/gluon/contrib/login_methods folder.
|
||||
@@ -22,7 +23,8 @@ class ExtendedLoginForm(object):
|
||||
api_key="...",
|
||||
domain="...",
|
||||
url = "http://localhost:8000/%s/default/user/login" % request.application)
|
||||
extended_login_form = ExtendedLoginForm(auth, alt_login_form, signals=['token'])
|
||||
extended_login_form = ExtendedLoginForm(
|
||||
auth, alt_login_form, signals=['token'])
|
||||
|
||||
auth.settings.login_form = extended_login_form
|
||||
|
||||
@@ -37,7 +39,7 @@ class ExtendedLoginForm(object):
|
||||
auth,
|
||||
alt_login_form,
|
||||
signals=[],
|
||||
login_arg = 'login'
|
||||
login_arg='login'
|
||||
):
|
||||
self.auth = auth
|
||||
self.alt_login_form = alt_login_form
|
||||
@@ -50,7 +52,7 @@ class ExtendedLoginForm(object):
|
||||
"""
|
||||
if hasattr(self.alt_login_form, 'get_user'):
|
||||
return self.alt_login_form.get_user()
|
||||
return None # let gluon.tools.Auth.get_or_create_user do the rest
|
||||
return None # let gluon.tools.Auth.get_or_create_user do the rest
|
||||
|
||||
def login_url(self, next):
|
||||
"""
|
||||
@@ -91,8 +93,8 @@ class ExtendedLoginForm(object):
|
||||
args = request.args
|
||||
|
||||
if (self.signals and
|
||||
any([True for signal in self.signals if request.vars.has_key(signal)])
|
||||
):
|
||||
any([True for signal in self.signals if signal in request.vars])
|
||||
):
|
||||
return self.alt_login_form.login_form()
|
||||
|
||||
self.auth.settings.login_form = self.auth
|
||||
@@ -101,5 +103,3 @@ class ExtendedLoginForm(object):
|
||||
|
||||
form.components.append(self.alt_login_form.login_form())
|
||||
return form
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ Thanks to Hans Donner <hans.donner@pobox.com> for GaeGoogleAccount.
|
||||
|
||||
from google.appengine.api import users
|
||||
|
||||
|
||||
class GaeGoogleAccount(object):
|
||||
"""
|
||||
Login will be done via Google's Appengine login object, instead of web2py's
|
||||
@@ -35,5 +36,3 @@ class GaeGoogleAccount(object):
|
||||
if user:
|
||||
return dict(nickname=user.nickname(), email=user.email(),
|
||||
user_id=user.user_id(), source="google account")
|
||||
|
||||
|
||||
@@ -188,18 +188,23 @@ def ldap_auth(server='ldap', port=None,
|
||||
str(custom_scope), str(manage_groups)))
|
||||
if manage_user:
|
||||
if user_firstname_attrib.count(':') > 0:
|
||||
(user_firstname_attrib, user_firstname_part) = user_firstname_attrib.split(':', 1)
|
||||
(user_firstname_attrib,
|
||||
user_firstname_part) = user_firstname_attrib.split(':', 1)
|
||||
user_firstname_part = (int(user_firstname_part) - 1)
|
||||
else:
|
||||
user_firstname_part = None
|
||||
if user_lastname_attrib.count(':') > 0:
|
||||
(user_lastname_attrib, user_lastname_part) = user_lastname_attrib.split(':', 1)
|
||||
(user_lastname_attrib,
|
||||
user_lastname_part) = user_lastname_attrib.split(':', 1)
|
||||
user_lastname_part = (int(user_lastname_part) - 1)
|
||||
else:
|
||||
user_lastname_part = None
|
||||
user_firstname_attrib = ldap.filter.escape_filter_chars(user_firstname_attrib)
|
||||
user_lastname_attrib = ldap.filter.escape_filter_chars(user_lastname_attrib)
|
||||
user_mail_attrib = ldap.filter.escape_filter_chars(user_mail_attrib)
|
||||
user_firstname_attrib = ldap.filter.escape_filter_chars(
|
||||
user_firstname_attrib)
|
||||
user_lastname_attrib = ldap.filter.escape_filter_chars(
|
||||
user_lastname_attrib)
|
||||
user_mail_attrib = ldap.filter.escape_filter_chars(
|
||||
user_mail_attrib)
|
||||
try:
|
||||
if allowed_groups:
|
||||
if not is_user_in_allowed_groups(username, password):
|
||||
@@ -310,7 +315,8 @@ def ldap_auth(server='ldap', port=None,
|
||||
basedns = ldap_basedn
|
||||
else:
|
||||
basedns = [ldap_basedn]
|
||||
filter = '(&(uid=%s)(%s))' % (ldap.filter.escape_filter_chars(username), filterstr)
|
||||
filter = '(&(uid=%s)(%s))' % (
|
||||
ldap.filter.escape_filter_chars(username), filterstr)
|
||||
found = False
|
||||
for basedn in basedns:
|
||||
try:
|
||||
@@ -338,7 +344,8 @@ def ldap_auth(server='ldap', port=None,
|
||||
else:
|
||||
basedns = [ldap_basedn]
|
||||
filter = '(&(%s=%s)(%s))' % (username_attrib,
|
||||
ldap.filter.escape_filter_chars(username),
|
||||
ldap.filter.escape_filter_chars(
|
||||
username),
|
||||
filterstr)
|
||||
if custom_scope == 'subtree':
|
||||
ldap_scope = ldap.SCOPE_SUBTREE
|
||||
@@ -368,14 +375,16 @@ def ldap_auth(server='ldap', port=None,
|
||||
logger.info('[%s] Manage user data' % str(username))
|
||||
try:
|
||||
if user_firstname_part is not None:
|
||||
store_user_firstname = result[user_firstname_attrib][0].split(' ', 1)[user_firstname_part]
|
||||
store_user_firstname = result[user_firstname_attrib][
|
||||
0].split(' ', 1)[user_firstname_part]
|
||||
else:
|
||||
store_user_firstname = result[user_firstname_attrib][0]
|
||||
except KeyError, e:
|
||||
store_user_firstname = None
|
||||
try:
|
||||
if user_lastname_part is not None:
|
||||
store_user_lastname = result[user_lastname_attrib][0].split(' ', 1)[user_lastname_part]
|
||||
store_user_lastname = result[user_lastname_attrib][
|
||||
0].split(' ', 1)[user_lastname_part]
|
||||
else:
|
||||
store_user_lastname = result[user_lastname_attrib][0]
|
||||
except KeyError, e:
|
||||
@@ -464,16 +473,19 @@ def ldap_auth(server='ldap', port=None,
|
||||
#
|
||||
# Get all group name where the user is in actually in ldap
|
||||
# #########################################################
|
||||
ldap_groups_of_the_user = get_user_groups_from_ldap(username, password)
|
||||
ldap_groups_of_the_user = get_user_groups_from_ldap(
|
||||
username, password)
|
||||
|
||||
#
|
||||
# Get all group name where the user is in actually in local db
|
||||
# #############################################################
|
||||
try:
|
||||
db_user_id = db(db.auth_user.username == username).select(db.auth_user.id).first().id
|
||||
db_user_id = db(db.auth_user.username == username).select(
|
||||
db.auth_user.id).first().id
|
||||
except:
|
||||
try:
|
||||
db_user_id = db(db.auth_user.email == username).select(db.auth_user.id).first().id
|
||||
db_user_id = db(db.auth_user.email == username).select(
|
||||
db.auth_user.id).first().id
|
||||
except AttributeError, e:
|
||||
#
|
||||
# There is no user in local db
|
||||
@@ -486,7 +498,8 @@ def ldap_auth(server='ldap', port=None,
|
||||
db_user_id = db.auth_user.insert(email=username,
|
||||
first_name=username)
|
||||
if not db_user_id:
|
||||
logging.error('There is no username or email for %s!' % username)
|
||||
logging.error(
|
||||
'There is no username or email for %s!' % username)
|
||||
raise
|
||||
db_group_search = db((db.auth_membership.user_id == db_user_id) &
|
||||
(db.auth_user.id == db.auth_membership.user_id) &
|
||||
@@ -520,7 +533,8 @@ def ldap_auth(server='ldap', port=None,
|
||||
gid = db.auth_group.insert(role=group_to_add,
|
||||
description='Generated from LDAP')
|
||||
else:
|
||||
gid = db(db.auth_group.role == group_to_add).select(db.auth_group.id).first().id
|
||||
gid = db(db.auth_group.role == group_to_add).select(
|
||||
db.auth_group.id).first().id
|
||||
db.auth_membership.insert(user_id=db_user_id,
|
||||
group_id=gid)
|
||||
except:
|
||||
@@ -634,4 +648,3 @@ def ldap_auth(server='ldap', port=None,
|
||||
if filterstr[0] == '(' and filterstr[-1] == ')': # rfc4515 syntax
|
||||
filterstr = filterstr[1:-1] # parens added again where used
|
||||
return ldap_auth_aux
|
||||
|
||||
@@ -14,7 +14,8 @@ from gluon.http import HTTP
|
||||
try:
|
||||
import linkedin
|
||||
except ImportError:
|
||||
raise HTTP(400,"linkedin module not found")
|
||||
raise HTTP(400, "linkedin module not found")
|
||||
|
||||
|
||||
class LinkedInAccount(object):
|
||||
"""
|
||||
@@ -28,9 +29,9 @@ class LinkedInAccount(object):
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self,request,key,secret,return_url):
|
||||
def __init__(self, request, key, secret, return_url):
|
||||
self.request = request
|
||||
self.api = linkedin.LinkedIn(key,secret,return_url)
|
||||
self.api = linkedin.LinkedIn(key, secret, return_url)
|
||||
self.token = result = self.api.requestToken()
|
||||
|
||||
def login_url(self, next="/"):
|
||||
@@ -40,13 +41,12 @@ class LinkedInAccount(object):
|
||||
return ''
|
||||
|
||||
def get_user(self):
|
||||
result = self.request.vars.verifier and self.api.accessToken(verifier = self.request.vars.verifier )
|
||||
result = self.request.vars.verifier and self.api.accessToken(
|
||||
verifier=self.request.vars.verifier)
|
||||
if result:
|
||||
profile = self.api.GetProfile()
|
||||
profile = self.api.GetProfile(profile).public_url = "http://www.linkedin.com/in/ozgurv"
|
||||
return dict(first_name = profile.first_name,
|
||||
last_name = profile.last_name,
|
||||
username = profile.id)
|
||||
|
||||
|
||||
|
||||
profile = self.api.GetProfile(
|
||||
profile).public_url = "http://www.linkedin.com/in/ozgurv"
|
||||
return dict(first_name=profile.first_name,
|
||||
last_name=profile.last_name,
|
||||
username=profile.id)
|
||||
@@ -13,6 +13,7 @@ from gluon.tools import fetch
|
||||
from gluon.storage import Storage
|
||||
import gluon.contrib.simplejson as json
|
||||
|
||||
|
||||
class Loginza(object):
|
||||
|
||||
"""
|
||||
@@ -23,13 +24,13 @@ class Loginza(object):
|
||||
|
||||
def __init__(self,
|
||||
request,
|
||||
url = "",
|
||||
embed = True,
|
||||
auth_url = "http://loginza.ru/api/authinfo",
|
||||
language = "en",
|
||||
prompt = "loginza",
|
||||
on_login_failure = None,
|
||||
):
|
||||
url="",
|
||||
embed=True,
|
||||
auth_url="http://loginza.ru/api/authinfo",
|
||||
language="en",
|
||||
prompt="loginza",
|
||||
on_login_failure=None,
|
||||
):
|
||||
|
||||
self.request = request
|
||||
self.token_url = url
|
||||
@@ -46,49 +47,50 @@ class Loginza(object):
|
||||
# FIXME: what if email is unique=True
|
||||
|
||||
self.mappings["http://twitter.com/"] = lambda profile:\
|
||||
dict(registration_id = profile.get("identity",""),
|
||||
username = profile.get("nickname",""),
|
||||
email = profile.get("email",""),
|
||||
last_name = profile.get("name","").get("full_name",""),
|
||||
dict(registration_id=profile.get("identity", ""),
|
||||
username=profile.get("nickname", ""),
|
||||
email=profile.get("email", ""),
|
||||
last_name=profile.get("name", "").get("full_name", ""),
|
||||
#avatar = profile.get("photo",""),
|
||||
)
|
||||
)
|
||||
self.mappings["https://www.google.com/accounts/o8/ud"] = lambda profile:\
|
||||
dict(registration_id = profile.get("identity",""),
|
||||
username = profile.get("name","").get("full_name",""),
|
||||
email = profile.get("email",""),
|
||||
first_name = profile.get("name","").get("first_name",""),
|
||||
last_name = profile.get("name","").get("last_name",""),
|
||||
dict(registration_id=profile.get("identity", ""),
|
||||
username=profile.get("name", "").get("full_name", ""),
|
||||
email=profile.get("email", ""),
|
||||
first_name=profile.get("name", "").get("first_name", ""),
|
||||
last_name=profile.get("name", "").get("last_name", ""),
|
||||
#avatar = profile.get("photo",""),
|
||||
)
|
||||
)
|
||||
self.mappings["http://vkontakte.ru/"] = lambda profile:\
|
||||
dict(registration_id=profile.get("identity",""),
|
||||
username = profile.get("name","").get("full_name",""),
|
||||
email = profile.get("email",""),
|
||||
first_name = profile.get("name","").get("first_name",""),
|
||||
last_name = profile.get("name","").get("last_name",""),
|
||||
dict(registration_id=profile.get("identity", ""),
|
||||
username=profile.get("name", "").get("full_name", ""),
|
||||
email=profile.get("email", ""),
|
||||
first_name=profile.get("name", "").get("first_name", ""),
|
||||
last_name=profile.get("name", "").get("last_name", ""),
|
||||
#avatar = profile.get("photo",""),
|
||||
)
|
||||
)
|
||||
self.mappings.default = lambda profile:\
|
||||
dict(registration_id = profile.get("identity",""),
|
||||
username = profile.get("name","").get("full_name"),
|
||||
email = profile.get("email",""),
|
||||
first_name = profile.get("name","").get("first_name",""),
|
||||
last_name = profile.get("name","").get("last_name",""),
|
||||
dict(registration_id=profile.get("identity", ""),
|
||||
username=profile.get("name", "").get("full_name"),
|
||||
email=profile.get("email", ""),
|
||||
first_name=profile.get("name", "").get("first_name", ""),
|
||||
last_name=profile.get("name", "").get("last_name", ""),
|
||||
#avatar = profile.get("photo",""),
|
||||
)
|
||||
)
|
||||
|
||||
def get_user(self):
|
||||
request = self.request
|
||||
if request.vars.token:
|
||||
user = Storage()
|
||||
data = urllib.urlencode(dict(token = request.vars.token))
|
||||
auth_info_json = fetch(self.auth_url+'?'+data)
|
||||
data = urllib.urlencode(dict(token=request.vars.token))
|
||||
auth_info_json = fetch(self.auth_url + '?' + data)
|
||||
#print auth_info_json
|
||||
auth_info = json.loads(auth_info_json)
|
||||
if auth_info["identity"] != None:
|
||||
if auth_info["identity"] is not None:
|
||||
self.profile = auth_info
|
||||
provider = self.profile["provider"]
|
||||
user = self.mappings.get(provider, self.mappings.default)(self.profile)
|
||||
user = self.mappings.get(
|
||||
provider, self.mappings.default)(self.profile)
|
||||
#user["password"] = ???
|
||||
#user["avatar"] = ???
|
||||
return user
|
||||
@@ -106,8 +108,8 @@ class Loginza(object):
|
||||
_frameborder="no",
|
||||
_style="width:359px;height:300px;")
|
||||
else:
|
||||
form = DIV(A(self.prompt, _href=LOGINZA_URL % (self.language, self.token_url), _class="loginza"),
|
||||
SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript"))
|
||||
form = DIV(
|
||||
A(self.prompt, _href=LOGINZA_URL % (
|
||||
self.language, self.token_url), _class="loginza"),
|
||||
SCRIPT(_src="https://s3-eu-west-1.amazonaws.com/s1.loginza.ru/js/widget.js", _type="text/javascript"))
|
||||
return form
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import time
|
||||
from hashlib import md5
|
||||
from gluon.dal import DAL
|
||||
|
||||
|
||||
def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
||||
time_offset=60):
|
||||
|
||||
@@ -44,7 +45,8 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
||||
writable=False, readable=False, default=''))
|
||||
|
||||
##validators
|
||||
custom_auth_table = db[auth.settings.table_user_name] # get the custom_auth_table
|
||||
custom_auth_table = db[auth.settings.table_user_name]
|
||||
# get the custom_auth_table
|
||||
custom_auth_table.first_name.requires = \
|
||||
IS_NOT_EMPTY(error_message=auth.messages.is_empty)
|
||||
custom_auth_table.last_name.requires = \
|
||||
@@ -76,14 +78,15 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
||||
- as of now user field is hardcoded to email. Some way of selecting user table and user field.
|
||||
"""
|
||||
|
||||
def verify_otp(otp,pin,secret,offset=60):
|
||||
def verify_otp(otp, pin, secret, offset=60):
|
||||
epoch_time = int(time.time())
|
||||
time_start = int(str(epoch_time - offset)[:-1])
|
||||
time_end = int(str(epoch_time + offset)[:-1])
|
||||
for t in range(time_start-1,time_end+1):
|
||||
to_hash = str(t)+secret+pin
|
||||
for t in range(time_start - 1, time_end + 1):
|
||||
to_hash = str(t) + secret + pin
|
||||
hash = md5(to_hash).hexdigest()[:6]
|
||||
if otp == hash: return True
|
||||
if otp == hash:
|
||||
return True
|
||||
return False
|
||||
|
||||
def motp_auth_aux(email,
|
||||
@@ -91,15 +94,18 @@ def motp_auth(db=DAL('sqlite://storage.sqlite'),
|
||||
db=db,
|
||||
offset=time_offset):
|
||||
if db:
|
||||
user_data = db(db.auth_user.email == email ).select().first()
|
||||
user_data = db(db.auth_user.email == email).select().first()
|
||||
if user_data:
|
||||
if user_data['motp_secret'] and user_data['motp_pin']:
|
||||
motp_secret = user_data['motp_secret']
|
||||
motp_pin = user_data['motp_pin']
|
||||
otp_check = verify_otp(password,motp_pin,motp_secret,offset=offset)
|
||||
if otp_check: return True
|
||||
else: return False
|
||||
else: return False
|
||||
otp_check = verify_otp(
|
||||
password, motp_pin, motp_secret, offset=offset)
|
||||
if otp_check:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
return False
|
||||
return motp_auth_aux
|
||||
|
||||
@@ -19,6 +19,7 @@ from urllib2 import urlopen
|
||||
import urllib2
|
||||
from urllib import urlencode
|
||||
|
||||
|
||||
class OAuthAccount(object):
|
||||
"""
|
||||
Login will be done via OAuth Framework, instead of web2py's
|
||||
@@ -51,7 +52,8 @@ class OAuthAccount(object):
|
||||
TOKEN_URL="..."
|
||||
ACCESS_TOKEN_URL="..."
|
||||
from gluon.contrib.login_methods.oauth10a_account import OAuthAccount
|
||||
auth.settings.login_form=OAuthAccount(globals(),CLIENT_ID,CLIENT_SECRET, AUTH_URL, TOKEN_URL, ACCESS_TOKEN_URL)
|
||||
auth.settings.login_form=OAuthAccount(globals(
|
||||
),CLIENT_ID,CLIENT_SECRET, AUTH_URL, TOKEN_URL, ACCESS_TOKEN_URL)
|
||||
|
||||
"""
|
||||
|
||||
@@ -61,20 +63,20 @@ class OAuthAccount(object):
|
||||
Appends the _next action to the generated url so the flows continues.
|
||||
"""
|
||||
r = self.request
|
||||
http_host=r.env.http_x_forwarded_for
|
||||
if not http_host: http_host=r.env.http_host
|
||||
http_host = r.env.http_x_forwarded_for
|
||||
if not http_host:
|
||||
http_host = r.env.http_host
|
||||
|
||||
url_scheme = r.env.wsgi_url_scheme
|
||||
if next:
|
||||
path_info = next
|
||||
else:
|
||||
path_info = r.env.path_info
|
||||
uri = '%s://%s%s' %(url_scheme, http_host, path_info)
|
||||
uri = '%s://%s%s' % (url_scheme, http_host, path_info)
|
||||
if r.get_vars and not next:
|
||||
uri += '?' + urlencode(r.get_vars)
|
||||
return uri
|
||||
|
||||
|
||||
def accessToken(self):
|
||||
"""Return the access token generated by the authenticating server.
|
||||
|
||||
@@ -97,12 +99,11 @@ class OAuthAccount(object):
|
||||
token.set_verifier(self.request.vars.oauth_verifier)
|
||||
client = oauth.Client(self.consumer, token)
|
||||
|
||||
|
||||
resp, content = client.request(self.access_token_url, "POST")
|
||||
if str(resp['status']) != '200':
|
||||
self.session.request_token = None
|
||||
self.globals['redirect'](self.globals['URL'](f='user',args='logout'))
|
||||
|
||||
self.globals['redirect'](self.globals[
|
||||
'URL'](f='user', args='logout'))
|
||||
|
||||
self.session.access_token = oauth.Token.from_string(content)
|
||||
|
||||
@@ -111,7 +112,7 @@ class OAuthAccount(object):
|
||||
self.session.access_token = None
|
||||
return None
|
||||
|
||||
def __init__(self, g, client_id, client_secret, auth_url, token_url, access_token_url):
|
||||
def __init__(self, g, client_id, client_secret, auth_url, token_url, access_token_url):
|
||||
self.globals = g
|
||||
self.client_id = client_id
|
||||
self.client_secret = client_secret
|
||||
@@ -125,7 +126,6 @@ class OAuthAccount(object):
|
||||
# consumer init
|
||||
self.consumer = oauth.Consumer(self.client_id, self.client_secret)
|
||||
|
||||
|
||||
def login_url(self, next="/"):
|
||||
self.__oauth_login(next)
|
||||
return next
|
||||
@@ -142,7 +142,7 @@ class OAuthAccount(object):
|
||||
is, this function must be implemented for the specific
|
||||
provider.
|
||||
'''
|
||||
raise NotImplementedError, "Must override get_user()"
|
||||
raise NotImplementedError("Must override get_user()")
|
||||
|
||||
def __oauth_login(self, next):
|
||||
'''This method redirects the user to the authenticating form
|
||||
@@ -163,10 +163,11 @@ class OAuthAccount(object):
|
||||
# putting it in the body seems to work.
|
||||
callback_url = self.__redirect_uri(next)
|
||||
data = urlencode(dict(oauth_callback=callback_url))
|
||||
resp, content = client.request(self.token_url, "POST", body=data)
|
||||
resp, content = client.request(self.token_url, "POST", body=data)
|
||||
if resp['status'] != '200':
|
||||
self.session.request_token = None
|
||||
self.globals['redirect'](self.globals['URL'](f='user',args='logout'))
|
||||
self.globals['redirect'](self.globals[
|
||||
'URL'](f='user', args='logout'))
|
||||
|
||||
# Store the request token in session.
|
||||
request_token = self.session.request_token = oauth.Token.from_string(content)
|
||||
@@ -174,18 +175,12 @@ class OAuthAccount(object):
|
||||
# Redirect the user to the authentication URL and pass the callback url.
|
||||
data = urlencode(dict(oauth_token=request_token.key,
|
||||
oauth_callback=callback_url))
|
||||
auth_request_url = self.auth_url + '?' +data
|
||||
|
||||
auth_request_url = self.auth_url + '?' + data
|
||||
|
||||
HTTP = self.globals['HTTP']
|
||||
|
||||
|
||||
raise HTTP(307,
|
||||
"You are not authenticated: you are being redirected to the <a href='" + auth_request_url + "'> authentication server</a>",
|
||||
Location=auth_request_url)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import urllib2
|
||||
from urllib import urlencode
|
||||
from gluon import current, redirect, HTTP
|
||||
|
||||
|
||||
class OAuthAccount(object):
|
||||
"""
|
||||
Login will be done via OAuth Framework, instead of web2py's
|
||||
@@ -84,7 +85,8 @@ class OAuthAccount(object):
|
||||
username = user['id'])
|
||||
|
||||
|
||||
auth.settings.actions_disabled=['register','change_password','request_reset_password','profile']
|
||||
auth.settings.actions_disabled=['register',
|
||||
'change_password','request_reset_password','profile']
|
||||
auth.settings.login_form=FaceBookAccount()
|
||||
|
||||
Any optional arg in the constructor will be passed asis to remote
|
||||
@@ -99,8 +101,9 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
"""
|
||||
|
||||
r = current.request
|
||||
http_host=r.env.http_x_forwarded_for
|
||||
if not http_host: http_host=r.env.http_host
|
||||
http_host = r.env.http_x_forwarded_for
|
||||
if not http_host:
|
||||
http_host = r.env.http_host
|
||||
|
||||
url_scheme = r.env.wsgi_url_scheme
|
||||
if next:
|
||||
@@ -112,7 +115,6 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
uri += '?' + urlencode(r.get_vars)
|
||||
return uri
|
||||
|
||||
|
||||
def __build_url_opener(self, uri):
|
||||
"""
|
||||
Build the url opener for managing HTTP Basic Athentication
|
||||
@@ -128,7 +130,6 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
opener = urllib2.build_opener(auth_handler)
|
||||
return opener
|
||||
|
||||
|
||||
def accessToken(self):
|
||||
"""
|
||||
Return the access token generated by the authenticating server.
|
||||
@@ -137,7 +138,7 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
Otherwise the token is fetched from the auth server.
|
||||
|
||||
"""
|
||||
if current.session.token and current.session.token.has_key('expires'):
|
||||
if current.session.token and 'expires' in current.session.token:
|
||||
expires = current.session.token['expires']
|
||||
# reuse token until expiration
|
||||
if expires == 0 or expires > time.time():
|
||||
@@ -159,19 +160,19 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
print tmp
|
||||
raise Exception(tmp)
|
||||
finally:
|
||||
del current.session.code # throw it away
|
||||
del current.session.code # throw it away
|
||||
|
||||
if open_url:
|
||||
try:
|
||||
data = open_url.read()
|
||||
tokendata = cgi.parse_qs(data)
|
||||
current.session.token = \
|
||||
dict([(k,v[-1]) for k,v in tokendata.items()])
|
||||
dict([(k, v[-1]) for k, v in tokendata.items()])
|
||||
# set expiration absolute time try to avoid broken
|
||||
# implementations where "expires_in" becomes "expires"
|
||||
if current.session.token.has_key('expires_in'):
|
||||
if 'expires_in' in current.session.token:
|
||||
exps = 'expires_in'
|
||||
elif current.session.token.has_key('expires'):
|
||||
elif 'expires' in current.session.token:
|
||||
exps = 'expires'
|
||||
else:
|
||||
exps = None
|
||||
@@ -217,11 +218,12 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
Override this method by sublcassing the class.
|
||||
|
||||
"""
|
||||
if not current.session.token: return None
|
||||
return dict(first_name = 'Pinco',
|
||||
last_name = 'Pallino',
|
||||
username = 'pincopallino')
|
||||
raise NotImplementedError, "Must override get_user()"
|
||||
if not current.session.token:
|
||||
return None
|
||||
return dict(first_name='Pinco',
|
||||
last_name='Pallino',
|
||||
username='pincopallino')
|
||||
raise NotImplementedError("Must override get_user()")
|
||||
|
||||
# Following code is never executed. It can be used as example
|
||||
# for overriding in subclasses.
|
||||
@@ -239,10 +241,9 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
self.graph = None
|
||||
|
||||
if user:
|
||||
return dict(first_name = user['first_name'],
|
||||
last_name = user['last_name'],
|
||||
username = user['id'])
|
||||
|
||||
return dict(first_name=user['first_name'],
|
||||
last_name=user['last_name'],
|
||||
username=user['id'])
|
||||
|
||||
def __oauth_login(self, next):
|
||||
"""
|
||||
@@ -258,13 +259,13 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
|
||||
if not self.accessToken():
|
||||
if not current.request.vars.code:
|
||||
current.session.redirect_uri=self.__redirect_uri(next)
|
||||
current.session.redirect_uri = self.__redirect_uri(next)
|
||||
data = dict(redirect_uri=current.session.redirect_uri,
|
||||
response_type='code',
|
||||
client_id=self.client_id)
|
||||
response_type='code',
|
||||
client_id=self.client_id)
|
||||
if self.args:
|
||||
data.update(self.args)
|
||||
auth_request_url = self.auth_url + "?" +urlencode(data)
|
||||
auth_request_url = self.auth_url + "?" + urlencode(data)
|
||||
raise HTTP(307,
|
||||
"You are not authenticated: you are being redirected to the <a href='" + auth_request_url + "'> authentication server</a>",
|
||||
Location=auth_request_url)
|
||||
@@ -273,5 +274,3 @@ server for requests. It can be used for the optional"scope" parameters for Face
|
||||
self.accessToken()
|
||||
return current.session.code
|
||||
return None
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ except ImportError, err:
|
||||
|
||||
DEFAULT = lambda: None
|
||||
|
||||
|
||||
class OpenIDAuth(object):
|
||||
"""
|
||||
OpenIDAuth
|
||||
@@ -94,7 +95,7 @@ class OpenIDAuth(object):
|
||||
if not auth.settings.table_user:
|
||||
raise
|
||||
self.table_user = self.auth.settings.table_user
|
||||
self.openid_expiration = 15 #minutes
|
||||
self.openid_expiration = 15 # minutes
|
||||
|
||||
self.messages = self._define_messages()
|
||||
|
||||
@@ -116,7 +117,7 @@ class OpenIDAuth(object):
|
||||
messages.flash_openid_associated = 'OpenID associated'
|
||||
messages.flash_associate_openid = 'Please login or register an account for this OpenID.'
|
||||
messages.p_openid_not_registered = "This Open ID haven't be registered. " \
|
||||
+ "Please login to associate with it or register an account for it."
|
||||
+ "Please login to associate with it or register an account for it."
|
||||
messages.flash_openid_authenticated = 'OpenID authenticated successfully.'
|
||||
messages.flash_openid_fail_authentication = 'OpenID authentication failed. (Error message: %s)'
|
||||
messages.flash_openid_canceled = 'OpenID authentication canceled by user.'
|
||||
@@ -158,7 +159,7 @@ class OpenIDAuth(object):
|
||||
and not processed yet. Else return the OpenID form for login.
|
||||
"""
|
||||
request = current.request
|
||||
if request.vars.has_key('janrain_nonce') and not self._processed():
|
||||
if 'janrain_nonce' in request.vars and not self._processed():
|
||||
self._process_response()
|
||||
return self.auth()
|
||||
return self._form()
|
||||
@@ -172,12 +173,12 @@ class OpenIDAuth(object):
|
||||
args = request.args
|
||||
|
||||
if args[0] == 'logout':
|
||||
return True # Let logout_url got called
|
||||
return True # Let logout_url got called
|
||||
|
||||
if current.session.w2popenid:
|
||||
w2popenid = current.session.w2popenid
|
||||
db = self.db
|
||||
if (w2popenid.ok is True and w2popenid.oid): # OpenID authenticated
|
||||
if (w2popenid.ok is True and w2popenid.oid): # OpenID authenticated
|
||||
if self._w2popenid_expired(w2popenid):
|
||||
del(current.session.w2popenid)
|
||||
flash = self.messages.flash_openid_expired
|
||||
@@ -196,22 +197,23 @@ class OpenIDAuth(object):
|
||||
if current.session.w2popenid:
|
||||
del(current.session.w2popenid)
|
||||
current.session.flash = self.messages.flash_openid_associated
|
||||
if request.vars.has_key(nextvar):
|
||||
if nextvar in request.vars:
|
||||
redirect(request.vars[nextvar])
|
||||
redirect(self.auth.settings.login_next)
|
||||
|
||||
if not request.vars.has_key(nextvar):
|
||||
if nextvar not in request.vars:
|
||||
# no next var, add it and do login again
|
||||
# so if user login or register can go back here to associate the OpenID
|
||||
redirect(URL(r=request,
|
||||
args=['login'],
|
||||
vars={nextvar:self.login_url}))
|
||||
args=['login'],
|
||||
vars={nextvar: self.login_url}))
|
||||
self.login_form = self._form_with_notification()
|
||||
current.session.flash = self.messages.flash_associate_openid
|
||||
return None # need to login or register to associate this openid
|
||||
return None # need to login or register to associate this openid
|
||||
|
||||
# Get existed OpenID user
|
||||
user = db(self.table_user.id==alt_login.user).select().first()
|
||||
user = db(
|
||||
self.table_user.id == alt_login.user).select().first()
|
||||
if user:
|
||||
if current.session.w2popenid:
|
||||
del(current.session.w2popenid)
|
||||
@@ -219,16 +221,17 @@ class OpenIDAuth(object):
|
||||
username = 'username'
|
||||
elif 'email' in self.table_user.fields():
|
||||
username = 'email'
|
||||
return {username: user[username]} if user else None # login success (almost)
|
||||
return {username: user[username]} if user else None # login success (almost)
|
||||
|
||||
return None # just start to login
|
||||
return None # just start to login
|
||||
|
||||
def _find_matched_openid(self, db, oid, type_='openid'):
|
||||
"""
|
||||
Get the matched OpenID for given
|
||||
"""
|
||||
query = ((db.alt_logins.username == oid) & (db.alt_logins.type == type_))
|
||||
alt_login = db(query).select().first() # Get the OpenID record
|
||||
query = (
|
||||
(db.alt_logins.username == oid) & (db.alt_logins.type == type_))
|
||||
alt_login = db(query).select().first() # Get the OpenID record
|
||||
return alt_login
|
||||
|
||||
def _associate_user_openid(self, user, oid):
|
||||
@@ -275,7 +278,6 @@ class OpenIDAuth(object):
|
||||
self.db)
|
||||
return self.consumerhelper
|
||||
|
||||
|
||||
def _form(self, style=None):
|
||||
form = DIV(H3(self.messages.h_openid_login), self._login_form(style))
|
||||
return form
|
||||
@@ -300,7 +302,7 @@ background-color: transparent;
|
||||
padding-left: 18px;
|
||||
width: 400px;
|
||||
"""
|
||||
style = style.replace("\n","")
|
||||
style = style.replace("\n", "")
|
||||
|
||||
request = current.request
|
||||
session = current.session
|
||||
@@ -308,21 +310,25 @@ width: 400px;
|
||||
hidden_next_input = ""
|
||||
if _next == 'profile':
|
||||
profile_url = URL(r=request, f='user', args=['profile'])
|
||||
hidden_next_input = INPUT(_type="hidden", _name="_next", _value=profile_url)
|
||||
form = FORM(openid_field_label or self.messages.label_alt_login_username,
|
||||
INPUT(_type="input", _name="oid",
|
||||
requires=IS_NOT_EMPTY(error_message=messages.openid_fail_discover),
|
||||
_style=style),
|
||||
hidden_next_input,
|
||||
INPUT(_type="submit", _value=submit_button or messages.submit_button),
|
||||
" ",
|
||||
A(messages.comment_openid_signin,
|
||||
_href=messages.comment_openid_help_url,
|
||||
_title=messages.comment_openid_help_title,
|
||||
_class='openid-identifier',
|
||||
_target="_blank"),
|
||||
_action=self.login_url
|
||||
)
|
||||
hidden_next_input = INPUT(
|
||||
_type="hidden", _name="_next", _value=profile_url)
|
||||
form = FORM(
|
||||
openid_field_label or self.messages.label_alt_login_username,
|
||||
INPUT(_type="input", _name="oid",
|
||||
requires=IS_NOT_EMPTY(
|
||||
error_message=messages.openid_fail_discover),
|
||||
_style=style),
|
||||
hidden_next_input,
|
||||
INPUT(_type="submit",
|
||||
_value=submit_button or messages.submit_button),
|
||||
" ",
|
||||
A(messages.comment_openid_signin,
|
||||
_href=messages.comment_openid_help_url,
|
||||
_title=messages.comment_openid_help_title,
|
||||
_class='openid-identifier',
|
||||
_target="_blank"),
|
||||
_action=self.login_url
|
||||
)
|
||||
if form.accepts(request.vars, session):
|
||||
oid = request.vars.oid
|
||||
consumerhelper = self._init_consumerhelper()
|
||||
@@ -332,8 +338,9 @@ width: 400px;
|
||||
warning_openid_fail(session)
|
||||
redirect(url)
|
||||
try:
|
||||
if request.vars.has_key('_next'):
|
||||
return_to_url = self.return_to_url + '?_next=' + request.vars._next
|
||||
if '_next' in request.vars:
|
||||
return_to_url = self.return_to_url + \
|
||||
'?_next=' + request.vars._next
|
||||
url = consumerhelper.begin(oid, self.realm, return_to_url)
|
||||
except DiscoveryFailure:
|
||||
warning_openid_fail(session)
|
||||
@@ -353,7 +360,8 @@ width: 400px;
|
||||
"""
|
||||
Set expiration for OpenID authentication.
|
||||
"""
|
||||
w2popenid.expiration = datetime.now() + timedelta(minutes=self.openid_expiration)
|
||||
w2popenid.expiration = datetime.now(
|
||||
) + timedelta(minutes=self.openid_expiration)
|
||||
|
||||
def _w2popenid_expired(self, w2popenid):
|
||||
"""
|
||||
@@ -369,7 +377,8 @@ width: 400px;
|
||||
request = current.request
|
||||
request_vars = request.vars
|
||||
consumerhelper = self._init_consumerhelper()
|
||||
process_status = consumerhelper.process_response(request_vars, self.return_to_url)
|
||||
process_status = consumerhelper.process_response(
|
||||
request_vars, self.return_to_url)
|
||||
if process_status == "success":
|
||||
w2popenid = current.session.w2popenid
|
||||
user_data = self.consumerhelper.sreg()
|
||||
@@ -388,7 +397,7 @@ width: 400px;
|
||||
def list_user_openids(self):
|
||||
messages = self.messages
|
||||
request = current.request
|
||||
if request.vars.has_key('delete_openid'):
|
||||
if 'delete_openid' in request.vars:
|
||||
self.remove_openid(request.vars.delete_openid)
|
||||
|
||||
query = self.db.alt_logins.user == self.auth.user.id
|
||||
@@ -397,8 +406,8 @@ width: 400px;
|
||||
for alt_login in alt_logins:
|
||||
username = alt_login.username
|
||||
delete_href = URL(r=request, f='user',
|
||||
args=['profile'],
|
||||
vars={'delete_openid': username})
|
||||
args=['profile'],
|
||||
vars={'delete_openid': username})
|
||||
delete_link = A(messages.a_delete, _href=delete_href)
|
||||
l.append(LI(username, " ", delete_link))
|
||||
|
||||
@@ -409,23 +418,23 @@ width: 400px;
|
||||
_next='profile',
|
||||
submit_button=messages.submit_button_add,
|
||||
openid_field_label=messages.label_add_alt_login_username)
|
||||
)
|
||||
)
|
||||
return openid_list
|
||||
|
||||
|
||||
def remove_openid(self, openid):
|
||||
query = self.db.alt_logins.username == openid
|
||||
self.db(query).delete()
|
||||
|
||||
|
||||
class ConsumerHelper(object):
|
||||
"""
|
||||
ConsumerHelper knows the python-openid and
|
||||
"""
|
||||
|
||||
def __init__(self, session, db):
|
||||
self.session = session
|
||||
store = self._init_store(db)
|
||||
self.consumer = openid.consumer.consumer.Consumer(session, store)
|
||||
self.session = session
|
||||
store = self._init_store(db)
|
||||
self.consumer = openid.consumer.consumer.Consumer(session, store)
|
||||
|
||||
def _init_store(self, db):
|
||||
"""
|
||||
@@ -434,7 +443,7 @@ class ConsumerHelper(object):
|
||||
if not hasattr(self, "store"):
|
||||
store = Web2pyStore(db)
|
||||
session = self.session
|
||||
if not session.has_key('w2popenid'):
|
||||
if 'w2popenid' not in session:
|
||||
session.w2popenid = Storage()
|
||||
self.store = store
|
||||
return self.store
|
||||
@@ -446,7 +455,7 @@ class ConsumerHelper(object):
|
||||
w2popenid = self.session.w2popenid
|
||||
w2popenid.oid = oid
|
||||
auth_req = self.consumer.begin(oid)
|
||||
auth_req.addExtension(SRegRequest(required=['email','nickname']))
|
||||
auth_req.addExtension(SRegRequest(required=['email', 'nickname']))
|
||||
url = auth_req.redirectURL(return_to=return_to_url, realm=realm)
|
||||
return url
|
||||
|
||||
@@ -504,19 +513,27 @@ class Web2pyStore(OpenIDStore):
|
||||
|
||||
if self.table_oid_associations_name not in self.database:
|
||||
self.database.define_table(self.table_oid_associations_name,
|
||||
Field('server_url', 'string', length=2047, required=True),
|
||||
Field('handle', 'string', length=255, required=True),
|
||||
Field('secret', 'blob', required=True),
|
||||
Field('issued', 'integer', required=True),
|
||||
Field('lifetime', 'integer', required=True),
|
||||
Field('assoc_type', 'string', length=64, required=True)
|
||||
)
|
||||
Field('server_url',
|
||||
'string', length=2047, required=True),
|
||||
Field('handle',
|
||||
'string', length=255, required=True),
|
||||
Field('secret', 'blob', required=True),
|
||||
Field('issued',
|
||||
'integer', required=True),
|
||||
Field('lifetime',
|
||||
'integer', required=True),
|
||||
Field('assoc_type',
|
||||
'string', length=64, required=True)
|
||||
)
|
||||
if self.table_oid_nonces_name not in self.database:
|
||||
self.database.define_table(self.table_oid_nonces_name,
|
||||
Field('server_url', 'string', length=2047, required=True),
|
||||
Field('timestamp', 'integer', required=True),
|
||||
Field('salt', 'string', length=40, required=True)
|
||||
)
|
||||
Field('server_url',
|
||||
'string', length=2047, required=True),
|
||||
Field('timestamp',
|
||||
'integer', required=True),
|
||||
Field('salt', 'string',
|
||||
length=40, required=True)
|
||||
)
|
||||
|
||||
def storeAssociation(self, server_url, association):
|
||||
"""
|
||||
@@ -525,14 +542,15 @@ class Web2pyStore(OpenIDStore):
|
||||
"""
|
||||
|
||||
db = self.database
|
||||
query = (db.oid_associations.server_url == server_url) & (db.oid_associations.handle == association.handle)
|
||||
query = (db.oid_associations.server_url == server_url) & (
|
||||
db.oid_associations.handle == association.handle)
|
||||
db(query).delete()
|
||||
db.oid_associations.insert(server_url = server_url,
|
||||
handle = association.handle,
|
||||
secret = association.secret,
|
||||
issued = association.issued,
|
||||
lifetime = association.lifetime,
|
||||
assoc_type = association.assoc_type), 'insert '*10
|
||||
db.oid_associations.insert(server_url=server_url,
|
||||
handle=association.handle,
|
||||
secret=association.secret,
|
||||
issued=association.issued,
|
||||
lifetime=association.lifetime,
|
||||
assoc_type=association.assoc_type), 'insert ' * 10
|
||||
|
||||
def getAssociation(self, server_url, handle=None):
|
||||
"""
|
||||
@@ -550,7 +568,8 @@ class Web2pyStore(OpenIDStore):
|
||||
if len(keep_assoc) == 0:
|
||||
return None
|
||||
else:
|
||||
assoc = keep_assoc.pop() # pop the last one as it should be the latest one
|
||||
assoc = keep_assoc.pop(
|
||||
) # pop the last one as it should be the latest one
|
||||
return Association(assoc['handle'],
|
||||
assoc['secret'],
|
||||
assoc['issued'],
|
||||
@@ -559,8 +578,9 @@ class Web2pyStore(OpenIDStore):
|
||||
|
||||
def removeAssociation(self, server_url, handle):
|
||||
db = self.database
|
||||
query = (db.oid_associations.server_url == server_url) & (db.oid_associations.handle == handle)
|
||||
return db(query).delete() != None
|
||||
query = (db.oid_associations.server_url == server_url) & (
|
||||
db.oid_associations.handle == handle)
|
||||
return db(query).delete() is not None
|
||||
|
||||
def useNonce(self, server_url, timestamp, salt):
|
||||
"""
|
||||
@@ -575,10 +595,10 @@ class Web2pyStore(OpenIDStore):
|
||||
if db(query).count() > 0:
|
||||
return False
|
||||
else:
|
||||
db.oid_nonces.insert(server_url = server_url,
|
||||
timestamp = timestamp,
|
||||
salt = salt)
|
||||
return True
|
||||
db.oid_nonces.insert(server_url=server_url,
|
||||
timestamp=timestamp,
|
||||
salt=salt)
|
||||
return True
|
||||
|
||||
def _removeExpiredAssocations(self, rows):
|
||||
"""
|
||||
@@ -599,7 +619,7 @@ class Web2pyStore(OpenIDStore):
|
||||
keep_assoc.append(r)
|
||||
for r in remove_assoc:
|
||||
del db.oid_associations[r['id']]
|
||||
return (keep_assoc, len(remove_assoc)) # return tuple (list of valid associations, number of deleted associations)
|
||||
return (keep_assoc, len(remove_assoc)) # return tuple (list of valid associations, number of deleted associations)
|
||||
|
||||
def cleanupNonces(self):
|
||||
"""
|
||||
@@ -619,7 +639,7 @@ class Web2pyStore(OpenIDStore):
|
||||
|
||||
db = self.database
|
||||
query = (db.oid_associations.id > 0)
|
||||
return self._removeExpiredAssocations(db(query).select())[1] #return number of assoc removed
|
||||
return self._removeExpiredAssocations(db(query).select())[1] # return number of assoc removed
|
||||
|
||||
def cleanup(self):
|
||||
"""
|
||||
@@ -628,6 +648,3 @@ class Web2pyStore(OpenIDStore):
|
||||
"""
|
||||
|
||||
return self.cleanupNonces(), self.cleanupAssociations()
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from gluon.contrib.pam import authenticate
|
||||
|
||||
|
||||
def pam_auth():
|
||||
"""
|
||||
to use pam_login:
|
||||
@@ -19,5 +20,3 @@ def pam_auth():
|
||||
return authenticate(username, password)
|
||||
|
||||
return pam_auth_aux
|
||||
|
||||
|
||||
@@ -19,11 +19,13 @@ from gluon.tools import fetch
|
||||
from gluon.storage import Storage
|
||||
import gluon.contrib.simplejson as json
|
||||
|
||||
|
||||
class RPXAccount(object):
|
||||
|
||||
"""
|
||||
from gluon.contrib.login_methods.rpx_account import RPXAccount
|
||||
auth.settings.actions_disabled=['register','change_password','request_reset_password']
|
||||
auth.settings.actions_disabled=['register','change_password',
|
||||
'request_reset_password']
|
||||
auth.settings.login_form = RPXAccount(request,
|
||||
api_key="...",
|
||||
domain="...",
|
||||
@@ -32,18 +34,18 @@ class RPXAccount(object):
|
||||
|
||||
def __init__(self,
|
||||
request,
|
||||
api_key = "",
|
||||
domain = "",
|
||||
url = "",
|
||||
embed = True,
|
||||
auth_url = "https://rpxnow.com/api/v2/auth_info",
|
||||
language= "en",
|
||||
api_key="",
|
||||
domain="",
|
||||
url="",
|
||||
embed=True,
|
||||
auth_url="https://rpxnow.com/api/v2/auth_info",
|
||||
language="en",
|
||||
prompt='rpx',
|
||||
on_login_failure = None,
|
||||
on_login_failure=None,
|
||||
):
|
||||
|
||||
self.request=request
|
||||
self.api_key=api_key
|
||||
self.request = request
|
||||
self.api_key = api_key
|
||||
self.embed = embed
|
||||
self.auth_url = auth_url
|
||||
self.domain = domain
|
||||
@@ -54,38 +56,40 @@ class RPXAccount(object):
|
||||
self.on_login_failure = on_login_failure
|
||||
self.mappings = Storage()
|
||||
|
||||
dn = {'givenName':'','familyName':''}
|
||||
dn = {'givenName': '', 'familyName': ''}
|
||||
self.mappings.Facebook = lambda profile, dn=dn:\
|
||||
dict(registration_id = profile.get("identifier",""),
|
||||
username = profile.get("preferredUsername",""),
|
||||
email = profile.get("email",""),
|
||||
first_name = profile.get("name",dn).get("givenName",""),
|
||||
last_name = profile.get("name",dn).get("familyName",""))
|
||||
dict(registration_id=profile.get("identifier", ""),
|
||||
username=profile.get("preferredUsername", ""),
|
||||
email=profile.get("email", ""),
|
||||
first_name=profile.get("name", dn).get("givenName", ""),
|
||||
last_name=profile.get("name", dn).get("familyName", ""))
|
||||
self.mappings.Google = lambda profile, dn=dn:\
|
||||
dict(registration_id=profile.get("identifier",""),
|
||||
username=profile.get("preferredUsername",""),
|
||||
email=profile.get("email",""),
|
||||
first_name=profile.get("name",dn).get("givenName",""),
|
||||
last_name=profile.get("name",dn).get("familyName",""))
|
||||
dict(registration_id=profile.get("identifier", ""),
|
||||
username=profile.get("preferredUsername", ""),
|
||||
email=profile.get("email", ""),
|
||||
first_name=profile.get("name", dn).get("givenName", ""),
|
||||
last_name=profile.get("name", dn).get("familyName", ""))
|
||||
self.mappings.default = lambda profile:\
|
||||
dict(registration_id=profile.get("identifier",""),
|
||||
username=profile.get("preferredUsername",""),
|
||||
email=profile.get("email",""),
|
||||
first_name=profile.get("preferredUsername",""),
|
||||
dict(registration_id=profile.get("identifier", ""),
|
||||
username=profile.get("preferredUsername", ""),
|
||||
email=profile.get("email", ""),
|
||||
first_name=profile.get("preferredUsername", ""),
|
||||
last_name='')
|
||||
|
||||
def get_user(self):
|
||||
request = self.request
|
||||
if request.vars.token:
|
||||
user = Storage()
|
||||
data = urllib.urlencode(dict(apiKey = self.api_key, token=request.vars.token))
|
||||
auth_info_json = fetch(self.auth_url+'?'+data)
|
||||
data = urllib.urlencode(
|
||||
dict(apiKey=self.api_key, token=request.vars.token))
|
||||
auth_info_json = fetch(self.auth_url + '?' + data)
|
||||
auth_info = json.loads(auth_info_json)
|
||||
|
||||
if auth_info['stat'] == 'ok':
|
||||
self.profile = auth_info['profile']
|
||||
provider = re.sub('[^\w\-]','',self.profile['providerName'])
|
||||
user = self.mappings.get(provider,self.mappings.default)(self.profile)
|
||||
provider = re.sub('[^\w\-]', '', self.profile['providerName'])
|
||||
user = self.mappings.get(
|
||||
provider, self.mappings.default)(self.profile)
|
||||
return user
|
||||
elif self.on_login_failure:
|
||||
redirect(self.on_login_failure)
|
||||
@@ -95,12 +99,14 @@ class RPXAccount(object):
|
||||
request = self.request
|
||||
args = request.args
|
||||
if self.embed:
|
||||
JANRAIN_URL = \
|
||||
"https://%s.rpxnow.com/openid/embed?token_url=%s&language_preference=%s"
|
||||
rpxform = IFRAME(_src=JANRAIN_URL % (self.domain,self.token_url,self.language),
|
||||
_scrolling="no",
|
||||
_frameborder="no",
|
||||
_style="width:400px;height:240px;")
|
||||
JANRAIN_URL = \
|
||||
"https://%s.rpxnow.com/openid/embed?token_url=%s&language_preference=%s"
|
||||
rpxform = IFRAME(
|
||||
_src=JANRAIN_URL % (
|
||||
self.domain, self.token_url, self.language),
|
||||
_scrolling="no",
|
||||
_frameborder="no",
|
||||
_style="width:400px;height:240px;")
|
||||
else:
|
||||
JANRAIN_URL = \
|
||||
"https://%s.rpxnow.com/openid/v2/signin?token_url=%s"
|
||||
@@ -114,15 +120,15 @@ class RPXAccount(object):
|
||||
_type="text/javascript"))
|
||||
return rpxform
|
||||
|
||||
def use_janrain(auth,filename='private/janrain.key',**kwargs):
|
||||
path = os.path.join(current.request.folder,filename)
|
||||
|
||||
def use_janrain(auth, filename='private/janrain.key', **kwargs):
|
||||
path = os.path.join(current.request.folder, filename)
|
||||
if os.path.exists(path):
|
||||
request = current.request
|
||||
domain,key = open(path,'r').read().strip().split(':')
|
||||
domain, key = open(path, 'r').read().strip().split(':')
|
||||
host = current.request.env.http_host
|
||||
url = URL('default', 'user', args='login', scheme=True)
|
||||
auth.settings.actions_disabled = \
|
||||
['register','change_password','request_reset_password']
|
||||
['register', 'change_password', 'request_reset_password']
|
||||
auth.settings.login_form = RPXAccount(
|
||||
request, api_key=key,domain=domain, url = url,**kwargs)
|
||||
|
||||
request, api_key=key, domain=domain, url=url, **kwargs)
|
||||
@@ -11,13 +11,12 @@ Adds support for x509 authentication.
|
||||
|
||||
from gluon.globals import current
|
||||
from gluon.storage import Storage
|
||||
from gluon.http import HTTP,redirect
|
||||
from gluon.http import HTTP, redirect
|
||||
|
||||
#requires M2Crypto
|
||||
from M2Crypto import X509
|
||||
|
||||
|
||||
|
||||
class X509Auth(object):
|
||||
"""
|
||||
Login using x509 cert from client.
|
||||
@@ -29,8 +28,6 @@ class X509Auth(object):
|
||||
|
||||
"""
|
||||
|
||||
|
||||
|
||||
def __init__(self):
|
||||
self.request = current.request
|
||||
self.ssl_client_raw_cert = self.request.env.ssl_client_raw_cert
|
||||
@@ -41,10 +38,11 @@ class X509Auth(object):
|
||||
|
||||
if self.ssl_client_raw_cert:
|
||||
|
||||
x509=X509.load_cert_string(self.ssl_client_raw_cert, X509.FORMAT_PEM)
|
||||
x509 = X509.load_cert_string(
|
||||
self.ssl_client_raw_cert, X509.FORMAT_PEM)
|
||||
# extract it from the cert
|
||||
self.serial = self.request.env.ssl_client_serial or ('%x' % x509.get_serial_number()).upper()
|
||||
|
||||
self.serial = self.request.env.ssl_client_serial or (
|
||||
'%x' % x509.get_serial_number()).upper()
|
||||
|
||||
subject = x509.get_subject()
|
||||
|
||||
@@ -53,23 +51,17 @@ class X509Auth(object):
|
||||
# cn = self.subject.cn
|
||||
self.subject = Storage(filter(None,
|
||||
map(lambda x:
|
||||
(x,map(lambda y:
|
||||
y.get_data().as_text(),
|
||||
subject.get_entries_by_nid(subject.nid[x]))),
|
||||
(x, map(lambda y:
|
||||
y.get_data(
|
||||
).as_text(),
|
||||
subject.get_entries_by_nid(subject.nid[x]))),
|
||||
subject.nid.keys())))
|
||||
|
||||
|
||||
|
||||
def login_form(self, **args):
|
||||
raise HTTP(403,'Login not allowed. No valid x509 crentials')
|
||||
|
||||
|
||||
raise HTTP(403, 'Login not allowed. No valid x509 crentials')
|
||||
|
||||
def login_url(self, next="/"):
|
||||
raise HTTP(403,'Login not allowed. No valid x509 crentials')
|
||||
|
||||
|
||||
|
||||
raise HTTP(403, 'Login not allowed. No valid x509 crentials')
|
||||
|
||||
def logout_url(self, next="/"):
|
||||
return next
|
||||
@@ -86,10 +78,14 @@ class X509Auth(object):
|
||||
|
||||
p = profile = dict()
|
||||
|
||||
username = p['username'] = reduce(lambda a,b: '%s | %s' % (a,b), self.subject.CN or self.subject.commonName)
|
||||
p['first_name'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.givenName or username)
|
||||
p['last_name'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.surname)
|
||||
p['email'] = reduce(lambda a,b: '%s | %s' % (a,b),self.subject.Email or self.subject.emailAddress)
|
||||
username = p['username'] = reduce(lambda a, b: '%s | %s' % (
|
||||
a, b), self.subject.CN or self.subject.commonName)
|
||||
p['first_name'] = reduce(lambda a, b: '%s | %s' % (a, b),
|
||||
self.subject.givenName or username)
|
||||
p['last_name'] = reduce(
|
||||
lambda a, b: '%s | %s' % (a, b), self.subject.surname)
|
||||
p['email'] = reduce(lambda a, b: '%s | %s' % (
|
||||
a, b), self.subject.Email or self.subject.emailAddress)
|
||||
|
||||
# IMPORTANT WE USE THE CERT SERIAL AS UNIQUE KEY FOR THE USER
|
||||
p['registration_id'] = self.serial
|
||||
@@ -100,6 +96,3 @@ class X509Auth(object):
|
||||
p['certificate'] = self.ssl_client_raw_cert
|
||||
|
||||
return profile
|
||||
|
||||
|
||||
|
||||
+61
-62
@@ -44,22 +44,21 @@ SQL_DIALECTS = {'memcache': {
|
||||
'is not null': 'IS NOT NULL',
|
||||
'extract': None,
|
||||
'left join': None,
|
||||
}}
|
||||
}}
|
||||
|
||||
|
||||
def cleanup(text):
|
||||
if re.compile('[^0-9a-zA-Z_]').findall(text):
|
||||
raise SyntaxError, \
|
||||
'Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text
|
||||
raise SyntaxError('Can\'t cleanup \'%s\': only [0-9a-zA-Z_] allowed in table and field names' % text)
|
||||
return text
|
||||
|
||||
|
||||
def assert_filter_fields(*fields):
|
||||
for field in fields:
|
||||
if isinstance(field, (Field, Expression)) and field.type\
|
||||
in ['text', 'blob']:
|
||||
raise SyntaxError, 'AppEngine does not index by: %s'\
|
||||
% field.type
|
||||
in ['text', 'blob']:
|
||||
raise SyntaxError('AppEngine does not index by: %s'
|
||||
% field.type)
|
||||
|
||||
|
||||
def dateobj_to_datetime(object):
|
||||
@@ -78,7 +77,7 @@ def dateobj_to_datetime(object):
|
||||
object.minute,
|
||||
object.second,
|
||||
object.microsecond,
|
||||
)
|
||||
)
|
||||
return object
|
||||
|
||||
|
||||
@@ -96,7 +95,7 @@ def sqlhtml_validators(field_type, length):
|
||||
'time': validators.IS_TIME(),
|
||||
'datetime': validators.IS_DATETIME(),
|
||||
'reference': validators.IS_INT_IN_RANGE(0, 1e100),
|
||||
}
|
||||
}
|
||||
try:
|
||||
return v[field_type[:9]]
|
||||
except KeyError:
|
||||
@@ -114,7 +113,8 @@ class DALStorage(dict):
|
||||
|
||||
def __setattr__(self, key, value):
|
||||
if key in self:
|
||||
raise SyntaxError, 'Object \'%s\'exists and cannot be redefined' % key
|
||||
raise SyntaxError(
|
||||
'Object \'%s\'exists and cannot be redefined' % key)
|
||||
self[key] = value
|
||||
|
||||
def __repr__(self):
|
||||
@@ -151,14 +151,14 @@ class MEMDB(DALStorage):
|
||||
tablename,
|
||||
*fields,
|
||||
**args
|
||||
):
|
||||
):
|
||||
tablename = cleanup(tablename)
|
||||
if tablename in dir(self) or tablename[0] == '_':
|
||||
raise SyntaxError, 'invalid table name: %s' % tablename
|
||||
raise SyntaxError('invalid table name: %s' % tablename)
|
||||
if not tablename in self.tables:
|
||||
self.tables.append(tablename)
|
||||
else:
|
||||
raise SyntaxError, 'table already defined: %s' % tablename
|
||||
raise SyntaxError('table already defined: %s' % tablename)
|
||||
t = self[tablename] = Table(self, tablename, *fields)
|
||||
t._create()
|
||||
return t
|
||||
@@ -190,7 +190,7 @@ class Table(DALStorage):
|
||||
db,
|
||||
tablename,
|
||||
*fields
|
||||
):
|
||||
):
|
||||
self._db = db
|
||||
self._tablename = tablename
|
||||
self.fields = SQLCallableList()
|
||||
@@ -219,24 +219,24 @@ class Table(DALStorage):
|
||||
if field.type[:9] == 'reference':
|
||||
referenced = field.type[10:].strip()
|
||||
if not referenced:
|
||||
raise SyntaxError, \
|
||||
'Table %s: reference \'%s\' to nothing!' % (self._tablename, k)
|
||||
raise SyntaxError('Table %s: reference \'%s\' to nothing!' % (
|
||||
self._tablename, k))
|
||||
if not referenced in self._db:
|
||||
raise SyntaxError, \
|
||||
'Table: table %s does not exist' % referenced
|
||||
raise SyntaxError(
|
||||
'Table: table %s does not exist' % referenced)
|
||||
referee = self._db[referenced]
|
||||
ftype = \
|
||||
self._db._translator[field.type[:9]](
|
||||
self._db[referenced]._tableobj)
|
||||
if self._tablename in referee.fields: # ## THIS IS OK
|
||||
raise SyntaxError, \
|
||||
'Field: table \'%s\' has same name as a field ' \
|
||||
'in referenced table \'%s\'' % (self._tablename, referenced)
|
||||
raise SyntaxError('Field: table \'%s\' has same name as a field '
|
||||
'in referenced table \'%s\'' % (
|
||||
self._tablename, referenced))
|
||||
self._db[referenced]._referenced_by.append((self._tablename,
|
||||
field.name))
|
||||
field.name))
|
||||
elif not field.type in self._db._translator\
|
||||
or not self._db._translator[field.type]:
|
||||
raise SyntaxError, 'Field: unkown field type %s' % field.type
|
||||
or not self._db._translator[field.type]:
|
||||
raise SyntaxError('Field: unkown field type %s' % field.type)
|
||||
self._tableobj = self._db.client
|
||||
return None
|
||||
|
||||
@@ -269,11 +269,11 @@ class Table(DALStorage):
|
||||
def update(self, id, **fields):
|
||||
for field in fields:
|
||||
if not field in fields and self[field].default\
|
||||
!= None:
|
||||
is not None:
|
||||
fields[field] = self[field].default
|
||||
if field in fields:
|
||||
fields[field] = obj_represent(fields[field],
|
||||
self[field].type, self._db)
|
||||
self[field].type, self._db)
|
||||
return self._tableobj.set(self._id_to_key(id), fields)
|
||||
|
||||
def delete(self, id):
|
||||
@@ -293,7 +293,7 @@ class Table(DALStorage):
|
||||
if self._tableobj.set(shard_id, '0'):
|
||||
id = 0
|
||||
else:
|
||||
raise Exception, 'cannot set memcache'
|
||||
raise Exception('cannot set memcache')
|
||||
return long(str(shard) + str(id))
|
||||
|
||||
def __str__(self):
|
||||
@@ -307,7 +307,7 @@ class Expression(object):
|
||||
name,
|
||||
type='string',
|
||||
db=None,
|
||||
):
|
||||
):
|
||||
(self.name, self.type, self._db) = (name, type, db)
|
||||
|
||||
def __str__(self):
|
||||
@@ -393,11 +393,11 @@ class Field(Expression):
|
||||
notnull=False,
|
||||
unique=False,
|
||||
uploadfield=True,
|
||||
):
|
||||
):
|
||||
|
||||
self.name = cleanup(fieldname)
|
||||
if fieldname in dir(Table) or fieldname[0] == '_':
|
||||
raise SyntaxError, 'Field: invalid field name: %s' % fieldname
|
||||
raise SyntaxError('Field: invalid field name: %s' % fieldname)
|
||||
if isinstance(type, Table):
|
||||
type = 'reference ' + type._tablename
|
||||
if not length:
|
||||
@@ -437,9 +437,9 @@ MEMDB.Field = Field # ## required by gluon/globals.py session.connect
|
||||
|
||||
|
||||
def obj_represent(object, fieldtype, db):
|
||||
if object != None:
|
||||
if object is not None:
|
||||
if fieldtype == 'date' and not isinstance(object,
|
||||
datetime.date):
|
||||
datetime.date):
|
||||
(y, m, d) = [int(x) for x in str(object).strip().split('-')]
|
||||
object = datetime.date(y, m, d)
|
||||
elif fieldtype == 'time' and not isinstance(object, datetime.time):
|
||||
@@ -450,7 +450,7 @@ def obj_represent(object, fieldtype, db):
|
||||
(h, mi, s) = time_items + [0]
|
||||
object = datetime.time(h, mi, s)
|
||||
elif fieldtype == 'datetime' and not isinstance(object,
|
||||
datetime.datetime):
|
||||
datetime.datetime):
|
||||
(y, m, d) = [int(x) for x in
|
||||
str(object)[:10].strip().split('-')]
|
||||
time_items = [int(x) for x in
|
||||
@@ -466,7 +466,7 @@ def obj_represent(object, fieldtype, db):
|
||||
h,
|
||||
mi,
|
||||
s,
|
||||
)
|
||||
)
|
||||
elif fieldtype == 'integer' and not isinstance(object, long):
|
||||
object = long(object)
|
||||
|
||||
@@ -496,10 +496,10 @@ class Query(object):
|
||||
left,
|
||||
op=None,
|
||||
right=None,
|
||||
):
|
||||
):
|
||||
if isinstance(right, (Field, Expression)):
|
||||
raise SyntaxError, \
|
||||
'Query: right side of filter must be a value or entity'
|
||||
raise SyntaxError(
|
||||
'Query: right side of filter must be a value or entity')
|
||||
if isinstance(left, Field) and left.name == 'id':
|
||||
if op == '=':
|
||||
self.get_one = \
|
||||
@@ -507,8 +507,8 @@ class Query(object):
|
||||
id=long(right))
|
||||
return
|
||||
else:
|
||||
raise SyntaxError, 'only equality by id is supported'
|
||||
raise SyntaxError, 'not supported'
|
||||
raise SyntaxError('only equality by id is supported')
|
||||
raise SyntaxError('not supported')
|
||||
|
||||
def __str__(self):
|
||||
return str(self.left)
|
||||
@@ -539,7 +539,7 @@ class Set(object):
|
||||
self.where = where
|
||||
self._tables.insert(0, where.get_all)
|
||||
elif hasattr(where, 'get_one') and isinstance(where.get_one,
|
||||
QueryException):
|
||||
QueryException):
|
||||
self.where = where.get_one
|
||||
else:
|
||||
|
||||
@@ -553,9 +553,8 @@ class Set(object):
|
||||
|
||||
def __call__(self, where):
|
||||
if isinstance(self.where, QueryException) or isinstance(where,
|
||||
QueryException):
|
||||
raise SyntaxError, \
|
||||
'neither self.where nor where can be a QueryException instance'
|
||||
QueryException):
|
||||
raise SyntaxError('neither self.where nor where can be a QueryException instance')
|
||||
if self.where:
|
||||
return Set(self._db, self.where & where)
|
||||
else:
|
||||
@@ -564,9 +563,9 @@ class Set(object):
|
||||
def _get_table_or_raise(self):
|
||||
tablenames = list(set(self._tables)) # unique
|
||||
if len(tablenames) < 1:
|
||||
raise SyntaxError, 'Set: no tables selected'
|
||||
raise SyntaxError('Set: no tables selected')
|
||||
if len(tablenames) > 1:
|
||||
raise SyntaxError, 'Set: no join in appengine'
|
||||
raise SyntaxError('Set: no join in appengine')
|
||||
return self._db[tablenames[0]]._tableobj
|
||||
|
||||
def _getitem_exception(self):
|
||||
@@ -597,7 +596,7 @@ class Set(object):
|
||||
if isinstance(self.where, QueryException):
|
||||
return self._select_except()
|
||||
else:
|
||||
raise SyntaxError, 'select arguments not supported'
|
||||
raise SyntaxError('select arguments not supported')
|
||||
|
||||
def count(self):
|
||||
return len(self.select())
|
||||
@@ -609,7 +608,7 @@ class Set(object):
|
||||
return
|
||||
self._db[tablename].delete(id)
|
||||
else:
|
||||
raise Exception, 'deletion not implemented'
|
||||
raise Exception('deletion not implemented')
|
||||
|
||||
def update(self, **update_fields):
|
||||
if isinstance(self.where, QueryException):
|
||||
@@ -620,7 +619,7 @@ class Set(object):
|
||||
setattr(item, key, value)
|
||||
self._db[tablename].update(id, **item)
|
||||
else:
|
||||
raise Exception, 'update not implemented'
|
||||
raise Exception('update not implemented')
|
||||
|
||||
|
||||
def update_record(
|
||||
@@ -628,7 +627,7 @@ def update_record(
|
||||
s,
|
||||
id,
|
||||
a,
|
||||
):
|
||||
):
|
||||
item = s.get(id)
|
||||
for (key, value) in a.items():
|
||||
t[key] = value
|
||||
@@ -650,7 +649,7 @@ class Rows(object):
|
||||
db,
|
||||
response,
|
||||
*colnames
|
||||
):
|
||||
):
|
||||
self._db = db
|
||||
self.colnames = colnames
|
||||
self.response = response
|
||||
@@ -660,9 +659,9 @@ class Rows(object):
|
||||
|
||||
def __getitem__(self, i):
|
||||
if i >= len(self.response) or i < 0:
|
||||
raise SyntaxError, 'Rows: no such row: %i' % i
|
||||
raise SyntaxError('Rows: no such row: %i' % i)
|
||||
if len(self.response[0]) != len(self.colnames):
|
||||
raise SyntaxError, 'Rows: internal error'
|
||||
raise SyntaxError('Rows: internal error')
|
||||
row = DALStorage()
|
||||
for j in xrange(len(self.colnames)):
|
||||
value = self.response[i][j]
|
||||
@@ -684,7 +683,7 @@ class Rows(object):
|
||||
referee = field.type[10:].strip()
|
||||
rid = value
|
||||
row[tablename][fieldname] = rid
|
||||
elif field.type == 'boolean' and value != None:
|
||||
elif field.type == 'boolean' and value is not None:
|
||||
|
||||
# row[tablename][fieldname]=Set(self._db[referee].id==rid)
|
||||
|
||||
@@ -692,13 +691,13 @@ class Rows(object):
|
||||
row[tablename][fieldname] = True
|
||||
else:
|
||||
row[tablename][fieldname] = False
|
||||
elif field.type == 'date' and value != None\
|
||||
and not isinstance(value, datetime.date):
|
||||
elif field.type == 'date' and value is not None\
|
||||
and not isinstance(value, datetime.date):
|
||||
(y, m, d) = [int(x) for x in
|
||||
str(value).strip().split('-')]
|
||||
row[tablename][fieldname] = datetime.date(y, m, d)
|
||||
elif field.type == 'time' and value != None\
|
||||
and not isinstance(value, datetime.time):
|
||||
elif field.type == 'time' and value is not None\
|
||||
and not isinstance(value, datetime.time):
|
||||
time_items = [int(x) for x in
|
||||
str(value).strip().split(':')[:3]]
|
||||
if len(time_items) == 3:
|
||||
@@ -706,8 +705,8 @@ class Rows(object):
|
||||
else:
|
||||
(h, mi, s) = time_items + [0]
|
||||
row[tablename][fieldname] = datetime.time(h, mi, s)
|
||||
elif field.type == 'datetime' and value != None\
|
||||
and not isinstance(value, datetime.datetime):
|
||||
elif field.type == 'datetime' and value is not None\
|
||||
and not isinstance(value, datetime.datetime):
|
||||
(y, m, d) = [int(x) for x in
|
||||
str(value)[:10].strip().split('-')]
|
||||
time_items = [int(x) for x in
|
||||
@@ -723,19 +722,19 @@ class Rows(object):
|
||||
h,
|
||||
mi,
|
||||
s,
|
||||
)
|
||||
)
|
||||
else:
|
||||
row[tablename][fieldname] = value
|
||||
if fieldname == 'id':
|
||||
id = row[tablename].id
|
||||
row[tablename].update_record = lambda t = row[tablename], \
|
||||
s = self._db[tablename], id = id, **a: update_record(t,
|
||||
s, id, a)
|
||||
s, id, a)
|
||||
for (referee_table, referee_name) in \
|
||||
table._referenced_by:
|
||||
table._referenced_by:
|
||||
s = self._db[referee_table][referee_name]
|
||||
row[tablename][referee_table] = Set(self._db, s
|
||||
== id)
|
||||
== id)
|
||||
if len(row.keys()) == 1:
|
||||
return row[row.keys()[0]]
|
||||
return row
|
||||
|
||||
@@ -11,7 +11,7 @@ Modified for inclusion into web2py by: Ross Peoples <ross.peoples@gmail.com>
|
||||
"""
|
||||
|
||||
|
||||
from StringIO import StringIO # The pure-Python StringIO supports unicode.
|
||||
from StringIO import StringIO # The pure-Python StringIO supports unicode.
|
||||
import re
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ __version__ = '0.1.4'
|
||||
|
||||
def remove_comments(css):
|
||||
"""Remove all CSS comment blocks."""
|
||||
|
||||
|
||||
iemac = False
|
||||
preserve = False
|
||||
comment_start = css.find("/*")
|
||||
@@ -28,7 +28,7 @@ def remove_comments(css):
|
||||
# Preserve comments that look like `/*!...*/`.
|
||||
# Slicing is used to make sure we don"t get an IndexError.
|
||||
preserve = css[comment_start + 2:comment_start + 3] == "!"
|
||||
|
||||
|
||||
comment_end = css.find("*/", comment_start + 2)
|
||||
if comment_end < 0:
|
||||
if not preserve:
|
||||
@@ -48,22 +48,22 @@ def remove_comments(css):
|
||||
else:
|
||||
comment_start = comment_end + 2
|
||||
comment_start = css.find("/*", comment_start)
|
||||
|
||||
|
||||
return css
|
||||
|
||||
|
||||
def remove_unnecessary_whitespace(css):
|
||||
"""Remove unnecessary whitespace characters."""
|
||||
|
||||
|
||||
def pseudoclasscolon(css):
|
||||
|
||||
|
||||
"""
|
||||
Prevents 'p :link' from becoming 'p:link'.
|
||||
|
||||
|
||||
Translates 'p :link' into 'p ___PSEUDOCLASSCOLON___link'; this is
|
||||
translated back again later.
|
||||
"""
|
||||
|
||||
|
||||
regex = re.compile(r"(^|\})(([^\{\:])+\:)+([^\{]*\{)")
|
||||
match = regex.search(css)
|
||||
while match:
|
||||
@@ -73,43 +73,43 @@ def remove_unnecessary_whitespace(css):
|
||||
css[match.end():]])
|
||||
match = regex.search(css)
|
||||
return css
|
||||
|
||||
|
||||
css = pseudoclasscolon(css)
|
||||
# Remove spaces from before things.
|
||||
css = re.sub(r"\s+([!{};:>+\(\)\],])", r"\1", css)
|
||||
|
||||
|
||||
# If there is a `@charset`, then only allow one, and move to the beginning.
|
||||
css = re.sub(r"^(.*)(@charset \"[^\"]*\";)", r"\2\1", css)
|
||||
css = re.sub(r"^(\s*@charset [^;]+;\s*)+", r"\1", css)
|
||||
|
||||
|
||||
# Put the space back in for a few cases, such as `@media screen` and
|
||||
# `(-webkit-min-device-pixel-ratio:0)`.
|
||||
css = re.sub(r"\band\(", "and (", css)
|
||||
|
||||
|
||||
# Put the colons back.
|
||||
css = css.replace('___PSEUDOCLASSCOLON___', ':')
|
||||
|
||||
|
||||
# Remove spaces from after things.
|
||||
css = re.sub(r"([!{}:;>+\(\[,])\s+", r"\1", css)
|
||||
|
||||
|
||||
return css
|
||||
|
||||
|
||||
def remove_unnecessary_semicolons(css):
|
||||
"""Remove unnecessary semicolons."""
|
||||
|
||||
|
||||
return re.sub(r";+\}", "}", css)
|
||||
|
||||
|
||||
def remove_empty_rules(css):
|
||||
"""Remove empty rules."""
|
||||
|
||||
|
||||
return re.sub(r"[^\}\{]+\{\}", "", css)
|
||||
|
||||
|
||||
def normalize_rgb_colors_to_hex(css):
|
||||
"""Convert `rgb(51,102,153)` to `#336699`."""
|
||||
|
||||
|
||||
regex = re.compile(r"rgb\s*\(\s*([0-9,\s]+)\s*\)")
|
||||
match = regex.search(css)
|
||||
while match:
|
||||
@@ -122,39 +122,40 @@ def normalize_rgb_colors_to_hex(css):
|
||||
|
||||
def condense_zero_units(css):
|
||||
"""Replace `0(px, em, %, etc)` with `0`."""
|
||||
|
||||
|
||||
return re.sub(r"([\s:])(0)(px|em|%|in|cm|mm|pc|pt|ex)", r"\1\2", css)
|
||||
|
||||
|
||||
def condense_multidimensional_zeros(css):
|
||||
"""Replace `:0 0 0 0;`, `:0 0 0;` etc. with `:0;`."""
|
||||
|
||||
|
||||
css = css.replace(":0 0 0 0;", ":0;")
|
||||
css = css.replace(":0 0 0;", ":0;")
|
||||
css = css.replace(":0 0;", ":0;")
|
||||
|
||||
|
||||
# Revert `background-position:0;` to the valid `background-position:0 0;`.
|
||||
css = css.replace("background-position:0;", "background-position:0 0;")
|
||||
|
||||
|
||||
return css
|
||||
|
||||
|
||||
def condense_floating_points(css):
|
||||
"""Replace `0.6` with `.6` where possible."""
|
||||
|
||||
|
||||
return re.sub(r"(:|\s)0+\.(\d+)", r"\1.\2", css)
|
||||
|
||||
|
||||
def condense_hex_colors(css):
|
||||
"""Shorten colors from #AABBCC to #ABC where possible."""
|
||||
|
||||
|
||||
regex = re.compile(r"([^\"'=\s])(\s*)#([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])([0-9a-fA-F])")
|
||||
match = regex.search(css)
|
||||
while match:
|
||||
first = match.group(3) + match.group(5) + match.group(7)
|
||||
second = match.group(4) + match.group(6) + match.group(8)
|
||||
if first.lower() == second.lower():
|
||||
css = css.replace(match.group(), match.group(1) + match.group(2) + '#' + first)
|
||||
css = css.replace(
|
||||
match.group(), match.group(1) + match.group(2) + '#' + first)
|
||||
match = regex.search(css, match.end() - 3)
|
||||
else:
|
||||
match = regex.search(css, match.end())
|
||||
@@ -163,19 +164,19 @@ def condense_hex_colors(css):
|
||||
|
||||
def condense_whitespace(css):
|
||||
"""Condense multiple adjacent whitespace characters into one."""
|
||||
|
||||
|
||||
return re.sub(r"\s+", " ", css)
|
||||
|
||||
|
||||
def condense_semicolons(css):
|
||||
"""Condense multiple adjacent semicolon characters into one."""
|
||||
|
||||
|
||||
return re.sub(r";;+", ";", css)
|
||||
|
||||
|
||||
def wrap_css_lines(css, line_length):
|
||||
"""Wrap the lines of the given CSS to an approximate length."""
|
||||
|
||||
|
||||
lines = []
|
||||
line_start = 0
|
||||
for i, char in enumerate(css):
|
||||
@@ -183,7 +184,7 @@ def wrap_css_lines(css, line_length):
|
||||
if char == '}' and (i - line_start >= line_length):
|
||||
lines.append(css[line_start:i + 1])
|
||||
line_start = i + 1
|
||||
|
||||
|
||||
if line_start < len(css):
|
||||
lines.append(css[line_start:])
|
||||
return '\n'.join(lines)
|
||||
@@ -212,19 +213,19 @@ def cssmin(css, wrap=None):
|
||||
def main():
|
||||
import optparse
|
||||
import sys
|
||||
|
||||
|
||||
p = optparse.OptionParser(
|
||||
prog="cssmin", version=__version__,
|
||||
usage="%prog [--wrap N]",
|
||||
description="""Reads raw CSS from stdin, and writes compressed CSS to stdout.""")
|
||||
|
||||
|
||||
p.add_option(
|
||||
'-w', '--wrap', type='int', default=None, metavar='N',
|
||||
help="Wrap output to approximately N chars per line.")
|
||||
|
||||
|
||||
options, args = p.parse_args()
|
||||
sys.stdout.write(cssmin(sys.stdin.read(), wrap=options.wrap))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
main()
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
import re
|
||||
|
||||
|
||||
def minify(response):
|
||||
def _replace(match):
|
||||
match = match.group()
|
||||
# save whole <pre>, <textarea> tags, and opening <!-- (so it doesn't break <script>)
|
||||
# otherwise, replace all whitespace with a single space character
|
||||
return match if match.startswith(('<pre', '<textarea', '<!--')) else ' '
|
||||
|
||||
cpat = re.compile(r'\s+|<pre(.*?)</pre>|<textarea(.*?)</textarea>|<!--\s', re.DOTALL)
|
||||
return cpat.sub(_replace, response)
|
||||
def _replace(match):
|
||||
match = match.group()
|
||||
# save whole <pre>, <textarea> tags, and opening <!-- (so it doesn't break <script>)
|
||||
# otherwise, replace all whitespace with a single space character
|
||||
return match if match.startswith(('<pre', '<textarea', '<!--')) else ' '
|
||||
|
||||
cpat = re.compile(
|
||||
r'\s+|<pre(.*?)</pre>|<textarea(.*?)</textarea>|<!--\s', re.DOTALL)
|
||||
return cpat.sub(_replace, response)
|
||||
@@ -49,7 +49,7 @@ Both python 2 and python 3 are supported.
|
||||
|
||||
.. _jsmin.c by Douglas Crockford:
|
||||
http://www.crockford.com/javascript/jsmin.c
|
||||
|
||||
|
||||
Original author of Python version: Andr\xe9 Malo
|
||||
Home page: http://opensource.perlig.de/rjsmin/
|
||||
Modified by Ross Peoples <ross.peoples@gmail.com> for inclusion into web2py.
|
||||
@@ -96,7 +96,7 @@ def _make_jsmin(extended=True, python_only=True):
|
||||
try:
|
||||
xrange
|
||||
except NameError:
|
||||
xrange = range # pylint: disable = W0622
|
||||
xrange = range # pylint: disable = W0622
|
||||
|
||||
space_chars = r'[\000-\011\013\014\016-\040]'
|
||||
|
||||
@@ -151,13 +151,13 @@ def _make_jsmin(extended=True, python_only=True):
|
||||
last != first and chr(last) or ''
|
||||
) for first, last in result])
|
||||
|
||||
return _re.sub(r'([\000-\040\047])', # for better portability
|
||||
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
|
||||
.replace('\\', '\\\\')
|
||||
.replace('[', '\\[')
|
||||
.replace(']', '\\]')
|
||||
)
|
||||
)
|
||||
return _re.sub(r'([\000-\040\047])', # for better portability
|
||||
lambda m: '\\%03o' % ord(m.group(1)), (sequentize(result)
|
||||
.replace('\\', '\\\\')
|
||||
.replace('[', '\\[')
|
||||
.replace(']', '\\]')
|
||||
)
|
||||
)
|
||||
|
||||
def id_literal_(what):
|
||||
""" Make id_literal like char class """
|
||||
@@ -190,25 +190,33 @@ def _make_jsmin(extended=True, python_only=True):
|
||||
r'|(?:(?<=%(preregex1)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
|
||||
r'|(?:(?<=%(preregex2)s)%(space)s*(%(regex)s[^\047"/\000-\040]*))'
|
||||
r'|(?<=%(id_literal_close)s)'
|
||||
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
|
||||
r'(?=%(id_literal_open)s)'
|
||||
r'%(space)s*(?:(%(newline)s)%(space)s*)+'
|
||||
r'(?=%(id_literal_open)s)'
|
||||
r'|(?<=%(id_literal)s)(%(space)s)+(?=%(id_literal)s)'
|
||||
r'|%(space)s+'
|
||||
r'|(?:%(newline)s%(space)s*)+'
|
||||
) % locals()).sub
|
||||
|
||||
def space_subber(match):
|
||||
""" Substitution callback """
|
||||
# pylint: disable = C0321, R0911
|
||||
groups = match.groups()
|
||||
if groups[0]: return groups[0]
|
||||
elif groups[1]: return groups[1]
|
||||
elif groups[2]: return groups[2]
|
||||
elif groups[3]: return groups[3]
|
||||
elif groups[4]: return '\n'
|
||||
elif groups[5]: return ' '
|
||||
else: return ''
|
||||
if groups[0]:
|
||||
return groups[0]
|
||||
elif groups[1]:
|
||||
return groups[1]
|
||||
elif groups[2]:
|
||||
return groups[2]
|
||||
elif groups[3]:
|
||||
return groups[3]
|
||||
elif groups[4]:
|
||||
return '\n'
|
||||
elif groups[5]:
|
||||
return ' '
|
||||
else:
|
||||
return ''
|
||||
|
||||
def jsmin(script): # pylint: disable = W0621
|
||||
def jsmin(script): # pylint: disable = W0621
|
||||
r"""
|
||||
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
||||
|
||||
@@ -239,20 +247,26 @@ def _make_jsmin(extended=True, python_only=True):
|
||||
r'|(%(space)s)+'
|
||||
r'|(?:(%(newline)s)%(space)s*)+'
|
||||
) % locals()).sub
|
||||
|
||||
def space_norm_subber(match):
|
||||
""" Substitution callback """
|
||||
# pylint: disable = C0321
|
||||
groups = match.groups()
|
||||
if groups[0]: return groups[0]
|
||||
elif groups[1]: return groups[1].replace('\r', '\n') + groups[2]
|
||||
elif groups[3]: return ' '
|
||||
elif groups[4]: return '\n'
|
||||
if groups[0]:
|
||||
return groups[0]
|
||||
elif groups[1]:
|
||||
return groups[1].replace('\r', '\n') + groups[2]
|
||||
elif groups[3]:
|
||||
return ' '
|
||||
elif groups[4]:
|
||||
return '\n'
|
||||
|
||||
space_sub1 = _re.compile((
|
||||
r'[\040\n]?(%(strings)s|%(pre_regex)s%(regex)s)'
|
||||
r'|\040(%(not_id_literal)s)'
|
||||
r'|\n(%(not_id_literal_open)s)'
|
||||
) % locals()).sub
|
||||
|
||||
def space_subber1(match):
|
||||
""" Substitution callback """
|
||||
groups = match.groups()
|
||||
@@ -264,6 +278,7 @@ def _make_jsmin(extended=True, python_only=True):
|
||||
r'|(%(not_id_literal)s)\040'
|
||||
r'|(%(not_id_literal_close)s)\n'
|
||||
) % locals()).sub
|
||||
|
||||
def space_subber2(match):
|
||||
""" Substitution callback """
|
||||
groups = match.groups()
|
||||
@@ -295,10 +310,11 @@ def _make_jsmin(extended=True, python_only=True):
|
||||
:Rtype: ``str``
|
||||
"""
|
||||
return space_sub2(space_subber2,
|
||||
space_sub1(space_subber1,
|
||||
space_norm_sub(space_norm_subber, '\n%s\n' % script)
|
||||
)
|
||||
).strip()
|
||||
space_sub1(space_subber1,
|
||||
space_norm_sub(space_norm_subber,
|
||||
'\n%s\n' % script)
|
||||
)
|
||||
).strip()
|
||||
return jsmin
|
||||
|
||||
jsmin = _make_jsmin()
|
||||
@@ -311,6 +327,7 @@ jsmin = _make_jsmin()
|
||||
# jsmin.jsmin(script)
|
||||
#
|
||||
|
||||
|
||||
def jsmin_for_posers(script):
|
||||
r"""
|
||||
Minify javascript based on `jsmin.c by Douglas Crockford`_\.
|
||||
|
||||
@@ -14,24 +14,27 @@ import os
|
||||
import hashlib
|
||||
import re
|
||||
|
||||
|
||||
def read_binary_file(filename):
|
||||
f = open(filename,'rb')
|
||||
f = open(filename, 'rb')
|
||||
data = f.read()
|
||||
f.close()
|
||||
return data
|
||||
|
||||
def write_binary_file(filename,data):
|
||||
f =open(filename,'wb')
|
||||
|
||||
def write_binary_file(filename, data):
|
||||
f = open(filename, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
|
||||
def fix_links(css,static_path):
|
||||
|
||||
def fix_links(css, static_path):
|
||||
return re.sub(r'url\((["\'])\.\./', 'url(\\1' + static_path, css)
|
||||
|
||||
|
||||
|
||||
|
||||
def minify(files, path_info, folder, optimize_css, optimize_js,
|
||||
ignore_concat = [],
|
||||
ignore_minify = ['/jquery.js', '/anytime.js']):
|
||||
ignore_concat=[],
|
||||
ignore_minify=['/jquery.js', '/anytime.js']):
|
||||
|
||||
"""
|
||||
Input:
|
||||
@@ -45,7 +48,7 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
||||
Returns a new list of:
|
||||
- filename (absolute or relative, css or js, actual or temporary) or
|
||||
- ('css:inline','...css..')
|
||||
- ('js:inline','...js..')
|
||||
- ('js:inline','...js..')
|
||||
"""
|
||||
optimize_css = optimize_css or ''
|
||||
optimize_js = optimize_js or ''
|
||||
@@ -55,27 +58,27 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
||||
concat_js = 'concat' in optimize_js
|
||||
minify_js = 'minify' in optimize_js
|
||||
inline_js = 'inline' in optimize_js
|
||||
static_path,temp = path_info.rsplit('/',1)
|
||||
static_path, temp = path_info.rsplit('/', 1)
|
||||
new_files = []
|
||||
css = []
|
||||
js = []
|
||||
processed = []
|
||||
for k,filename in enumerate(files):
|
||||
for k, filename in enumerate(files):
|
||||
if not filename.startswith('/') or \
|
||||
any(filename.endswith(x) \
|
||||
for x in ignore_concat):
|
||||
any(filename.endswith(x)
|
||||
for x in ignore_concat):
|
||||
new_files.append(filename)
|
||||
continue
|
||||
|
||||
abs_filename = os.path.join(
|
||||
folder,'static', filename[len(static_path)+1:])
|
||||
folder, 'static', filename[len(static_path) + 1:])
|
||||
|
||||
if filename.lower().endswith('.css'):
|
||||
processed.append(filename)
|
||||
spath_info, sfilename = \
|
||||
path_info.split('/'), filename.split('/')
|
||||
u = 0
|
||||
for i,a in enumerate(sfilename):
|
||||
for i, a in enumerate(sfilename):
|
||||
try:
|
||||
if a != spath_info[i]:
|
||||
u = i
|
||||
@@ -99,8 +102,8 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
||||
|
||||
if minify_js and \
|
||||
not filename.endswith('.min.js') and \
|
||||
not any(filename.endswith(x) \
|
||||
for x in ignore_minify):
|
||||
not any(filename.endswith(x)
|
||||
for x in ignore_minify):
|
||||
js.append(jsmin.jsmin(contents))
|
||||
else:
|
||||
js.append(contents)
|
||||
@@ -110,32 +113,31 @@ def minify(files, path_info, folder, optimize_css, optimize_js,
|
||||
if css and concat_css:
|
||||
css = '\n\n'.join(contents for contents in css)
|
||||
if not inline_css:
|
||||
temppath = os.path.join(folder,'static',temp)
|
||||
temppath = os.path.join(folder, 'static', temp)
|
||||
if not os.path.exists(temppath):
|
||||
os.mkdir(temppath)
|
||||
dest = "compressed_%s.css" % dest_key
|
||||
tempfile = os.path.join(temppath, dest)
|
||||
write_binary_file(tempfile,css)
|
||||
css = path_info+'/%s' % dest
|
||||
write_binary_file(tempfile, css)
|
||||
css = path_info + '/%s' % dest
|
||||
new_files.append(css)
|
||||
else:
|
||||
new_files.append(('css:inline',css))
|
||||
new_files.append(('css:inline', css))
|
||||
else:
|
||||
new_files += css
|
||||
if js and concat_js:
|
||||
js = '\n'.join(contents for contents in js)
|
||||
if inline_js:
|
||||
js = ('js:inline',js)
|
||||
js = ('js:inline', js)
|
||||
else:
|
||||
temppath = os.path.join(folder,'static',temp)
|
||||
if not os.path.exists(temppath):
|
||||
temppath = os.path.join(folder, 'static', temp)
|
||||
if not os.path.exists(temppath):
|
||||
os.mkdir(temppath)
|
||||
dest = "compressed_%s.js" % dest_key
|
||||
tempfile = os.path.join(folder,'static',temp,dest)
|
||||
write_binary_file(tempfile,js)
|
||||
js = path_info+'/%s' % dest
|
||||
tempfile = os.path.join(folder, 'static', temp, dest)
|
||||
write_binary_file(tempfile, js)
|
||||
js = path_info + '/%s' % dest
|
||||
new_files.append(js)
|
||||
else:
|
||||
new_files += js
|
||||
return new_files
|
||||
|
||||
+20
-15
@@ -24,7 +24,7 @@ CALLOC.argtypes = [c_uint, c_uint]
|
||||
|
||||
STRDUP = LIBC.strdup
|
||||
STRDUP.argstypes = [c_char_p]
|
||||
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
|
||||
STRDUP.restype = POINTER(c_char) # NOT c_char_p !!!!
|
||||
|
||||
# Various constants
|
||||
PAM_PROMPT_ECHO_OFF = 1
|
||||
@@ -32,56 +32,61 @@ PAM_PROMPT_ECHO_ON = 2
|
||||
PAM_ERROR_MSG = 3
|
||||
PAM_TEXT_INFO = 4
|
||||
|
||||
|
||||
class PamHandle(Structure):
|
||||
"""wrapper class for pam_handle_t"""
|
||||
_fields_ = [
|
||||
("handle", c_void_p)
|
||||
]
|
||||
("handle", c_void_p)
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
Structure.__init__(self)
|
||||
self.handle = 0
|
||||
|
||||
|
||||
class PamMessage(Structure):
|
||||
"""wrapper class for pam_message structure"""
|
||||
_fields_ = [
|
||||
("msg_style", c_int),
|
||||
("msg", c_char_p),
|
||||
]
|
||||
("msg_style", c_int),
|
||||
("msg", c_char_p),
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
return "<PamMessage %i '%s'>" % (self.msg_style, self.msg)
|
||||
|
||||
|
||||
class PamResponse(Structure):
|
||||
"""wrapper class for pam_response structure"""
|
||||
_fields_ = [
|
||||
("resp", c_char_p),
|
||||
("resp_retcode", c_int),
|
||||
]
|
||||
("resp", c_char_p),
|
||||
("resp_retcode", c_int),
|
||||
]
|
||||
|
||||
def __repr__(self):
|
||||
return "<PamResponse %i '%s'>" % (self.resp_retcode, self.resp)
|
||||
|
||||
CONV_FUNC = CFUNCTYPE(c_int,
|
||||
c_int, POINTER(POINTER(PamMessage)),
|
||||
POINTER(POINTER(PamResponse)), c_void_p)
|
||||
c_int, POINTER(POINTER(PamMessage)),
|
||||
POINTER(POINTER(PamResponse)), c_void_p)
|
||||
|
||||
|
||||
class PamConv(Structure):
|
||||
"""wrapper class for pam_conv structure"""
|
||||
_fields_ = [
|
||||
("conv", CONV_FUNC),
|
||||
("appdata_ptr", c_void_p)
|
||||
]
|
||||
("conv", CONV_FUNC),
|
||||
("appdata_ptr", c_void_p)
|
||||
]
|
||||
|
||||
PAM_START = LIBPAM.pam_start
|
||||
PAM_START.restype = c_int
|
||||
PAM_START.argtypes = [c_char_p, c_char_p, POINTER(PamConv),
|
||||
POINTER(PamHandle)]
|
||||
POINTER(PamHandle)]
|
||||
|
||||
PAM_AUTHENTICATE = LIBPAM.pam_authenticate
|
||||
PAM_AUTHENTICATE.restype = c_int
|
||||
PAM_AUTHENTICATE.argtypes = [PamHandle, c_int]
|
||||
|
||||
|
||||
def authenticate(username, password, service='login'):
|
||||
"""Returns True if the given username and password authenticate for the
|
||||
given service. Returns False otherwise
|
||||
|
||||
+28
-21
@@ -24,6 +24,7 @@ import cmd
|
||||
import pydoc
|
||||
import threading
|
||||
|
||||
|
||||
class Qdb(bdb.Bdb):
|
||||
"Qdb Debugger Backend"
|
||||
|
||||
@@ -36,7 +37,7 @@ class Qdb(bdb.Bdb):
|
||||
self.frame = None
|
||||
self.i = 1 # sequential RPC call id
|
||||
self.waiting = False
|
||||
self.pipe = pipe # for communication
|
||||
self.pipe = pipe # for communication
|
||||
self._wait_for_mainpyfile = False
|
||||
self._wait_for_breakpoint = False
|
||||
self.mainpyfile = ""
|
||||
@@ -67,7 +68,7 @@ class Qdb(bdb.Bdb):
|
||||
# dispatch message (JSON RPC like)
|
||||
method = getattr(self, request['method'])
|
||||
response['result'] = method.__call__(*request['args'],
|
||||
**request.get('kwargs', {}))
|
||||
**request.get('kwargs', {}))
|
||||
except Exception, e:
|
||||
response['error'] = {'code': 0, 'message': str(e)}
|
||||
# send the result for normal method calls, not for notifications
|
||||
@@ -83,7 +84,7 @@ class Qdb(bdb.Bdb):
|
||||
self.pull_actions()
|
||||
# process the frame (see Bdb.trace_dispatch)
|
||||
if self.quitting:
|
||||
return # None
|
||||
return # None
|
||||
if event == 'line':
|
||||
return self.dispatch_line(frame)
|
||||
if event == 'call':
|
||||
@@ -106,7 +107,7 @@ class Qdb(bdb.Bdb):
|
||||
"""This function is called when we stop or break at this line."""
|
||||
if self._wait_for_mainpyfile:
|
||||
if (not self.canonic(frame.f_code.co_filename).startswith(self.mainpyfile)
|
||||
or frame.f_lineno<= 0):
|
||||
or frame.f_lineno <= 0):
|
||||
return
|
||||
self._wait_for_mainpyfile = 0
|
||||
if self._wait_for_breakpoint:
|
||||
@@ -150,11 +151,11 @@ class Qdb(bdb.Bdb):
|
||||
import __main__
|
||||
import imp
|
||||
__main__.__dict__.clear()
|
||||
__main__.__dict__.update({"__name__" : "__main__",
|
||||
"__file__" : filename,
|
||||
__main__.__dict__.update({"__name__": "__main__",
|
||||
"__file__": filename,
|
||||
"__builtins__": __builtins__,
|
||||
"imp" : imp, # need for run
|
||||
})
|
||||
"imp": imp, # need for run
|
||||
})
|
||||
|
||||
# avoid stopping before we reach the main script
|
||||
self._wait_for_mainpyfile = 1
|
||||
@@ -200,8 +201,8 @@ class Qdb(bdb.Bdb):
|
||||
if self.params.get('environment'):
|
||||
kwargs['environment'] = self.do_environment()
|
||||
self.pipe.send({'method': 'interaction', 'id': None,
|
||||
'args': (filename, self.frame.f_lineno, line),
|
||||
'kwargs': kwargs})
|
||||
'args': (filename, self.frame.f_lineno, line),
|
||||
'kwargs': kwargs})
|
||||
|
||||
self.pull_actions()
|
||||
|
||||
@@ -278,7 +279,7 @@ class Qdb(bdb.Bdb):
|
||||
filename = self.frame.f_code.co_filename
|
||||
breaklist = self.get_file_breaks(filename)
|
||||
lines = []
|
||||
for lineno in range(first, last+1):
|
||||
for lineno in range(first, last + 1):
|
||||
line = linecache.getline(filename, lineno,
|
||||
self.frame.f_globals)
|
||||
if not line:
|
||||
@@ -303,7 +304,7 @@ class Qdb(bdb.Bdb):
|
||||
for bp in bdb.Breakpoint.bpbynumber:
|
||||
if bp:
|
||||
breaks.append((bp.number, bp.file, bp.line,
|
||||
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
|
||||
bp.temporary, bp.enabled, bp.hits, bp.cond, ))
|
||||
return breaks
|
||||
|
||||
def do_clear_breakpoint(self, filename, lineno):
|
||||
@@ -320,7 +321,7 @@ class Qdb(bdb.Bdb):
|
||||
|
||||
def do_eval(self, arg, safe=True):
|
||||
ret = eval(arg, self.frame.f_globals,
|
||||
self.frame_locals)
|
||||
self.frame_locals)
|
||||
if safe:
|
||||
ret = pydoc.cram(repr(ret), 255)
|
||||
return ret
|
||||
@@ -354,9 +355,11 @@ class Qdb(bdb.Bdb):
|
||||
# converts the frame global and locals to a short text representation:
|
||||
if self.frame:
|
||||
for name, value in self.frame_locals.items():
|
||||
env['locals'][name] = pydoc.cram(repr(value), 255), repr(type(value))
|
||||
env['locals'][name] = pydoc.cram(repr(
|
||||
value), 255), repr(type(value))
|
||||
for name, value in self.frame.f_globals.items():
|
||||
env['globals'][name] = pydoc.cram(repr(value), 20), repr(type(value))
|
||||
env['globals'][name] = pydoc.cram(repr(
|
||||
value), 20), repr(type(value))
|
||||
return env
|
||||
|
||||
def get_autocomplete_list(self, expression):
|
||||
@@ -589,8 +592,8 @@ class Frontend(object):
|
||||
result = self.readline()
|
||||
if result:
|
||||
response = {'version': '1.1', 'id': request.get('id'),
|
||||
'result': result,
|
||||
'error': None}
|
||||
'result': result,
|
||||
'error': None}
|
||||
self.send(response)
|
||||
return True
|
||||
|
||||
@@ -609,7 +612,8 @@ class Frontend(object):
|
||||
# nested request received (i.e. readline)! process it!
|
||||
self.process_message(res)
|
||||
elif long(req['id']) != long(res['id']):
|
||||
print "DEBUGGER wrong packet received: expecting id", req['id'], res['id']
|
||||
print "DEBUGGER wrong packet received: expecting id", req[
|
||||
'id'], res['id']
|
||||
# protocol state is unknown
|
||||
elif 'error' in res and res['error']:
|
||||
raise RPCError(res['error']['message'])
|
||||
@@ -736,7 +740,7 @@ class Cli(Frontend, cmd.Cmd):
|
||||
return raw_input()
|
||||
|
||||
def postcmd(self, stop, line):
|
||||
return not line.startswith("h") # stop
|
||||
return not line.startswith("h") # stop
|
||||
|
||||
do_h = cmd.Cmd.do_help
|
||||
|
||||
@@ -838,6 +842,7 @@ def test():
|
||||
class Test(Frontend):
|
||||
def interaction(self, *args):
|
||||
print "interaction!", args
|
||||
|
||||
def exception(self, *args):
|
||||
print "exception", args
|
||||
#raise RuntimeError("exception %s" % repr(args))
|
||||
@@ -877,7 +882,7 @@ def main(host='localhost', port=6000, authkey='secret password'):
|
||||
print "usage: pdb.py scriptfile [arg] ..."
|
||||
sys.exit(2)
|
||||
|
||||
mainpyfile = sys.argv[1] # Get script filename
|
||||
mainpyfile = sys.argv[1] # Get script filename
|
||||
if not os.path.exists(mainpyfile):
|
||||
print 'Error:', mainpyfile, 'does not exist'
|
||||
sys.exit(1)
|
||||
@@ -913,6 +918,8 @@ def main(host='localhost', port=6000, authkey='secret password'):
|
||||
|
||||
|
||||
qdb = None
|
||||
|
||||
|
||||
def set_trace(host='localhost', port=6000, authkey='secret password'):
|
||||
"Simplified interface to debug running programs"
|
||||
global qdb, listener, conn
|
||||
@@ -950,7 +957,7 @@ if __name__ == '__main__':
|
||||
# Check environment for configuration parameters:
|
||||
kwargs = {}
|
||||
for param in 'host', 'port', 'authkey':
|
||||
if 'QDB_%s' % param.upper() in os.environ:
|
||||
if 'QDB_%s' % param.upper() in os.environ:
|
||||
kwargs[param] = os.environ['QDB_%s' % param.upper()]
|
||||
|
||||
if not sys.argv[1:]:
|
||||
|
||||
@@ -17,6 +17,7 @@ logger = logging.getLogger("web2py.cache.redis")
|
||||
|
||||
locker = thread.allocate_lock()
|
||||
|
||||
|
||||
def RedisCache(*args, **vars):
|
||||
"""
|
||||
Usage example: put in models
|
||||
@@ -47,10 +48,11 @@ class RedisClient(object):
|
||||
meta_storage = {}
|
||||
MAX_RETRIES = 5
|
||||
RETRIES = 0
|
||||
|
||||
def __init__(self, server='localhost:6379', db=None, debug=False):
|
||||
self.server = server
|
||||
self.db = db or 0
|
||||
host,port = (self.server.split(':')+['6379'])[:2]
|
||||
host, port = (self.server.split(':') + ['6379'])[:2]
|
||||
port = int(port)
|
||||
self.request = current.request
|
||||
self.debug = debug
|
||||
@@ -64,7 +66,7 @@ class RedisClient(object):
|
||||
CacheAbstract.cache_stats_name: {
|
||||
'hit_total': 0,
|
||||
'misses': 0,
|
||||
}}
|
||||
}}
|
||||
else:
|
||||
self.storage = self.meta_storage[app]
|
||||
|
||||
@@ -72,8 +74,8 @@ class RedisClient(object):
|
||||
|
||||
def __call__(self, key, f, time_expire=300):
|
||||
try:
|
||||
if time_expire == None:
|
||||
time_expire = 24*60*60
|
||||
if time_expire is None:
|
||||
time_expire = 24 * 60 * 60
|
||||
newKey = self.__keyFormat__(key)
|
||||
value = None
|
||||
obj = self.r_server.get(newKey)
|
||||
@@ -100,13 +102,15 @@ class RedisClient(object):
|
||||
def retry_call(self, key, f, time_expire):
|
||||
self.RETRIES += 1
|
||||
if self.RETRIES <= self.MAX_RETRIES:
|
||||
logger.error("sleeping %s seconds before reconnecting" % (2 * self.RETRIES))
|
||||
logger.error("sleeping %s seconds before reconnecting" %
|
||||
(2 * self.RETRIES))
|
||||
time.sleep(2 * self.RETRIES)
|
||||
self.__init__(self.server, self.db, self.debug)
|
||||
return self.__call__(key, f, time_expire)
|
||||
else:
|
||||
self.RETRIES = 0
|
||||
raise ConnectionError , 'Redis instance is unavailable at %s' % (self.server)
|
||||
raise ConnectionError('Redis instance is unavailable at %s' % (
|
||||
self.server))
|
||||
|
||||
def increment(self, key, value=1, time_expire=300):
|
||||
try:
|
||||
@@ -129,7 +133,8 @@ class RedisClient(object):
|
||||
return self.increment(key, value, time_expire)
|
||||
else:
|
||||
self.RETRIES = 0
|
||||
raise ConnectionError , 'Redis instance is unavailable at %s' % (self.server)
|
||||
raise ConnectionError('Redis instance is unavailable at %s' % (
|
||||
self.server))
|
||||
|
||||
def clear(self, regex):
|
||||
"""
|
||||
@@ -139,8 +144,8 @@ class RedisClient(object):
|
||||
r = re.compile(regex)
|
||||
prefix = "w2p:%s:" % (self.request.application)
|
||||
pipe = self.r_server.pipeline()
|
||||
for a in self.r_server.keys("%s*" % \
|
||||
(prefix)):
|
||||
for a in self.r_server.keys("%s*" %
|
||||
(prefix)):
|
||||
if r.match(str(a).replace(prefix, '', 1)):
|
||||
pipe.delete(a)
|
||||
pipe.execute()
|
||||
@@ -149,10 +154,10 @@ class RedisClient(object):
|
||||
statscollector = self.r_server.info()
|
||||
if self.debug:
|
||||
statscollector['w2p_stats'] = dict(
|
||||
hit_total = self.r_server.get(
|
||||
hit_total=self.r_server.get(
|
||||
'web2py_cache_statistics:hit_total'),
|
||||
misses=self.r_server.get('web2py_cache_statistics:misses')
|
||||
)
|
||||
)
|
||||
statscollector['w2p_keys'] = dict()
|
||||
for a in self.r_server.keys("w2p:%s:*" % (
|
||||
self.request.application)):
|
||||
|
||||
@@ -16,6 +16,7 @@ logger = logging.getLogger("web2py.session.redis")
|
||||
|
||||
locker = thread.allocate_lock()
|
||||
|
||||
|
||||
def RedisSession(*args, **vars):
|
||||
"""
|
||||
Usage example: put in models
|
||||
@@ -48,7 +49,7 @@ class RedisClient(object):
|
||||
"""
|
||||
self.server = server
|
||||
self.db = db or 0
|
||||
host,port = (self.server.split(':')+['6379'])[:2]
|
||||
host, port = (self.server.split(':') + ['6379'])[:2]
|
||||
port = int(port)
|
||||
self.debug = debug
|
||||
if current and current.request:
|
||||
@@ -63,12 +64,13 @@ class RedisClient(object):
|
||||
return self.tablename
|
||||
|
||||
def Field(self, fieldname, type='string', length=None, default=None,
|
||||
required=False,requires=None):
|
||||
required=False, requires=None):
|
||||
return None
|
||||
|
||||
def define_table(self,tablename,*fields,**args):
|
||||
def define_table(self, tablename, *fields, **args):
|
||||
if not self.tablename:
|
||||
self.tablename = MockTable(self, self.r_server, tablename, self.session_expiry)
|
||||
self.tablename = MockTable(
|
||||
self, self.r_server, tablename, self.session_expiry)
|
||||
return self.tablename
|
||||
|
||||
def __getitem__(self, key):
|
||||
@@ -82,6 +84,7 @@ class RedisClient(object):
|
||||
#this is only called by session2trash.py
|
||||
pass
|
||||
|
||||
|
||||
class MockTable(object):
|
||||
|
||||
def __init__(self, db, r_server, tablename, session_expiry):
|
||||
@@ -89,7 +92,8 @@ class MockTable(object):
|
||||
self.r_server = r_server
|
||||
self.tablename = tablename
|
||||
#set the namespace for sessions of this app
|
||||
self.keyprefix = 'w2p:sess:%s' % tablename.replace('web2py_session_', '')
|
||||
self.keyprefix = 'w2p:sess:%s' % tablename.replace(
|
||||
'web2py_session_', '')
|
||||
#fast auto-increment id (needed for session handling)
|
||||
self.serial = "%s:serial" % self.keyprefix
|
||||
#index of all the session keys of this app
|
||||
@@ -125,6 +129,7 @@ class MockTable(object):
|
||||
self.r_server.expire(key, self.session_expiry)
|
||||
return newid
|
||||
|
||||
|
||||
class MockQuery(object):
|
||||
"""a fake Query object that supports querying by id
|
||||
and listing all keys. No other operation is supported
|
||||
@@ -171,7 +176,8 @@ class MockQuery(object):
|
||||
continue
|
||||
val = Storage(val)
|
||||
#add a delete_record method (necessary for sessions2trash.py)
|
||||
val.delete_record = RecordDeleter(self.db, sess, self.keyprefix)
|
||||
val.delete_record = RecordDeleter(
|
||||
self.db, sess, self.keyprefix)
|
||||
rtn.append(val)
|
||||
return rtn
|
||||
else:
|
||||
@@ -185,6 +191,7 @@ class MockQuery(object):
|
||||
self.db.expire(key, self.session.expiry)
|
||||
return rtn
|
||||
|
||||
|
||||
class RecordDeleter(object):
|
||||
"""Dumb record deleter to support sessions2trash.py"""
|
||||
|
||||
|
||||
+10
-7
@@ -54,14 +54,14 @@ UNPICKLABLE_TYPES = (
|
||||
types.TypeType,
|
||||
types.ClassType,
|
||||
types.FunctionType,
|
||||
)
|
||||
)
|
||||
|
||||
# Unpicklable statements to seed new historys with.
|
||||
INITIAL_UNPICKLABLES = [
|
||||
'import logging',
|
||||
'import os',
|
||||
'import sys',
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
class History:
|
||||
@@ -154,6 +154,7 @@ class History:
|
||||
if name in self.unpicklable_names:
|
||||
self.unpicklable_names.remove(name)
|
||||
|
||||
|
||||
def represent(obj):
|
||||
"""Returns a string representing the given object's value, which should allow the
|
||||
code below to determine whether the object changes over time.
|
||||
@@ -163,6 +164,7 @@ def represent(obj):
|
||||
except:
|
||||
return repr(obj)
|
||||
|
||||
|
||||
def run(history, statement, env={}):
|
||||
"""
|
||||
Evaluates a python statement in a given history and returns the result.
|
||||
@@ -180,7 +182,6 @@ def run(history, statement, env={}):
|
||||
# single-line expressions such as 'class Foo: pass' evaluate happily.
|
||||
statement += '\n\n'
|
||||
|
||||
|
||||
# log and compile the statement up front
|
||||
try:
|
||||
logging.info('Compiling and evaluating:\n%s' % statement)
|
||||
@@ -224,7 +225,8 @@ def run(history, statement, env={}):
|
||||
history.remove_global(name)
|
||||
|
||||
# run!
|
||||
old_globals = dict((key,represent(value)) for key,value in statement_module.__dict__.items())
|
||||
old_globals = dict((key, represent(
|
||||
value)) for key, value in statement_module.__dict__.items())
|
||||
try:
|
||||
old_stdout, old_stderr = sys.stdout, sys.stderr
|
||||
try:
|
||||
@@ -261,6 +263,7 @@ def run(history, statement, env={}):
|
||||
sys.modules['__main__'] = old_main
|
||||
return output.getvalue()
|
||||
|
||||
if __name__=='__main__':
|
||||
history=History()
|
||||
while True: print run(history, raw_input('>>> ')).rstrip()
|
||||
if __name__ == '__main__':
|
||||
history = History()
|
||||
while True:
|
||||
print run(history, raw_input('>>> ')).rstrip()
|
||||
@@ -46,8 +46,10 @@ class JSONDummyParser:
|
||||
"json wrapper for xmlrpclib parser interfase"
|
||||
def __init__(self):
|
||||
self.buf = StringIO()
|
||||
|
||||
def feed(self, data):
|
||||
self.buf.write(data)
|
||||
|
||||
def close(self):
|
||||
return self.buf.getvalue()
|
||||
|
||||
@@ -72,6 +74,7 @@ class JSONTransportMixin:
|
||||
class JSONTransport(JSONTransportMixin, Transport):
|
||||
pass
|
||||
|
||||
|
||||
class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
||||
pass
|
||||
|
||||
@@ -79,7 +82,7 @@ class JSONSafeTransport(JSONTransportMixin, SafeTransport):
|
||||
class ServerProxy(object):
|
||||
"JSON RPC Simple Client Service Proxy"
|
||||
|
||||
def __init__(self, uri, transport=None, encoding=None, verbose=0):
|
||||
def __init__(self, uri, transport=None, encoding=None, verbose=0):
|
||||
self.location = uri # server location (url)
|
||||
self.trace = verbose # show debug messages
|
||||
self.exceptions = True # raise errors? (JSONRPCError)
|
||||
@@ -88,7 +91,7 @@ class ServerProxy(object):
|
||||
|
||||
type, uri = urllib.splittype(uri)
|
||||
if type not in ("http", "https"):
|
||||
raise IOError, "unsupported JSON-RPC protocol"
|
||||
raise IOError("unsupported JSON-RPC protocol")
|
||||
self.__host, self.__handler = urllib.splithost(uri)
|
||||
|
||||
if transport is None:
|
||||
@@ -118,7 +121,7 @@ class ServerProxy(object):
|
||||
self.__handler,
|
||||
request,
|
||||
verbose=self.__verbose
|
||||
)
|
||||
)
|
||||
|
||||
# store plain request and response for further debugging
|
||||
self.json_request = request
|
||||
|
||||
+108
-105
@@ -1,112 +1,115 @@
|
||||
SMSCODES = {
|
||||
'Aliant':'@chat.wirefree.ca',
|
||||
'Alltel':'@message.alltel.com',
|
||||
'Ameritech':'@paging.acswireless.com',
|
||||
'AT&T':'@txt.att.net',
|
||||
'AU by KDDI':'@ezweb.ne.jp',
|
||||
'BeeLine GSM':'@sms.beemail.ru',
|
||||
'Bell Mobility Canada':'@txt.bellmobility.ca',
|
||||
'Bellsouth':'@bellsouth.cl',
|
||||
'BellSouth Mobility':'@blsdcs.net',
|
||||
'Blue Sky Frog':'@blueskyfrog.com',
|
||||
'Boost':'@myboostmobile.com',
|
||||
'Cellular South':'@csouth1.com',
|
||||
'CellularOne':'@mobile.celloneusa.com',
|
||||
'CellularOne West':'@mycellone.com',
|
||||
'Cincinnati Bell':'@gocbw.com',
|
||||
'Claro':'@clarotorpedo.com.br',
|
||||
'Comviq':'@sms.comviq.se',
|
||||
'Dutchtone/Orange-NL':'@sms.orange.nl',
|
||||
'Edge Wireless':'@sms.edgewireless.com',
|
||||
'EinsteinPCS / Airadigm Communications':'@einsteinsms.com',
|
||||
'EPlus':'@smsmail.eplus.de',
|
||||
'Fido Canada':'@fido.ca',
|
||||
'Golden Telecom':'@sms.goldentele.com',
|
||||
'Idea Cellular':'@ideacellular.net',
|
||||
'Kyivstar':'@sms.kyivstar.net',
|
||||
'LMT':'@sms.lmt.lv',
|
||||
'Manitoba Telecom Systems':'@text.mtsmobility.com',
|
||||
'Meteor':'@sms.mymeteor.ie',
|
||||
'Metro PCS':'@mymetropcs.com',
|
||||
'Metrocall Pager':'@page.metrocall.com',
|
||||
'MobileOne':'@m1.com.sg',
|
||||
'Mobilfone':'@page.mobilfone.com',
|
||||
'Mobility Bermuda':'@ml.bm',
|
||||
'Netcom':'@sms.netcom.no',
|
||||
'Nextel':'@messaging.nextel.com',
|
||||
'NPI Wireless':'@npiwireless.com',
|
||||
'O2':'@o2.co.uk',
|
||||
'O2 M-mail':'@mmail.co.uk',
|
||||
'Optus':'@optusmobile.com.au',
|
||||
'Orange':'@orange.net',
|
||||
'Oskar':'@mujoskar.cz',
|
||||
'Pagenet':'@pagenet.net',
|
||||
'PCS Rogers':'@pcs.rogers.com',
|
||||
'Personal Communication':'@pcom.ru',
|
||||
'Plus GSM Poland':'@text.plusgsm.pl',
|
||||
'Powertel':'@ptel.net',
|
||||
'Primtel':'@sms.primtel.ru',
|
||||
'PSC Wireless':'@sms.pscel.com',
|
||||
'Qualcomm':'@pager.qualcomm.com',
|
||||
'Qwest':'@qwestmp.com',
|
||||
'Safaricom':'@safaricomsms.com',
|
||||
'Satelindo GSM':'@satelindogsm.com',
|
||||
'SCS-900':'@scs-900.ru',
|
||||
'Simple Freedom':'@text.simplefreedom.net',
|
||||
'Skytel - Alphanumeric':'@skytel.com',
|
||||
'Smart Telecom':'@mysmart.mymobile.ph',
|
||||
'Southern Linc':'@page.southernlinc.com',
|
||||
'Sprint PCS':'@messaging.sprintpcs.com',
|
||||
'Sprint PCS - Short Mail':'@sprintpcs.com',
|
||||
'SunCom':'@tms.suncom.com',
|
||||
'SureWest Communications':'@mobile.surewest.com',
|
||||
'SwissCom Mobile':'@bluewin.ch',
|
||||
'T-Mobile Germany':'@T-D1-SMS.de',
|
||||
'T-Mobile Netherlands':'@gin.nl',
|
||||
'T-Mobile UK':'@t-mobile.uk.net',
|
||||
'T-Mobile USA (tmail)':'@tmail.com',
|
||||
'T-Mobile USA (tmomail)':'@tmomail.net',
|
||||
'Tele2 Latvia':'@sms.tele2.lv',
|
||||
'Telefonica Movistar':'@movistar.net',
|
||||
'Telenor':'@mobilpost.no',
|
||||
'Telia Denmark':'@gsm1800.telia.dk',
|
||||
'Telus Mobility':'@msg.telus.com',
|
||||
'The Phone House':'@sms.phonehouse.de',
|
||||
'TIM':'@timnet.com',
|
||||
'UMC':'@sms.umc.com.ua',
|
||||
'Unicel':'@utext.com',
|
||||
'US Cellular':'@email.uscc.net',
|
||||
'Verizon Wireless (vtext)':'@vtext.com',
|
||||
'Verizon Wireless (airtouchpaging)':'@airtouchpaging.com',
|
||||
'Verizon Wireless (myairmail)':'@myairmail.com',
|
||||
'Vessotel':'@pager.irkutsk.ru',
|
||||
'Virgin Mobile Canada':'@vmobile.ca',
|
||||
'Virgin Mobile USA':'@vmobl.com',
|
||||
'Vodafone Italy':'@sms.vodafone.it',
|
||||
'Vodafone Japan (n)':'@n.vodafone.ne.jp',
|
||||
'Vodafone Japan (d)':'@d.vodafone.ne.jp',
|
||||
'Vodafone Japan (r)':'@r.vodafone.ne.jp',
|
||||
'Vodafone Japan (k)':'@k.vodafone.ne.jp',
|
||||
'Vodafone Japan (t)':'@t.vodafone.ne.jp',
|
||||
'Vodafone Japan (q)':'@q.vodafone.ne.jp',
|
||||
'Vodafone Japan (s)':'@s.vodafone.ne.jp',
|
||||
'Vodafone Japan (h)':'@h.vodafone.ne.jp',
|
||||
'Vodafone Japan (c)':'@c.vodafone.ne.jp',
|
||||
'Vodafone Spain':'@vodafone.es',
|
||||
'Vodafone UK':'@vodafone.net',
|
||||
'Weblink Wireless':'@airmessage.net',
|
||||
'WellCom':'@sms.welcome2well.com',
|
||||
'WyndTell':'@wyndtell.com',
|
||||
}
|
||||
'Aliant': '@chat.wirefree.ca',
|
||||
'Alltel': '@message.alltel.com',
|
||||
'Ameritech': '@paging.acswireless.com',
|
||||
'AT&T': '@txt.att.net',
|
||||
'AU by KDDI': '@ezweb.ne.jp',
|
||||
'BeeLine GSM': '@sms.beemail.ru',
|
||||
'Bell Mobility Canada': '@txt.bellmobility.ca',
|
||||
'Bellsouth': '@bellsouth.cl',
|
||||
'BellSouth Mobility': '@blsdcs.net',
|
||||
'Blue Sky Frog': '@blueskyfrog.com',
|
||||
'Boost': '@myboostmobile.com',
|
||||
'Cellular South': '@csouth1.com',
|
||||
'CellularOne': '@mobile.celloneusa.com',
|
||||
'CellularOne West': '@mycellone.com',
|
||||
'Cincinnati Bell': '@gocbw.com',
|
||||
'Claro': '@clarotorpedo.com.br',
|
||||
'Comviq': '@sms.comviq.se',
|
||||
'Dutchtone/Orange-NL': '@sms.orange.nl',
|
||||
'Edge Wireless': '@sms.edgewireless.com',
|
||||
'EinsteinPCS / Airadigm Communications': '@einsteinsms.com',
|
||||
'EPlus': '@smsmail.eplus.de',
|
||||
'Fido Canada': '@fido.ca',
|
||||
'Golden Telecom': '@sms.goldentele.com',
|
||||
'Idea Cellular': '@ideacellular.net',
|
||||
'Kyivstar': '@sms.kyivstar.net',
|
||||
'LMT': '@sms.lmt.lv',
|
||||
'Manitoba Telecom Systems': '@text.mtsmobility.com',
|
||||
'Meteor': '@sms.mymeteor.ie',
|
||||
'Metro PCS': '@mymetropcs.com',
|
||||
'Metrocall Pager': '@page.metrocall.com',
|
||||
'MobileOne': '@m1.com.sg',
|
||||
'Mobilfone': '@page.mobilfone.com',
|
||||
'Mobility Bermuda': '@ml.bm',
|
||||
'Netcom': '@sms.netcom.no',
|
||||
'Nextel': '@messaging.nextel.com',
|
||||
'NPI Wireless': '@npiwireless.com',
|
||||
'O2': '@o2.co.uk',
|
||||
'O2 M-mail': '@mmail.co.uk',
|
||||
'Optus': '@optusmobile.com.au',
|
||||
'Orange': '@orange.net',
|
||||
'Oskar': '@mujoskar.cz',
|
||||
'Pagenet': '@pagenet.net',
|
||||
'PCS Rogers': '@pcs.rogers.com',
|
||||
'Personal Communication': '@pcom.ru',
|
||||
'Plus GSM Poland': '@text.plusgsm.pl',
|
||||
'Powertel': '@ptel.net',
|
||||
'Primtel': '@sms.primtel.ru',
|
||||
'PSC Wireless': '@sms.pscel.com',
|
||||
'Qualcomm': '@pager.qualcomm.com',
|
||||
'Qwest': '@qwestmp.com',
|
||||
'Safaricom': '@safaricomsms.com',
|
||||
'Satelindo GSM': '@satelindogsm.com',
|
||||
'SCS-900': '@scs-900.ru',
|
||||
'Simple Freedom': '@text.simplefreedom.net',
|
||||
'Skytel - Alphanumeric': '@skytel.com',
|
||||
'Smart Telecom': '@mysmart.mymobile.ph',
|
||||
'Southern Linc': '@page.southernlinc.com',
|
||||
'Sprint PCS': '@messaging.sprintpcs.com',
|
||||
'Sprint PCS - Short Mail': '@sprintpcs.com',
|
||||
'SunCom': '@tms.suncom.com',
|
||||
'SureWest Communications': '@mobile.surewest.com',
|
||||
'SwissCom Mobile': '@bluewin.ch',
|
||||
'T-Mobile Germany': '@T-D1-SMS.de',
|
||||
'T-Mobile Netherlands': '@gin.nl',
|
||||
'T-Mobile UK': '@t-mobile.uk.net',
|
||||
'T-Mobile USA (tmail)': '@tmail.com',
|
||||
'T-Mobile USA (tmomail)': '@tmomail.net',
|
||||
'Tele2 Latvia': '@sms.tele2.lv',
|
||||
'Telefonica Movistar': '@movistar.net',
|
||||
'Telenor': '@mobilpost.no',
|
||||
'Telia Denmark': '@gsm1800.telia.dk',
|
||||
'Telus Mobility': '@msg.telus.com',
|
||||
'The Phone House': '@sms.phonehouse.de',
|
||||
'TIM': '@timnet.com',
|
||||
'UMC': '@sms.umc.com.ua',
|
||||
'Unicel': '@utext.com',
|
||||
'US Cellular': '@email.uscc.net',
|
||||
'Verizon Wireless (vtext)': '@vtext.com',
|
||||
'Verizon Wireless (airtouchpaging)': '@airtouchpaging.com',
|
||||
'Verizon Wireless (myairmail)': '@myairmail.com',
|
||||
'Vessotel': '@pager.irkutsk.ru',
|
||||
'Virgin Mobile Canada': '@vmobile.ca',
|
||||
'Virgin Mobile USA': '@vmobl.com',
|
||||
'Vodafone Italy': '@sms.vodafone.it',
|
||||
'Vodafone Japan (n)': '@n.vodafone.ne.jp',
|
||||
'Vodafone Japan (d)': '@d.vodafone.ne.jp',
|
||||
'Vodafone Japan (r)': '@r.vodafone.ne.jp',
|
||||
'Vodafone Japan (k)': '@k.vodafone.ne.jp',
|
||||
'Vodafone Japan (t)': '@t.vodafone.ne.jp',
|
||||
'Vodafone Japan (q)': '@q.vodafone.ne.jp',
|
||||
'Vodafone Japan (s)': '@s.vodafone.ne.jp',
|
||||
'Vodafone Japan (h)': '@h.vodafone.ne.jp',
|
||||
'Vodafone Japan (c)': '@c.vodafone.ne.jp',
|
||||
'Vodafone Spain': '@vodafone.es',
|
||||
'Vodafone UK': '@vodafone.net',
|
||||
'Weblink Wireless': '@airmessage.net',
|
||||
'WellCom': '@sms.welcome2well.com',
|
||||
'WyndTell': '@wyndtell.com',
|
||||
}
|
||||
|
||||
def sms_email(number,provider):
|
||||
|
||||
def sms_email(number, provider):
|
||||
"""
|
||||
>>> print sms_email('1 (312) 375-6536','T-Mobile USA (tmail)')
|
||||
print 13123756536@tmail.com
|
||||
"""
|
||||
import re
|
||||
if number[0]=='+1': number=number[1:]
|
||||
elif number[0]=='+': number=number[3:]
|
||||
elif number[:2]=='00': number=number[3:]
|
||||
number=re.sub('[^\d]','',number)
|
||||
return number+SMSCODES[provider]
|
||||
if number[0] == '+1':
|
||||
number = number[1:]
|
||||
elif number[0] == '+':
|
||||
number = number[3:]
|
||||
elif number[:2] == '00': number = number[3:]
|
||||
number = re.sub('[^\d]', '', number)
|
||||
return number + SMSCODES[provider]
|
||||
@@ -9,13 +9,14 @@ import pickle
|
||||
import copy
|
||||
import simplejson
|
||||
|
||||
|
||||
def quote(text):
|
||||
return str(text).replace('\\', '\\\\').replace("'", "\\'")
|
||||
|
||||
|
||||
class Node:
|
||||
def __init__(self, name, value, url='.', readonly=False, active=True,
|
||||
onchange=None, **kwarg):
|
||||
onchange=None, **kwarg):
|
||||
self.url = url
|
||||
self.name = name
|
||||
self.value = str(value)
|
||||
@@ -246,8 +247,8 @@ class Sheet:
|
||||
r, c = int(r), int(c)
|
||||
except (ValueError, IndexError, TypeError), e:
|
||||
error = "%s. %s" % \
|
||||
("Unexpected position parameter",
|
||||
"Must be a key of type 'rncn'")
|
||||
("Unexpected position parameter",
|
||||
"Must be a key of type 'rncn'")
|
||||
raise ValueError(error)
|
||||
return r, c
|
||||
|
||||
@@ -316,7 +317,6 @@ class Sheet:
|
||||
|
||||
return changes
|
||||
|
||||
|
||||
def process(self, request, db=None, db_callback=None):
|
||||
"""
|
||||
call this in action that creates table, it will handle ajax callbacks
|
||||
@@ -333,22 +333,22 @@ class Sheet:
|
||||
|
||||
if request.args(0) == 'focus':
|
||||
return "jQuery('#%(cell)s').val('%(value)s');" % \
|
||||
dict(cell=cell, value=quote(self[cell].value))
|
||||
dict(cell=cell, value=quote(self[cell].value))
|
||||
|
||||
value = request.vars[cell]
|
||||
self[cell] = value
|
||||
|
||||
if request.args(0) == 'blur':
|
||||
return "jQuery('#%(cell)s').val('%(value)s');" % \
|
||||
dict(cell=cell, value=quote(self[cell].computed_value))
|
||||
dict(cell=cell, value=quote(self[cell].computed_value))
|
||||
|
||||
elif request.args(0) == 'keyup':
|
||||
jquery = ''
|
||||
for other_key in self.modified:
|
||||
if other_key != cell:
|
||||
jquery += "jQuery('#%(other_key)s').val('%(value)s');" % \
|
||||
dict(other_key=other_key,
|
||||
value=quote(self[other_key].computed_value))
|
||||
dict(other_key=other_key,
|
||||
value=quote(self[other_key].computed_value))
|
||||
|
||||
else:
|
||||
# spreadsheet db update
|
||||
@@ -390,8 +390,8 @@ class Sheet:
|
||||
return "odd"
|
||||
|
||||
def __init__(self, rows, cols, url='.', readonly=False,
|
||||
active=True, onchange=None, value=None, data=None,
|
||||
headers=None, update_button="", **kwarg):
|
||||
active=True, onchange=None, value=None, data=None,
|
||||
headers=None, update_button="", **kwarg):
|
||||
|
||||
"""
|
||||
Arguments:
|
||||
@@ -418,7 +418,7 @@ class Sheet:
|
||||
self.nodes = {}
|
||||
self.error = 'ERROR: %(error)s'
|
||||
self.allowed_keywords = ['for', 'in', 'if', 'else', 'and', 'or', 'not',
|
||||
'i', 'j', 'k', 'x', 'y', 'z', 'sum']
|
||||
'i', 'j', 'k', 'x', 'y', 'z', 'sum']
|
||||
self.value = value
|
||||
self.environment = {}
|
||||
self.attributes = self.get_attributes(kwarg)
|
||||
@@ -431,14 +431,14 @@ class Sheet:
|
||||
self.update_button = update_button
|
||||
|
||||
self.client = {
|
||||
"columns": {},
|
||||
"colnames": {},
|
||||
"id_columns": [],
|
||||
"id_colnames": {},
|
||||
"cells": {},
|
||||
"modified": {},
|
||||
"headers": headers
|
||||
}
|
||||
"columns": {},
|
||||
"colnames": {},
|
||||
"id_columns": [],
|
||||
"id_colnames": {},
|
||||
"cells": {},
|
||||
"modified": {},
|
||||
"headers": headers
|
||||
}
|
||||
|
||||
# if db and query:
|
||||
if self.data is not None:
|
||||
@@ -467,10 +467,10 @@ class Sheet:
|
||||
self.client["id_columns"].append(y)
|
||||
self.client["id_colnames"][colname] = y
|
||||
|
||||
for k in xrange(self.rows*self.cols):
|
||||
key = 'r%sc%s'%(k/self.cols, k%self.cols)
|
||||
for k in xrange(self.rows * self.cols):
|
||||
key = 'r%sc%s' % (k / self.cols, k % self.cols)
|
||||
r, c = self.position(key)
|
||||
if self.client["cells"].has_key(key):
|
||||
if key in self.client["cells"]:
|
||||
value = self.client["cells"][key]
|
||||
# readonly id values
|
||||
if c in self.client["id_columns"]:
|
||||
@@ -485,7 +485,7 @@ class Sheet:
|
||||
else:
|
||||
value = '0.00'
|
||||
self.cell(key, value,
|
||||
readonly, active, onchange)
|
||||
readonly, active, onchange)
|
||||
|
||||
exec('from math import *', {}, self.environment)
|
||||
|
||||
@@ -565,7 +565,7 @@ class Sheet:
|
||||
for col, data in cells.iteritems():
|
||||
key = "r%sc%s" % (row, col)
|
||||
active, onchange, readonly, cell_value = \
|
||||
self.get_cell_arguments(data, default=kwarg)
|
||||
self.get_cell_arguments(data, default=kwarg)
|
||||
if value is None:
|
||||
v = cell_value
|
||||
else:
|
||||
@@ -575,7 +575,7 @@ class Sheet:
|
||||
onchange=onchange, **attributes)
|
||||
else:
|
||||
active, onchange, readonly, all_value = \
|
||||
self.get_cell_arguments(kwarg)
|
||||
self.get_cell_arguments(kwarg)
|
||||
for col, cell_value in enumerate(cells):
|
||||
key = "r%sc%s" % (row, col)
|
||||
if value is None:
|
||||
@@ -601,7 +601,7 @@ class Sheet:
|
||||
for row, data in cells.iteritems():
|
||||
key = "r%sc%s" % (row, col)
|
||||
active, onchange, readonly, cell_value = \
|
||||
self.get_cell_arguments(data, default=kwarg)
|
||||
self.get_cell_arguments(data, default=kwarg)
|
||||
if value is None:
|
||||
v = cell_value
|
||||
else:
|
||||
@@ -610,7 +610,7 @@ class Sheet:
|
||||
onchange=onchange, **attributes)
|
||||
else:
|
||||
active, onchange, readonly, all_value = \
|
||||
self.get_cell_arguments(kwarg)
|
||||
self.get_cell_arguments(kwarg)
|
||||
for row, cell_value in enumerate(cells):
|
||||
key = "r%sc%s" % (row, col)
|
||||
if value is None:
|
||||
@@ -646,36 +646,36 @@ class Sheet:
|
||||
if isinstance(cells, dict):
|
||||
for key, data in cells.iteritems():
|
||||
r, c = self.position(key)
|
||||
key = "r%sc%s" % (r+starts_r, c+starts_c)
|
||||
key = "r%sc%s" % (r + starts_r, c + starts_c)
|
||||
active, onchange, readonly, cell_value = \
|
||||
self.get_cell_arguments(data, default=kwarg)
|
||||
self.get_cell_arguments(data, default=kwarg)
|
||||
if value is None:
|
||||
v = cell_value
|
||||
else:
|
||||
v = value
|
||||
if (ends is None) or ((ends_r >= r+starts_r) and\
|
||||
(ends_c >= c+starts_c)):
|
||||
if (ends is None) or ((ends_r >= r + starts_r) and
|
||||
(ends_c >= c + starts_c)):
|
||||
self.cell(key, v, active=active,
|
||||
readonly=readonly,
|
||||
onchange=onchange, **attributes)
|
||||
readonly=readonly,
|
||||
onchange=onchange, **attributes)
|
||||
else:
|
||||
active, onchange, readonly, all_value = \
|
||||
self.get_cell_arguments(kwarg)
|
||||
self.get_cell_arguments(kwarg)
|
||||
for r, row in enumerate(cells):
|
||||
for c, cell_value in enumerate(row):
|
||||
if value is None:
|
||||
v = cell_value
|
||||
else:
|
||||
v = value
|
||||
key = "r%sc%s" % (r+starts_r, c+starts_c)
|
||||
key = "r%sc%s" % (r + starts_r, c + starts_c)
|
||||
if (ends is None) or \
|
||||
((ends_r >= r+starts_r) and\
|
||||
(ends_c >= c+starts_c)):
|
||||
((ends_r >= r + starts_r) and
|
||||
(ends_c >= c + starts_c)):
|
||||
self.cell(key, v,
|
||||
active=active,
|
||||
onchange=onchange,
|
||||
readonly=readonly,
|
||||
**attributes)
|
||||
active=active,
|
||||
onchange=onchange,
|
||||
readonly=readonly,
|
||||
**attributes)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
key = str(key)
|
||||
@@ -694,7 +694,7 @@ class Sheet:
|
||||
other_key = match.group()
|
||||
if other_key == key:
|
||||
self.computed_value = self.error % dict(error='cycle')
|
||||
self.modified={}
|
||||
self.modified = {}
|
||||
break
|
||||
if other_key in self.nodes:
|
||||
other_node = self.nodes[other_key]
|
||||
@@ -704,7 +704,8 @@ class Sheet:
|
||||
not other_key in self.environment:
|
||||
node.locked = True
|
||||
node.computed_value = \
|
||||
self.error % dict(error='invalid keyword: ' + other_key)
|
||||
self.error % dict(
|
||||
error='invalid keyword: ' + other_key)
|
||||
self.modified = {}
|
||||
break
|
||||
self.compute(node)
|
||||
@@ -737,7 +738,7 @@ class Sheet:
|
||||
output = {node.name: node.computed_value}
|
||||
changed_nodes = self.changed(node)
|
||||
while changed_nodes:
|
||||
ok=False
|
||||
ok = False
|
||||
set_changed_nodes = set(changed_nodes)
|
||||
for (k, other_node) in enumerate(changed_nodes):
|
||||
#print other_node, changed_nodes
|
||||
@@ -758,7 +759,7 @@ class Sheet:
|
||||
return self.nodes[str(key)]
|
||||
|
||||
def get_computed_values(self):
|
||||
d={}
|
||||
d = {}
|
||||
for key in self.nodes:
|
||||
node = self.nodes[key]
|
||||
if node.value[:1] != '=' or not node.active:
|
||||
@@ -780,21 +781,21 @@ class Sheet:
|
||||
gluon.html.TH, gluon.html.BR, gluon.html.SCRIPT)
|
||||
regex = re.compile('r\d+c\d+')
|
||||
|
||||
header = TR(TH(), *[TH('c%s' % c) \
|
||||
for c in range(self.cols)])
|
||||
header = TR(TH(), *[TH('c%s' % c)
|
||||
for c in range(self.cols)])
|
||||
rows = []
|
||||
for r in range(self.rows):
|
||||
tds = [TH('r%s' % r),]
|
||||
tds = [TH('r%s' % r), ]
|
||||
for c in range(self.cols):
|
||||
key = 'r%sc%s'%(r, c)
|
||||
attributes = {"_class": "w2p_spreadsheet_col_%s" % \
|
||||
self.even_or_odd(c)}
|
||||
key = 'r%sc%s' % (r, c)
|
||||
attributes = {"_class": "w2p_spreadsheet_col_%s" %
|
||||
self.even_or_odd(c)}
|
||||
if key in self.td_attributes:
|
||||
attributes.update(self.td_attributes[key])
|
||||
td = TD(self.nodes[key], **attributes)
|
||||
tds.append(td)
|
||||
attributes = {"_class": "w2p_spreadsheet_row_%s" % \
|
||||
self.even_or_odd(r)}
|
||||
attributes = {"_class": "w2p_spreadsheet_row_%s" %
|
||||
self.even_or_odd(r)}
|
||||
if str(r) in self.tr_attributes:
|
||||
attributes.update(self.tr_attributes[str(r)])
|
||||
rows.append(TR(*tds, **attributes))
|
||||
@@ -805,8 +806,8 @@ class Sheet:
|
||||
table = TABLE(header, *rows, **self.attributes)
|
||||
|
||||
if len(self.client["cells"]) >= 1:
|
||||
data = SCRIPT(\
|
||||
"""
|
||||
data = SCRIPT(
|
||||
"""
|
||||
var w2p_spreadsheet_data = %(data)s;
|
||||
var w2p_spreadsheet_update_button = "%(update_button)s";
|
||||
var w2p_spreadsheet_update_result = null;
|
||||
@@ -835,7 +836,8 @@ class Sheet:
|
||||
|
||||
if (w2p_spreadsheet_update_button != ""){
|
||||
jQuery(function(){
|
||||
jQuery("#" + w2p_spreadsheet_update_button).click(w2p_spreadsheet_update_db);
|
||||
jQuery("#" + w2p_spreadsheet_update_button).click(
|
||||
w2p_spreadsheet_update_db);
|
||||
});
|
||||
}
|
||||
""" % dict(data=simplejson.dumps(self.client),
|
||||
@@ -848,22 +850,22 @@ class Sheet:
|
||||
if self.client["headers"] is not None:
|
||||
for fieldname, name in self.client["headers"].iteritems():
|
||||
unsorted_headers.append((self.client["columns"][fieldname],
|
||||
name))
|
||||
name))
|
||||
else:
|
||||
for fieldname, c in self.client["columns"].iteritems():
|
||||
unsorted_headers.append((c, fieldname))
|
||||
|
||||
sorted_headers = [TH(),] + \
|
||||
[TH(header[1]) for header in sorted(unsorted_headers)]
|
||||
sorted_headers = [TH(), ] + \
|
||||
[TH(header[1]) for header in sorted(unsorted_headers)]
|
||||
table.insert(0, TR(*sorted_headers,
|
||||
**{_class:"%s_fieldnames" % \
|
||||
attributes["_class"]}))
|
||||
**{_class: "%s_fieldnames" %
|
||||
attributes["_class"]}))
|
||||
else:
|
||||
data = SCRIPT(""" // web2py Spreadsheets: no db data.""")
|
||||
|
||||
return DIV(table,
|
||||
BR(),
|
||||
TABLE(*[TR(TH(key), TD(self.nodes[key])) \
|
||||
TABLE(*[TR(TH(key), TD(self.nodes[key]))
|
||||
for key in self.nodes if not regex.match(key)]),
|
||||
data, **attributes)
|
||||
|
||||
|
||||
+23
-21
@@ -1,6 +1,7 @@
|
||||
import urllib
|
||||
import simplejson
|
||||
|
||||
|
||||
class Stripe:
|
||||
"""
|
||||
Usage:
|
||||
@@ -22,8 +23,9 @@ class Stripe:
|
||||
if paid is True than transaction was processed
|
||||
"""
|
||||
|
||||
def __init__(self,key):
|
||||
def __init__(self, key):
|
||||
self.key = key
|
||||
|
||||
def charge(self,
|
||||
amount,
|
||||
currency='usd',
|
||||
@@ -32,33 +34,33 @@ class Stripe:
|
||||
card_exp_year='2012',
|
||||
card_cvc_check='123',
|
||||
description='test charge'):
|
||||
params = urllib.urlencode({'amount':amount,
|
||||
'currency':currency,
|
||||
'card[number]':card_number,
|
||||
'card[exp_month]':card_exp_month,
|
||||
'card[exp_year]':card_exp_year,
|
||||
'card[cvc_check]':card_cvc_check,
|
||||
'description':description})
|
||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges' % \
|
||||
self.key,params)
|
||||
params = urllib.urlencode({'amount': amount,
|
||||
'currency': currency,
|
||||
'card[number]': card_number,
|
||||
'card[exp_month]': card_exp_month,
|
||||
'card[exp_year]': card_exp_year,
|
||||
'card[cvc_check]': card_cvc_check,
|
||||
'description': description})
|
||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges' %
|
||||
self.key, params)
|
||||
return simplejson.loads(u.read())
|
||||
|
||||
def check(self,charge_id):
|
||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s' % \
|
||||
(self.key,charge_id))
|
||||
def check(self, charge_id):
|
||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s' %
|
||||
(self.key, charge_id))
|
||||
return simplejson.loads(u.read())
|
||||
|
||||
def refund(self,charge_id):
|
||||
def refund(self, charge_id):
|
||||
params = urllib.urlencode({})
|
||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s/refund'%\
|
||||
(self.key,charge_id),params)
|
||||
u = urllib.urlopen('https://%s:@api.stripe.com/v1/charges/%s/refund' %
|
||||
(self.key, charge_id), params)
|
||||
return simplejson.loads(u.read())
|
||||
|
||||
if __name__=='__main__':
|
||||
if __name__ == '__main__':
|
||||
key = raw_input('user>')
|
||||
d=Stripe(key).charge(100)
|
||||
print 'charged',d['paid']
|
||||
d = Stripe(key).charge(100)
|
||||
print 'charged', d['paid']
|
||||
s = Stripe(key).check(d[u'id'])
|
||||
print 'paid',s['paid'],s['amount'] ,s['currency']
|
||||
print 'paid', s['paid'], s['amount'], s['currency']
|
||||
s = Stripe(key).refund(d[u'id'])
|
||||
print 'refunded',s['refunded']
|
||||
print 'refunded', s['refunded']
|
||||
@@ -28,7 +28,7 @@ class TaskBarIcon:
|
||||
win32con.WM_DESTROY: self.OnDestroy,
|
||||
win32con.WM_COMMAND: self.OnCommand,
|
||||
win32con.WM_USER + 20: self.OnTaskbarNotify,
|
||||
}
|
||||
}
|
||||
|
||||
# Register the Window class.
|
||||
|
||||
@@ -56,7 +56,7 @@ class TaskBarIcon:
|
||||
0,
|
||||
hinst,
|
||||
None,
|
||||
)
|
||||
)
|
||||
win32gui.UpdateWindow(self.hwnd)
|
||||
self.SetServerStopped()
|
||||
|
||||
@@ -82,7 +82,7 @@ class TaskBarIcon:
|
||||
hicon = win32gui.LoadIcon(0, win32con.IDI_APPLICATION)
|
||||
|
||||
flags = win32gui.NIF_ICON | win32gui.NIF_MESSAGE\
|
||||
| win32gui.NIF_TIP
|
||||
| win32gui.NIF_TIP
|
||||
nid = (
|
||||
self.hwnd,
|
||||
0,
|
||||
@@ -90,7 +90,7 @@ class TaskBarIcon:
|
||||
win32con.WM_USER + 20,
|
||||
hicon,
|
||||
'web2py Framework',
|
||||
)
|
||||
)
|
||||
try:
|
||||
win32gui.Shell_NotifyIcon(win32gui.NIM_MODIFY, nid)
|
||||
except:
|
||||
@@ -111,7 +111,7 @@ class TaskBarIcon:
|
||||
msg,
|
||||
wparam,
|
||||
lparam,
|
||||
):
|
||||
):
|
||||
self._DoCreateIcons()
|
||||
|
||||
def OnDestroy(
|
||||
@@ -120,7 +120,7 @@ class TaskBarIcon:
|
||||
msg,
|
||||
wparam,
|
||||
lparam,
|
||||
):
|
||||
):
|
||||
nid = (self.hwnd, 0)
|
||||
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
|
||||
|
||||
@@ -130,7 +130,7 @@ class TaskBarIcon:
|
||||
msg,
|
||||
wparam,
|
||||
lparam,
|
||||
):
|
||||
):
|
||||
if lparam == win32con.WM_LBUTTONUP:
|
||||
pass
|
||||
elif lparam == win32con.WM_LBUTTONDBLCLK:
|
||||
@@ -144,14 +144,14 @@ class TaskBarIcon:
|
||||
win32gui.AppendMenu(menu, win32con.MF_STRING, 1024,
|
||||
'Start Server')
|
||||
win32gui.AppendMenu(menu, win32con.MF_STRING
|
||||
| win32con.MF_GRAYED, 1025,
|
||||
| win32con.MF_GRAYED, 1025,
|
||||
'Restart Server')
|
||||
win32gui.AppendMenu(menu, win32con.MF_STRING
|
||||
| win32con.MF_GRAYED, 1026,
|
||||
| win32con.MF_GRAYED, 1026,
|
||||
'Stop Server')
|
||||
else:
|
||||
win32gui.AppendMenu(menu, win32con.MF_STRING
|
||||
| win32con.MF_GRAYED, 1024,
|
||||
| win32con.MF_GRAYED, 1024,
|
||||
'Start Server')
|
||||
win32gui.AppendMenu(menu, win32con.MF_STRING, 1025,
|
||||
'Restart Server')
|
||||
@@ -173,7 +173,7 @@ class TaskBarIcon:
|
||||
0,
|
||||
self.hwnd,
|
||||
None,
|
||||
)
|
||||
)
|
||||
win32api.PostMessage(self.hwnd, win32con.WM_NULL, 0, 0)
|
||||
return 1
|
||||
|
||||
@@ -183,7 +183,7 @@ class TaskBarIcon:
|
||||
msg,
|
||||
wparam,
|
||||
lparam,
|
||||
):
|
||||
):
|
||||
id = win32api.LOWORD(wparam)
|
||||
if id == 1023:
|
||||
self.status.append(self.EnumStatus.TOGGLE)
|
||||
@@ -226,7 +226,7 @@ class TaskBarIcon:
|
||||
0,
|
||||
0,
|
||||
icon_flags,
|
||||
)
|
||||
)
|
||||
return hicon
|
||||
|
||||
class EnumStatus:
|
||||
|
||||
@@ -3,16 +3,20 @@ import time
|
||||
from collections import namedtuple
|
||||
Score = namedtuple('Score', ['tag', 'stamp'])
|
||||
|
||||
|
||||
class TimeCollector(object):
|
||||
def __init__(self):
|
||||
'''The first time stamp is created here'''
|
||||
self.scores = [Score(tag='start',stamp=time.clock())]
|
||||
self.scores = [Score(tag='start', stamp=time.clock())]
|
||||
|
||||
def addStamp(self, description):
|
||||
'''Adds a new time stamp, with a description.'''
|
||||
self.scores.append(Score(tag=description, stamp=time.clock()))
|
||||
|
||||
def _stampDelta(self, index1, index2):
|
||||
'''Private utility function to clean up this common calculation.'''
|
||||
return self.scores[index1].stamp - self.scores[index2].stamp
|
||||
|
||||
def getReportItems(self, orderByCost=True):
|
||||
'''Returns a list of dicts. Each dict has
|
||||
start (ms),
|
||||
@@ -25,23 +29,24 @@ class TimeCollector(object):
|
||||
total_time = self._stampDelta(-1, 0)
|
||||
data = []
|
||||
for i in range(1, len(self.scores)):
|
||||
delta = self._stampDelta(i, i-1)
|
||||
delta = self._stampDelta(i, i - 1)
|
||||
if abs(total_time) < 1e-6:
|
||||
perc = 0
|
||||
else:
|
||||
perc = delta / total_time * 100
|
||||
data.append(
|
||||
dict(
|
||||
start = self._stampDelta(i-1, 0) * 1000,
|
||||
end = self._stampDelta(i, 0) * 1000,
|
||||
delta = delta * 1000,
|
||||
perc = perc,
|
||||
tag = self.scores[i].tag
|
||||
)
|
||||
start=self._stampDelta(i - 1, 0) * 1000,
|
||||
end=self._stampDelta(i, 0) * 1000,
|
||||
delta=delta * 1000,
|
||||
perc=perc,
|
||||
tag=self.scores[i].tag
|
||||
)
|
||||
)
|
||||
if orderByCost:
|
||||
data.sort(key=lambda x: x['perc'], reverse=True)
|
||||
return data
|
||||
|
||||
def getReportLines(self, orderByCost=True):
|
||||
'''Produces a report of logged time-stamps as a list of strings.
|
||||
if orderByCost is False, then the order of the stamps is
|
||||
@@ -51,14 +56,16 @@ class TimeCollector(object):
|
||||
headerData = ('Start(ms)', 'End(ms)', 'Delta(ms)', 'Time Cost',
|
||||
'Description')
|
||||
bodyTemplate = '%(start)10.0f | %(end)10.0f | %(delta)10.0f |' \
|
||||
+ ' %(perc)10.0f%% | %(tag)-30s'
|
||||
+ ' %(perc)10.0f%% | %(tag)-30s'
|
||||
return [headerTemplate % headerData] + [bodyTemplate % d for d in data]
|
||||
|
||||
def getReportText(self, **kwargs):
|
||||
return '\n'.join(self.getReportLines(**kwargs))
|
||||
def restart(self):
|
||||
self.scores = [Score(tag='start',stamp=time.clock())]
|
||||
|
||||
if __name__=='__main__':
|
||||
def restart(self):
|
||||
self.scores = [Score(tag='start', stamp=time.clock())]
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('')
|
||||
print('Testing:')
|
||||
print('')
|
||||
@@ -87,7 +94,7 @@ if __name__=='__main__':
|
||||
print(t.getReportText())
|
||||
t.restart()
|
||||
for y in range(1, 200, 20):
|
||||
x = [i for i in range(10000)*y]
|
||||
x = [i for i in range(10000) * y]
|
||||
t.addStamp('Iteration when y = ' + str(y))
|
||||
|
||||
print('')
|
||||
|
||||
@@ -15,6 +15,7 @@ Symbian.
|
||||
"""
|
||||
import sys
|
||||
|
||||
|
||||
class DetectorsHub(dict):
|
||||
_known_types = ['os', 'dist', 'flavor', 'browser']
|
||||
|
||||
@@ -38,6 +39,7 @@ class DetectorsHub(dict):
|
||||
return detectors
|
||||
else:
|
||||
prefs.insert(0, '')
|
||||
|
||||
def key_name(d):
|
||||
return d.name in prefs and prefs.index(d.name) or sys.maxint
|
||||
return sorted(detectors, key=key_name)
|
||||
@@ -46,24 +48,24 @@ class DetectorsHub(dict):
|
||||
return iter(self._known_types)
|
||||
|
||||
def registerDetectors(self):
|
||||
detectors = [v() for v in globals().values() \
|
||||
if DetectorBase in getattr(v, '__mro__', [])]
|
||||
detectors = [v() for v in globals().values()
|
||||
if DetectorBase in getattr(v, '__mro__', [])]
|
||||
for d in detectors:
|
||||
if d.can_register:
|
||||
self.register(d)
|
||||
|
||||
|
||||
class DetectorBase(object):
|
||||
name = "" # "to perform match in DetectorsHub object"
|
||||
info_type = '' #override me
|
||||
result_key = '' #override me
|
||||
order = 10 # 0 is highest
|
||||
look_for = [] # list of words to look for
|
||||
skip_if_found = [] # strings if present stop processin
|
||||
name = "" # "to perform match in DetectorsHub object"
|
||||
info_type = '' # override me
|
||||
result_key = '' # override me
|
||||
order = 10 # 0 is highest
|
||||
look_for = [] # list of words to look for
|
||||
skip_if_found = [] # strings if present stop processin
|
||||
can_register = False
|
||||
is_mobile = False
|
||||
is_tablet = False
|
||||
prefs = dict() # dict(info_type = [name1, name2], ..)
|
||||
prefs = dict() # dict(info_type = [name1, name2], ..)
|
||||
version_splitters = ["/", " "]
|
||||
_suggested_detectors = None
|
||||
|
||||
@@ -75,8 +77,8 @@ class DetectorBase(object):
|
||||
def detect(self, agent, result):
|
||||
if agent and self.checkWords(agent):
|
||||
result[self.info_type] = dict(name=self.name)
|
||||
is_mobile=self.is_mobile
|
||||
is_tablet=self.is_tablet
|
||||
is_mobile = self.is_mobile
|
||||
is_tablet = self.is_tablet
|
||||
if is_mobile:
|
||||
result['is_mobile'] = is_mobile
|
||||
if is_tablet:
|
||||
@@ -131,6 +133,7 @@ class Browser(DetectorBase):
|
||||
class Macintosh(OS):
|
||||
look_for = ['Macintosh']
|
||||
prefs = dict(dist=None)
|
||||
|
||||
def getVersion(self, agent):
|
||||
pass
|
||||
|
||||
@@ -146,12 +149,15 @@ class Konqueror(Browser):
|
||||
|
||||
class Opera(Browser):
|
||||
look_for = ["Opera"]
|
||||
|
||||
def getVersion(self, agent):
|
||||
return agent.partition(self.look_for[0])[2][1:].partition(' ')[0]
|
||||
|
||||
|
||||
class Netscape(Browser):
|
||||
look_for = ["Netscape"]
|
||||
|
||||
|
||||
class MSIE(Browser):
|
||||
look_for = ["MSIE"]
|
||||
skip_if_found = ["Opera"]
|
||||
@@ -171,6 +177,7 @@ class Safari(Browser):
|
||||
if "Version/" in agent:
|
||||
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
||||
|
||||
|
||||
class SafariTablet(Browser):
|
||||
name = "Safari"
|
||||
look_for = ['Safari', 'Android']
|
||||
@@ -182,6 +189,7 @@ class SafariTablet(Browser):
|
||||
if "Version/" in agent:
|
||||
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
||||
|
||||
|
||||
class SafariMobile(Browser):
|
||||
name = "Safari"
|
||||
look_for = ["Safari", "Mobile"]
|
||||
@@ -191,6 +199,7 @@ class SafariMobile(Browser):
|
||||
if "Version/" in agent:
|
||||
return agent.partition('Version/')[2].partition(' ')[0].strip()
|
||||
|
||||
|
||||
class SafariNokia(Browser):
|
||||
name = "Safari"
|
||||
look_for = ["Safari", "SymbianOS"]
|
||||
@@ -199,6 +208,7 @@ class SafariNokia(Browser):
|
||||
def getVersion(self, agent):
|
||||
pass
|
||||
|
||||
|
||||
class SafariiPad(Browser):
|
||||
name = "Safari"
|
||||
look_for = ["Safari", "iPad"]
|
||||
@@ -218,9 +228,10 @@ class Linux(OS):
|
||||
def getVersion(self, agent):
|
||||
pass
|
||||
|
||||
|
||||
class BlackBerry(OS):
|
||||
look_for = ['BlackBerry']
|
||||
prefs = dict( flavor=['PlayBook'])
|
||||
prefs = dict(flavor=['PlayBook'])
|
||||
is_mobile = True
|
||||
|
||||
# Manual check for tablet
|
||||
@@ -232,14 +243,16 @@ class BlackBerry(OS):
|
||||
def getVersion(self, agent):
|
||||
pass
|
||||
|
||||
|
||||
class PlayBook(Flavor):
|
||||
look_for = ['PlayBook']
|
||||
is_mobile=True
|
||||
is_tablet=True
|
||||
is_mobile = True
|
||||
is_tablet = True
|
||||
|
||||
def getVersion(self, agent):
|
||||
return agent.partition('Tablet OS')[2].partition(';')[0].strip()
|
||||
|
||||
|
||||
class Macintosh(OS):
|
||||
look_for = ['Macintosh']
|
||||
prefs = dict(dist=None, flavor=['MacOS'])
|
||||
@@ -250,7 +263,8 @@ class Macintosh(OS):
|
||||
|
||||
class MacOS(Flavor):
|
||||
look_for = ['Mac OS']
|
||||
prefs = dict(browser=['Safari','SafariMobile', 'SafariIpad', 'Firefox', 'Opera', "Microsoft Internet Explorer"])
|
||||
prefs = dict(browser=['Safari', 'SafariMobile', 'SafariIpad',
|
||||
'Firefox', 'Opera', "Microsoft Internet Explorer"])
|
||||
|
||||
def getVersion(self, agent):
|
||||
version_end_chars = [';', ')']
|
||||
@@ -269,7 +283,8 @@ class Windows(OS):
|
||||
|
||||
def getVersion(self, agent):
|
||||
v = agent.partition('NT')
|
||||
return v[1]+' '+v[2].replace(')',';').partition(';')[0].strip()
|
||||
return v[1] + ' ' + v[2].replace(')', ';').partition(';')[0].strip()
|
||||
|
||||
|
||||
class WindowsMobile(Dist):
|
||||
name = 'Phone'
|
||||
@@ -277,7 +292,8 @@ class WindowsMobile(Dist):
|
||||
is_mobile = True
|
||||
|
||||
def getVersion(self, agent):
|
||||
return agent.partition('Windows Phone')[2].replace(')','').partition(';')[0].strip()
|
||||
return agent.partition('Windows Phone')[2].replace(')', '').partition(';')[0].strip()
|
||||
|
||||
|
||||
class Ubuntu(Dist):
|
||||
look_for = ['Ubuntu']
|
||||
@@ -295,13 +311,16 @@ class Chrome(Browser):
|
||||
look_for = ['Chrome']
|
||||
version_splitters = ["/", " "]
|
||||
|
||||
|
||||
class ChromeOS(OS):
|
||||
look_for = ['CrOS']
|
||||
version_splitters = [" ", ")"]
|
||||
prefs = dict(browser=['Chrome'])
|
||||
|
||||
def getVersion(self, agent):
|
||||
vs = self.version_splitters
|
||||
return agent.partition(self.look_for[0]+vs[0])[2].partition(vs[1])[0].partition(" ")[2].strip()
|
||||
return agent.partition(self.look_for[0] + vs[0])[2].partition(vs[1])[0].partition(" ")[2].strip()
|
||||
|
||||
|
||||
class Android(Dist):
|
||||
look_for = ['Android']
|
||||
@@ -311,23 +330,27 @@ class Android(Dist):
|
||||
def getVersion(self, agent):
|
||||
return agent.partition('Android')[2].partition(';')[0].strip()
|
||||
|
||||
|
||||
class SymbianOS(OS):
|
||||
look_for = ['SymbianOS']
|
||||
prefs = dict(dist = ['Series'], browser = ['Safari', 'Opera'])
|
||||
prefs = dict(dist=['Series'], browser=['Safari', 'Opera'])
|
||||
is_mobile = True
|
||||
version_splitters = ['/', '; ']
|
||||
|
||||
|
||||
class Series(Flavor):
|
||||
look_for = ['SymbianOS', 'Series']
|
||||
version_splitters = ['/', ';']
|
||||
|
||||
def getVersion(self, agent):
|
||||
return agent.partition('Series')[2].partition(' ')[0].replace('/',' ')
|
||||
return agent.partition('Series')[2].partition(' ')[0].replace('/', ' ')
|
||||
|
||||
|
||||
class BrowserNG(Browser):
|
||||
look_for = ['BrowserNG']
|
||||
version_splitters = ['/', ';']
|
||||
|
||||
|
||||
class iPhone(Dist):
|
||||
look_for = ['iPhone']
|
||||
is_mobile = True
|
||||
@@ -344,6 +367,7 @@ class iPhone(Dist):
|
||||
break
|
||||
return version.replace('_', '.')
|
||||
|
||||
|
||||
class iPad(Dist):
|
||||
look_for = ['iPad']
|
||||
is_mobile = True
|
||||
@@ -362,6 +386,7 @@ class iPad(Dist):
|
||||
|
||||
detectorshub = DetectorsHub()
|
||||
|
||||
|
||||
def detect(agent):
|
||||
result = dict()
|
||||
prefs = dict()
|
||||
@@ -385,21 +410,26 @@ class Result(dict):
|
||||
def __missing__(self, k):
|
||||
return ""
|
||||
|
||||
|
||||
def simple_detect(agent):
|
||||
"""
|
||||
-> (os, browser, is_mobile) # tuple of strings
|
||||
"""
|
||||
result = detect(agent)
|
||||
os_list = []
|
||||
if 'flavor' in result: os_list.append(result['flavor']['name'])
|
||||
if 'dist' in result: os_list.append(result['dist']['name'])
|
||||
if 'os' in result: os_list.append(result['os']['name'])
|
||||
if 'flavor' in result:
|
||||
os_list.append(result['flavor']['name'])
|
||||
if 'dist' in result:
|
||||
os_list.append(result['dist']['name'])
|
||||
if 'os' in result:
|
||||
os_list.append(result['os']['name'])
|
||||
|
||||
os = os_list and " ".join(os_list) or "Unknown OS"
|
||||
os_version = os_list and ('flavor' in result and result['flavor'] and result['flavor'].get(
|
||||
'version')) or ('dist' in result and result['dist'] and result['dist'].get('version')) \
|
||||
or ('os' in result and result['os'] and result['os'].get('version')) or ""
|
||||
browser = 'browser' in result and result['browser']['name'] or 'Unknown Browser'
|
||||
'version')) or ('dist' in result and result['dist'] and result['dist'].get('version')) \
|
||||
or ('os' in result and result['os'] and result['os'].get('version')) or ""
|
||||
browser = 'browser' in result and result['browser'][
|
||||
'name'] or 'Unknown Browser'
|
||||
browser_version = 'browser' in result \
|
||||
and result['browser'].get('version') or ""
|
||||
if browser_version:
|
||||
@@ -417,64 +447,82 @@ if __name__ == '__main__':
|
||||
data = (
|
||||
|
||||
|
||||
('Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/3.83; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413',
|
||||
('Series SymbianOS 60 3.1', 'Safari', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 3.1'}, 'os': {'name': 'SymbianOS', 'version': '9.2'}, 'browser': {'name': 'Safari'}},),
|
||||
('Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124',
|
||||
('Series SymbianOS 60 5.0', 'BrowserNG 7.1.18124', True),
|
||||
(
|
||||
'Mozilla/5.0 (SymbianOS/9.2; U; Series60/3.1 Nokia6120c/3.83; Profile/MIDP-2.0 Configuration/CLDC-1.1) AppleWebKit/413 (KHTML, like Gecko) Safari/413',
|
||||
('Series SymbianOS 60 3.1', 'Safari', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 3.1'}, 'os': {'name': 'SymbianOS', 'version': '9.2'}, 'browser': {'name': 'Safari'}},),
|
||||
(
|
||||
'Mozilla/5.0 (SymbianOS/9.4; Series60/5.0 NokiaN97-1/20.0.019; Profile/MIDP-2.1 Configuration/CLDC-1.1) AppleWebKit/525 (KHTML, like Gecko) BrowserNG/7.1.18124',
|
||||
('Series SymbianOS 60 5.0', 'BrowserNG 7.1.18124', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'flavor': {'name': 'Series', 'version': '60 5.0'}, 'os': {'name': 'SymbianOS', 'version': '9.4'}, 'browser': {'name': 'BrowserNG', 'version': '7.1.18124'}},),
|
||||
('Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Windows Phone 6.5.3.5)',
|
||||
('Phone Windows 6.5.3.5', 'Microsoft Internet Explorer 6.0', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'name': 'Phone', 'version': '6.5.3.5'}, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Microsoft Internet Explorer', 'version': '6.0'}},),
|
||||
('Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+',
|
||||
('PlayBook BlackBerry 1.0.0', 'Safari 0.0.1', True),
|
||||
(
|
||||
'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Windows Phone 6.5.3.5)',
|
||||
('Phone Windows 6.5.3.5', 'Microsoft Internet Explorer 6.0', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'name': 'Phone', 'version': '6.5.3.5'}, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Microsoft Internet Explorer', 'version': '6.0'}},),
|
||||
(
|
||||
'Mozilla/5.0 (PlayBook; U; RIM Tablet OS 1.0.0; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/0.0.1 Safari/534.8+',
|
||||
('PlayBook BlackBerry 1.0.0', 'Safari 0.0.1', True),
|
||||
{'is_mobile': True, 'is_tablet': True, 'flavor': {'name': 'PlayBook', 'version': '1.0.0'}, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '0.0.1'}},),
|
||||
('Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.246 Mobile Safari/534.1+',
|
||||
('BlackBerry', 'Safari 6.0.0.246', True),
|
||||
(
|
||||
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.1+ (KHTML, like Gecko) Version/6.0.0.246 Mobile Safari/534.1+',
|
||||
('BlackBerry', 'Safari 6.0.0.246', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.246'}},),
|
||||
('Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.600 Mobile Safari/534.8+',
|
||||
('BlackBerry', 'Safari 6.0.0.600', True),
|
||||
(
|
||||
'Mozilla/5.0 (BlackBerry; U; BlackBerry 9800; en-US) AppleWebKit/534.8+ (KHTML, like Gecko) Version/6.0.0.600 Mobile Safari/534.8+',
|
||||
('BlackBerry', 'Safari 6.0.0.600', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'os': {'name': 'BlackBerry'}, 'browser': {'name': 'Safari', 'version': '6.0.0.600'}},),
|
||||
('Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5',
|
||||
('MacOS iPad X', 'Safari 5.0.2', True),
|
||||
{'is_mobile': True, 'is_tablet': True, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'iOS 4.2.1', 'name': 'iPad'}, 'browser': {'name': 'Safari', 'version': '5.0.2'}},),
|
||||
('Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1',
|
||||
('Windows NT 5.1', 'Netscape 8.1', False),
|
||||
(
|
||||
'Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; en-us) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5',
|
||||
('MacOS iPad X', 'Safari 5.0.2', True),
|
||||
{'is_mobile': True, 'is_tablet': True, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'iOS 4.2.1', 'name': 'iPad'}, 'browser': {'name': 'Safari', 'version': '5.0.2'}},),
|
||||
(
|
||||
'Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7.5) Gecko/20060127 Netscape/8.1',
|
||||
('Windows NT 5.1', 'Netscape 8.1', False),
|
||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Netscape', 'version': '8.1'}},),
|
||||
('Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13',
|
||||
('Android Linux 3.0.1', 'Safari 4.0', True),
|
||||
(
|
||||
'Mozilla/5.0 (Linux; U; Android 3.0.1; en-us; A500 Build/HRI66) AppleWebKit/534.13 (KHTML, like Gecko) Version/4.0 Safari/534.13',
|
||||
('Android Linux 3.0.1', 'Safari 4.0', True),
|
||||
{'is_mobile': True, 'is_tablet': True, 'dist': {'version': '3.0.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||
('Mozilla/5.0 (Linux; U; Android 2.3.7; it-it; Dream/Sapphire Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
|
||||
('Android Linux 2.3.7', 'Safari 4.0', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.7', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||
('Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-GB; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10',
|
||||
('MacOS Macintosh X 10.5', 'Firefox 3.0.10', False),
|
||||
{ 'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.5', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '3.0.10', 'name': 'Firefox'}},),
|
||||
('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24,gzip(gfe)',
|
||||
('MacOS Macintosh X 10.6.6', 'Chrome 11.0.696.3', False),
|
||||
(
|
||||
'Mozilla/5.0 (Linux; U; Android 2.3.7; it-it; Dream/Sapphire Build/FRG83) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
|
||||
('Android Linux 2.3.7', 'Safari 4.0', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.7', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||
(
|
||||
'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-GB; rv:1.9.0.10) Gecko/2009042315 Firefox/3.0.10',
|
||||
('MacOS Macintosh X 10.5', 'Firefox 3.0.10', False),
|
||||
{'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.5', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '3.0.10', 'name': 'Firefox'}},),
|
||||
(
|
||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_6_6) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24,gzip(gfe)',
|
||||
('MacOS Macintosh X 10.6.6', 'Chrome 11.0.696.3', False),
|
||||
{'is_mobile': False, 'is_tablet': False, 'flavor': {'version': 'X 10.6.6', 'name': 'MacOS'}, 'os': {'name': 'Macintosh'}, 'browser': {'version': '11.0.696.3', 'name': 'Chrome'}},),
|
||||
('Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 GTB7.1',
|
||||
('Ubuntu Linux 10.04', 'Firefox 3.6', False),
|
||||
(
|
||||
'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100308 Ubuntu/10.04 (lucid) Firefox/3.6 GTB7.1',
|
||||
('Ubuntu Linux 10.04', 'Firefox 3.6', False),
|
||||
{'is_mobile': False, 'is_tablet': False, 'dist': {'version': '10.04', 'name': 'Ubuntu'}, 'os': {'name': 'Linux'}, 'browser': {'version': '3.6', 'name': 'Firefox'}},),
|
||||
('Mozilla/5.0 (Linux; U; Android 2.2.1; fr-ch; A43 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
|
||||
('Android Linux 2.2.1', 'Safari 4.0', True),
|
||||
(
|
||||
'Mozilla/5.0 (Linux; U; Android 2.2.1; fr-ch; A43 Build/FROYO) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1',
|
||||
('Android Linux 2.2.1', 'Safari 4.0', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.2.1', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||
('Mozilla/5.0 (Linux; U; Android 2.3.4; it-it; LG-P990 Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2',
|
||||
('Android Linux 2.3.4', 'Safari 4.0', True),
|
||||
(
|
||||
'Mozilla/5.0 (Linux; U; Android 2.3.4; it-it; LG-P990 Build/GRJ22) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1 MMS/LG-Android-MMS-V1.0/1.2',
|
||||
('Android Linux 2.3.4', 'Safari 4.0', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'dist': {'version': '2.3.4', 'name': 'Android'}, 'os': {'name': 'Linux'}, 'browser': {'version': '4.0', 'name': 'Safari'}},),
|
||||
('Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
|
||||
('MacOS iPhone X', 'Safari 3.0', True),
|
||||
(
|
||||
'Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543a Safari/419.3',
|
||||
('MacOS iPhone X', 'Safari 3.0', True),
|
||||
{'is_mobile': True, 'is_tablet': False, 'flavor': {'version': 'X', 'name': 'MacOS'}, 'dist': {'version': 'X', 'name': 'iPhone'}, 'browser': {'version': '3.0', 'name': 'Safari'}},),
|
||||
('Mozilla/5.0 (X11; CrOS i686 0.0.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.27 Safari/534.24,gzip(gfe)',
|
||||
('ChromeOS 0.0.0', 'Chrome 11.0.696.27', False),
|
||||
(
|
||||
'Mozilla/5.0 (X11; CrOS i686 0.0.0) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.27 Safari/534.24,gzip(gfe)',
|
||||
('ChromeOS 0.0.0', 'Chrome 11.0.696.27', False),
|
||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'ChromeOS', 'version': '0.0.0'}, 'browser': {'name': 'Chrome', 'version': '11.0.696.27'}},),
|
||||
('Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.02 [en]',
|
||||
('Windows NT 5.1', 'Opera 7.02', False),
|
||||
(
|
||||
'Mozilla/4.0 (compatible; MSIE 6.0; MSIE 5.5; Windows NT 5.1) Opera 7.02 [en]',
|
||||
('Windows NT 5.1', 'Opera 7.02', False),
|
||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Windows', 'version': 'NT 5.1'}, 'browser': {'name': 'Opera', 'version': '7.02'}},),
|
||||
('Opera/9.80 (X11; Linux i686; U; en) Presto/2.9.168 Version/11.50',
|
||||
('Linux', 'Opera 9.80', False),
|
||||
{'is_mobile': False, 'is_tablet': False, 'os': {'name': 'Linux'}, 'browser': {'name': 'Opera', 'version': '9.80'}},),
|
||||
)
|
||||
)
|
||||
|
||||
class TestHAP(unittest.TestCase):
|
||||
def setUp(self):
|
||||
@@ -513,6 +561,6 @@ class mobilize(object):
|
||||
user_agent = current.request.user_agent()
|
||||
if user_agent.is_mobile:
|
||||
items = current.response.view.split('.')
|
||||
items.insert(-1,'mobile')
|
||||
items.insert(-1, 'mobile')
|
||||
current.response.view = '.'.join(items)
|
||||
return self.func()
|
||||
+48
-45
@@ -22,21 +22,22 @@ import urllib
|
||||
import urllib2
|
||||
|
||||
DEFAULT_HEADERS = {
|
||||
'user-agent': 'Mozilla/4.0', # some servers are picky
|
||||
'user-agent': 'Mozilla/4.0', # some servers are picky
|
||||
'accept-language': 'en',
|
||||
}
|
||||
}
|
||||
|
||||
FORM_REGEX = re.compile('(\<input name\="_formkey" type\="hidden" value\="(?P<formkey>.+?)" \/\>)?\<input name\="_formname" type\="hidden" value\="(?P<formname>.+?)" \/\>')
|
||||
|
||||
SESSION_REGEX = 'session_id_(?P<name>.+)'
|
||||
|
||||
|
||||
class WebClient(object):
|
||||
|
||||
def __init__(self,
|
||||
app = '',
|
||||
postbacks = True,
|
||||
default_headers = DEFAULT_HEADERS,
|
||||
session_regex = SESSION_REGEX):
|
||||
app='',
|
||||
postbacks=True,
|
||||
default_headers=DEFAULT_HEADERS,
|
||||
session_regex=SESSION_REGEX):
|
||||
self.app = app
|
||||
self.postbacks = postbacks
|
||||
self.forms = {}
|
||||
@@ -46,18 +47,18 @@ class WebClient(object):
|
||||
self.sessions = {}
|
||||
self.session_regex = session_regex and re.compile(session_regex)
|
||||
|
||||
def get(self,url,cookies=None,headers=None,auth=None):
|
||||
return self.post(url,data=None,cookies=cookies,headers=headers)
|
||||
def get(self, url, cookies=None, headers=None, auth=None):
|
||||
return self.post(url, data=None, cookies=cookies, headers=headers)
|
||||
|
||||
def post(self,url,data=None,cookies=None,headers=None,auth=None):
|
||||
self.url = self.app+url
|
||||
def post(self, url, data=None, cookies=None, headers=None, auth=None):
|
||||
self.url = self.app + url
|
||||
|
||||
# if this POST form requires a postback do it
|
||||
if data and '_formname' in data and self.postbacks and \
|
||||
self.history and self.history[-1][1]!=self.url:
|
||||
self.history and self.history[-1][1] != self.url:
|
||||
# to bypass the web2py CSRF need to get formkey
|
||||
# before submitting the form
|
||||
self.get(url,cookies=cookies,headers=headers,auth=auth)
|
||||
self.get(url, cookies=cookies, headers=headers, auth=auth)
|
||||
|
||||
# unless cookies are specified, recycle cookies
|
||||
if cookies is None:
|
||||
@@ -75,22 +76,23 @@ class WebClient(object):
|
||||
|
||||
# copy headers from dict to list of key,value
|
||||
headers_list = []
|
||||
for key,value in self.default_headers.iteritems():
|
||||
for key, value in self.default_headers.iteritems():
|
||||
if not key in headers:
|
||||
headers[key] = value
|
||||
for key,value in headers.iteritems():
|
||||
if isinstance(value,(list,tuple)):
|
||||
for v in value: headers_list.append((key,v))
|
||||
for key, value in headers.iteritems():
|
||||
if isinstance(value, (list, tuple)):
|
||||
for v in value:
|
||||
headers_list.append((key, v))
|
||||
else:
|
||||
headers_list.append((key,value))
|
||||
headers_list.append((key, value))
|
||||
|
||||
# move cookies to headers
|
||||
for key,value in cookies.iteritems():
|
||||
headers_list.append(('Cookie','%s=%s' % (key,value)))
|
||||
for key, value in cookies.iteritems():
|
||||
headers_list.append(('Cookie', '%s=%s' % (key, value)))
|
||||
|
||||
# add headers to request
|
||||
for key,value in headers_list:
|
||||
opener.addheaders.append((key,str(value)))
|
||||
for key, value in headers_list:
|
||||
opener.addheaders.append((key, str(value)))
|
||||
|
||||
# assume everything is ok and make http request
|
||||
error = None
|
||||
@@ -99,7 +101,7 @@ class WebClient(object):
|
||||
self.method = 'POST'
|
||||
|
||||
# if there is only one form, set _formname automatically
|
||||
if not '_formname' in data and len(self.forms)==1:
|
||||
if not '_formname' in data and len(self.forms) == 1:
|
||||
data['_formname'] = self.forms.keys()[0]
|
||||
|
||||
# if there is no formkey but it is known, set it
|
||||
@@ -110,18 +112,18 @@ class WebClient(object):
|
||||
# time the POST request
|
||||
data = urllib.urlencode(data)
|
||||
t0 = time.time()
|
||||
self.response = opener.open(self.url,data)
|
||||
self.time = time.time()-t0
|
||||
self.response = opener.open(self.url, data)
|
||||
self.time = time.time() - t0
|
||||
else:
|
||||
self.method = 'GET'
|
||||
|
||||
# time the GET request
|
||||
t0 = time.time()
|
||||
self.response = opener.open(self.url)
|
||||
self.time = time.time()-t0
|
||||
self.time = time.time() - t0
|
||||
except urllib2.HTTPError, error:
|
||||
# catch HTTP errors
|
||||
self.time = time.time()-t0
|
||||
self.time = time.time() - t0
|
||||
self.response = error
|
||||
|
||||
self.status = self.response.getcode()
|
||||
@@ -131,7 +133,7 @@ class WebClient(object):
|
||||
# treat web2py tickets as special types of errors
|
||||
if error is not None:
|
||||
if 'web2py_error' in self.headers:
|
||||
raise RuntimeError, self.headers['web2py_error']
|
||||
raise RuntimeError(self.headers['web2py_error'])
|
||||
else:
|
||||
raise error
|
||||
|
||||
@@ -139,8 +141,8 @@ class WebClient(object):
|
||||
self.cookies = {}
|
||||
if 'set-cookie' in self.headers:
|
||||
for item in self.headers['set-cookie'].split(','):
|
||||
key,value = item[:item.find(';')].split('=')
|
||||
self.cookies[key.strip()]=value.strip()
|
||||
key, value = item[:item.find(';')].split('=')
|
||||
self.cookies[key.strip()] = value.strip()
|
||||
|
||||
# check is a new session id has been issued, symptom of broken session
|
||||
if self.session_regex is not None:
|
||||
@@ -148,8 +150,8 @@ class WebClient(object):
|
||||
match = self.session_regex.match(cookie)
|
||||
if match:
|
||||
name = match.group('name')
|
||||
if name in self.sessions and self.sessions[name]!=value:
|
||||
raise RuntimeError, 'Broken sessions %s' % name
|
||||
if name in self.sessions and self.sessions[name] != value:
|
||||
raise RuntimeError('Broken sessions %s' % name)
|
||||
self.sessions[name] = value
|
||||
|
||||
# find all forms and formkeys in page
|
||||
@@ -158,7 +160,8 @@ class WebClient(object):
|
||||
self.forms[match.group('formname')] = match.group('formkey')
|
||||
|
||||
# log this request
|
||||
self.history.append((self.method,self.url,self.status,self.time))
|
||||
self.history.append((self.method, self.url, self.status, self.time))
|
||||
|
||||
|
||||
def test_web2py_registration_and_login():
|
||||
# from gluon.contrib.webclient import WebClient
|
||||
@@ -168,13 +171,13 @@ def test_web2py_registration_and_login():
|
||||
client.get('index')
|
||||
|
||||
# register
|
||||
data = dict(first_name = 'Homer',
|
||||
last_name = 'Simpson',
|
||||
email = 'homer@web2py.com',
|
||||
password = 'test',
|
||||
password_two = 'test',
|
||||
_formname = 'register')
|
||||
client.post('user/register',data = data)
|
||||
data = dict(first_name='Homer',
|
||||
last_name='Simpson',
|
||||
email='homer@web2py.com',
|
||||
password='test',
|
||||
password_two='test',
|
||||
_formname='register')
|
||||
client.post('user/register', data=data)
|
||||
|
||||
# logout
|
||||
client.get('user/logout')
|
||||
@@ -182,18 +185,18 @@ def test_web2py_registration_and_login():
|
||||
# login
|
||||
data = dict(email='homer@web2py.com',
|
||||
password='test',
|
||||
_formname = 'login')
|
||||
client.post('user/login',data = data)
|
||||
_formname='login')
|
||||
client.post('user/login', data=data)
|
||||
|
||||
# check registration and login were successful
|
||||
client.get('user/profile')
|
||||
assert 'Welcome Homer' in client.text
|
||||
|
||||
# print some variables
|
||||
print '\nsessions:\n',client.sessions
|
||||
print '\nheaders:\n',client.headers
|
||||
print '\ncookies:\n',client.cookies
|
||||
print '\nforms:\n',client.forms
|
||||
print '\nsessions:\n', client.sessions
|
||||
print '\nheaders:\n', client.headers
|
||||
print '\ncookies:\n', client.cookies
|
||||
print '\nforms:\n', client.forms
|
||||
print
|
||||
for method, url, status, t in client.history:
|
||||
print method, url, status, t
|
||||
|
||||
@@ -61,7 +61,8 @@ Here is a complete sample web2py action:
|
||||
form=SQLFORM.factory(Field('message'))
|
||||
if form.accepts(request,session):
|
||||
from gluon.contrib.websocket_messaging import websocket_send
|
||||
websocket_send('http://127.0.0.1:8888',form.vars.message,'mykey','mygroup')
|
||||
websocket_send(
|
||||
'http://127.0.0.1:8888',form.vars.message,'mykey','mygroup')
|
||||
return form
|
||||
|
||||
Acknowledgements:
|
||||
@@ -83,31 +84,38 @@ listeners = {}
|
||||
names = {}
|
||||
tokens = {}
|
||||
|
||||
def websocket_send(url,message,hmac_key=None,group='default'):
|
||||
sig = hmac_key and hmac.new(hmac_key,message).hexdigest() or ''
|
||||
params = urllib.urlencode({'message': message, 'signature': sig, 'group':group})
|
||||
|
||||
def websocket_send(url, message, hmac_key=None, group='default'):
|
||||
sig = hmac_key and hmac.new(hmac_key, message).hexdigest() or ''
|
||||
params = urllib.urlencode(
|
||||
{'message': message, 'signature': sig, 'group': group})
|
||||
f = urllib.urlopen(url, params)
|
||||
data= f.read()
|
||||
data = f.read()
|
||||
f.close()
|
||||
return data
|
||||
|
||||
|
||||
class PostHandler(tornado.web.RequestHandler):
|
||||
"""
|
||||
only authorized parties can post messages
|
||||
"""
|
||||
def post(self):
|
||||
if hmac_key and not 'signature' in self.request.arguments: return 'false'
|
||||
if hmac_key and not 'signature' in self.request.arguments:
|
||||
return 'false'
|
||||
if 'message' in self.request.arguments:
|
||||
message = self.request.arguments['message'][0]
|
||||
group = self.request.arguments.get('group',['default'])[0]
|
||||
group = self.request.arguments.get('group', ['default'])[0]
|
||||
print '%s:MESSAGE to %s:%s' % (time.time(), group, message)
|
||||
if hmac_key:
|
||||
signature = self.request.arguments['signature'][0]
|
||||
if not hmac.new(hmac_key,message).hexdigest()==signature: return 'false'
|
||||
for client in listeners.get(group,[]): client.write_message(message)
|
||||
if not hmac.new(hmac_key, message).hexdigest() == signature:
|
||||
return 'false'
|
||||
for client in listeners.get(group, []):
|
||||
client.write_message(message)
|
||||
return 'true'
|
||||
return 'false'
|
||||
|
||||
|
||||
class TokenHandler(tornado.web.RequestHandler):
|
||||
"""
|
||||
if running with -t post a token to allow a client to join using the token
|
||||
@@ -115,46 +123,55 @@ class TokenHandler(tornado.web.RequestHandler):
|
||||
allows only authorized parties to joins, for example, a chat
|
||||
"""
|
||||
def post(self):
|
||||
if hmac_key and not 'message' in self.request.arguments: return 'false'
|
||||
if hmac_key and not 'message' in self.request.arguments:
|
||||
return 'false'
|
||||
if 'message' in self.request.arguments:
|
||||
message = self.request.arguments['message'][0]
|
||||
if hmac_key:
|
||||
signature = self.request.arguments['signature'][0]
|
||||
if not hmac.new(hmac_key,message).hexdigest()==signature: return 'false'
|
||||
if not hmac.new(hmac_key, message).hexdigest() == signature:
|
||||
return 'false'
|
||||
tokens[message] = None
|
||||
return 'true'
|
||||
return 'false'
|
||||
|
||||
|
||||
class DistributeHandler(tornado.websocket.WebSocketHandler):
|
||||
def open(self,params):
|
||||
group,token,name = params.split('/')+[None,None]
|
||||
def open(self, params):
|
||||
group, token, name = params.split('/') + [None, None]
|
||||
self.group = group or 'default'
|
||||
self.token = token or 'none'
|
||||
self.name = name or 'anonymous'
|
||||
# only authorized parties can join
|
||||
if DistributeHandler.tokens:
|
||||
if not self.token in tokens or not token[self.token]==None:
|
||||
if not self.token in tokens or not token[self.token] is None:
|
||||
self.close()
|
||||
else:
|
||||
tokens[self.token] = self
|
||||
if not self.group in listeners: listeners[self.group]=[]
|
||||
if not self.group in listeners:
|
||||
listeners[self.group] = []
|
||||
# notify clients that a member has joined the groups
|
||||
for client in listeners.get(self.group,[]): client.write_message('+'+self.name)
|
||||
for client in listeners.get(self.group, []):
|
||||
client.write_message('+' + self.name)
|
||||
listeners[self.group].append(self)
|
||||
names[self] = self.name
|
||||
print '%s:CONNECT to %s' % (time.time(), self.group)
|
||||
|
||||
def on_message(self, message):
|
||||
pass
|
||||
|
||||
def on_close(self):
|
||||
if self.group in listeners: listeners[self.group].remove(self)
|
||||
if self.group in listeners:
|
||||
listeners[self.group].remove(self)
|
||||
del names[self]
|
||||
# notify clients that a member has left the groups
|
||||
for client in listeners.get(self.group,[]): client.write_message('-'+self.name)
|
||||
for client in listeners.get(self.group, []):
|
||||
client.write_message('-' + self.name)
|
||||
print '%s:DISCONNECT from %s' % (time.time(), self.group)
|
||||
|
||||
if __name__ == "__main__":
|
||||
usage = __doc__
|
||||
version= ""
|
||||
version = ""
|
||||
parser = optparse.OptionParser(usage, None, optparse.Option, version)
|
||||
parser.add_option('-p',
|
||||
'--port',
|
||||
@@ -180,7 +197,7 @@ if __name__ == "__main__":
|
||||
(options, args) = parser.parse_args()
|
||||
hmac_key = options.hmac_key
|
||||
DistributeHandler.tokens = options.tokens
|
||||
urls=[
|
||||
urls = [
|
||||
(r'/', PostHandler),
|
||||
(r'/token', TokenHandler),
|
||||
(r'/realtime/(.*)', DistributeHandler)]
|
||||
|
||||
Reference in new issue
Block a user