<?php 

set_include_path(get_include_path() . PATH_SEPARATOR . '../../app');
include_once "base_controller.php";
include_once "utils.php";

class OpenVPNController extends BaseController {
  protected function signinRequired() {
    return true;
  }

  protected function adminRequired() {
    return true;
  }

  protected function platformControl() {
    return array('white_list' => ['FS']);
  }

  protected function parseServerConf() {
    $config = array();
    $content = file_get_contents("/etc/openvpn/server.conf");
    $lines = explode(PHP_EOL, $content);
    foreach($lines as $line) {
      //remove trailing comment
      $comment_start = strpos($line, '#');
      if ($comment_start)
        $line = substr($line, 0, $comment_start);

      $parts = explode(' ', $line, 2);
      if (count($parts) != 2)
        continue;
      $config[trim($parts[0])] = trim($parts[1]);
    }
    return $config;
  }

  protected function doesCertFileExist($file) {
    if (!isset($file))
      return false;

    return file_exists(build_file_path("/etc/openvpn", $file));
  }

  public function doAjaxGet() {
    $response = array();
    $config = $this->parseServerConf();
    $data = array();
    $keys = array('port', 'proto', 'server');
    foreach($keys as $key) {
      if (!isset($config[$key]))
        continue;
      $data[$key] = $config[$key];
    }

    $data['ca'] = $this->doesCertFileExist($config['ca']);
    $data['serverCert'] = $this->doesCertFileExist($config['cert']) && $this->doesCertFileExist($config['key']);
    $response['data'] = $data;
    $this->renderAjaxSuccess2($response);
  }

  protected function saveServerConfig() {
    $response = array();
    $config = $this->parseServerConf();

    if (!$this->doesCertFileExist($config['ca']))
      $this->renderAjaxError($response, "Please generate CA first");
    
    if (!$this->doesCertFileExist($config['cert']) && $this->doesCertFileExist($config['key']))
      $this->renderAjaxError($response, "Please generate server certificate first");

    if (!isset($_POST['server']))
      $this->renderAjaxError($response, "'server' config is missing");

    if (!isset($_POST['proto']) || ($_POST['proto'] != 'tcp' && $_POST['proto'] != 'udp'))
      $this->renderAjaxError($response, "'proto' config is invalid");
     
    if (!isset($_POST['port']) || preg_match('/^[0-9]{1,5}$/', $_POST['port'])!=1 )
      $this->renderAjaxError($response, "'port' config is invalid");

    $args = array();
    $args['proto'] = $_POST['proto'];
    $args['port'] = $_POST['port'];
    $args['server'] = $_POST['server'];
    $args['cert'] = $config['cert'];
    $args['key'] = $config['key'];

    file_put_contents("/etc/openvpn/server.conf", 
      render_template("server_conf_template.php", $args));

    execCmd("/etc/init.d/S60openvpn restart");

    $this->renderAjaxSuccess2($response);
  }

  protected function generateCA() {
    $response = array();
    if (!isset($_POST['cn'])) 
      $this->renderAjaxError($response, "'Common Name' of CA is missing");

    $common_name = $_POST['cn'];
    if (preg_match('/^[a-zA-Z0-9-_]{1,64}$/', $common_name) != 1) 
      $this->renderAjaxError($response, "'Common Name' is invalid");

    //prepare pki 
    //  1. init pki folder 
    //  2. generate CA
    //  3. generate dh.pem (slow, ~13s)
    //  4. generate ta.key
    $output = shell_exec('cd /etc/openvpn && easyrsa --batch init-pki && easyrsa --batch --req-cn="' . escapeshellarg($common_name) . '" --days=3650 build-ca nopass && openssl dhparam -dsaparam -out pki/dh.pem 2048 && openvpn --genkey --secret ta.key && echo "SUCCESS"')
    if (is_null($output))
      $this->renderAjaxError($response, "failed to create CA");

    if (preg_match("/SUCCESS\s*$/", $output) != 1)
      $this->renderAjaxError($response, "failed to create CA");
    $this->renderAjaxSuccess2($response);
  }

  protected function generateServerCert() {
    $response = array();
    if (!isset($_POST['cn'])) 
      $this->renderAjaxError($response, "'Common Name' of sever certificate is missing");

    $common_name = $_POST['cn'];
    if (preg_match('/^[a-zA-Z0-9-_]{1,64}$/', $common_name) != 1) 
      $this->renderAjaxError($response, "'Common Name' is invalid");

    if (!$this->doesCertFileExist("pki/ca.crt"))
      $this->renderAjaxError($response, "can not create server certificate without CA");

    $output = shell_exec('cd /etc/openvpn && easyrsa --batch --days=3650 build-server-full ' . escapeshellarg($common_name) .' nopass && echo "SUCCESS"')

    if (is_null($output))
      $this->renderAjaxError($response, "failed to create server certificate");

    if (preg_match("/SUCCESS\s*$/", $output) != 1)
      $this->renderAjaxError($response, "failed to create server certificate");

    //update server.conf with new openvpn server keypair
    $config = $this->parseServerConf();

    $args = array();
    $args['proto'] = $config['proto'];
    $args['port'] = $config['port'];
    $args['server'] = $config['server'];
    $args['cert'] = "pki/issued/" . $common_name . ".crt";
    $args['key'] = "pki/private/" . $common_name . ".key";

    file_put_contents("/etc/openvpn/server.conf", 
      render_template("server_conf_template.php", $args));
    
    $this->renderAjaxSuccess2($response);
  }

  protected function generateClientCert() {
    $response = array();
    if (!isset($_POST['cn'])) 
      $this->renderAjaxError($response, "'Common Name' of client certificate is missing");

    $common_name = $_POST['cn'];
    if (preg_match('/^[a-zA-Z0-9-_]{1,64}$/', $common_name) != 1) 
      $this->renderAjaxError($response, "'Common Name' is invalid");

    if (!isset($_POST['remote']))
      $this->renderAjaxError($response, "'remote' parameter is missing");

    //prevent possible duplicate client cert
    if ($this->doesCertFileExist('pki/issued/'.$common_name.'.crt'))
      $this->renderAjaxError($response, "client certificate '".$common_name."' already exists.");

    if (!$this->doesCertFileExist("pki/ca.crt"))
      $this->renderAjaxError($response, "can not create client certificate without CA");

    $output = shell_exec('cd /etc/openvpn && easyrsa --batch --days=3650 build-client-full ' . escapeshellarg($common_name) . ' nopass && echo "SUCCESS"')
    if (is_null($output))
      $this->renderAjaxError($response, "failed to create client certificate");

    if (preg_match("/SUCCESS\s*$/", $output) != 1)
      $this->renderAjaxError($response, "failed to create client certificate");

    $url = 'openvpn_controller.php?action=dlClientCert&cn=' . urlencode($common_name) . '&remote=' . urlencode($_POST['remote']);
    $this->redirect($url)
  }

  public function doAjaxPost() {
    $response = array();
    if (!isset($_POST['action']))
      $this->renderAjaxError($response, "'action' parameter is missing");

    $action = $_POST['action'];
    if ($action == 'saveConfig')
      $this->saveServerConfig();
    else if ($action == 'genCA')
      $this->generateCA();
    else if ($action == 'genServerCert')
      $this->generateServerCert();
    else if ($action == 'genClientCert')
      $this->generateClientCert();
    else
      $this->renderAjaxError($response, "'action' parameter is invalid");
  }

  public function doGet() {
    if (!isset($_GET['cn']))
      die("'cn' parameter is missing");
    $common_name = $_GET['cn'];

    if (preg_match('/^[a-zA-Z0-9-_]{1,64}$/', $common_name) != 1) 
      die("'Common Name' is invalid");

    if (!isset($_GET['remote']))
      die("'remote' parameter is missing");

    //generate client.ovpn
    $server_config = $this->parseServerConf();
    $args = array();
    $args['proto'] = $server_config['proto'];
    $args['remote'] = $_GET['remote'];
    $args['cn'] = $common_name;
    $client_config_str = render_template("client_ovpn_template.php", $args);

    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="'. $common_name . '.ovpn"')
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . strlen($client_config_str))

    print $client_config_str;
  }
}

$controller = new OpenVPNController();
$controller->run();
?>