Changeset - 6f9fd557a088
ennstatus/api/model.py
Show inline comments
 
@@ -234,38 +234,41 @@ class Server:
 
            raise NotImplementedError
 

	
 
        if data is not None:
 
            data = data.relays[0]
 

	
 
        self.mean_consensus_weight = calculate_weight(data.consensus_weight)
 
        self.mean_exit_probability = calculate_weight(data.exit_probability)
 
        self.mean_guard_probability = calculate_weight(
 
            data.guard_probability
 
        )
 
        self.mean_middle_probability = calculate_weight(
 
            data.middle_probability
 
        )
 
        self.mean_consensus_weight_fraction = calculate_weight(
 
            data.consensus_weight_fraction
 
        )
 

	
 
    def update_flags(self):
 

	
 
        try:
 
            data = manager.query('details', lookup=self.fingerprint)
 
        except:
 
            raise NotImplementedError
 

	
 
        if data is not None:
 
            self.flags = data.relays[0].flags
 
        else:
 
            raise NotImplementedError
 
        try:
 
            if data is not None:
 
                self.flags = data.relays[0].flags
 
            else:
 
                raise NotImplementedError
 
        except IndexError as e:
 
            raise NotImplementedError from e
 

	
 
    def check_status(self):
 

	
 
        now = datetime.utcnow()
 
        delta = now - self.last_updated
 

	
 
        if delta.seconds >= 3600:
 
            self.status = False
 
        elif delta.seconds >= 600:
 
            self.status = None
ennstatus/api/views.py
Show inline comments
 
@@ -94,57 +94,58 @@ def update():
 
        except ipaddress.AddressValueError:
 
            return 'IP not allowed!\n', 403, {'Content-Type': 'text/plain'}
 
        else:
 
            if temp_ip.version == 4:
 
                data['ip'] = ip
 
            elif temp_ip.verison == 6:
 
                data['ip6'] = ip
 

	
 
    try:
 
        country = gi4.country_name_by_addr(ip)
 
    except pygeoip.GeoIPError:
 
        country = gi6.country_name_by_addr(ip)
 

	
 
    data['country'] = country
 
    data['last_updated'] = strict_rfc3339.timestamp_to_rfc3339_utcoffset(
 
        datetime.utcnow().timestamp()
 
    )
 

	
 
    try:
 
        server = Server.from_dict(data)
 
    except Exception as e:
 
        current_app.logger.warning(' '.join([str(e), str(data)]))
 
        return str(e), 409, {'Content-Type': 'text/plain'}
 

	
 
    try:
 
        server.update_weights()
 
    except NotImplementedError:
 
        pass
 
    if current_app.config['ENNSTATUS_ENABLE_ONIONOO']:
 
        try:
 
            server.update_weights()
 
        except NotImplementedError:
 
            pass
 

	
 
    try:
 
        server.update_flags()
 
    except NotImplementedError:
 
        pass
 
        try:
 
            server.update_flags()
 
        except NotImplementedError:
 
            pass
 

	
 
    try:
 
        server.save()
 
    except Exception as e:
 
        current_app.logger.error(str(e))
 
        return str(e), 500, {'Content-Type': 'text/plain'}
 

	
 
    current_app.logger.info('Return result')
 
    return (
 
        server.json(), 201,
 
        {
 
            'Location': '/api/export/json/single?server_name={}'.format(
 
                server.name
 
            )
 
        }
 
    )
 

	
 

	
 
@api_page.route('/export', defaults={'server_type': 'all'})
 
@api_page.route(('/export/<any("all", "exit", "bridge", "relay", "single")'
 
                 ':server_type>'))
 
def export(server_type):
 

	
 
    current_app.logger.info('Handling export')
ennstatus/cli/__init__.py
Show inline comments
 
# Ënnstatus
 
# Copyright (C) 2015  Dennis Fink
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import pathlib
 
import importlib
 
import operator
 

	
 
import click
 

	
 
from .commands import __all__ as commands_all
 

	
 

	
 
@click.group()
 
@click.option('-p', '--path', default='/srv/http/enn.lu',
 
              help='Path where the config files are',
 
              type=click.Path(exists=True,
 
                              file_okay=False,
 
                              writable=True,
 
                              readable=True)
 
              )
 
@click.pass_context
 
def cli(ctx, path):
 
    ctx.obj = {}
 
    path = pathlib.Path(path)
 
    ctx.obj['path'] = path
 
    ctx.obj['config_file'] = path / 'config.json'
 
    ctx.obj['data_dir'] = path / 'data'
 

	
 

	
 
from .commands import config
 
cli.add_command(config, 'config')
 
subcommands = importlib.import_module('ennstatus.cli.commands')
 
for command in commands_all:
 
    get = operator.attrgetter(command)
 
    cli.add_command(get(subcommands), command)
 

	
 
if __name__ == '__main__':
 
    cli()
ennstatus/cli/commands/__init__.py
Show inline comments
 
# Ënnstatus
 
# Copyright (C) 2015  Dennis Fink
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
from .config import config
 
from .stats import stats
 

	
 
__all__ = ['config']
 
__all__ = ['config', 'stats']
ennstatus/cli/commands/config.py
Show inline comments
 
# Ënnstatus
 
# Copyright (C) 2015  Dennis Fink
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import json
 
import ipaddress
 
import subprocess
 

	
 
from pprint import pprint
 

	
 
import click
 

	
 
from ...utils import check_ip
 

	
 

	
 
@click.group(short_help='Configure ennstatus')
 
def config():
 
    pass
 

	
 

	
 
@config.command('show', short_help='Show current configuration')
 
@click.pass_obj
 
def show(obj):
 

	
 
    with obj['config_file'].open(encoding='utf-8') as f:
 
        config = json.load(f)
 
    pprint(config)
 

	
 

	
 
@config.group(short_help='Configure servers')
 
def server():
 
@@ -62,55 +63,90 @@ def add(obj, name, ips, password, bridge
 
            try:
 
                click.confirm('Server already exits! Overwrite?', abort=True)
 
            except click.Abort as e:
 
                raise SystemExit from e
 
    except KeyError:
 
        config['ENNSTATUS_SERVERS'] = {}
 

	
 
    converted_ips = {ipaddress.ip_address(ip) for ip in ips}
 

	
 
    for ip in converted_ips:
 
        if not check_ip(ip):
 
            raise SystemExit('ip {} is not accepted!'.format(str(ip)))
 

	
 
    config['ENNSTATUS_SERVERS'][name] = {
 
        'IPS': ips,
 
        'PASSWORD': password
 
    }
 

	
 
    if bridgeprogram:
 
        try:
 
            config['ENNSTATUS_BRIDGE_PROGRAM'].append(name)
 
        except KeyError:
 
            config['ENNSTATUS_BRIDGE_PROGRAM'] = [name]
 

	
 
    click.echo('%s password: %s' % (name, password))
 
    with obj['config_file'].open(mode='w', encoding='utf-8') as f:
 
        json.dump(config, f, indent=4, sort_keys=True)
 

	
 

	
 
@server.command('delete', short_help='Remove server')
 
@click.argument('name')
 
@click.pass_obj
 
def delete(obj, name):
 

	
 
    with obj['config_file'].open() as f:
 
        config = json.load(f)
 
    name = name.lower()
 

	
 
    try:
 
        del config['ENNSTATUS_SERVERS'][name]
 
    except KeyError:
 
        raise SystemExit('{} does not exists!'.format(name))
 

	
 
    try:
 
        config['ENNSTATUS_BRIDGE_PROGRAM'].remove(name)
 
    except KeyError:
 
    except (KeyError, ValueError):
 
        pass
 

	
 
    with obj['config_file'].open(mode='w', encoding='utf-8') as f:
 
        json.dump(config, f, indent=4, sort_keys=True)
 

	
 
    filename = obj['data_dir'] / (name + '.json')
 
    try:
 
        filename.unlink()
 
    except FileNotFoundError:
 
        pass
 

	
 

	
 
@config.group(short_help='Configure more servers at once')
 
def servers():
 
    pass
 

	
 

	
 
@servers.command('add', short_help='Add servers')
 
@click.argument('names', nargs=-1, required=True)
 
@click.option('-i', '--ips', prompt='IPs (comma separated)')
 
@click.option('--bridgeprogram/--no-bridgeprogram', default=False)
 
@click.pass_context
 
def adds(ctx, names, ips, bridgeprogram):
 

	
 
    for name in names:
 
        password = subprocess.check_output(['pwgen', '8', '1'])
 
        password = password.decode('utf-8')
 
        password = password.strip()
 
        ctx.invoke(
 
            add,
 
            name=name,
 
            ips=ips,
 
            password=password,
 
            bridgeprogram=bridgeprogram
 
        )
 

	
 

	
 
@servers.command('delete', short_help='Delete servers')
 
@click.argument('names', nargs=-1, required=True)
 
@click.pass_context
 
def dels(ctx, names):
 

	
 
    for name in names:
 
        ctx.invoke(delete, name=name)
ennstatus/cli/commands/stats.py
Show inline comments
 
new file 100644
 
import json
 

	
 
from collections import defaultdict
 

	
 
import click
 

	
 
from ennstatus import create_app
 
from ...status.functions import split_all_servers_to_types
 

	
 

	
 
@click.group(short_help='Get statistics')
 
def stats():
 
    pass
 

	
 

	
 
@stats.command('count')
 
@click.option('--by-type', 'by_type', is_flag=True, default=False)
 
@click.pass_obj
 
def count(obj, by_type):
 

	
 
    def calculate_host_number(config, type='all', servers=None):
 

	
 
        hosts = set()
 
        if type == 'all':
 

	
 
            for values in config['ENNSTATUS_SERVERS'].values():
 
                ips = frozenset(values['IPS'])
 
                hosts.add(ips)
 

	
 
            return len(hosts)
 
        else:
 
            for server in servers[servertype]:
 
                ips = frozenset(
 
                    config['ENNSTATUS_SERVERS'][server.name.lower()]['IPS']
 
                )
 
                hosts.add(ips)
 
        return len(hosts)
 

	
 
    app = create_app()
 

	
 
    with app.app_context():
 
        app.logger.disabled = True
 
        servers = split_all_servers_to_types()
 

	
 
    if not by_type:
 

	
 
        click.echo(
 
            'We have %s servers in total!' % (
 
                click.style(
 
                    str(sum(len(x) for x in servers.values())),
 
                    fg='blue'
 
                )
 
            )
 
        )
 

	
 
        click.echo(
 
            'We have %s different hosts!' % (
 
                click.style(
 
                    str(calculate_host_number(app.config)),
 
                    fg='blue'
 
                )
 
            )
 
        )
 
    else:
 
        for servertype, server in servers.items():
 
            click.echo(
 
                'We have %s %s servers!' % (
 
                    click.style(
 
                        str(len(server)),
 
                        fg='blue'
 
                    ),
 
                    click.style(
 
                        servertype,
 
                        fg='red'
 
                    )
 
                )
 
            )
 
            click.echo(
 
                'We have %s different %s hosts!' % (
 
                    click.style(
 
                        str(
 
                            calculate_host_number(
 
                                app.config,
 
                                type=servertype,
 
                                servers=servers
 
                            )
 
                        ),
 
                        fg='blue'
 
                    ),
 
                    click.style(
 
                        servertype,
 
                        fg='red'
 
                    )
 
                )
 
            )
 

	
 

	
 
@stats.command('countries')
 
@click.option('--by-type', 'by_type', is_flag=True, default=False)
 
@click.pass_obj
 
def countries(obj, by_type):
 
    app = create_app()
 

	
 
    with app.app_context():
 
        app.logger.disabled = True
 
        servers = split_all_servers_to_types()
 

	
 
    if not by_type:
 
        countries = defaultdict(int)
 

	
 
        for key, value in servers.items():
 
            for server in value:
 
                countries[server.country] += 1
 

	
 
        for key, value in sorted(countries.items(), key=lambda x: x[1]):
 
            click.echo(
 
                '%s: %s' % (
 
                    click.style(key, fg='green'),
 
                    click.style(str(value), fg='blue')
 
                )
 
            )
 

	
 
        click.echo(
 
            'We are hosted in %s different countries' % click.style(
 
                str(len(countries.keys())),
 
                fg='blue'
 
            )
 
        )
 
    else:
 
        type_countries = {
 
            'exit': defaultdict(int),
 
            'relay': defaultdict(int),
 
            'bridge': defaultdict(int)
 
        }
 

	
 
        for key, value in servers.items():
 
            for server in value:
 
                type_countries[key][server.country] += 1
 

	
 
        type_countries = dict((k, v) for k, v in type_countries.items() if v)
 

	
 
        for key, value in sorted(type_countries.items(), key=lambda x: x[0]):
 
            click.echo(
 
                click.style(
 
                    key.capitalize(),
 
                    fg='red',
 
                    bold=True,
 
                    underline=True
 
                )
 
            )
 

	
 
            for country, count in sorted(type_countries[key].items(), key=lambda x: x[1]):
 
                click.echo(
 
                    '%s: %s' % (
 
                        click.style(country, fg='green'),
 
                        click.style(str(count), fg='blue')
 
                    )
 
                )
 

	
 
            click.echo(
 
                '%s are hosted in %s different countries' % (
 
                    key,
 
                    click.style(
 
                        str(len(type_countries[key].keys())),
 
                        fg='blue'
 
                    )
 
                )
 
            )
 

	
 

	
 
@stats.command('exit_probability')
 
@click.option('--by-server', 'by_server', is_flag=True, default=False)
 
@click.pass_obj
 
def exit_probability(obj, by_server):
 

	
 
    app = create_app()
 
    with app.app_context():
 
        app.logger.disabled = True
 
        servers = split_all_servers_to_types()
 

	
 
    if not by_server:
 
        exit_probability = defaultdict(int)
 
        for server in servers['exit']:
 
            for subkey in ('1_week', '1_month', '3_months', '1_year', '5_years'):
 
                try:
 
                    if server.mean_exit_probability[subkey] is not None:
 
                        exit_probability[subkey] += server.mean_exit_probability[subkey]
 
                except KeyError:
 
                    continue
 
        for subkey in ('1_week', '1_month', '3_months', '1_year', '5_years'):
 
            try:
 
                click.echo(
 
                    'Mean exit probability over %s: %s' % (
 
                        click.style(
 
                            subkey,
 
                            fg='blue'
 
                        ),
 
                        click.style(
 
                            str(round(exit_probability[subkey], 2)) + '%',
 
                            fg='red'
 
                        )
 
                    )
 
                )
 
            except KeyError:
 
                continue
 
    else:
 
        for server in servers['exit']:
 
            click.echo(
 
                click.style(
 
                    server.name.capitalize(),
 
                    fg='red',
 
                    bold=True,
 
                    underline=True
 
                )
 
            )
 
            for subkey in ('1_week', '1_month', '3_months', '1_year', '5_years'):
 
                try:
 
                    if server.mean_exit_probability[subkey] is not None:
 
                        click.echo(
 
                            'Mean exit probabilty over %s: %s' % (
 
                                click.style(
 
                                    subkey,
 
                                    fg='blue'
 
                                ),
 
                                click.style(
 
                                    str(round(server.mean_exit_probability[subkey], 2)) + "%",
 
                                    fg='red'
 
                                )
 
                            )
 
                        )
 
                except KeyError:
 
                    continue
ennstatus/config.py
Show inline comments
 
@@ -15,24 +15,26 @@
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
import os
 
import base64
 

	
 

	
 
def init_app(app):
 

	
 
    config = app.config
 

	
 
    _default_secret_key = base64.b64encode(os.urandom(32)).decode('utf-8')
 

	
 
    config['SECRET_KEY'] = os.environ.get('SECRET_KEY', _default_secret_key)
 

	
 
    # Flask-Bootstrap
 
    config.setdefault('BOOTSTRAP_SERVE_LOCAL', True)
 

	
 
    # ennstatus
 

	
 
    # moment.js string formatting
 
    # http://momentjs.com/docs/#/displaying/format/
 
    config.setdefault('ENNSTATUS_MOMENTJS_FORMAT', 'DD MMMM YYYY HH:mm:ss')
 

	
 
    config.setdefault('ENNSTATUS_STRFTIME_FORMAT', '%d %B %Y %H:%M:%S')
 

	
 
    config.setdefault('ENNSTATUS_ENABLE_ONIONOO', False)
ennstatus/donate/views.py
Show inline comments
 
# Ënnstatus
 
# Copyright (C) 2015  Dennis Fink
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
from flask import (Blueprint, render_template, request,
 
                   redirect, url_for, current_app)
 

	
 
from babel.numbers import parse_decimal, format_decimal
 

	
 
from ennstatus.donate.forms import DateForm
 
from ennstatus.donate.functions import load_csv, get_choices
 

	
 
from ennstatus.root.forms import BPMForm
 
from ennstatus.root.constants import BPM_ADDRESSES
 

	
 
donate_page = Blueprint('donate', __name__)
 

	
 

	
 
@donate_page.route('/', methods=('GET', 'POST'))
 
@donate_page.route('/')
 
def index():
 

	
 
    current_app.logger.info('Handling index')
 
    form = BPMForm()
 
    country_choices = [choice[0] for choice in form.country.choices]
 

	
 
    if request.method == 'POST':
 
        current_app.logger.debug('Validating form')
 
        if form.validate_on_submit():
 
            country = form.country.data
 
            return redirect(url_for('donate.index', country=country))
 
    else:
 
        if 'country' in request.args:
 
            country = request.args['country']
 
            if country in country_choices:
 
                current_app.logger.info('Showing country %s' % country)
 
            else:
 
                current_app.logger.warn('Country %s not found' % country)
 
                country = 'luxembourg'
 
        else:
 
            current_app.logger.info('Using default country')
 
            country = 'luxembourg'
 

	
 
    form.country.data = country
 
    address = BPM_ADDRESSES[country]
 

	
 
    return render_template('donate/index.html', form=form, address=address)
 
    return render_template('donate/index.html')
 

	
 

	
 
@donate_page.route('/received',
 
                   methods=('GET', 'POST'))
 
def received():
 

	
 
    current_app.logger.info('Handling received')
 
    form = DateForm()
 

	
 
    current_app.logger.debug('Creating choices')
 

	
 
    files = [name for name in get_choices()]
 
    files.sort()
 

	
 
    year_choices = list({name.split('-')[0] for name in files})
 
    year_choices.sort()
 
    form.year.choices = [(name, name) for name in year_choices]
 

	
 
    if not year_choices:
 
        current_app.logger.warn('No donations found!')
 
        return render_template('donate/received.html',
 
                               form=form, csv_file=None,
 
                               year=None, month=None, total=None)
 

	
ennstatus/root/constants.py
Show inline comments
 
deleted file
ennstatus/root/forms.py
Show inline comments
 
# Ënnstatus
 
# Copyright (C) 2015  Dennis Fink
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
from flask_wtf import Form
 
from wtforms import (SelectField,
 
                     StringField,
 
from wtforms import (StringField,
 
                     RadioField,
 
                     BooleanField,
 
                     SubmitField
 
                     )
 
from wtforms.validators import InputRequired, Email, Length, DataRequired
 

	
 

	
 
COUNTRIES = [
 
    ('luxembourg', 'Luxembourg'),
 
    ('belgium', 'Belgium'),
 
    ('france', 'France'),
 
    ('germany', 'Germany'),
 
]
 

	
 

	
 
class BPMForm(Form):
 
    country = SelectField('Country',
 
                          validators=[DataRequired()],
 
                          choices=COUNTRIES)
 
    submit = SubmitField('Submit')
 
from wtforms.validators import InputRequired, Email, Length
 

	
 

	
 
class MembershipForm(Form):
 

	
 
    username = StringField('Username*',
 
                           validators=[
 
                               InputRequired('This field is required!'),
 
                               Length(max=255)
 
                           ]
 
                           )
 
    email = StringField('E-Mail*',
 
                        validators=[
 
                            InputRequired('This field is required!'),
 
                            Email()
 
                        ]
 
                        )
 
    fullname = StringField('Full name',
 
                           validators=[Length(max=65536)],
 
                           )
 
    street = StringField('Nr., Street',
 
                         validators=[Length(max=4000)],
 
                         )
 
    zip = StringField('ZIP-Code',
 
                      validators=[Length(max=30)],
ennstatus/root/views.py
Show inline comments
 
# Ënnstatus
 
# Copyright (C) 2015  Dennis Fink
 
#
 
# This program is free software: you can redistribute it and/or modify
 
# it under the terms of the GNU General Public License as published by
 
# the Free Software Foundation, either version 3 of the License, or
 
# (at your option) any later version.
 
#
 
# This program is distributed in the hope that it will be useful,
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
# GNU General Public License for more details.
 
#
 
# You should have received a copy of the GNU General Public License
 
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 

	
 
from flask import (Blueprint, render_template, current_app,
 
                   request, redirect, url_for, flash)
 

	
 
from ennstatus.root.forms import BPMForm, MembershipForm, BridgeprogramForm
 
from ennstatus.root.constants import BPM_ADDRESSES
 
from ennstatus.root.forms import MembershipForm, BridgeprogramForm
 
from ennstatus.root.functions import (create_membership_ticket,
 
                                      create_bridgeprogram_ticket)
 

	
 
root_page = Blueprint('root', __name__)
 

	
 

	
 
@root_page.route('/')
 
def index():
 
    return render_template('root/index.html')
 

	
 

	
 
@root_page.route('/about')
 
def about():
 
    return render_template('root/about.html')
 

	
 

	
 
@root_page.route('/partners')
 
def partners():
 
    return render_template('root/partners.html')
 

	
 

	
 
@root_page.route('/bridgeprogram', methods=('GET', 'POST'))
 
def bridgeprogram():
 

	
 
@@ -59,63 +58,37 @@ def bridgeprogram():
 
def member():
 
    return render_template('root/member.html')
 

	
 

	
 
@root_page.route('/membership', methods=('GET', 'POST'))
 
def membership():
 

	
 
    current_app.logger.info('Handling membership')
 
    form = MembershipForm()
 

	
 
    if request.method == 'POST':
 
        current_app.logger.debug('Validating form')
 
        if form.validate_on_submit():
 
            create_membership_ticket(form)
 
            return redirect(url_for('root.member'))
 

	
 
    return render_template('root/membership.html', form=form)
 

	
 

	
 
@root_page.route('/mirrors')
 
def mirrors():
 
    return render_template('root/mirrors.html')
 

	
 

	
 
@root_page.route('/contact', methods=('GET', 'POST'))
 
@root_page.route('/contact')
 
def contact():
 

	
 
    current_app.logger.info('Handling contact')
 
    form = BPMForm()
 
    country_choices = [choice[0] for choice in form.country.choices]
 

	
 
    if request.method == 'POST':
 
        current_app.logger.debug('Validating form')
 
        if form.validate_on_submit():
 
            country = form.country.data
 
            return redirect(url_for('root.contact', country=country))
 
    else:
 
        if 'country' in request.args:
 
            country = request.args['country']
 
            if country in country_choices:
 
                current_app.logger.info('Showing country %s' % country)
 
            else:
 
                current_app.logger.warn('Country %s not found' % country)
 
                country = 'luxembourg'
 
        else:
 
            current_app.logger.info('Using default country')
 
            country = 'luxembourg'
 

	
 
    form.country.data = country
 

	
 
    address = BPM_ADDRESSES[country]
 

	
 
    return render_template('root/contact.html', form=form, address=address)
 
    return render_template('root/contact.html')
 

	
 

	
 
@root_page.route('/abuse')
 
def abuse():
 
    return render_template('root/abuse.html')
 

	
 

	
 
@root_page.route('/ennstatus')
 
def ennstatus():
 
    return render_template('root/ennstatus.html')
ennstatus/static/css/ennstatus.css
Show inline comments
 
@@ -144,24 +144,33 @@ a, a:hover, a:active, a:visited {
 
    line-height: 1.1;
 
    box-sizing: border-box;
 
    display: inline-block;
 
}
 

	
 
.highlighted-label-big {
 
    font-size: 30px;
 
    margin-bottom: 10px;
 
    font-weight: 500;
 
    line-height: 1.1;
 
    box-sizing: border-box;
 
    display: inline-block;
 

	
 
}
 

	
 
.btn-enn {
 
    color: #fff;
 
    background-color: #00ae18;
 
    border-color: #009414;
 
}
 

	
 
footer {
 
    margin-bottom: 20px;
 
}
 

	
 
@media (min-width: 768px) {
 
    .dl-horizontal dd {
 
        margin-left: 200px
 
    }
 
    .dl-horizontal dt {
 
        width: 180px
 
    }
 
}
ennstatus/templates/base.html
Show inline comments
 
@@ -90,63 +90,67 @@
 
          <a href="#" class="dropdown-toggle" data-toggle="dropdown">Support <b class="caret"></b></a>
 
          <ul class="dropdown-menu">
 
            <li><a href="{{ url_for('donate.index') }}">Donate</a></li>
 
            <li><a href="{{ url_for('donate.received') }}">Donation history</a></li>
 
            <li><a href="{{ url_for('root.bridgeprogram')}}">Adopt a Bridge</a></li>
 
            <li class="divider"></li>
 
            <li><a href="{{ url_for('root.member') }}">Join Us</a></li>
 
            <li class="dropdown-submenu">
 
            </li>
 
          </ul>
 
        </li>
 
        <li class="dropdown">
 
          <a href="#" class="dropdown-toggle" data-toggle="dropdown">Contact <b class="caret"></b></a>
 
          <ul class="dropdown-menu">
 
            <li><a href="{{ url_for('root.contact') }}">General</a></li>
 
            <li><a href="{{ url_for('root.abuse') }}">Abuse</a></li>
 
            <li class="divider"></li>
 
            <li><a href="https://twitter.com/FrennVunDerEnn" target="blank">Twitter</a></li>
 
            <li><a href="http://lists.enn.lu/listinfo/discuss" target="blank">Mailing List</a></li>
 
          </ul>
 
        </li>
 
        <li class="dropdown">
 
          <a href="#" class="dropdown-toggle" data-toggle="dropdown">Statistics <b class="caret"></b></a>
 
          <ul class="dropdown-menu">
 
            <li><a href="https://stats.enn.lu/">Exit Nodes Traffic</a></li>
 
            <li class="divider"></li>
 
            <li><a href="{{ url_for('statistics.worldmap') }}">Worldmap</a></li>
 
          </ul>
 
        </li>
 
      </ul>
 
    </div>
 
  </div>
 
  {% endblock %}
 
  <div class="row" id="content">
 
  {% with messages = get_flashed_messages(with_categories=True) %}
 
    {% if messages %}
 
      <div class="col-md-12">
 
        {% for category, message in messages %}
 
          {{ base_macros.display_error(category, message) }}
 
        {% endfor %}
 
      </div>
 
    {% endif %}
 
  {% endwith %}
 
  {% block content %}
 
  {% endblock %}
 
  </div>
 
  <footer class="col-md-12">
 
    <hr style="margin-bottom: 0.5%;">
 
    <div class="text-center clearfix">
 
      <div class="pull-left">
 
        <a href="https://twitter.com/FrennVunDerEnn" target="blank">@FrennVunDerEnn</a>
 
  <div class="row">
 
    <footer class="col-md-12">
 
      <hr style="margin-bottom: 0.5%;">
 
      <div class="text-center clearfix">
 
        <div class="pull-left">
 
          <a href="https://twitter.com/FrennVunDerEnn" target="blank">@FrennVunDerEnn</a>
 
        </div>
 
        <strong>Frënn vun der Enn a.s.b.l.</strong>  <em>(R.C.S. Luxembourg F 9.478)</em>
 
        <div class="pull-right">
 
          <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US" target="blank">CC-BY-NC-SA</a>
 
        </div>
 
      </div>
 
      <strong>Frënn vun der Enn a.s.b.l.</strong>  <em>(R.C.S. Luxembourg F 9.478)</em>
 
      <div class="pull-right">
 
        <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/deed.en_US" target="blank">CC-BY-NC-SA</a>
 
      </div>
 
    </div>
 
  </footer>
 
    </footer>
 
  </div>
 
</div>
 

	
 
{% block scripts %}
 
  {{ super() }}
 
{% endblock %}
 

	
 
{% endblock %}
ennstatus/templates/donate/index.html
Show inline comments
 
@@ -62,63 +62,55 @@
 
        </center>
 
        <div class="well well-sm">
 
          <p>
 
            <strong>Account holder:</strong> Frënn vun der Ënn A.S.B.L.<br>
 
            <strong>BIC/SWIFT:</strong> BCEELULL<br>
 
            <strong>IBAN:</strong> LU65 0019 4055 6782 7000<br>
 
          </p>
 
       </div>
 
       <div class="well well-sm">
 
       <p>
 
         <strong>Account holder:</strong> Chaos Computer Club Lëtzebuerg A.S.B.L.<br>
 
         <strong>BIC/SWIFT:</strong> BCEELULL<br>
 
         <strong>IBAN:</strong> LU29 0019 2855 3890 4000<br>
 
         <strong>Subject:</strong> * to FVDE<br>
 
       </p>
 
       </div>
 
      </div>
 
    </div>
 
    <div class="col-md-4 clearfix">
 
      <div class="thumbnail">
 
        <center>
 
          <h3>SnailMail</h3>
 
        </center>
 
        <div class="well well-sm">
 
          <div class="pull-right">
 
            <form class="form-inline" role="form" method="POST" action"{{ url_for('donate.index') }}">
 
              {{ form.hidden_tag() }}
 
              <div class="form-group">
 
                {{ form.country(class_='form-control input-sm', onchange='this.form.submit()') }}
 
                <noscript>{{ form.submit(class_='btn btn-enn btn-sm') }}</noscript>
 
              </div>
 
            </form>
 
          </div>
 
          <address>
 
            <strong>Frënn vun der Ënn, ASBL</strong><br>
 
                    BPM 381892<br>
 
                    {{ address['address'] }}<br>
 
                    {{ address['postal_code'] }}, {{ address['city'] }}<br>
 
                    {{ address['country'] }}
 
           <address>
 
             <strong>Frënn vun der Ënn, ASBL</strong><br>
 
                    {{ config['ENNSTATUS_ADDRESS_CO'] }}<br>
 
                    {{ config['ENNSTATUS_HOUSE_NAME'] }}<br>
 
                    {{ config['ENNSTATUS_STREET'] }}<br>
 
                    {{ config['ENNSTATUS_POSTAL_CODE'] }}, {{ config['ENNSTATUS_CITY'] }}<br>
 
                    {{ config['ENNSTATUS_COUNTRY'] }}, {{ config['ENNSTATUS_CONTINENT'] }}<br>
 
          </address>
 
        </div>
 
      </div>
 
    </div>
 
    <div class="col-md-4">
 
      <div class="thumbnail">
 
        <center>
 
          <h3>Bitcoin</h3>
 
        </center>
 
        <div class="well well-sm">
 
          <p>
 
            <strong>Bitcoin Address:</strong> 1EYZCq2ZL6chWXYYkJoDo7fz39UC7do5cC
 
          </p>
 
          <form class="bitpay-donate form-horizontal" action="https://bitpay.com/checkout" method="POST" onsubmit="return checkRequiredFields(this);">
 
            <input name="action" type="hidden" value="checkout">
 
            <div class="form-group">
 
              <label class="col-sm-2 control-label">Email:</label>
 
              <div class="col-sm-10">
 
                <input class="bitpay-donate-field-email form-control" name="orderID" type="email" placeholder="Email address (optional)" maxlenght=50 autocapitalize=off autocorrect=off />
 
              </div>
 
            </div>
 
            <div class="form-group">
 
              <label class="col-sm-2 control-label">Amount:</label>
 
              <div class="col-sm-10">
 
@@ -183,37 +175,36 @@
 
    	  	    <input type="hidden" name="hosted_button_id" value="JKNKAGHS65QN4">
 
    		      <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!">
 
    		      <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1">
 
            </form>
 
          </div>
 
        </center>
 
      </div>
 
    </div>
 
    <div class="col-md-4">
 
      <div class="thumbnail">
 
        <center>
 
          <h3>Flattr</h3>
 
          <div class="well well-sm">
 
            <p>Flattr us!</p>
 
            <a href="https://flattr.com/submit/auto?user_id=FVDE&url=https%3A%2F%2Fenn.lu/donate/flattr" target="_blank">
 
              <img src="https://button.flattr.com/flattr-badge-large.png" alt="Flattr this" title="Flattr this" border="0"></img>
 
            </a>
 
          </div>
 
        </center>
 
      </div>
 
    </div>
 
    <div class="col-md-4">
 
      <div class="thumbnail">
 
        <center>
 
          <h3>BPM Points</h3>
 
          <h3>Patreon</h3>
 
          <div class="well well-sm">
 
            <p>For our parcel station and international mail boxes.</p>
 
            <p>
 
              Send your BPM voucher code to: <br>
 
              <abbr title="E-Mail"><span class="glyphicon glyphicon-envelope"></span></abbr> : <a href="mailto:info@enn.lu">info@enn.lu</a> <span class="glyphicon glyphicon-lock"></span> GPG: <a href="http://keyserver.cypherpunk.lu:11371/pks/lookup?search=info@enn.lu&op=vindex" target="_blank">0x02225522</a>
 
            </p>
 
            <p>Support us!</p>
 
            <a href="https://www.patreon.com/FVDE?utm_medium=social&ty=h&utm_campaign=creatorshare" target="_blank">
 
              <img src="https://s3.amazonaws.com/patreon_public_assets/toolbox/patreon.png" alt="Patreon" title="Patreon" border="0"></img>
 
            </a>
 
          </div>
 
        </center>
 
      </div>
 
    </div>
 
  </div>
 
{% endblock %}
ennstatus/templates/root/about.html
Show inline comments
 
@@ -25,33 +25,34 @@
 
    <h3>Organization</h3>
 
    <p class="lead">
 
      <i>Frënn vun der Ënn a.s.b.l.</i> is a non-profit organization based in Luxembourg (EU).
 
      It has been founded by members of <i>Chaos Computer Club Lëtzebuerg a.s.b.l.</i> in 2013.
 
    </p>
 
    <p>
 
      As defined in the statutes (see below), the goals of our organization are:
 
      <ul>
 
        <li>defending civil rights, especially on the internet</li>
 
        <li>defending the right to anonymity on the internet by supporting Tor Project Inc.</li>
 
        <li>fighting against censorship and ensuring free access to information</li>
 
        <li>defending the freedom of expression</li>
 
        <li>protecting privacy</li>
 
        <li>promoting free software</li>
 
      </ul>
 
    </p>
 
    <p>
 
      The name <i>Frënn vun der Ënn</i> is Luxembourgish and stands for <i>Friends of the Onion</i>.
 
      The onion is used as a logo by the Tor Project Inc. and many other related organizations.
 
    </p>
 
    <h3>Administrative Council &amp; Staff</h3>
 
    <p>
 
      <dl class="dl-horizontal" >
 
        <dt>President</dt><dd>Sam Grüneisen</dd>
 
        <dt>Secretary</dt><dd>Gilles Hamen</dd>
 
        <dt>Secretary</dt><dd>Nadine Schneider</dd>
 
        <dt>Treasurer</dt><dd>Patrick Kahr</dd>
 
        <dt>International Ambassador</dt><dd>Christophe Kemp</dd>
 
        <dt>Developers</dt><dd>Dennis Fink (Ënnstatus Lead Developer), Sam Grüneisen</dd>
 
      </dl>
 
    </p>
 
    <h3>Statutes</h3>
 
    <p>Our statutes can be looked up <a href="//statutes.enn.lu">here</a>!</p>
 
  </div>
 
{% endblock %}
ennstatus/templates/root/contact.html
Show inline comments
 
@@ -5,66 +5,56 @@
 
   it under the terms of the GNU General Public License as published by
 
   the Free Software Foundation, either version 3 of the License, or
 
   (at your option) any later version.
 

	
 
   This program is distributed in the hope that it will be useful,
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
   GNU General Public License for more details.
 

	
 
   You should have received a copy of the GNU General Public License
 
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
#}
 

	
 
{% extends "base.html" %}
 

	
 
{% set title = "Contact" %}
 

	
 
{% block content %}
 
  <div class="col-md-4">
 
    <img src="{{ url_for('static', filename='images/Contact.png') }}" class="img-responsive img-rounded center-block" alt="WhoIs" width="160"></img>
 
    <div class="text-center">
 
      <h2>Contact</h2>
 
    </div>
 
  </div>
 
  <div class="col-md-8 clearfix">
 
    <h2 class="pull-left">General</h2>
 
    <div class="pull-right">
 
      <form class="form-inline" role="form" method="POST" action="{{ url_for('root.contact') }}">
 
        {{ form.hidden_tag()}}
 
        <div class="form-group">
 
          {{ form.country(class_='form-control input-sm', onchange='this.form.submit()') }}
 
          <noscript>{{ form.submit(class_='btn btn-enn btn-sm') }}</noscript>
 
        </div>
 
      </form>
 
    </div>
 
  </div>
 
  <div class="col-md-8">
 
    <h2>General</h2>
 
    <p>Please mail all general inquiries to:</p>
 
    <address>
 
      <strong>Frënn vun der Ënn, ASBL</strong><br>
 
        BPM 381892<br>
 
        {{ address['address'] }}<br>
 
        {{ address['postal_code'] }}, {{ address['city'] }}<br>
 
        {{ address['country'] }}<br>
 
        {{ config['ENNSTATUS_ADDRESS_CO'] }}<br>
 
        {{ config['ENNSTATUS_HOUSE_NAME'] }}<br>
 
        {{ config['ENNSTATUS_STREET'] }}<br>
 
        {{ config['ENNSTATUS_POSTAL_CODE'] }}, {{ config['ENNSTATUS_CITY'] }}<br>
 
        {{ config['ENNSTATUS_COUNTRY'] }}, {{ config['ENNSTATUS_CONTINENT'] }}
 
        <br>
 
      <abbr title="E-Mail"><span class="glyphicon glyphicon-envelope"></span></abbr> : <a href="mailto:info@enn.lu">info@enn.lu</a> <span class="glyphicon glyphicon-lock"></span> GPG: <a href="http://keyserver.cypherpunk.lu:11371/pks/lookup?search=info@enn.lu&op=vindex" target="_blank">0x02225522</a><br>
 
      <abbr title="Website"><span class="glyphicon glyphicon-cloud"></span></abbr> : <a href="//enn.lu/">enn.lu/</a> <strong>or</strong> <a href="//{{ config['ENNSTATUS_ONION_ADDRESS'] }}/" target="_blank">{{ config['ENNSTATUS_ONION_ADDRESS'] }}</a><br>
 
      <abbr title="Phone"><span class="glyphicon glyphicon-earphone"></span></abbr> : {{ config['ENNSTATUS_PHONE_NUMBER'] }}<br>
 
    </address>
 

	
 
    <h2>Abuse</h2>
 
    <p>For further details about abuse handling, have a look over <a href="{{ url_for('root.abuse') }}">here</a>.</p>
 
    <address>
 
      <abbr title="E-Mail"><span class="glyphicon glyphicon-envelope"></span></abbr> : <a href="mailto:abuse@enn.lu">abuse@enn.lu</a> <span class="glyphicon glyphicon-lock"></span> GPG: <a href="http://keyserver.cypherpunk.lu:11371/pks/lookup?search=info@enn.lu&op=vindex" target="_blank">0x02225522</a><br>
 
      <abbr title="Phone"><span class="glyphicon glyphicon-earphone"></span></abbr> : </strong>{{ config['ENNSTATUS_PHONE_NUMBER'] }}<br>
 
    </address>
 

	
 
    <h2>Press</h2>
 
    <p>Press related inqueries should go to:</p>
 
    <address>
 
      <abbr title="E-Mail"><span class="glyphicon glyphicon-envelope"></span></abbr> : <a href="mailto:press@enn.lu">press@enn.lu</a> <span class="glyphicon glyphicon-lock"></span> GPG: <a href="http://keyserver.cypherpunk.lu:11371/pks/lookup?search=info@enn.lu&op=vindex" target="_blank">0x02225522</a><br>
 
      <abbr title="Phone"><span class="glyphicon glyphicon-earphone"></span></abbr> : </strong>{{ config['ENNSTATUS_PHONE_NUMBER'] }}<br>
 
    </address>
 

	
 
  </div>
 
{% endblock %}
ennstatus/templates/root/membership.html
Show inline comments
 
@@ -59,31 +59,30 @@
 
            {% endfor %}
 
          </div>
 
        {% endif %}
 
      </div>
 
      <div class="form-group">
 
        <label class="control-label col-lg-2">Double membership</label>
 
        <div class="col-lg-10">
 
          {{ form.c3l() }}
 
          <div class="btn-group">
 
            <label class="btn btn-success" for="c3l">
 
              <span class="glyphicon glyphicon-plus"></span>
 
              <span class="glyphicon glyphicon-minus"></span>
 
            </label>
 
            <label class="btn btn-default" for="c3l">
 
              {{ form.c3l.label.text|safe }}
 
            </label>
 
          </div>
 
        </div>
 
      </div>
 
      <div class="form-group">
 
        <div class="col-lg-offset-2 col-lg-10">
 
        {{ form.submit(class_='btn btn-enn') }}
 
        </div>
 
      </div>
 
    </div>
 
    </form>
 
    <p>Field marked with * are required!</p>
 
    <p><sup>1</sup>: If you have decided to apply for the <i>double membership</i>, the membership fees are 150€/year for the regular
 
    membership and 70€/year for the student membership.</p>
 
  </div>
 
{% endblock %}
setup.py
Show inline comments
 
from setuptools import setup, find_packages
 

	
 

	
 
def _get_requirements():
 

	
 
    with open('requirements.in', encoding='utf-8') as f:
 
        lines = f.readlines()
 

	
 
    lines = [line[:-1] for line in lines if not line.startswith('#')]
 
    return lines
 

	
 

	
 
setup(name='Ennstatus',
 
      version='5.4.2-dev',
 
      version='5.6.3',
 
      description=('Ennstatus provides the user with vital information about '
 
                   'the status of the organizations Tor servers.'),
 
      author='Frënn vun der Ënn',
 
      author_email='info@enn.lu',
 
      url='https://bitbucket.org/fvde/ennstatus',
 
      license='GPLv3+',
 
      packages=find_packages(),
 
      install_requires=_get_requirements(),
 
      include_package_data=True,
 
      entry_points={
 
          'console_scripts': [
 
              'ennstatuscli = ennstatus.cli:cli',
 
          ]
 
      },
 
      classifiers=['Development Status :: 5 - Production/Stable',
 
                   'Environment :: Web Environment',
 
                   'Operating System :: POSIX',
 
                   'Programming Language :: Python',
 
                   'Programming Language :: Python :: 3',
 
                   'Programming Language :: Python :: 3.3',
 
                   'Topic :: Internet',
 
                   'Topic :: Internet :: WWW/HTTP',
 
                   'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
 
                   'Topic :: Internet :: WWW/HTTP :: WSGI',
0 comments (0 inline, 0 general)