import sys
import os
import io
import cgi
import ssl
import argparse
import logging

try:
    from OpenSSL import crypto
except ImportError:
    print("Missing dependency pyOpenSSL: <https://pypi.org/project/pyOpenSSL/>. Please install it with `pip install pyOpenSSL`.")
    sys.exit(1)

if sys.version_info[0] == 2:
    from SimpleHTTPServer import SimpleHTTPRequestHandler
    import SocketServer as socketserver
else:
    from http.server import SimpleHTTPRequestHandler
    import socketserver


# defines the log format
LOG_FORMAT = "%(asctime)s.%(msecs)03d - %(levelname)s - %(message)s"
DATE_FORMAT = "%m/%d/%Y %H:%M:%S"
logging.basicConfig(filename='my.log', level=logging.INFO, format=LOG_FORMAT, datefmt=DATE_FORMAT)


# Thanks http://django-notes.blogspot.com/2012/02/generating-self-signed-ssl-certificate.html
def cert_generator(cert_dir, cert_file, key_file):
    """Generate a SSL certificate.

    If the cert_path and the key_path are present they will be overwritten.
    """
    if not os.path.exists(cert_dir):
        os.makedirs(cert_dir)
    cert_path = os.path.join(cert_dir, cert_file)
    key_path = os.path.join(cert_dir, key_file)
 
    if os.path.exists(cert_path):
        os.unlink(cert_path)
    if os.path.exists(key_path):
        os.unlink(key_path)
 
    # create a key pair
    key = crypto.PKey()
    key.generate_key(crypto.TYPE_RSA, 2048)
 
    # create a self-signed cert
    cert = crypto.X509()
    cert.get_subject().C = 'US'
    cert.get_subject().ST = 'Lorem'
    cert.get_subject().L = 'Ipsum'
    cert.get_subject().O = 'Lorem'
    cert.get_subject().OU = 'Ipsum'
    cert.get_subject().CN = 'Unknown'
    cert.set_serial_number(1000)
    cert.gmtime_adj_notBefore(0)
    cert.gmtime_adj_notAfter(10 * 365 * 24 * 60 * 60) 
    cert.set_issuer(cert.get_subject())
    cert.set_pubkey(key)
    cert.sign(key, 'sha1')
 
    with open(cert_path, 'wt') as fd: 
        fd.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert).decode())
 
    with open(key_path, 'wt') as fd: 
        fd.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, key).decode())
 
    return cert_path, key_path


class CustomHTTPRequestHandler(SimpleHTTPRequestHandler):

    def do_POST(self):
        r, info = self.deal_post_data()
        logging.info(info+"by: "+str(self.client_address))
        f = io.BytesIO()
        if r:
            f.write(b"Success\n")
        else:
            f.write(b"Failed\n")
        length = f.tell()
        f.seek(0)
        self.send_response(200)
        self.send_header("Content-type", "text/plain")
        self.send_header("Content-Length", str(length))
        self.end_headers()
        if f:
            self.copyfile(f, self.wfile)
            f.close()

    def deal_post_data(self):
        ctype, pdict = cgi.parse_header(self.headers['Content-Type'])
        #pdict['boundary'] = bytes(pdict['boundary'], "utf-8")
        #pdict['CONTENT-LENGTH'] = int(self.headers['Content-Length'])
        if ctype == 'multipart/form-data':
            form = cgi.FieldStorage( fp=self.rfile, headers=self.headers, environ={'REQUEST_METHOD':'POST', 'CONTENT_TYPE':self.headers['Content-Type'], })
            try:
                open("./{filename}".format(filename=form["file"].filename), "wb").write(form["file"].file.read())
            except IOError:
                    return (False, "Can't create {filename} to write, do you have permission to write?".format(filename=form["file"].filename))
        return (True, "{filename} uploaded".format(filename=form["file"].filename))



if __name__ == '__main__':
    parser = argparse.ArgumentParser(description="HTTP Upload of PII Data emulation")
    parser.add_argument("-port", type=int, help = "listening port, default to be 80 for http, and 4443 for https", required=False)
    parser.add_argument("-https", dest='https', help="start a HTTPS server", action='store_true')
    args = parser.parse_args()

    # generate self-signed cert
    cert_file = 'my_cert.crt'
    key_file = 'my_key.key'

    if not args.port:
        if args.https:
            port = 4443
        else:
            port = 80
    else:
        port = args.port

    # 'with' statement cannot be used in python2
    server = socketserver.TCPServer(("", port), CustomHTTPRequestHandler)
    if args.https:
        cert_generator('./', cert_file, key_file)
        server.socket = ssl.wrap_socket(server.socket, keyfile='my_key.key', certfile='my_cert.crt', server_side=True)
    print("serving at port {port}".format(port=port))
    server.serve_forever()
