PK 4HdqA A iprestrict/__init__.pyfrom .restrictor import IPRestrictor
__all__ = ["IPRestrictor"]
PK 4HAZ Z iprestrict/admin.pyfrom django.contrib import admin
from django import forms
from . import ip_utils as ipu
from . import models
class RuleAdmin(admin.ModelAdmin):
model = models.Rule
exclude = ('rank',)
list_display = ('url_pattern', 'ip_group', 'is_allowed', 'move_up_url', 'move_down_url')
class IPRangeForm(forms.ModelForm):
class Meta:
model = models.IPRange
exclude = ()
def clean_cidr_prefix_length(self):
cidr = self.cleaned_data['cidr_prefix_length']
if cidr:
if not (1 <= cidr <= 31):
raise forms.ValidationError("Must be a number between 1 and 31")
return cidr
def clean(self):
cleaned_data = super(IPRangeForm, self).clean()
first_ip = cleaned_data.get('first_ip')
if first_ip is None:
# first_ip is Mandatory, so just let the default validator catch this
return cleaned_data
last_ip = cleaned_data['last_ip']
cidr = cleaned_data['cidr_prefix_length']
if last_ip and cidr:
raise forms.ValidationError("Don't specify the Last IP if you specified a CIDR prefix length")
if last_ip:
if ipu.get_version(first_ip) != ipu.get_version(last_ip):
raise forms.ValidationError("Last IP should be the same type as First IP (%s)" % ipu.get_version(first_ip))
if ipu.get_version(last_ip) != 'ipv6':
# Ignore rest of validation for ipv6, support isn't there yet
if ipu.to_number(first_ip) > ipu.to_number(last_ip):
raise forms.ValidationError("Last IP should be greater than First IP")
if cidr:
# With CIDR the starting address could be different than the one
# the user specified. Making sure it is set to the first ip in the
# subnet.
start, end = ipu.cidr_to_range(first_ip, cidr)
cleaned_data['first_ip'] = ipu.to_ip(start)
return cleaned_data
class IPRangeInline(admin.TabularInline):
model = models.IPRange
form = IPRangeForm
fields = ['first_ip', 'cidr_prefix_length', 'last_ip', 'ip_type']
readonly_fields = ['ip_type']
extra = 2
class IPGroupAdmin(admin.ModelAdmin):
model = models.IPGroup
inlines = [IPRangeInline]
admin.site.register(models.Rule, RuleAdmin)
admin.site.register(models.IPGroup, IPGroupAdmin)
PK 4H"h, iprestrict/decorators.pyfrom functools import wraps
from django.utils.translation import ugettext as _
from django.contrib.admin.forms import AdminAuthenticationForm
from django.contrib.auth.views import login
from django.contrib.auth import REDIRECT_FIELD_NAME
from django.http import HttpResponseForbidden
# Copied from django staff_member_required
# Why isn't this provided by Django?
def superuser_required(view_func):
"""
Decorator for views that checks that the user is logged in and is a staff
member, displaying the login page if necessary.
"""
@wraps(view_func)
def _checklogin(request, *args, **kwargs):
if request.user.is_active and request.user.is_superuser:
# The user is valid. Continue to the admin page.
return view_func(request, *args, **kwargs)
if request.user.is_authenticated():
return HttpResponseForbidden('Forbidden!')
assert hasattr(request, 'session'), "The Django admin requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."
defaults = {
'template_name': 'admin/login.html',
'authentication_form': AdminAuthenticationForm,
'extra_context': {
'title': _('Log in'),
'app_path': request.get_full_path(),
REDIRECT_FIELD_NAME: request.get_full_path(),
},
}
return login(request, **defaults)
return _checklogin
PK 4H7Zm iprestrict/ip_utils.pydef get_version(ip):
return 'ipv4' if '.' in ip else 'ipv6'
def to_number(ip):
parts = ip.split('.')
parts = [int(p) for p in reversed(parts)]
nr = 0
for i, d in enumerate(parts):
nr += (256 ** i) * d
return nr
def to_ip(number):
mask = int('1' * 8, 2)
parts = []
for i in range(4):
shifted_number = number >> (8 * i)
parts.append(shifted_number & mask)
return '.'.join(map(lambda i: str(i), reversed(parts)))
def cidr_to_range(ip, prefix_length):
ip = to_number(ip)
start_mask = int('1' * prefix_length, 2) << (32 - prefix_length)
end_mask = int('1' * (32 - prefix_length), 2)
start = ip & start_mask
end = start | end_mask
return (start, end)
PK 4HN iprestrict/middleware.pyfrom django.core import exceptions
from django.conf import settings
import logging
from .models import ReloadRulesRequest
from .restrictor import IPRestrictor
logger = logging.getLogger(__name__)
class IPRestrictMiddleware(object):
restrictor = None
trusted_proxies = None
allow_proxies = None
dont_reload_rules = None
def __init__(self):
self.restrictor = IPRestrictor()
self.trusted_proxies = tuple(getattr(settings, 'TRUSTED_PROXIES', []))
self.dont_reload_rules = bool(getattr(settings, 'DONT_RELOAD_RULES', False))
self.ignore_proxy_header = bool(getattr(settings, 'IGNORE_PROXY_HEADER', False))
def process_request(self, request):
if not self.dont_reload_rules:
self.reload_rules_if_needed()
url = request.path_info
client_ip = self.extract_client_ip(request)
if self.restrictor.is_restricted(url, client_ip):
logger.info("Denying access of %s to %s" % (url, client_ip))
raise exceptions.PermissionDenied
def extract_client_ip(self, request):
client_ip = request.META['REMOTE_ADDR']
if not self.ignore_proxy_header:
forwarded_for = self.get_forwarded_for(request)
if forwarded_for:
closest_proxy = client_ip
client_ip = forwarded_for.pop(0)
proxies = [closest_proxy] + forwarded_for
for proxy in proxies:
if proxy not in self.trusted_proxies:
logger.info("Client IP %s forwarded by untrusted proxy %s" % (client_ip, proxy))
raise exceptions.PermissionDenied
return client_ip
def get_forwarded_for(self, request):
hdr = request.META.get('HTTP_X_FORWARDED_FOR')
if hdr is not None:
return [ip.strip() for ip in hdr.split(',')]
else:
return []
def reload_rules_if_needed(self):
last_reload_request = ReloadRulesRequest.last_request()
if last_reload_request is not None:
if self.restrictor.last_reload < last_reload_request:
self.restrictor.reload_rules()
PK 4H[ [ iprestrict/models.pyimport re
from django.core.urlresolvers import reverse
from django.db import models
from datetime import datetime
from . import ip_utils as ipu
class IPGroup(models.Model):
class Meta:
verbose_name = "IP Group"
name = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
def __init__(self, *args, **kwargs):
models.Model.__init__(self, *args, **kwargs)
self.load_ranges()
def load_ranges(self):
self._ranges = {'ipv4': [], 'ipv6': []}
for r in self.iprange_set.all():
self._ranges[r.ip_type].append(r)
def ranges(self, ip_type='ipv4'):
return self._ranges[ip_type]
def matches(self, ip):
ip_type = ipu.get_version(ip)
for r in self.ranges(ip_type):
if ip in r:
return True
return False
def ranges_str(self):
return ', '.join([str(r) for r in self.ranges()])
def __str__(self):
return self.name
__unicode__ = __str__
class IPRange(models.Model):
class Meta:
verbose_name = "IP Range"
ip_group = models.ForeignKey(IPGroup)
first_ip = models.GenericIPAddressField()
cidr_prefix_length = models.PositiveSmallIntegerField(null=True, blank=True)
last_ip = models.GenericIPAddressField(null=True, blank=True)
@property
def start(self):
if self.cidr_prefix_length is not None:
start, end = ipu.cidr_to_range(self.first_ip,
self.cidr_prefix_length)
return start
else:
return ipu.to_number(self.first_ip)
@property
def end(self):
if self.last_ip is not None:
return ipu.to_number(self.last_ip)
if self.cidr_prefix_length is not None:
start, end = ipu.cidr_to_range(self.first_ip,
self.cidr_prefix_length)
return end
return self.start
@property
def ip_type(self):
if not self.first_ip:
return ''
return ipu.get_version(self.first_ip)
def __contains__(self, ip):
ip_nr = ipu.to_number(ip)
return self.start <= ip_nr <= self.end
def __str__(self):
result = str(self.first_ip)
if self.cidr_prefix_length is not None:
result += '/' + str(self.cidr_prefix_length)
elif self.last_ip is not None:
result += '-' + str(self.last_ip)
return result
__unicode__ = __str__
class Rule(models.Model):
class Meta:
ordering = ['rank', 'id']
ACTION_CHOICES = (
('A', 'ALLOW'),
('D', 'DENY')
)
url_pattern = models.CharField(max_length=500)
ip_group = models.ForeignKey(IPGroup, default=1)
action = models.CharField(max_length=1, choices=ACTION_CHOICES, default='D')
rank = models.IntegerField(blank=True)
@property
def regex(self):
if not hasattr(self, '_regex'):
self._regex = re.compile(self.url_pattern)
return self._regex
def matches_url(self, url):
if self.url_pattern == 'ALL':
return True
else:
return self.regex.match(url) is not None
def matches_ip(self, ip):
return self.ip_group.matches(ip)
def is_restricted(self):
return self.action != 'A'
def is_allowed(self):
return self.action == 'A'
is_allowed.boolean = True
is_allowed.short_description = 'Is allowed?'
def action_str(self):
return 'Allowed' if self.is_allowed() else 'Denied'
def swap_with_rule(self, other):
other.rank, self.rank = self.rank, other.rank
other.save()
self.save()
def move_up(self):
rules_above = Rule.objects.filter(rank__lt=self.rank).order_by('-rank')
if len(rules_above) == 0:
return
self.swap_with_rule(rules_above[0])
def move_up_url(self):
url = reverse('iprestrict.views.move_rule_up', args=[self.pk])
return 'Move Up' % url
move_up_url.allow_tags = True
move_up_url.short_description = 'Move Up'
def move_down_url(self):
url = reverse('iprestrict.views.move_rule_down', args=[self.pk])
return 'Move Down' % url
move_down_url.allow_tags = True
move_down_url.short_description = 'Move Down'
def move_down(self):
rules_below = Rule.objects.filter(rank__gt=self.rank)
if len(rules_below) == 0:
return
self.swap_with_rule(rules_below[0])
def save(self, *args, **kwargs):
if self.rank is None:
max_aggr = Rule.objects.filter(rank__lt=65000).aggregate(models.Max('rank'))
max_rank = max_aggr.get('rank__max')
if max_rank is None:
max_rank = 0
self.rank = max_rank + 1
super(Rule, self).save(*args, **kwargs)
class ReloadRulesRequest(models.Model):
at = models.DateTimeField(auto_now_add=True)
@classmethod
def request_reload(cls):
rrs = ReloadRulesRequest.objects.all()
if len(rrs) > 0:
obj = rrs[0]
obj.at = datetime.now()
obj.save()
else:
cls.objects.create()
@staticmethod
def last_request():
result = None
rrs = ReloadRulesRequest.objects.all()
if len(rrs) > 0:
result = rrs[0].at
return result
PK 4H@+W W iprestrict/restrictor.pyfrom datetime import datetime
class IPRestrictor(object):
rules = None
last_reload = None
def __init__(self):
self.load_rules()
def is_restricted(self, url, ip):
for rule in self.rules:
if rule.matches_url(url) and rule.matches_ip(ip):
return rule.is_restricted()
return False
def load_rules(self):
# We are caching the rules, to avoid DB lookup on each request
from .models import Rule
self.rules = list(Rule.objects.all())
self.last_reload = datetime.now()
reload_rules = load_rules
PK 4H2 iprestrict/urls.pyfrom django.conf.urls import url
from .views import test_rules_page, test_match, reload_rules
from .views import move_rule_up, move_rule_down
app_name = "iprestrict"
urlpatterns = [
url(r'^$', test_rules_page),
url(r'^move_rule_up/(?P\d+)[/]?$', move_rule_up),
url(r'^move_rule_down/(?P\d+)[/]?$', move_rule_down),
url(r'^reload_rules[/]?$', reload_rules),
url(r'^test_match[/]?$', test_match),
]
PK 4H iprestrict/views.pyfrom django.core.urlresolvers import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render_to_response
import json
from . import models
from .decorators import superuser_required
@superuser_required
def move_rule_up(request, rule_id):
rule = models.Rule.objects.get(pk=rule_id)
rule.move_up()
return HttpResponseRedirect(reverse('admin:iprestrict_rule_changelist'))
@superuser_required
def move_rule_down(request, rule_id):
rule = models.Rule.objects.get(pk=rule_id)
rule.move_down()
return HttpResponseRedirect(reverse('admin:iprestrict_rule_changelist'))
@superuser_required
def reload_rules(request):
models.ReloadRulesRequest.request_reload()
return HttpResponse('ok')
@superuser_required
def test_rules_page(request):
return render_to_response('iprestrict/test_rules.html')
@superuser_required
def test_match(request):
url = request.REQUEST['url']
ip = request.REQUEST['ip']
matching_rule_id, action = find_matching_rule(url, ip)
rules = list_rules(matching_rule_id, url, ip)
if matching_rule_id is None:
result = {
'action': 'Allowed',
'msg': 'No rules matched.',
}
else:
result = {
'action': action,
'msg': 'URL matched Rule highlighted below.'
}
result['rules'] = rules
return HttpResponse(json.dumps(result))
def find_matching_rule(url, ip):
for r in models.Rule.objects.all():
if r.matches_url(url) and r.matches_ip(ip):
return r.pk, r.action_str()
return None, None
def list_rules(matching_rule_id, url, ip):
return [map_rule(r, matching_rule_id, url, ip) for r in models.Rule.objects.all()]
def map_rule(r, matching_rule_id, url, ip):
rule = {
'url_pattern': {
'value': r.url_pattern,
'matchStatus': 'match' if r.matches_url(url) else 'noMatch'
},
'ip_group': {
'name': r.ip_group.name,
'ranges': r.ip_group.ranges_str(),
'matchStatus': 'match' if r.matches_ip(ip) else 'noMatch'
},
'action': r.action_str(),
}
if r.pk == matching_rule_id:
rule['matched'] = True
return rule
PK a1H ! iprestrict/management/__init__.pyPK a1H * iprestrict/management/commands/__init__.pyPK 4H. - iprestrict/management/commands/importrules.pyfrom django.core.management.base import BaseCommand
from django.core.management import call_command
from ... import models
class Command(BaseCommand):
help = 'Replaces the current rules in the DB with the rules in the given fixture file(s).'
args = "fixture [fixture ...]"
def handle(self, *args, **options):
verbosity = int(options.get('verbosity', '1'))
self.delete_existing_rules()
if verbosity >= 1:
self.stdout.write('Successfully deleted rules\n')
call_command('loaddata', *args,
verbosity=verbosity,
interactive=False)
def delete_existing_rules(self):
models.Rule.objects.all().delete()
models.IPRange.objects.all().delete()
models.IPGroup.objects.all().delete()
PK 4HwqD - iprestrict/management/commands/reloadrules.pyfrom django.core.management.base import BaseCommand
from ... import models
class Command(BaseCommand):
help = 'Requests the reload of the ip restriction rules from the DB.'
def handle(self, *args, **options):
verbosity = int(options.get('verbosity', '1'))
models.ReloadRulesRequest.request_reload()
if verbosity >= 1:
self.stdout.write('Successfully requested reload of rules\n')
PK d1H+5 5 ' iprestrict/migrations/0002_init_data.py# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
def create_data(apps, schema_editor):
IPGroup = apps.get_model("iprestrict", "IPGroup")
IPRange = apps.get_model("iprestrict", "IPRange")
Rule = apps.get_model("iprestrict", "Rule")
# Default IP Groups: ALL and localhost
all_group = IPGroup.objects.create(
name='ALL', description="Matches all IP Addresses")
localhost_group = IPGroup.objects.create(
name='localhost', description="IP Address of localhost")
# IP ranges defining ALL and localhost for ipv4 and ipv6
IPRange.objects.create(ip_group=all_group,
first_ip='0.0.0.0', last_ip='255.255.255.255')
IPRange.objects.create(ip_group=all_group,
first_ip='0:0:0:0:0:0:0:0',
last_ip='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
IPRange.objects.create(ip_group=localhost_group,
first_ip='127.0.0.1')
IPRange.objects.create(ip_group=all_group,
first_ip='::1')
# Default rules: Allow all for localhost and Deny everything else
Rule.objects.create(ip_group=localhost_group,
action='A', url_pattern='ALL', rank=65535)
Rule.objects.create(ip_group=all_group,
action='D', url_pattern='ALL', rank=65536)
class Migration(migrations.Migration):
dependencies = [
('iprestrict', '0001_initial'),
]
operations = [
migrations.RunPython(create_data)
]
PK 4H=9 % iprestrict/migrations/0001_initial.py# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='IPGroup',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('name', models.CharField(max_length=100)),
('description', models.TextField(null=True, blank=True)),
],
options={
'verbose_name': 'IP Group',
},
),
migrations.CreateModel(
name='IPRange',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('first_ip', models.GenericIPAddressField()),
('cidr_prefix_length', models.PositiveSmallIntegerField(null=True, blank=True)),
('last_ip', models.GenericIPAddressField(null=True, blank=True)),
('ip_group', models.ForeignKey(to='iprestrict.IPGroup')),
],
options={
'verbose_name': 'IP Range',
},
),
migrations.CreateModel(
name='ReloadRulesRequest',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('at', models.DateTimeField(auto_now_add=True)),
],
),
migrations.CreateModel(
name='Rule',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('url_pattern', models.CharField(max_length=500)),
('action', models.CharField(default=b'D', max_length=1, choices=[(b'A', b'ALLOW'), (b'D', b'DENY')])),
('rank', models.IntegerField(blank=True)),
('ip_group', models.ForeignKey(default=1, to='iprestrict.IPGroup')),
],
options={
'ordering': ['rank', 'id'],
},
),
]
PK 4HgsN N * iprestrict/migrations/0002_initial_data.py# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations
def create_data(apps, schema_editor):
IPGroup = apps.get_model('iprestrict', 'IPGroup')
IPRange = apps.get_model("iprestrict", "IPRange")
Rule = apps.get_model("iprestrict", "Rule")
# Default IP Groups: ALL and localhost
all_group = IPGroup.objects.create(
name='ALL',
description='Matches all IP Addresses')
localhost_group = IPGroup.objects.create(
name='localhost',
description='IP address of localhost')
# IP ranges defining ALL and localhost for ipv4 and ipv6
IPRange.objects.create(
ip_group=all_group,
first_ip='0.0.0.0',
last_ip='255.255.255.255')
IPRange.objects.create(
ip_group=all_group,
first_ip='0:0:0:0:0:0:0:0',
last_ip='ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')
IPRange.objects.create(
ip_group=localhost_group,
first_ip='127.0.0.1',
last_ip=None)
IPRange.objects.create(
ip_group=localhost_group,
first_ip='::1',
last_ip=None)
# Default rules: Allow all for localhost and Deny everything else
Rule.objects.create(
ip_group=localhost_group,
action='A',
url_pattern='ALL',
rank=65535)
Rule.objects.create(
ip_group=all_group,
action='D',
url_pattern='ALL',
rank=65536)
class Migration(migrations.Migration):
dependencies = [
('iprestrict', '0001_initial'),
]
operations = [
migrations.RunPython(create_data),
]
PK 4HFu u ! iprestrict/migrations/__init__.py# -*- coding: utf-8 -*-
from __future__ import (
absolute_import, division, print_function, unicode_literals
)
PK t[Ccu u $ iprestrict/static/css/test_rules.css
body {
padding-left: 10px;
color: #333333;
font-family: ‘Palatino Linotype’, ‘Book Antiqua’, Palatino, serif;
font-size: 18px;
}
h2 {
font-size: 1.7em;
padding-left: 20px;
}
p, table {
padding-left: 35px;
}
input, button {
font-size: 110%;
padding: 5px;
}
#result {
font-size: 1.2em;
}
#action.Allowed {
color: green;
}
#action.Denied {
color: red;
}
table#rules {
width: 75%;
border-collapse: collapse;
border: 1px solid #333333;
}
table#rules th {
border: 1px solid #333333;
padding: 10px;
background-color: #333333;
color: white;
}
table#rules td {
text-align: center;
border: 1px solid #333333;
padding: 5px;
}
tr.ruleMatch {
background-color: lightgreen;
}
td.match {
color: green;
font-weight: bold;
}
td.noMatch {
color: red;
font-weight: bold;
}
PK t[C1# # . iprestrict/static/javascript/lib/handlebars.js// lib/handlebars/base.js
var Handlebars = {};
Handlebars.VERSION = "1.0.beta.6";
Handlebars.helpers = {};
Handlebars.partials = {};
Handlebars.registerHelper = function(name, fn, inverse) {
if(inverse) { fn.not = inverse; }
this.helpers[name] = fn;
};
Handlebars.registerPartial = function(name, str) {
this.partials[name] = str;
};
Handlebars.registerHelper('helperMissing', function(arg) {
if(arguments.length === 2) {
return undefined;
} else {
throw new Error("Could not find property '" + arg + "'");
}
});
var toString = Object.prototype.toString, functionType = "[object Function]";
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse || function() {}, fn = options.fn;
var ret = "";
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
if(context === true) {
return fn(this);
} else if(context === false || context == null) {
return inverse(this);
} else if(type === "[object Array]") {
if(context.length > 0) {
for(var i=0, j=context.length; i 0) {
for(var i=0, j=context.length; i 2) {
expected.push("'" + this.terminals_[p] + "'");
}
var errStr = "";
if (this.lexer.showPosition) {
errStr = "Parse error on line " + (yylineno + 1) + ":\n" + this.lexer.showPosition() + "\nExpecting " + expected.join(", ") + ", got '" + this.terminals_[symbol] + "'";
} else {
errStr = "Parse error on line " + (yylineno + 1) + ": Unexpected " + (symbol == 1?"end of input":"'" + (this.terminals_[symbol] || symbol) + "'");
}
this.parseError(errStr, {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
}
}
if (action[0] instanceof Array && action.length > 1) {
throw new Error("Parse Error: multiple actions possible at state: " + state + ", token: " + symbol);
}
switch (action[0]) {
case 1:
stack.push(symbol);
vstack.push(this.lexer.yytext);
lstack.push(this.lexer.yylloc);
stack.push(action[1]);
symbol = null;
if (!preErrorSymbol) {
yyleng = this.lexer.yyleng;
yytext = this.lexer.yytext;
yylineno = this.lexer.yylineno;
yyloc = this.lexer.yylloc;
if (recovering > 0)
recovering--;
} else {
symbol = preErrorSymbol;
preErrorSymbol = null;
}
break;
case 2:
len = this.productions_[action[1]][1];
yyval.$ = vstack[vstack.length - len];
yyval._$ = {first_line: lstack[lstack.length - (len || 1)].first_line, last_line: lstack[lstack.length - 1].last_line, first_column: lstack[lstack.length - (len || 1)].first_column, last_column: lstack[lstack.length - 1].last_column};
r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
if (typeof r !== "undefined") {
return r;
}
if (len) {
stack = stack.slice(0, -1 * len * 2);
vstack = vstack.slice(0, -1 * len);
lstack = lstack.slice(0, -1 * len);
}
stack.push(this.productions_[action[1]][0]);
vstack.push(yyval.$);
lstack.push(yyval._$);
newState = table[stack[stack.length - 2]][stack[stack.length - 1]];
stack.push(newState);
break;
case 3:
return true;
}
}
return true;
}
};/* Jison generated lexer */
var lexer = (function(){
var lexer = ({EOF:1,
parseError:function parseError(str, hash) {
if (this.yy.parseError) {
this.yy.parseError(str, hash);
} else {
throw new Error(str);
}
},
setInput:function (input) {
this._input = input;
this._more = this._less = this.done = false;
this.yylineno = this.yyleng = 0;
this.yytext = this.matched = this.match = '';
this.conditionStack = ['INITIAL'];
this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
return this;
},
input:function () {
var ch = this._input[0];
this.yytext+=ch;
this.yyleng++;
this.match+=ch;
this.matched+=ch;
var lines = ch.match(/\n/);
if (lines) this.yylineno++;
this._input = this._input.slice(1);
return ch;
},
unput:function (ch) {
this._input = ch + this._input;
return this;
},
more:function () {
this._more = true;
return this;
},
pastInput:function () {
var past = this.matched.substr(0, this.matched.length - this.match.length);
return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
},
upcomingInput:function () {
var next = this.match;
if (next.length < 20) {
next += this._input.substr(0, 20-next.length);
}
return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
},
showPosition:function () {
var pre = this.pastInput();
var c = new Array(pre.length + 1).join("-");
return pre + this.upcomingInput() + "\n" + c+"^";
},
next:function () {
if (this.done) {
return this.EOF;
}
if (!this._input) this.done = true;
var token,
match,
col,
lines;
if (!this._more) {
this.yytext = '';
this.match = '';
}
var rules = this._currentRules();
for (var i=0;i < rules.length; i++) {
match = this._input.match(this.rules[rules[i]]);
if (match) {
lines = match[0].match(/\n.*/g);
if (lines) this.yylineno += lines.length;
this.yylloc = {first_line: this.yylloc.last_line,
last_line: this.yylineno+1,
first_column: this.yylloc.last_column,
last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
this.yytext += match[0];
this.match += match[0];
this.matches = match;
this.yyleng = this.yytext.length;
this._more = false;
this._input = this._input.slice(match[0].length);
this.matched += match[0];
token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
if (token) return token;
else return;
}
}
if (this._input === "") {
return this.EOF;
} else {
this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
{text: "", token: null, line: this.yylineno});
}
},
lex:function lex() {
var r = this.next();
if (typeof r !== 'undefined') {
return r;
} else {
return this.lex();
}
},
begin:function begin(condition) {
this.conditionStack.push(condition);
},
popState:function popState() {
return this.conditionStack.pop();
},
_currentRules:function _currentRules() {
return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
},
topState:function () {
return this.conditionStack[this.conditionStack.length-2];
},
pushState:function begin(condition) {
this.begin(condition);
}});
lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
var YYSTATE=YY_START
switch($avoiding_name_collisions) {
case 0:
if(yy_.yytext.slice(-1) !== "\\") this.begin("mu");
if(yy_.yytext.slice(-1) === "\\") yy_.yytext = yy_.yytext.substr(0,yy_.yyleng-1), this.begin("emu");
if(yy_.yytext) return 14;
break;
case 1: return 14;
break;
case 2: this.popState(); return 14;
break;
case 3: return 24;
break;
case 4: return 16;
break;
case 5: return 20;
break;
case 6: return 19;
break;
case 7: return 19;
break;
case 8: return 23;
break;
case 9: return 23;
break;
case 10: yy_.yytext = yy_.yytext.substr(3,yy_.yyleng-5); this.popState(); return 15;
break;
case 11: return 22;
break;
case 12: return 34;
break;
case 13: return 33;
break;
case 14: return 33;
break;
case 15: return 36;
break;
case 16: /*ignore whitespace*/
break;
case 17: this.popState(); return 18;
break;
case 18: this.popState(); return 18;
break;
case 19: yy_.yytext = yy_.yytext.substr(1,yy_.yyleng-2).replace(/\\"/g,'"'); return 28;
break;
case 20: return 30;
break;
case 21: return 30;
break;
case 22: return 29;
break;
case 23: return 33;
break;
case 24: yy_.yytext = yy_.yytext.substr(1, yy_.yyleng-2); return 33;
break;
case 25: return 'INVALID';
break;
case 26: return 5;
break;
}
};
lexer.rules = [/^[^\x00]*?(?=(\{\{))/,/^[^\x00]+/,/^[^\x00]{2,}?(?=(\{\{))/,/^\{\{>/,/^\{\{#/,/^\{\{\//,/^\{\{\^/,/^\{\{\s*else\b/,/^\{\{\{/,/^\{\{&/,/^\{\{![\s\S]*?\}\}/,/^\{\{/,/^=/,/^\.(?=[} ])/,/^\.\./,/^[\/.]/,/^\s+/,/^\}\}\}/,/^\}\}/,/^"(\\["]|[^"])*"/,/^true(?=[}\s])/,/^false(?=[}\s])/,/^[0-9]+(?=[}\s])/,/^[a-zA-Z0-9_$-]+(?=[=}\s\/.])/,/^\[[^\]]*\]/,/^./,/^$/];
lexer.conditions = {"mu":{"rules":[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"INITIAL":{"rules":[0,1,26],"inclusive":true}};return lexer;})()
parser.lexer = lexer;
return parser;
})();
if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
exports.parser = handlebars;
exports.parse = function () { return handlebars.parse.apply(handlebars, arguments); }
exports.main = function commonjsMain(args) {
if (!args[1])
throw new Error('Usage: '+args[0]+' FILE');
if (typeof process !== 'undefined') {
var source = require('fs').readFileSync(require('path').join(process.cwd(), args[1]), "utf8");
} else {
var cwd = require("file").path(require("file").cwd());
var source = cwd.join(args[1]).read({charset: "utf-8"});
}
return exports.parser.parse(source);
}
if (typeof module !== 'undefined' && require.main === module) {
exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
}
};
;
// lib/handlebars/compiler/base.js
Handlebars.Parser = handlebars;
Handlebars.parse = function(string) {
Handlebars.Parser.yy = Handlebars.AST;
return Handlebars.Parser.parse(string);
};
Handlebars.print = function(ast) {
return new Handlebars.PrintVisitor().accept(ast);
};
Handlebars.logger = {
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
// override in the host environment
log: function(level, str) {}
};
Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
;
// lib/handlebars/compiler/ast.js
(function() {
Handlebars.AST = {};
Handlebars.AST.ProgramNode = function(statements, inverse) {
this.type = "program";
this.statements = statements;
if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
};
Handlebars.AST.MustacheNode = function(params, hash, unescaped) {
this.type = "mustache";
this.id = params[0];
this.params = params.slice(1);
this.hash = hash;
this.escaped = !unescaped;
};
Handlebars.AST.PartialNode = function(id, context) {
this.type = "partial";
// TODO: disallow complex IDs
this.id = id;
this.context = context;
};
var verifyMatch = function(open, close) {
if(open.original !== close.original) {
throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
}
};
Handlebars.AST.BlockNode = function(mustache, program, close) {
verifyMatch(mustache.id, close);
this.type = "block";
this.mustache = mustache;
this.program = program;
};
Handlebars.AST.InverseNode = function(mustache, program, close) {
verifyMatch(mustache.id, close);
this.type = "inverse";
this.mustache = mustache;
this.program = program;
};
Handlebars.AST.ContentNode = function(string) {
this.type = "content";
this.string = string;
};
Handlebars.AST.HashNode = function(pairs) {
this.type = "hash";
this.pairs = pairs;
};
Handlebars.AST.IdNode = function(parts) {
this.type = "ID";
this.original = parts.join(".");
var dig = [], depth = 0;
for(var i=0,l=parts.length; i": ">",
'"': """,
"'": "'",
"`": "`"
};
var badChars = /&(?!\w+;)|[<>"'`]/g;
var possible = /[&<>"'`]/;
var escapeChar = function(chr) {
return escape[chr] || "&";
};
Handlebars.Utils = {
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (typeof value === "undefined") {
return true;
} else if (value === null) {
return true;
} else if (value === false) {
return true;
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
}
};
})();;
// lib/handlebars/compiler/compiler.js
Handlebars.Compiler = function() {};
Handlebars.JavaScriptCompiler = function() {};
(function(Compiler, JavaScriptCompiler) {
Compiler.OPCODE_MAP = {
appendContent: 1,
getContext: 2,
lookupWithHelpers: 3,
lookup: 4,
append: 5,
invokeMustache: 6,
appendEscaped: 7,
pushString: 8,
truthyOrFallback: 9,
functionOrFallback: 10,
invokeProgram: 11,
invokePartial: 12,
push: 13,
assignToHash: 15,
pushStringParam: 16
};
Compiler.MULTI_PARAM_OPCODES = {
appendContent: 1,
getContext: 1,
lookupWithHelpers: 2,
lookup: 1,
invokeMustache: 3,
pushString: 1,
truthyOrFallback: 1,
functionOrFallback: 1,
invokeProgram: 3,
invokePartial: 1,
push: 1,
assignToHash: 1,
pushStringParam: 1
};
Compiler.DISASSEMBLE_MAP = {};
for(var prop in Compiler.OPCODE_MAP) {
var value = Compiler.OPCODE_MAP[prop];
Compiler.DISASSEMBLE_MAP[value] = prop;
}
Compiler.multiParamSize = function(code) {
return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]];
};
Compiler.prototype = {
compiler: Compiler,
disassemble: function() {
var opcodes = this.opcodes, opcode, nextCode;
var out = [], str, name, value;
for(var i=0, l=opcodes.length; i 0) {
this.source[1] = this.source[1] + ", " + locals.join(", ");
}
// Generate minimizer alias mappings
if (!this.isChild) {
var aliases = []
for (var alias in this.context.aliases) {
this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
}
}
if (this.source[1]) {
this.source[1] = "var " + this.source[1].substring(2) + ";";
}
// Merge children
if (!this.isChild) {
this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
}
if (!this.environment.isSimple) {
this.source.push("return buffer;");
}
var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
return "stack" + this.stackSlot;
},
popStack: function() {
return "stack" + this.stackSlot--;
},
topStack: function() {
return "stack" + this.stackSlot;
},
quotedString: function(str) {
return '"' + str
.replace(/\\/g, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r') + '"';
}
};
var reservedWords = (
"break else new var" +
" case finally return void" +
" catch for switch while" +
" continue function this with" +
" default if throw" +
" delete in try" +
" do instanceof typeof" +
" abstract enum int short" +
" boolean export interface static" +
" byte extends long super" +
" char final native synchronized" +
" class float package throws" +
" const goto private transient" +
" debugger implements protected volatile" +
" double import public let yield"
).split(" ");
var compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
for(var i=0, l=reservedWords.length; i