<?php
include_once("EsmithDatabase.php");
define('CONFIG_KEY', 'client-manager');

$hosts_db = new EsmithDatabase('hosts');
$config_db = new EsmithDatabase('configuration');
$dhcp_db = new EsmithDatabase('dhcp');

$uri = $_SERVER['REQUEST_URI'];
// add leading '/' to uri string, works if $uri is empty
$uri = (substr($uri, 0, 1) == '/') ? $uri : '/' . $uri;
$pos = strpos($uri, '?');
if (false !== $pos) {
    $route = substr($uri, 0, (int) $pos);
} else {
    $route = $uri;
}
// remove trailing '/'
$route = rtrim($route, '/');

$query_string = $_SERVER['QUERY_STRING'];
$method = $_SERVER["REQUEST_METHOD"];
$get_params = array();
parse_str($query_string, $get_params);

// determine internal function name (all lowercase, dash2underscore, named ROUTE_methodname_routename )
$function = preg_replace(':\-:', '_', strtolower($route));
$function = preg_replace(':^/:', 'ROUTE_'.$method.'_', $function);

if (function_exists($function)) {
    $function();
} else {
    die('Go away.');
}


function getClientFromUUID($uuid=false) {
    global $hosts_db;
    $hosts = $hosts_db->getAllByProp('uuid',$uuid);
    if (count($hosts)>0) {
        return array_pop($hosts);
    } else {
        return false;
    }
}

function getClientFromIP($ip=false) {
    global $hosts_db;
    $hosts = $hosts_db->getAllByProp('IpAddress',$ip);
    if (count($hosts)>0) {
        return array_pop($hosts);
    } else {
        return false;
    }
}

function getClientMac($ip=false) {
    if (false === $ip) {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    $result = pingHost($ip,1);
    if (!$result) return false;
    $macAddr=false;
    $arptable=`arp -n $ip`;
    $lines=explode("\n", $arptable);
    foreach($lines as $line) {
       $cols=preg_split('/\s+/', trim($line));
       if ($cols[0]==$ip)
       {
           return $cols[2];
       }
    }
    return false;
}



function pingHost($host, $timeout=1) {
    $pingresult = exec("/bin/ping -c 1 -t 2 -W $timeout $host", $output, $retvar);
    return (0 == $retvar);
}

function filtered_json() {
  global $_filtered_json;
  if (is_null($_filtered_json)) {
    $ok_elements = array('client_uuid','server_uuid','last_config','hostname');
    $data = json_decode(file_get_contents('php://input'), true);
    $_filtered_json = array_intersect_key($data,array_flip($ok_elements));
  }
  return $_filtered_json;
}

function my_uuid() {
  global $config_db;
  $uuid = $config_db->getProp(CONFIG_KEY,'uuid');
  if (empty($uuid)) {
    $uuid=trim(shell_exec('/usr/bin/uuidgen'));
    $config_db->setProp(CONFIG_KEY,array('uuid'=>$uuid));
  }
  return $uuid;
}

function find_free_ipaddress() {
    global $hosts_db, $dhcp_db;

    $ip = ip2long($ip_str);
    if ( ! $ip_str_start = $dhcp_db->getProp('lan0','DhcpRangeStart') ) {
        return false;
    }
    if ( ! $ip_str_end = $dhcp_db->getProp('lan0','DhcpRangeEnd') ) {
        return false;
    }
    $ip_start = ip2long($ip_str_start)+1;
    $ip_end = ip2long($ip_str_end);

    while($ip_start <= $ip_end){
        $hosts = $hosts_db->getAllByProp('IpAddress',long2ip($ip_start));
        if (count($hosts)==0) {
            return long2ip($ip_start);
        }
      $start_ip++;
    }
    return false;
}


function ROUTE_POST_ping() {
  global $config_db, $hosts_db;
  $data = filtered_json();
  $server_uuid=my_uuid();

  $out=array();
  $domainName = (string)$config_db->getKeyValue('DomainName');

  if (!isset($data['server_uuid'])) {
    # client is unmanaged, let's fix that
    $systemName = (string)$config_db->getKeyValue('SystemName');
    $TCPPort = $config_db->getProp(CONFIG_KEY,'TCPPort');
    $out['ping_url']="http://$systemName.$domainName:$TCPPort/ping";
    $out['server_uuid']=$server_uuid;
  } elseif ($data['server_uuid'] != $server_uuid) {
    # this client belongs to someone else, let's ignore it
    echo json_encode(array('error'=>'you\'re devoted to another master'));
    exit;
  }

  if (!isset($data['client_uuid'])) {
    # client hasn't sent its uuid. This is not allowed (client uuid is always generated client-side)
    echo json_encode(array('error'=>'missing client uuid'));
    exit;
  }

  $client_ip = $_SERVER['REMOTE_ADDR'];

  $client = false;
  if (! $client = getClientFromUUID($data['client_uuid'])) {
      # client uuid not registered, let's search for IP
      $mac = getClientMac();
      if ($client = getClientFromIP($client_ip)) {
          # we have one
          if (isset($client['uuid'])) {
              # with a registered UUID, that we know to be different from ours
              # so it's an IP collision.
              # we investigate:

              if ($mac == $client['MacAddress']) {
                  # the macaddress is the same, but we have different uuid
                  echo json_encode(array('error'=>'uuid mismatch'));
                  exit;
              } elseif (!$mac) {
                  # we don't have any macaddress info (maybe because we're on
                  # different networks, so no arp)
                  echo json_encode(array('error'=>'can\'t add host because no macaddress'));
                  exit;
              } else {
                  # we have a macadrress different from the one we have
                  # registered for this IP address, so it's a real IP collision
                  # and we add a new host reservation
                  if (! $new_ip = find_free_ipaddress()) {
                      echo json_encode(array('error'=>'no more free ip addresses'));
                      exit;
                  }
                  $props = array(
                      'Description' => '',
                      'IpAddress'   => $new_ip,
                      'MacAddress'  => $mac,
                      'uuid'        => $data['client_uuid']
                  );
                  if (!$hosts_db->setKey($data['hostname'], 'local', $props)) {
                      echo json_encode(array('error'=>'can\'t add new host'));
                      exit;
                  }
                  $client = getClientFromUUID($data['client_uuid']);
              }
          } else {
              # the configured client doen't have the uuid
              # so we update it
              $props = array(
                  'uuid'        => $data['client_uuid']
              );
              if (!$hosts_db->setProp($client['name'], $props)) {
                  echo json_encode(array('error'=>'can\'t update host'));
                  exit;
              }
              $client['uuid'] = $data['client_uuid'];
          }

      } else {
          # no client registered with this uuid or this IP
          if ($mac) {
              # we have a client mac address,
              # we can register a new host
              if (isset($data['hostname'])) {
                  $props = array(
                      'Description' => '',
                      'IpAddress'   => $client_ip,
                      'MacAddress'  => $mac,
                      'uuid'        => $data['client_uuid']
                  );
                  if (!$hosts_db->setKey($data['hostname'], 'local', $props)) {
                      echo json_encode(array('error'=>'can\'t add host'));
                      exit;
                  }
                  $client = getClientFromUUID($data['client_uuid']);
              } else {
                  echo json_encode(array('error'=>'missing client hostname'));
                  exit;
              }
          } else {
              # we don't have a mac address (maybe a different network)
              # we can't register a new host
              echo json_encode(array('error'=>'missing client mac address'));
              exit;
          }
      }

  }

  $hosts_db->setProp($client['name'],array('LastSeen' => time()));
  if (isset($data['last_config'])) {
    $hosts_db->setProp($client['name'],array('LastConfig' => $data['last_config']));
    $current_config = $config_db->getProp(CONFIG_KEY,'lastConfig');
    if ($data['last_config'] != $current_config ) {
      exec('/usr/bin/sudo /sbin/e-smith/signal-event client-reconfigure ' . $client['name'] . ' > /dev/null &');
    }
  }
  $out['hostname']=$client['name'];
  $out['domainname']=$domainName;
  $out['ipaddress']=$client['IpAddress'];
  $out['server_uuid']=$server_uuid;

  echo json_encode($out);
}