<?php
/*
 * include.php
 *
 * Copyright 2015 Péter Szládovics <peti@szladovics.hu>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 *
 **********************************************************************
 *
 * Contains the main definitions, functions and elements
 *
 */

define('PATH_PATTERN','#(src/|)[^\./]+\.php$#');

$docroot = preg_replace(PATH_PATTERN, '', $_SERVER['SCRIPT_FILENAME']);
$base = preg_replace(PATH_PATTERN, '', $_SERVER['PHP_SELF']);
$src = $base . 'src/';

set_include_path(get_include_path() . ":" . $docroot . 'lib/');
require_once("smbind.class.php");
$conf = new Configuration($docroot);
$session = new Session();

if ($conf->ReCaptcha) {
    require_once("recaptchalib.php");
}

set_include_path(get_include_path() . ":" . $conf->Smarty_Path);

header("Content-Type: text/html; charset=UTF-8");
header("X-Content-Security-Policy: default-src 'self'; script-src 'self' self www.gstatic.com www.google.com google.com; img-src 'self' data: www.gstatic.com www.google.com google.com; style-src 'self'; font-src 'self'; frame-src 'self'; connect-src 'self' apis.google.com; object-src 'self'");

require_once("Smarty.class.php");
$smarty = new Smarty;
$smarty->assign('TITLE',$var = $conf->Title);
$smarty->assign('footerleft',$conf->Footer);
$smarty->assign('footerright',$conf->Marker);
$smarty->assign('sdomain',$conf->StaticDomain);
$smarty->assign('skin',$conf->Template);
$smarty->template_dir = $conf->Smbind_Ng . "templates";
$smarty->compile_dir = $conf->Smbind_Ng . "templates_c";
$smarty->assign("base", $base);
$smarty->assign("static", $base . "static/");
$smarty->assign("src", $src);
$smarty->assign("captcha","no");
$smarty->assign("recaptcha","");
$smarty->assign("menu_button","");
$smarty->assign("donotcommit","no");
$smarty->assign('sec', ($conf->IsDNSSec) ? "yes" : "no");
$smarty->assign('popuperror',NULL);

$cap_rsp = NULL;
if(isset($_POST["recaptcha_response_field"])){
    if ($_POST["recaptcha_response_field"]!=''){
        $rsp = recaptcha_check_answer (
            $conf->RC_PrivKey,
            $_SERVER["REMOTE_ADDR"],
            $_POST["recaptcha_challenge_field"],
            $_POST["recaptcha_response_field"]
        );
        if(!$rsp->is_valid) {
            $cap_rsp = $rsp->error;
        }
      } else {
        $cap_rsp = 'incorrect-captcha-sol';
      }
}

if (isset($_POST['username']) && isset($_POST['password']) && ($cap_rsp == NULL)) {
    $session->login($_POST['username'], $_POST['password']);
}

$user = new User();
$smarty->assign("loggedinuser",$user->getFullName());

if ($user->getId() == 0) {
    login_page($smarty);
}

if ((isset($_SERVER['PHP_SELF'])) && (basename($_SERVER['PHP_SELF']) != 'index.php')) {
    $smarty->assign("menu_current", $src . basename($_SERVER['PHP_SELF']));
} else {
    $smarty->assign("menu_current", $base);
}

if($user->isAdmin()) {
    $smarty->assign("admin", "yes");
} else {
    $smarty->assign("admin", "no");
}

/*
 *
 * name: debug
 * @param   variable type a count of parameters for dumping it to the
 * error_log
 *
 * @return <empty>
 *
 */
function debug() {
    for($i = 0 ; $i < func_num_args(); $i++) {
        $param = func_get_arg($i);
        $dump = '';
        if ((is_array($param)) || (is_object($param))) {
            ob_start();
            if (is_object($param)) {
                $phpv = explode('.', phpversion());
                if (intval($phpv[0] . $phpv[1]) < 56) {
                    var_dump($param->__debugInfo());
                } else {
                    var_dump($param);
                }
            } else {
                var_dump($param);
            }
            $dump .= ob_get_clean();
        } else {
            $dump .= $param;
        }
        $bt = debug_backtrace();
        error_log($bt[0]['file'] . "(" . $bt[0]['line'] . "):\n" . $dump);
    }
}

/*
 *
 * name: makePart
 *
 * Creates paging feature
 *
 * @param
 * $all -> lentgt of the listed elements
 * $page -> actual page count
 *
 * @return range for this page
 *
 */
function makePart($all, $page) {
    global $smarty;
    global $conf;
    $from = 0;
    $to = $all;
    $pages = 0;
    $max = 0;
    if ($conf->isExists('range')) {
        $max = $conf->Range;
    }
    if ($max > 0) {
        $pages = (int)($all/$max);
        $mod = $all % $max;
        if ($mod > 0) {
            $pages++;
        }
        if ($pages < $page) {
            $page = $pages;
        }
        $from = ($page-1) * $max;
        $to = $from;
        if (($page == $pages) && ($mod > 0)) {
            $to += $mod;
        } else {
            $to += $max;
        }
        $from = ($from < 0) ? 0 : $from;
    }
    $pagelist = array();
    for ($i=1;$i<=$pages;$i++) {
        $pagelist[] = $i;
    }
    $pagelist = (sizeof($pagelist) > 1) ? $pagelist : NULL;
    $smarty->assign("current_page", $page);
    $smarty->assign("pages", $pagelist);
    return array($from, $to);
}

/*
 *
 * name: rndc_status
 * @param no
 * @return the status of rndc command
 *
 */
function rndc_status() {
    global $conf;
    $cmd = $conf->Rndc . " status > /dev/null";
    system($cmd, $exit);
    return $exit;
}

/*
 *
 * name: logout
 * @param no
 * @return no
 *
 * Displays the logout page and destroy the http session
 *
 */
function logout() {
    global $smarty;
    global $session;
    $session->destroy();
    $smarty->assign("menu_button", array());
    $smarty->assign("pagetitle", "Logout");
    $smarty->assign("template", "logout.tpl");
    $smarty->assign("help", help("logout"));
    $smarty->display("main.tpl");
}

/*
 *
 * name: login_page
 * @param Smarty object
 * @return no
 *
 * Checks the captcha requirements and provides the login page
 *
 */
function login_page($smarty) {
    global $cap_rsp;
    global $base;
    global $conf;

    if($conf->ReCaptcha) {
        $nocap = false;
        foreach ($conf->NoCaptcha as $ip) {
            $nocap = ($nocap or ($ip == $_SERVER['REMOTE_ADDR']));
        }
        if(!$nocap) {
        $smarty->assign("captcha","yes");
        $smarty->assign("recaptcha",recaptcha_get_html($conf->Rc_Pubkey,$cap_rsp,true));
        }
    }
    $smarty->assign("action", $base);
    $smarty->assign("pagetitle", "Login");
    $smarty->assign("template", "login.tpl");
    $smarty->assign("help", help("login"));
    $smarty->display("main.tpl");
    die();
}

/*
 *
 * name: problem
 * @param
 * $reason -> short tag for the problem
 * $title -> header title
 *
 * @return no
 *
 * Provides a kind of error page
 *
 */
function problem($reason = NULL, $title = NULL) {
    global $smarty;
    $tit = (isset($title)) ? $title : title($reason);
    $smarty->assign("pagetitle", $tit);
    $smarty->assign("template", "accessdenied.tpl");
    $smarty->assign("reason", reason($reason));
    $smarty->assign("help", help("accessdenied"));
    $smarty->assign("menu_button", menu_buttons());
    $smarty->display("main.tpl");
    die();
}

/*
 *
 * name: access_denied
 * @param no
 * @return no
 *
 * Generates the access denied problem
 *
 */
function access_denied() {
    problem("notadmin");
}

/*
 *
 * name: reason
 * @param optional
 * $reason -> short tag of the problem
 *
 * @return description string
 *
 * Generates description string for the known reason
 *
 */
function reason($reason = '') {
    switch ($reason) {
        case "notown":
            return "You don't own this zone.";
        case "unauth":
            return "You are not authorized for this procedure";
        case "notadmin":
            return "You are not an administrator.";
        case "noslave":
            return "The slave zone hasn't replicated yet. Try again later.";
        case "existzone":
            return "The zone already exists in the database.";
        case "existfile":
            return "The zonefile already exists on the system.";
        case "existuser":
            return "The user already exists in the database.";
        case "nozonename":
            return "That's not much of a zone name.";
        case "deleteadmin":
            return "You may not delete the default admin user.";
        case "deleteys":
            return "You may not delete yourself.";
        case "nocontent":
            return "The given content empty or invalid.";
        case "nocommit":
            return "There is no commitable content.";
        default:
            return "Unknown error. Please it report to your administrator";
    }
}

/*
 *
 * name: title
 * @param optional
 * $reason -> short tag of the problem
 *
 * @return title string
 *
 * Generates title string for the known reason
 *
 */
 function title($reason = '') {
    switch ($reason) {
        case "notown":
        case "notadmin":
        case "deleteadmin":
        case "deleteys":
        case "unauth":
            return "Access denied";
        case "noslave":
        case "nocontent":
            return "No data";
        case "nocommit":
        case "existzone":
        case "existfile":
        case "existuser":
        case "nozonename":
            return "Error in process";
        default:
            return "Something wrong happened";
    }
}

/*
 *
 * name: help
 * @param
 * $help tag for the info footer
 *
 * @return the info footer
 *
 * Generates the info footer
 *
 */
function help($help) {
    switch ($help) {
        case "login":
            return "Please log in.";
        case "mainpage":
            return "User status and Server status are displayed, along with any zone informations.";
        case "zoneview":
            return "Your zone is dumped. Here you can view the zone in bind syntax.<small><br />" .
                "<span class=attention>You cannot edit the zonefile directly!</span></small>";
        case "zonepview":
            return "Your zone will look like as above. You can view it in bind syntax.<small><br />" .
                "<span class=attention>This zone hasn't commited yet!</span></small>";
        case "zoneread":
            return "Your zones are displayed. Here you can create a zone, edit a zone, view a zone, or delete a zone.";
        case "newzone":
            return "Enter your new zone's domain name, name servers and smbind-ng owner. " .
                "This will create a new zone with a SOA and NS record.<small><br />" .
                "The Web/Mail/FTP fields will create these A, CNAME, and MX template records for you. " .
                "Otherwise, leave them blank.<br />In these fields you can use IP addresses or hostnames too. In this case you need to take care of the trailing dots.</small>";
        case "newslavezone":
            return "Enter your new slave zone's domain name, address of the master server and smbind-ng owner.";
        case "recordread":
            return "Here you can modify your zone's SOA record, or add, edit, or delete resource records.";
        case "userlistread":
            return "Here you can add, edit, or delete smbind-ng users.";
        case "commit":
            return "Your zone files are commited to disk, error checked, and reloaded.";
        case "precommit":
            return "Your modifications will be applied to the system, and the related services will notified about the changes.";
        case "optionsread":
            return "Here you can change options that define how smbind-ng works.";
        case "deletezone":
            return "Please confirm.";
        case "uploadreview":
            return "Please confirm your uploaded data. Some records may be missig basad on your handled record-type options";
        case "uploadproblem":
            return "Please fix the errors. The file output of namedcheckzone has errors.";
        case "upload":
            return "Here you can import a zone what is in legal bind zonefile format. Choose import method!" .
                " Available methods:<small><br /><strong>Orphan files:</strong>(maybe disabled) Some file in" .
                " your config directory without database records. <strong>Browse:</strong> Zone file upload" .
                " from your computer. <strong>Edit:</strong> Paste contents into the box from your clipboard.</small>";
        case "deleteuser":
            return "Please confirm.";
        case "newuser":
            return "Here can you add a new user.<br />" .
                "<span class=attention>Password requirements:</span> You must use letters (both upper- and lowercase), numbers and symols. Minimum length is 8 characters. " .
                "10 charachers length, and using more uppercase letters and numbers is recommended.";
        case "userread":
            return "Here can you change the user's properties. If you don't want to change the password, leave it empty." .
                "<br /><span class=attention>Password requirements:</span> You must use letters (both upper- and lowercase), numbers and symols. Minimum length is 8 characters. " .
                "10 charachers length, and using more uppercase letters and numbers is recommended.";
        case "chpass":
            return "Here can you change your password. <small>You need to give your current password before the new!</small><br />" .
                "<span class=attention>Password requirements:</span> You must use letters (both upper- and lowercase), numbers and symols. Minimum length is 8 characters. " .
                "10 charachers length, and using more uppercase letters and numbers is recommended.";
        case "savepass":
            return "Login using your new password." .
                "<small><br />This page automatically open it within 10 seconds</small>";
        case "accessdenied":
            return "<span class=attention>Access denied.</span><br />This procedure not allowed with your privileges.";
        case "problem":
            return "A problem has occurred.";
        case "logout":
            return "You have been successfully logged out. Click <a class=attention id=reload href=\"../\">here</a> if you wish to log in again." .
                "<small><br />This page automatically open it within <span id=counter>10</span> seconds</small>";
        default:
            return "";
    }
}

/*
 *
 * name: getorphan
 * @param no
 * @return an array with found filenames
 *
 * Find and check local files for importing
 *
 */
function getorphan() {
    global $conf;
    global $user;

    $files = ' ' . implode(' ', scandir($conf->Path)) . ' ';
    $mzones = $user->getMasters();
    $szones = $user->getSlaves();
    foreach ($mzones as $id) {
        $z = new masterZone(intval($id));
        $z->loadZoneHead();
        $zone = $z->getZoneHeadRaw();
        $files = str_replace(' ' . $zone['name'] . ' ', ' ', $files);
    }
    foreach ($szones as $id) {
        $z = new slaveZone(intval($id));
        $z->loadZoneHead();
        $zone = $z->getZoneHeadRaw();
        $files = str_replace(' ' . $zone['name'] . ' ', ' ', $files);
    }
    $vzf = array();
    foreach (explode(' ', trim($files)) as $entry) {
        if ((is_file($conf->Path . $entry)) && (preg_replace('/\.(signed|private|key|krf|jnl|bind)$/', '', $entry) == $entry)) {
            if (checkZoneFile($conf->Path . $entry, $entry)) {
                $vzf[] = hostToIdn($entry);
            }
        }
    }

    return $vzf;
}

/*
 *
 * name: checkZoneFile
 * @param
 * $file -> path the file
 * $zone -> zonename
 *
 * @return true/false (aftert chacking the zoneile)
 *
 */
function checkZoneFile($file, $zone) {
    global $conf;

    $cmd = $conf->namedCheckZone . " -i local " . $zone. " " . $file . " 2>/dev/stdout";
    unset($coutput);
    exec($cmd, $coutput, $exit);
    return ($coutput[sizeof($coutput)-1] == 'OK');
}

/*
 *
 * name: menu_buttons
 * @param no
 * @return no
 *
 * Generates the menu entries
 *
 */
function menu_buttons() {
    global $user;
    global $smarty;
    global $src;
    global $base;

    $cmasters = $user->getCommitableZones('master');
    $cmc = (is_array($cmasters)) ? sizeof($cmasters) : 0;
    $cslaves = $user->getCommitableZones('slave');
    $csc = (is_array($cslaves)) ? sizeof($cslaves) : 0;
    $commitables = $cmc + $csc;
    if($commitables == 0) {
        $committext = "";
        $smarty->assign("donotcommit","yes");
    }
    else {
        $committext = "\" id=\"commitable\" class=\"attention";
    }

    if (sizeof($user->getUnvalidatedZones('master')) +
        sizeof($user->getUnvalidatedZones('slave')) +
        sizeof($user->getDeletedZones('slave')) +
        sizeof($user->getDeletedZones('master')) > 0) {
        $maintext = "\" class=\"attention";
    }
    else {
        $maintext = "";
    }

    return array(
        array("title" => "Main", "link" => $base . $maintext),
        array("title" => "Master zones", "link" => $src . "zonelist.php"),
        array("title" => "Slave zones", "link" => $src . "slave_zonelist.php"),
        array("title" => "Import zones", "link" => $src . "upload.php"),
        array("title" => "Users", "link" => $src . "userlist.php"),
        array("title" => "Change password", "link" => $src . "chpass.php"),
        array("title" => "Commit changes", "link" => $src . "commit.php" . $committext),
        array("title" => "Options", "link" => $src . "options.php"),
        array("title" => "Log out", "link" => $src . "logout.php")
    );
}

?>