#!/usr/bin/env python
# Single purpose HTTP server
# - serves files specified as arguments in order of appearance

import os
import sys
import json
import ssl
import cgi
import BaseHTTPServer

class Handler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_POST(self):
        form = cgi.FieldStorage(
            fp=self.rfile,
            headers=self.headers,
            environ={
                'REQUEST_METHOD': 'POST',
                'CONTENT_TYPE': self.headers['Content-Type'],
            }
        )

        self.send_response(202)
        self.send_header('Content-Type', 'application/json')
        self.send_header('Connection', 'close')
        self.end_headers()

        if self.path != '/faf/reports/new/':
            print 'Invalid request path'
            return

        json_str = form['file'].file.read()
        try:
            #just check that it's a JSON
            json.loads(json_str)
        except ValueError:
            print 'Received invalid JSON data:\n{0}'.format(json_str)
            return

        response = {
            'bthash': '691cf824e3e07457156125636e86c50279e29496',
            'message': 'https://retrace.fedoraproject.org/faf/reports/6437/\nhttps://bugzilla.redhat.com/show_bug.cgi?id=851210',
            'reported_to': [
                {
                    'type': 'url',
                    'value': 'https://retrace.fedoraproject.org/faf/reports/6437/',
                    'reporter': 'ABRT Server'
                },
                {
                    'type': 'url',
                    'value': 'https://bugzilla.redhat.com/show_bug.cgi?id=851210',
                    'reporter': 'Bugzilla'
                }
            ],
            'result': True
        }
        json.dump(response, self.wfile, indent=2)
        #write result to stdout
        self.auth_status()

    # Useful for debugging this script with a browser.
    def do_GET(self):
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.end_headers()

        self.auth_status(self.wfile)

    def auth_status(self, fh=sys.stdout):
        fh.write('NOAUTH\n')
        fh.flush()


class SSLAuthHandler(Handler):
    def auth_status(self, fh=sys.stdout):
        cert = self.request.getpeercert()
        if not cert:
            Handler.auth_status(self)
        else:
            # "which is a fairly poorly-formed subject field"
            for item in cert['subject']:
                (k, v) = item[0]
                if k == 'commonName':
                    fh.write('AUTH {0}\n'.format(v))
                    break
            else:
                fh.write('AUTHERROR: no commonName\n')
        fh.flush()


class HttpAuthHandler(Handler):
    def __init__(self, address, handler, arg):
        Handler.__init__(self, address, handler, arg)
        self._auth_status = 'Bug: barely initialized'

    def do_AUTHHEAD(self):
         self.send_response(401)
         self.send_header('WWW-Authenticate', 'Basic realm=\"Test\"')
         self.send_header('Content-type', 'text/html')
         self.end_headers()

    def do_AUTH(self, protected_method):
        if self.headers.getheader('Authorization') == None:
            self.do_AUTHHEAD()
            self._auth_status = 'prompted for credentials'
            return False
        #key "ureport:password"
        elif self.headers.getheader('Authorization') == 'Basic ' + 'dXJlcG9ydDpwYXNzd29yZA==':
            self._auth_status = 'HTTPAUTH OK'
            protected_method(self)
            return True
        else:
            self.do_AUTHHEAD()
            self._auth_status = 'invalid credentials'
            return False

    def do_POST(self):
        if not self.do_AUTH(Handler.do_POST):
            self.auth_status()

    def do_GET(self):
        if not self.do_AUTH(Handler.do_GET):
            self.auth_status(self.wfile)

    def auth_status(self, fh=sys.stdout):
        fh.write(self._auth_status)
        fh.write('\n')
        fh.flush()


if __name__ == '__main__':
    try:
        reqs = { 'none': ssl.CERT_NONE,
                 'optional': ssl.CERT_OPTIONAL,
                 'required': ssl.CERT_REQUIRED,
                 'http_required' : ssl.CERT_NONE + ssl.CERT_OPTIONAL + ssl.CERT_REQUIRED
                  }[sys.argv[1]]
    except (KeyError, IndexError):
        sys.exit('Usage: {0} <none|optional|required,http_required>'.format(sys.argv[0]))

    PORT = 12345
    print "Serving at port", PORT

    if reqs == ssl.CERT_NONE:
        httpd = BaseHTTPServer.HTTPServer(("", PORT), Handler)
    elif reqs == ssl.CERT_OPTIONAL or reqs == ssl.CERT_REQUIRED:
        httpd = BaseHTTPServer.HTTPServer(("", PORT), SSLAuthHandler)
    elif reqs == ssl.CERT_NONE + ssl.CERT_OPTIONAL + ssl.CERT_REQUIRED:
        httpd = BaseHTTPServer.HTTPServer(("", PORT), HttpAuthHandler)
        reqs = ssl.CERT_NONE
    else:
        raise Exception('BUG in Server AUTH type configuration')

    ca_certs = None if reqs == ssl.CERT_NONE else 'cert/ca_cert.pem'
    httpd.socket = ssl.wrap_socket(httpd.socket,
            server_side=True,
            certfile='cert/server_cert.pem',
            keyfile='cert/server_key.pem',
            ca_certs=ca_certs,
            cert_reqs=reqs)

    httpd.serve_forever()
