# -*- coding: utf-8 -*-


import os
import sys
import traceback
from OpenSSL import crypto
import uuid
from flask import (Flask, request, redirect, url_for, render_template,
                   jsonify, session, abort, send_file)

app = Flask(__name__)


@app.errorhandler(403)
def error403(res):
  return "403: Forbidden. Your are not allowed to access %s" % res, 403
  return response

@app.errorhandler(404)
def error404(msg=""):
  return "404: Resource not found.\n%s\n" % msg, 404

@app.errorhandler(501)
def error501(msg=""):
  return "501: Internal Error. %s\n" % msg, 501

@app.errorhandler(412)
def error412(msg):
  return "412: Precondition Failed: %s.\n" % msg, 412

@app.errorhandler(400)
def error400():
  return "400: Bad Request", 400

def writefile(path, content, mode=0640):
  with open(path, 'w') as fd:
    fd.write(content)
  os.chmod(path, mode)


@app.route('/')
def index():
  # page to list certificates, also connection link
  cert_list = [os.path.basename(app.config.ca.certificate)]
  cert_list.extend([x for x in os.listdir(app.config.cert_dir)])
  return render_template("index.html", filename_list=cert_list)

@app.route('/get/<string:name>', methods=['GET'])
def getfile(name):
  ca_name = os.path.basename(app.config.ca.certificate)
  if name == ca_name:
    return send_file(app.config.ca.certificate,
                      attachment_filename=ca_name,
                      as_attachment=True)
  else:
    cert_file = os.path.join(app.config.cert_dir, name)
    if os.path.exists(cert_file) and os.path.isfile(cert_file):
      filename = os.path.basename(cert_file)
      return send_file(cert_file,
                        attachment_filename=filename,
                        as_attachment=True)

  return abort(404, name)

@app.route('/request', methods=['POST'])
def do_request():
  csr_content = request.form.get('csr', '').encode('utf-8')
  if not csr_content:
    return abort(400)

  try:
    req = app.config.ca.readCertificateRequest(csr_content)
    # XXX - Maybe more check if the csr is valid with CA
  except crypto.Error, e:
    return abort(412, str(e))

  cert_id = str(uuid.uuid1())
  csr_keyfile = '%s+%s.csr.pem' % (cert_id, str(uuid.uuid4()))
  request_file = os.path.join(app.config.req_dir, csr_keyfile)

  if os.path.exists(request_file):
    # The request already exist
    raise Exception("Certificate Signature Request file should be unique")
  try:
    writefile(request_file, csr_content)
  except OSError, e:
    raise

  return cert_id

@app.route('/signcert', methods=['POST'])
def do_signcert():
  """
    This method should be called by a list of host allowed, can be used to sign with command line
    For security, it's good to only allow local ip (127.0.0.1)
  """
  key = request.form.get('key', '').encode('utf-8')
  if not key:
    return abort(400)
  remote_client = request.remote_addr
  x_forwarded_for_list = request.headers.getlist("X-Forwarded-For")
  if remote_client not in app.config.trusted_host_list or \
      (x_forwarded_for_list and x_forwarded_for_list[0] not in app.config.trusted_host_list):

    return abort(403)  # Forbidden

  req_file = os.path.join(app.config.req_dir, '%s.csr.pem' % key)
  if os.path.exists(req_file):
    cert_id = key.split('+')[0]
    output = os.path.join(app.config.cert_dir, '%s.cert.pem' % cert_id)
    try:
      if os.path.exists(output):
        # This should not happend normally
        return abort(412, "The certificate already exists.")

      app.config.ca.signCertificateRequest(req_file, output)
      os.rename(req_file, os.path.join(app.config.csr_dir, '%s.csr.pem' % cert_id))
    except Exception, e:
      if os.path.exists(output):
        try:
          os.unlink(output)
        except:
          pass
      traceback.print_exc()
      return abort(501, str(e))
  else:
    return abort(403)
  return "Certificate is signed"

@app.route('/renewcert/<string:serial>', methods=['POST'])
def renewcert(serial):
  """
  this method is used to renew expired certificate.
  """
  return abort(200, "Done. The method is not implemented yet!")

