# Ë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 .
import ipaddress
import json
from datetime import datetime
from flask import (Blueprint, request, current_app, jsonify, render_template,
abort)
from werkzeug.exceptions import BadRequest
import strict_rfc3339
import pygeoip
from ennstatus import csrf
from ennstatus.status.functions import (single_server, all_servers,
all_servers_by_type)
from .model import Server
from .auth import httpauth
api_page = Blueprint('api', __name__)
gi4 = pygeoip.GeoIP('/usr/share/GeoIP/GeoIP.dat', pygeoip.MEMORY_CACHE)
gi6 = pygeoip.GeoIP('/usr/share/GeoIP/GeoIPv6.dat', pygeoip.MEMORY_CACHE)
@csrf.exempt
@api_page.route('/update', methods=('POST',))
@httpauth.login_required
def update():
current_app.logger.info('Handling update')
try:
servers = current_app.config['ENNSTATUS_SERVERS']
except KeyError as e:
current_app.logger.error(str(e))
return abort(500)
username = httpauth.username()
try:
if request.remote_addr not in servers[username]['IPS']:
current_app.logger.warn(
'Unallowed IP {} tried to update data!'.format(
request.remote_addr
)
)
return 'IP not allowed!\n', 403, {'Content-Type': 'text/plain'}
except KeyError as e:
current_app.logger.error(str(e))
return abort(500)
try:
data = request.get_json()
except BadRequest:
current_app.logger.info('No JSON data supplied!')
return 'No JSON data supplied!\n', 400, {'Content-Type': 'text/plain'}
try:
if username != data['name'].lower():
current_app.logger.warn(
'Unallowed user {} tried to update {}!'.format(
username, data['name']
)
)
return ('You are not allowed to update this server\n',
403, {'Content-Type': 'text/plain'})
except KeyError:
return abort(409)
if 'ip' in data:
ip = data['ip']
elif 'ip6' in data:
ip = data['ip6']
else:
ip = request.remote_addr
try:
temp_ip = ipaddress.ip_address(ip)
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
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/'))
def export(server_type):
current_app.logger.info('Handling export')
if server_type == 'single':
server_name = request.args.get('server_name', None)
if server_name is not None:
server = single_server(server_name)
if server:
return server.json(), 200, {'Content-Type': 'application/json'}
else:
current_app.logger.warning('Server not found!')
return ('Server not found!\n',
404, {'Content-Type': 'text/plain'})
else:
current_app.logger.warning('No server_name specified!')
return ('No server_name specified!\n',
400, {'Content-Type': 'text/plain'})
else:
if server_type == 'all':
current_app.logger.info('Getting all servers!')
servers = [
json.loads(
server.json()
) for server in all_servers()
]
else:
current_app.logger.info('Getting all {}!'.format(server_type))
servers = [
json.loads(
server.json()
) for server in all_servers_by_type(server_type.lower())
]
return json.dumps(servers), 200, {'Content-Type': 'application/json'}
@api_page.route('/fingerprints', defaults={'server_type': 'all'})
@api_page.route('/fingerprints/')
def fingerprint(server_type):
if server_type == 'all':
servers = [server.fingerprint for server in all_servers()
if server.type != 'bridge']
else:
servers = [server.fingerprint
for server in all_servers_by_type(server_type.lower())]
return '\n'.join(servers), 200, {'Content-Type': 'text/plain'}