<?php
#----------------------------------------------------------------------
# Plugin: page_protect - Single Page Simple Password Protection
# Version 1.0.0
# (c) 2012 -2013 Fernando Morgado (JoMorg) jomorg.morg@gmail.com
# 
# Simple pluging to allow to protect a page with a password
# 
#-------------------------------------------------------------------------
# CMS - CMS Made Simple is (c) 2005 by Ted Kulp (wishy@cmsmadesimple.org)
# This project's homepage is: http://www.cmsmadesimple.org
#-------------------------------------------------------------------------
#
# 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.
#
# However, as a special exception to the GPL, this software is distributed
# as an addon module to CMS Made Simple.  You may not use this software
# in any Non GPL version of CMS Made simple, or in any version of CMS
# Made simple that does not indicate clearly and obviously in its admin 
# section that the site was built with CMS Made simple.
#
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# Or read it online: http://www.gnu.org/licenses/licenses.html#GPL
#
#-------------------------------------------------------------------------

# The smarty block callback
if(!function_exists('pp_protect')) 
{
  function pp_protect($params, $content, $smarty, &$repeat, $template)
  {
    $current_page_alias = cms_utils::get_current_alias();
    $allow = cms_utils::get_app_data('_PP_Allow');

    if (isset($content))
    {
      if ($allow) 
      {   
          $ret = isset($params['assign']) 
                 ? $smarty->assign($params['assign'], $content)
                 : $content;
                 
          return $ret;
      }
      else
      {
        $protected_msg = (  # we add ability to override protected_msg from {protect protected_msg='some message'}
                            isset($params['protected_msg']) 
                            ? $params['protected_msg'] 
                            : cms_utils::get_app_data('_PP_Prot_Msg') 
                          );
                              
        if (isset($protected_msg))
        {
          $ret = isset($params['assign']) 
                 ? $smarty->assign($params['assign'], $protected_msg)
                 : $protected_msg;
                 
          return $ret;
        }
      }
    }

  }
}

function smarty_cms_function_page_protect($params, &$template) 
{
  # initialize a few vars
  $smarty = $template->smarty;
  $gCms = cmsms();
  
  $current_page_alias = cms_utils::get_current_alias();
  $logout_alias = isset($params['logout_alias']) ? $params['logout_alias'] : $current_page_alias;
  
  
  # some of the params have to be persistent through all the page request
  # so we use the same method as ModuleHint i.e. cms_utils Data Storage
  
  # flag to check if current user is authenticated
  $allow  = cms_utils::get_app_data('_PP_Allow_' . $current_page_alias);
  
  if (!isset($allow))
  {
    $allow = false;
  } 
  
  # timeout in minutes (cookie lifecycle). Null or 0 (zero) means off
  $timeout  = cms_utils::get_app_data('_PP_Allow_TOut');
  $use_timeout  = cms_utils::get_app_data('_PP_Allow_useTOut');
  
  if (!isset($timeout))
  {
    $timeout = ( 
                  isset($params['timeout'])
                  ? ($params['timeout'] == 0 ? 0 : time() + $params['timeout'] * 60)  # if $params['timeout'] use it
                  : 0  # else 0
               );
               
    $use_timeout = ( $timeout != 0 );
    
    cms_utils::set_app_data('_PP_Allow_TOut', $timeout);
    cms_utils::set_app_data('_PP_Allow_useTOut', $use_timeout);
  }
  
  $cookie_name  = cms_utils::get_app_data('_PP_Allow_CookieN');
  
  if (!isset($cookie_name))
  {
    $cookie_name = isset($params['cookie_name']) ? $params['cookie_name'] : 'pp_auth';
    cms_utils::set_app_data('_PP_Allow_CookieN', $cookie_name );
  } 
  
  ##### messages #####
  
  $protected_msg = cms_utils::get_app_data('_PP_Prot_Msg');
  
  if (!isset($protected_msg))
  {
    $protected_msg = isset($params['protected_msg']) ? $params['$protected_msg'] : $protected_msg;
    cms_utils::set_app_data('_PP_Prot_Msg', $protected_msg );
  } 
    
  $error_msg = cms_utils::get_app_data('_PP_Error_Msg');
  
  if (!isset($error_msg))
  {
    $error_msg = isset($params['protected_msg']) ? $params['protected_msg'] : 'The password is not correct.';
    cms_utils::set_app_data('_PP_Error_Msg', $error_msg );
  } 
  
  $welcome_msg = cms_utils::get_app_data('_PP_Welcome_Msg');
  
    
  if (!isset($welcome_msg))
  {
    $welcome_msg = isset($params['welcome_msg']) ? $params['welcome_msg'] : 'Please enter the password to access this page.';
    cms_utils::set_app_data('_PP_Welcome_Msg', $welcome_msg );
  } 
  ##### messages end #####
  
  $welcome_msg = cms_utils::get_app_data('_PP_Welcome_Msg');
  
    
  if (!isset($welcome_msg))
  {
    $welcome_msg = isset($params['welcome_msg']) ? $params['welcome_msg'] : 'Please enter the password to access this page.';
    cms_utils::set_app_data('_PP_Welcome_Msg', $welcome_msg );
  } 
  
  cms_utils::set_app_data('_PP_Allow' , $allow);
  
  if (!isset($params['passwords']))
  {
    $passwords = cms_utils::get_app_data('_PP_Allow_Pass');
  }
  else
  {
    $passwords = &$params['passwords'];
  }

  if (!is_array($passwords))
  {
    if (strpos($passwords, ',') === false)
    {
      $passwords = array($passwords);   
    }
    else
    {
      $passwords = explode(',', $passwords);
    }
  }
   
  cms_utils::set_app_data('_PP_Allow_Pass', $passwords);

  ##### process requests and Cookies #####

  $cookie_found = false;
  
  # logout
  if(isset($_POST['pp_logout'])) 
  {
    # clear pass from cookie and redirect
    setcookie($cookie_name, '', 0, '/'); 
    $allow = false;
    redirect_to_alias($logout_alias);
  }
  
  # process pass
  if (isset($_POST['pp_password'])) 
  {
    $pass = $_POST['pp_password'];
    
    if ( !in_array($pass, $passwords) )  
    {
      $msg = $error_msg;
      $allow = false;
    }
    else 
    {
      if ($use_timeout)
      {
        # we set cookie here
        #setcookie($cookie_name, md5($username . '%pp%' . $pass), $timeout, '/'); # todo
        setcookie($cookie_name, md5($pass), $timeout, '/');
      }
      
      $allow = true;
    }

  }
  else 
  {
    if ($use_timeout)
    {
      # check if cookie exists and is set
      if (!isset($_COOKIE[$cookie_name])) 
      {
        $msg = '';
        $allow = false;
      }
      
      foreach($passwords as $one) 
      {
        if ($_COOKIE[$cookie_name] == md5($one)) 
        {
          $cookie_found = true;
          $allow = true;
          
          # so we have a valid request: extend timeout         
          setcookie($cookie_name, md5($one), $timeout, '/');
          break;
        }
      }
      
      if (!$cookie_found) 
      {
        $allow = false;
        # may very well be redundant but hey!!! we clear any pending messages anyway
        $msg = '';
      }
    }


  }
  ##### end process requests and Cookies #####
  
  cms_utils::set_app_data('_PP_Allow', $allow);
  
  ##### actions #####
  switch ($params['action']) 
  {
    case 'form':
    {
      # common to both buttons
      $button_id = isset($params['button_id']) 
                   ? 'id="' . $params['button_id'] . '" '
                   : '';        
      
      $button_class = isset($params['button_class']) 
                      ? 'class="' . $params['button_class'] . '" '
                      : '';
                          
      if (!$allow)
      {
        $login_btn = isset($params['login_btn']) ? $params['login_btn'] : 'Login';
        
        $form_id = isset($params['form_id']) 
                   ? 'id="' . $params['form_id'] . '" '
                   : '';        
        
        $form_class = isset($params['form_class']) 
                      ? 'class="' . $params['form_class'] . '" '
                      : '';
                    
                            
        $in_pass_id = isset($params['in_pass_id']) 
                      ? 'id="' . $params['in_pass_id'] . '" '
                      : '';        
        
        $in_pass_class = isset($params['in_pass_class']) 
                         ? 'class="' . $params['in_pass_class'] . '" '
                         : '';
    
        $html = '';
        $html .= '<form ' . $form_id . $form_class . ' method="post">';
        $html .= '<h3>'. $welcome_msg . '</h3>';
        $html .= '<p><font color="red">' . $msg . '</font></p>';
        $html .= '<input ' . $in_pass_id . $in_pass_class . 'type="password" name="pp_password" autocomplete="off" />';
        $html .= '<input ' . $button_id . $button_class . 'type="submit" name="Submit" value="' . $login_btn . '" />';
        $html .= '</form>';
        
        $ret = isset($params['assign']) 
               ? $smarty->assign($params['assign'], $html)
               : $html;
               
        return $ret;  
      }
      else
      {
        $logout_btn = isset($params['logout_btn']) ? $params['logout_btn'] : 'Logout';
        
        $html = '';
        $html .= '<form method="post">';
        $html .= '<input' . $button_id . $button_class . ' type="submit" name="pp_logout" value="' . $logout_btn . '" />';
        $html .= '</form>';
        
        $ret = isset($params['assign']) 
               ? $smarty->assign($params['assign'], $html)
               : $html;
               
        return $ret;        
      }
    }
    break;
    
    # default usage: has to be located on top of the template 
    # OR preferably on the "Smarty data or logic that is specific to this page" field  
    # otherwise you'll get smarty error: {protect} tag is unknown
    # also has to be the top-most tag of any sequence of calls
    
    case 'default':

    default:
    {
      # register with smarty 
      # usage: {protect}Content you want to protect {/protect}
      $smarty->registerPlugin("block", "protect", "pp_protect");
    }

  }

}

##### Docs #####
function smarty_cms_help_function_page_protect() 
{
  $txt = <<<'EOT'
<h3>What does this do?</h3>
<p>This plugin allows you to protect a page with one or more passwords.</p>
<p>This plugin can be used on any number of pages. It has however to be set once for each page you want to protect.</p>
<h3>How to use it?</h3>
<p>This plugin needs to be set once for every page. You can set any number of passwords, by using commas to separate them. You can also use a Smarty array to set a list of passwords.</p>
<p>This plugin needs three calls in order to work properly: </p>
<ul>
  <li>once on the field <strong>Smarty data or logic that is specific to this page</strong> found on the page <em><strong>options tab</strong></em> and where you should define most of the options;</li>
  <li>once on any content block where you might want to set the <strong>login/logout form</strong>;</li>
  <li>and finally  on the content you want to protect, by wrapping it with <strong>{protect}{/protect}</strong> tags;</li>
</ul>
<p>The first tag should have most of the needed parameters set. These will be persistent through all the duration of the request, but can be overriden by  subsequent calls with one exception: the <strong>{protect}{/protect}</strong> tags which only accept one optional parameter: <strong>protected_msg</strong>.</p>
<p>The order by which the tags are called is extremely important: the default action (parameter <strong>action='default'</strong> which can be omitted) <strong>can not</strong> be set on the same content block as all other tags, and should be called on the topmost block of the template. This tag registers a smarty block pluging <strong>{protect}</strong> which won't be recongnised before being parsed once (and registered) by the Smarty engine. After this first call, all other call don't have a specific order.</p>
<h4>Default Action</h4>
<p>This should be placed on the topmost block of the template: the most suited place would be the <strong>Smarty data or logic that is specific to this page</strong>* found on each page <em><strong>options tab</strong></em>.</p>
<div class="warning" style="display:block;">
<p><strong>* Note: </strong>this only work if the tag <strong>{process_pagedata}</strong> is present on the template and is called before any other content block.</p>
</div>

<label>Default action examples</label>
<fieldset>
 <ul>
  <li> 
    use <strong>{page_protect passwords='</strong>pass1[,pass<em><strong>2</strong></em>]...[,pass<em><strong>n</strong></em>]<strong>'}</strong> or <strong>{page_protect action='</strong>default<strong>' passwords='</strong>pass1[,pass<em><strong>2</strong></em>]...[,pass<em><strong>n</strong></em>]<strong>'}</strong></li>
 </ul>
</fieldset>
<h5>Passwords</h5>
<p>The passwords parameter can accept a range of values, from a single password, a comma separated list of passwords, or an array of values:</p>

<p><pre>{page_protect passwords='pass1'}</pre></p>
<p><pre>{page_protect passwords='pass1,'pass2,pass3}</pre></p>
<p>
<pre>
{* Use smarty syntax to create an array *}
{$passwords[]='pass1'}
{$passwords[]='pass2'}
{$passwords[]='pass3'}
{* and use it as the value for the parmeter passwords *}
{page_protect passwords=$passwords}
</pre>
</p>
<div class="information" style="display:block;">
<p>You can also use an User Defined Tag to create an array and assign it to a smarty variable.</p>
</div>
<div class="warning" style="display:block;">
<p><strong>Note:</strong> Unless you use an array to set the passwords, avoid the use of commas (<strong>,</strong>) and of vertical slashes (<strong>|</strong>) as password symbols as these are reserved to internal use and will unavoidably lead to passwords not being recognised by the plugin.</p>
</div>
<h4>Form Action</h4>
<p>Although you can set parameters here, you should set them all only once and at the initial default tag. Other than that just place the tag where you want a login/logout form.</p>
<label>Form action example</label>
<fieldset>
 <ul>
  <li> 
      use <strong>{page_protect action='</strong><em>form</em><strong>'}</strong></li>
 </ul>
</fieldset>

<h4>The Content Wrapping Tags</h4>
<p>These tags are block smarty tags, and can be used several times on the page, by pairs i.e an opening tag and a closing tag. The opening tag accepts only one parameter, the <strong>protected_msg</strong> which overrides the default one if set. This is a per occurence tag, meaning that if it is set on the default action it is persistent, but if set on a <strong>{protect}</strong> tag it affects only the tag where it is used and doesn't persist to the next ocurrence.</p>
<label>Content wrapping tags example</label>
<fieldset>
 <ul>
  <li> 
      use <strong>{protect}</strong><em>whatever content you want protected.</em><strong>{/protect}</strong> or <strong>{protect protected_msg='</strong><em>well, you really should be logged in if you what to see the content</em><strong>'}</strong><em>whatever content you want protected.</em><strong>{/protect}</strong></li>
 </ul>
</fieldset>
<h3>What parameters does it take?</h3>
<ul>
  <li><em>(optional) <strong>action</strong></em> - possible values:
    <ul>
      <li><em><strong>'default</strong></em><strong>'</strong>: the default action of the plugin which is to inicialize itself;</li>
      <li><em><strong>'form</strong></em><strong>'</strong>: show either the <strong>login</strong> form or the <strong>logout</strong> form, depending on current user state;</li>
      </ul>
  </li>
  <li><em>(optional) (persistent) <strong>logout_alias</strong></em> - an existing page alias (defaults to current page i.e. the page where the plugin is being used): this will be used to redirect after logout;</li>
  <li><em>(optional) (persistent) <strong>timeout</strong></em> - if set to a value higher then 0 it will set a cookie and use this value as minutes before login times out;</li>
  <li><em>(optional) (persistent) <strong>cookie_name</strong></em> - the name of the cookie (defaults to <strong>pp_auth</strong>);</li>
  <li><em>(optional) (persistent) <strong>welcome_msg</strong></em> - a message to be shown as a welcome text on the loggin form;</li>
  <li><em>(optional) (persistent) <strong>protected_msg</strong></em> - a message to replace the protected content (defaults to no message at all);</li>
  <li><em>(optional) (persistent) <strong>error_msg</strong></em> - an error message to be shown on password errors;</li>
</ul>
<h5>Exclusive to form action:</h5>
<ul>
  <li><em>(optional) <strong>login_btn</strong></em> - text of the login button caption (defaults to Login);</li>
  <li><em>(optional) <strong>logout_btn</strong></em> - text of the logout button caption(defaults to Logout;</li>      
  <li><em>(optional) <strong>form_id</strong></em> - login form id;</li>
  <li><em>(optional) <strong>form_class</strong></em> - login form class;</li>
  <li><em>(optional) <strong>in_pass_id</strong></em> - password input id;</li>
  <li><em>(optional) <strong>in_pass_class</strong></em> - password input class;</li>
  <li><em>(optional) <strong>button_id</strong></em> - both buttons id;</li>
  <li><em>(optional) <strong>button_class</strong></em> - both buttons class;</li>
</ul>
  <h5>Protect tag accepts only one parameter:</h5>
<ul>
  <li><em>(optional) <strong>protected_msg</strong></em> - a message to replace the protected content (defaults to whatever was set on a previous persistent tag call, typically on the <em><strong>default</strong></em> action);</li>
</ul>
<h3>Notes:</h3>
<p>This is a weak protection measure if compared with .htacess methods or with a proper Front End User management module, but as long as you keep the passwords private and the access to the backend restricted and well organized, it should keep the pages where it is used protected.</p>
<p>This plugin may need PHP version 5.3.</p>    
EOT;

  echo $txt;
}

function smarty_cms_about_function_page_protect() 
{
  $txt = <<<'EOT'
<h3>About</h3>
<p>Author: Jo Morg (Fernando Morgado)</p>
<h3>History</h3>
<ul>
    <li> version 1.0.RC.1 Added a few parameters to further style the form;</li>
    <li> version 1.0.RC Initial version (for CMSMS 1.11.x). A release candidate;</li>
</ul>
<h3>To Do List</h3>
<ul>
    <li>Allow pairs of username and password;</li>
    <li><strike> Add a few parameters to further style the form;</strike> </li>
</ul>
EOT;

  echo $txt;
}
?>