<?php

/* ------------------------------------------------------------------ */
/*  HINT: YOU DON'T NEED TO EDIT ANYTHING IN THIS FILE !! (266 Lines) */
/* ------------------------------------------------------------------ */

/**
 * easy-protect.php - Easy Password Protection Library
 * 
 * @version  v4 - 11/2020
 * @copyright adilbo - all rights reserved
 */


// CHECK IF LOGOUT LINK IS CLICKED
if ( isset( $_GET['logout'] ) ) logout();

// MAIN
function protect( $user_pass = '', $option = '' ) {
  // SETUP VAR
  if ( !isset( $option['timeout'] ) or $option['timeout'] == 0) $option['timeout'] = 05; // DEFAULT 30 MINUTES
  // VAR ini
  $wrong = $error1 = $error2 = $error3 = $error4 = $status = '';
  // START SESSION IF IT IS NOT STARTET
  start_session();
  // CHECK IF PASS IS SET
  if ( empty ( $user_pass ) ) die('<tt><b>ERROR</b> No Password Set!');
  // CHECK IF MD5 IS SET
  if ( isset( $_POST['access_pass'] ) and isset( $option['md5'] ) and $option['md5'] == true ) {
    // USE MD5
    $_POST['access_pass'] = md5( $_POST['access_pass'] );
  }
  // GROUP PAGES TO CLUSTER
  if ( isset( $option['page_group_name'] ) and !empty( $option['page_group_name'] ) ) {
    $session_name = $option['page_group_name'];
  } else {
    $session_name = '_';
  }
  // CHECK IF PASSWORD IS EXPIRED
  if ( isset($option['maximum_validity_date']) and strtotime($option['maximum_validity_date']) <= time() ) {
    $_SESSION[$session_name.'error4'] = ' show';
    unset( $_POST['access_pass'] );
  }
  // CHECK IF IP BLOCKING IS USED
  if ( isset( $option['block'] ) ) {
    // CHECK IF IPs ARE BLOCKED
  	$error3 = ip_in_array( $option['block'] );
    // RESET PASS INPUT
  	if ( $error3 != '' ) unset( $_POST['access_pass'] );
  }
  // CHECK IF IP BYPASS IS USED
  if ( isset( $option['bypass'] ) ) {
    // CHECK IF IPs ARE ALLOWED FOR BYPASS
  	if ( ip_in_array( $option['bypass'] ) != '' ) {
      // SAVE HINT IN SESSION
      $_SESSION[$session_name.'bypass'] = TRUE;
    }
  }
  // CHECK IF PASS IS CORRECT and SET TIME-OUT
  if ((    // SINGLE PASS
        !is_array($user_pass) and
        isset( $_POST['access_pass'] ) and
        $_POST['access_pass'] == $user_pass and
        empty( $_SESSION[$session_name.'error'] )
      )or( // MULTI PASS WITH EXPIRATION
        is_assoc($user_pass) and
        isset( $_POST['access_pass'] ) and
        in_array( $_POST['access_pass'], array_keys($user_pass) ) and
        empty( $_SESSION[$session_name.'error'] )
      )or( // MULTI PASS
        is_array( $user_pass ) and
        isset( $_POST['access_pass'] ) and
        in_array( $_POST['access_pass'], $user_pass ) and
        empty( $_SESSION[$session_name.'error'] )
     ))
  {
    // SAVE ENCRYPTED PASSWORD IN SESSION
    $_SESSION[$session_name.'access_pass'] = crypt( $_POST['access_pass'], md5( time() ) );
    // LEAVE USERS x MINUTES LOGGED IN
    $_SESSION[$session_name.'time'] = time() + ( 60 * $option['timeout'] );
    // FOR SAFETY
    if ( !isset($_SERVER['HTTP_REFERER']) ) die(header('Location:' . htmlspecialchars(basename($_SERVER['SCRIPT_NAME'])) ));
    // REDIRECT TO CONTENT
    die(header('Location:' . htmlspecialchars( $_SERVER['HTTP_REFERER'] ) ));
  }
  // PASS NOT CORRECT
  else if ( isset( $_POST['access_pass'] ) ) {
  	// SET ERROR CSS
  	$wrong = ' wrong'; // CAUTION: do not remove the blank at the beginning
    $error1 = ' show'; // CAUTION: do not remove the blank at the beginning
    // CHECK IF ATTEMPTS OPTION IS SET
    if ( isset( $option['attempts'] ) ) {
      // ADD INCORRECT LOGIN ATTEMPT
      @$_SESSION[$session_name.'attempts']++;
      // SET TIME FOR LAST WRONG LOGIN ATTEMPTS
      if ( ( !isset( $_SESSION[$session_name.'attempts_time'] ) && empty( $_SESSION[$session_name.'attempts_time'] ) ) ) {
        // RESET WRONG LOGIN ATTEMPTS AFTER x MINUTES
        $_SESSION[$session_name.'attempts_time'] = time() + ( 60 * $option['timeout'] );
      }
      // CHECK IF TOO MANY ATTEMPTS or ATTEMPTS ARE NOT TIMED-OUT
      if ( $_SESSION[$session_name.'attempts'] > $option['attempts'] ) {
        // ERROR CSS
        $error1 = '';
        $error2 = ' show'; // CAUTION: do not remove the blank at the beginning
        // SET ERROR
        $_SESSION[$session_name.'error'] = true;
      }
      if ( $_SESSION[$session_name.'attempts'] > $option['attempts'] && $_SESSION[$session_name.'attempts_time'] < time() ) {
        // RESET TRIES
        unset( $_SESSION[$session_name.'error'] );
        unset( $_SESSION[$session_name.'attempts_time'] );
        unset( $_SESSION[$session_name.'attempts'] );
      }
    }
  }
  // FUNCTION - Allow multiple Passwords - ALSO CHECK IF PASSWORD IS EXPIRED FOR MULTI PASSORD
  function crypt_loop( $user_pass, $access_pass, $session_name ) {
    if ( !is_array($user_pass) ) {
      // SINGLE PASS
      #die('SINGLE PASS WITHOUT EXPIRATION DATE');
      if ( crypt( $user_pass, $access_pass ) == $access_pass ) {
        return true;
      } else {
        return false;
      }
    } else {      
      // MULTI PASS WITH EXPIRATION DATE
      if ( is_assoc($user_pass) ) {
        #die('MULTI PASS WITH EXPIRATION DATE');
        $user_pass_keys = array_keys($user_pass);
        $user_pass_values = array_values($user_pass);
        for ( $i = 0; $i < count( $user_pass ); $i++ ) {
          if ( crypt( $user_pass_keys[$i], $access_pass ) == $access_pass ) {
            if ( isset($user_pass_values[$i]) and strtotime($user_pass_values[$i]) <= time()) {
              $_SESSION[$session_name.'error4'] = ' show';
              return false;
            }
            return true;
          }
        }
        return false;
      }
      // MULTI PASS
      #die('MULTI PASS WITHOUT EXPIRATION DATE');
      for ( $i = 0; $i < count( $user_pass ); $i++ ) {
        $crypt_user_pass[] = crypt( $user_pass[$i], $access_pass );
      }
      if ( in_array( $access_pass, $crypt_user_pass)  ) {
        return true;
      } else {
        return false;
      }
    }
  } 
  // OUTPUT LOGIN FORM
  if ( isset($_SESSION[$session_name.'access_pass']) ) {
    $status = crypt_loop( $user_pass, $_SESSION[$session_name.'access_pass'], $session_name );
  }
  if ( ((isset($_SESSION[$session_name.'access_pass']) and $status == false ) or !isset($_SESSION[$session_name.'access_pass']) or 
        ($_SESSION[$session_name.'time'] <= time()) ) and @$_SESSION[$session_name.'bypass'] != 1 ) {
    // WAIT
    sleep( .5 );
    // LOAD TEMPLATE
    if ( file_exists( 'templates/' . (int)$option['skin'] . '.htm' ) ) {
      // SKIN FROM OPTION
      $login_template = file_get_contents( 'templates/' . (int)$option['skin'] . '.htm' );
    } elseif ( file_exists( 'templates/1.htm' ) ) {
      // DEFAULT SKIN 
      $login_template = file_get_contents( 'templates/1.htm' );
    } else {
      // NO SKIN ERROR
      die( '<tt><b>ERROR</b> Skin Template not found!' );
    }
    // INSERT ERROR DATA
    $login_template = str_replace( '{$wrong}',  $wrong,  $login_template );                    // RED BORDER INPUT FIELD ON WRONG PASSWORD
    $login_template = str_replace( '{$error1}', $error1, $login_template );                    // Wrong password! Try again!
    $login_template = str_replace( '{$error2}', $error2, $login_template );                    // Too many attempted sign-ins!
    $login_template = str_replace( '{$error3}', $error3, $login_template );                    // Your IP address is banned!
    if (isset($_SESSION[$session_name.'error4'])) $error4 = $_SESSION[$session_name.'error4']; // Get Exired Error from Session if set!
    if ($error1 === ' show') $error4 = '';                                                     // If Password wrong, do not show expired hint!
    $login_template = str_replace( '{$error4}', $error4, $login_template );                    // Your password has expired!
    // HTML TO BROWSER
    die( $login_template );
  }
}

/* ------------------------------------------------------------------ */
/* HELPER                                                             */
/* ------------------------------------------------------------------ */

// CHECK IF PHP ARRAY IS ASSOCIATIVE OR SEQUENTIAL? - https://stackoverflow.com/a/173479
function is_assoc($array) {
  if (!is_array($array)) return false;
  if (array() === $array) return false;
  return array_keys($array) !== range(0, count($array) - 1);
}

// VISITOR IP
function ip() {
  if ( !empty( $_SERVER['HTTP_CLIENT_IP'] ) ) $ip = $_SERVER['HTTP_CLIENT_IP'];
  elseif ( !empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) $ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
  else $ip = $_SERVER['REMOTE_ADDR'];
  if ( $ip == '::1' ) $ip = '127.0.0.1'; // IP6 FIX http://php.net/manual/en/function.inet-ntop.php
  return $ip;
}

// CHECK IF IP IS IN ARRAY
function ip_in_array( $array ) {
  if ( array_search( ip(), $array ) !== FALSE )
    return ' show'; // CAUTION: do not remove the blank at the beginning
}

// LOGOUT
function logout() {
  // INI SESSION
  session_start();
  // UNSET SESSION VARs
  $_SESSION = array();
  // DEL SESSION COOKIE
  if ( ini_get( "session.use_cookies" ) ) {
    $cookie = session_get_cookie_params();
    setcookie(
      session_name(),
      '',
      time() - 42000,
      $cookie["path"],
      $cookie["domain"],
      $cookie["secure"],
      $cookie["httponly"]
    );
  }
  // DESTROY SESSION
  session_destroy();
  // BACK TO PAGE
  if ( isset( $_GET['logout'] ) && isset($_SERVER['HTTP_REFERER']) ) {
    die(header( 'Location:' . htmlspecialchars( $_SERVER['HTTP_REFERER'] ) ));

  }
  // FOR SAFETY
  die(header('Location:' . htmlspecialchars(basename($_SERVER['SCRIPT_NAME'])) ));
}

// CHECK IF SESSION STARTED - Fix for use Script with PHP < 5.4.0
function start_session() {
  if ( php_sapi_name() !== 'cli' ) {
    if ( version_compare(phpversion(), '5.4.0', '>=') ) {
      if (session_status() == PHP_SESSION_NONE) { // session has not started
        session_set_cookie_params(0);
        session_start();
        return TRUE;
      }
    } else {
      if (session_id() == '') { // session has not started
        session_set_cookie_params(0);
        session_start();
        return TRUE;
      }
    }
  }
  return FALSE;
}

/* ------------------------------------------------------------------ */
/* EOF                                                                */
/* ------------------------------------------------------------------ */
