<?php if (!defined('PmWiki')) exit();

/*

 *****
 *
 *  OnRecipeVer -Activate recipes based on recipe version
 *  OnNameLen - Activate recipes matching lenght of page name with operators "=,!,<,>,<=,>="
 *  OnSearchLen - Activate recipes matching lenght of search query with operators "=,!,<,>,<=,>="
 *  OnDownload - Activate recipes matching mark(s) on file download
 *  OnSession - Activate recipes in just one line for pairs of session name=value pair(s)
 *  OnAction - Activate recipes in just one line for one action or more
 *  OnSearch - Activate recipes in just one line for one or more matches against a search query
 *  OnRecipe - Activate recipes matching recipe names
 *  OnUpload - Activate recipes in just one line for a _FILES var=value pair(s)
 *  OnCookie - Activate recipes in just one line for pairs of cookie name=value pair(s)
 *  OnGroup - Activate recipes in just one line for one or more groups
 *  OnName - Activate recipes in just one line for one or more page names
 *  OnFile -  Activate recipes based on file exitance in UploadDir or any other dir 
 *  OnMark - Activate recipes by matching mark(s) in a page
 *  OnPost - Activate recipes in just one line for a _POST var=value pair(s)
 *  OnGet - Activate recipes in just one line for a _GET var=value pair(s)
 *
 *  16 events so far, if you have more ideas please post to the recipe talk page.
 *
 *  All OnEvents accept arrays now
 *  since most important vars get to config already sanitized
 *  we don't do too much checking against it
 *  just enough.
 *
 * Copyright 2018 Carlos A. Bonamigo <cabsec.pmwiki@gmail.com>
 *
 *  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 *****

 Changelog:

  20180416

  - OnMark loads GroupHeader and GroupFooter
  - New OnRecipe event good to do dependency checks
  - New OnFile recipe to check files in upload folder or any other

 Next:

  - Enable some of the conditions already available on PmWiki as events
  

*/

$RecipeInfo['OnEvent']['Version'] = '20180417';

SDV($OnOperArr,array(
  '=','!','<','>','<=','>=',
));

SDV($OnRMChars, array(
  '-','_',':','.','~','+','!','(',')','[',']','{','}',' '
));

# OnRecipe
# Checks if a recipe is loaded
# case insensitive and special
# char insensitive (-,_,:,.,~,+,!,(,),[,],{,})
# just in case
# OnRecipe('ONEVENT,BoldLISTS,HaCkErReCiPe')
# Both recipe name and needle are normalized 
function OnRecipe($r){
global $OnRMChars,$RecipeInfo;

if($r == '' ) return false;

$rcpi = $RecipeInfo;
$rcps = OnToArray($r);
$rmch = $OnRMChars;

foreach($rcps as $k){
  $nk = str_replace($rmch,'',$k);
  $nk = strtolower($nk);
  $drcp[$nk] = 1;
}

# create a normalized RecipeInfo
foreach($rcpi as $k => $v){
  $nk = str_replace($rmch,'',$k);
  $nk = strtolower($nk);
  $nrcpi[$nk] = 1;
}

if(OnMultiMatchEqual($drcp,$nrcpi)) return true;
else return false;

}

# OnRecipeVer
# Checks recipe versions
# OnRecipeVer('onEvEnT=2018-03-12')
#       return true;
# we sanitize recipe name=version
# and version(s) we want to check
function OnRecipeVer($v){
global $RecipeInfo,$OnRMChars;

if ($v == '') return false;

$rc = $OnRMChars;
$ri = $RecipeInfo;
$rv = '';
$vr = trim(
    strtolower(
    str_replace(
      $rc,'',$v)
));

foreach($ri as $k => $v){
  $nk = trim(
    strtolower(
    str_replace(
      $rc,'',$k)
  ));
  $nv = trim(
    strtolower(
    str_replace(
      $rc, '',
      $v['Version'])
  ));
  $rv[$nk] = $nv;
}

if(OnMultiMatchPairEqual($vr,$rv)) return true;
else return false;

}

# OnFile
# checks if a file exists
# in the upload area
# we assume the default uploadir
# with prefix, that can be changed
# to anything you need in $u
function OnFile($f,$u='uploads/$Group/'){
global $pagename;

if($f == '') return false;

$pgnm = $pagename;
$nrep = array(
  '$Group' => PageVar($pgnm,'$Group'),
  '$Name' => PageVar($pgnm,'$Name')
);
$dupdir = str_replace(
  array_keys($nrep),
  array_values($nrep),$u
);
$dfile = OnToArray($f);

 foreach($dfile as $df){
   if(file_exists($dupdir.$df)) $e = 1 ;
 }

if($e == 1) return true;
else false;

}

# You can include one or more marks
# if one mark is found return true
# use: if(OnMark('loadFonts'))
#   include_once("cookbook/loadfonts.php");
function OnMark($m){
global $pagename;

if($m == '' ) return false;

$pgnm = $pagename;
$group = PageVar($pgnm,'$Group');
$hpage = $group.'.GroupHeader';
$fpage = $group.'.GroupFooter';
$dmrk = trim($m); # defined mark(s)

if (!PageExists($pgnm)) return false;
else{
  if(PageExists($hpage))
    $head = ReadPage($hpage,READPAGE_CURRENT); 
  $page = ReadPage($pgnm,READPAGE_CURRENT);
  if(PageExists($fpage))
    $foot = ReadPage($fpage,READPAGE_CURRENT); 
}

$pagetxt = $head['text'].
  $page['text'].$foot['text'];

if($pagetxt == '') return false;
else $ptxt = $pagetxt;

if(OnMultiMatchContained($dmrk,$ptxt)) return true;
else return false;

}

# OnGet('gato=botas')
# OnGet('weather=warm,sea=cold,sand=thin') or
# if there is one match it returns true
function OnGet($g){

if($GLOBALS['_GET'] == '') return false;
if(stripos($g,'=') === false) return false;

$dget = trim($g); # all get pairs
$aget = $GLOBALS['_GET']; # all get

if(OnMultiMatchPairEqual($dget,$aget)) return true;
else return false;

}

# OnPost('gato=botas,puss=inBoots')
# OnPost('weather=warm,sea=cold,sand=thin') ou
# if there is one match it returns true
function OnPost($p){

if($GLOBALS['_POST'] == '') return false;
if(stripos($p,'=') === false) return false;

$dpst = trim($p); # all get pairs
$apst = $GLOBALS['_POST']; # all get

if(OnMultiMatchPairEqual($dpst,$apst)) return true;
else return false;

}

# You can include one or more pages
# if one page is found return true
# if(OnName('Home,Main,PmWiki')) echo "found";
function OnName($n){
global $pagename;

if($n == '' ) return false;

$pgnm = $pagename;
$dnam = trim($n);# defined name(s)
$cnam= PageVar($pgnm,'$Name');# called name

if(OnMultiMatchEqual($dnam,$cnam)) return true;
else return false;

}

# OnNameLen('>30,<256')
# trigger happens upon hitting
# on one the lenghts specified .
# For specificity call with just
# one lenght
# if(OnNameLen('<10')
#   || OnNameLen('>256'){
#     Redirect($pagename, "Site.LimitsExceeded");
# }
function OnNameLen($nl){
global $pagename;

$alen = PageVar($pagename,'$Name');

if($alen == '') return false;
if(OnOperator($nl) == false) return false;

$dlen = trim($nl); # all get pairs

# check needle(s) with operators
# against $Name
if(OnMultiMatchLen($dlen,$alen)) return true;
else return false;

}

# You can include one or more groups
# if one group is found return true
# use: if(OnGroup('Site,Main')) echo "Found!";
function OnGroup($g){
global $pagename;

if($g == '' ) return false;

$pgnm = $pagename;
$dgrp = $g; # defined group(s)
$cgrp = PageVar($pgnm,'$Group'); # called group
$cgrp = $cgrp;

if(OnMultiMatchEqual($dgrp,$cgrp)) return true;
else return false;

}

# OnSearch('linux,OS,screenshots')
# will return true on
# if one is found
function OnSearch($q){
global $action;

if($action != 'search') return false;

$tmp = @$GLOBALS['_REQUEST']['q'];
$aqry = stripmagic($tmp); # n itself

if ($aqry == '') return false;
if ($q == '') return false;

$dqry = trim($q);

if(OnMultiMatchContained($dqry,$aqry)) return true;
else return false;

}

# OnSearchLen('<7,>50',<=50,!22) {
#   Redirect($pagename,'Site.LenghtAndLimitations')
# }
function OnSearchLen($sl){
global $action;

if($action != 'search') return false;

$tmp = @$_GET['q'];
$alen = stripmagic($tmp); # n itself

if($alen == '') return false;
if(OnOperator($sl) == false) return false;

$dlen = trim($sl); # all get pairs

# check needle(s) with operators
# against $_GET['n']
if(OnMultiMatchLen($dlen,$alen)) return true;
else return false;

}

# OnUpload('')
# here you are limited to
# check itens inside userfile
# on PmWikis case 'uploadfile'
# name, tmp_name, type, size,
# error,
function OnUpload($f){
global $action;

if($action != 'upload') return false;
if(stripos($f,'=') === false) return false;
if($GLOBALS['_FILES']['uploadfile'] == '') return false;

$dfil = trim($f); # all get pairs
$afil = $GLOBALS['_FILES']['uploadfile']; # all get

if(OnMultiPairEqual($dfil,$afil)) return true;
else return false;

}

# OnDownload('.txt,.bin,rad.gif,virus.zip')
# checks for marks on $_REQUEST['upname']
# on download, more than one filename
# requires ',' between elements
function OnDownload($d){
global $action;

if($action != 'download') return false;
if($d == '') return false;
if($GLOBALS['_GET']['upname'] == '') return false; 

$dwnl = trim($d); # all get pairs
$awnl = $GLOBALS['_GET']['upname']; # all get

if(OnMultiMatchContained($dwnl,$awnl)) return true;
else return false;

}

# You can include one or more actions
# if one action is found return true
function OnAction($a){
global $action;

if($a == '') return false;
if($action == '') return false;

$dac = trim($a); # defined action(s)
$act = trim($action); # called action

if(OnMultiMatchEqual($dac,$act)) return true;
else return false;

}

# OnCookie('gato=botas,meowMix=PleaseDeliver')
# OnCookie('weather=warm,sea=cold,sand=thin') ou
# if there is one match it returns true
# if you need match specificity call it with
# just one option
function OnCookie($c){

if($GLOBALS['_COOKIE'] == '') return false;
if(stripos($c,'=') === false) return false;

$dcke = trim($c); # defined cookie(s)
$acke = $GLOBALS['_COOKIE']; # all cookies

if(OnMultiMatchPairEqual($dcke,$acke)) return true;
else return false;

}

# OnSession('gato=botas,meowMix=PleaseDeliver')
# OnSession('weather=warm,sea=cold,sand=thin') ou
# if there is one match it returns true
function OnSession($s){

@session_start();
if($GLOBALS['_SESSION'] == '')
if(stripos($s,'=') === false) return false;


$dsss = trim($s); # defined group(s)
$asss = $GLOBALS['_SESSION']; # all session

if(OnMultiMatchPairEqual($dsss,$asss)) return true;
else return false;

}

# turn strings to arrays
# for further use
# and evaluation
function OnToArray($x){

if($x == '') return false;

if (is_array($x)) $xa = $x;
  if (!is_array($x)){
     if (strpos($x,',') !== false) 
       $xa = explode(',',$x); # multi ;
     else $xa = (array)$x; # single
  }

return $xa;
}

# checks multiple lines
# for multiple operators
# all lines must have an
# operator otherwise
# return false
function OnOperator($x,$m='=,!,<,>,<=,>='){

if($x == '' || $m == '') return false;

$xa = OnToArray($x);
$ma = OnToArray($m);

$ne = count($xa); # num of entries

  if(is_array($xa) && is_array($ma)){
    foreach($xa as $e){
      foreach($ma as $o)
        if(stripos($e,$o) !== false) $f[] = 1; #f=found 
    }
    if($ne == count($f)) $r = true; # r=result
  }
  if($r == true) return true;
  else return false;
}

# get the operator
# for one needle entry
function OnGetOperator($o){
global $OnOperArr;

if(strlen(
     str_replace($OnOperArr,'',$o)
   ) < 1 ) return '';

$f = $o{0};
$s = $o{1};

  if($f == '=' && $s == '') return '=';
  if($f == '!' && $s == '') return '!';
  if($f == '<' && $s != '=') return '<';
  if($f == '>' && $s != '=') return '>';
  if($f == '<' && $s == '=') return '<=';
  if($f == '>' && $s == '=') return '>=';

}

function OnGetValue($v){
global $OnOperArr;

$rv = str_replace($OnOperArr,'', $v);
return $rv;

}

function OnDoOperator($d,$o,$v){
global $OnOperArr;

if(in_array($o,$OnOperArr) === false) return false;
$v = OnGetValue($v);

      if($o == '=' && ($d == $v)) $m = 1;
      if($o == '!' && ($d != $v)) $m = 1;
      if($o == '<' && ($d < $v)) $m = 1;
      if($o == '>' && ($d > $v)) $m = 1;
      if($o == '<=' && ($d <= $v)) $m =1;
      if($o == '>=' && ($d >= $v)) $m =1;
      if($o == '' && ($d == $v)) $m = 1;

if ($m == 1) return true;

}

# check len of n
# for each in needle
function OnMultiMatchLen($l,$h){

if($l == '' || $h == '') return false;

$la = OnToArray($l);
$nd = $h;
$nl = strlen("$nd");

  if(OnOperator($la)){
    foreach($la as $lu){
      $o = OnGetOperator($lu);
      $v = OnGetValue($lu);
      if(OnDoOperator($nl,$o,$lu)) $m =1;
    }
  }
  if($m == 1) return true;
  else return false;
}

function OnMultiMatchEqual($n,$c){

$na = OnToArray($n);
$ca = OnToArray($c);

if($na[0] != ''){
  $nr = $na; $na = '';
  foreach($nr as $k => $v){
    $na[trim($v)] = 1; 
  }
}

if($ca[0] != ''){
  $cr = $ca; $ca = '';
  foreach($cr as $k => $v){
    $ca[trim($v)] = 1; 
  }
}

  foreach($na as $ni => $v){
    # if one match happens
    # we will return true
      if($ca[$ni] == 1) $e = 1; 
  }

  if($e == 1) return true;
  else return false;
}

function OnMultiMatchPairEqual($n,$h){

$na = OnToArray($n);

  foreach($na as $e){
    $i = explode('=',$e);
    if($h[$i[0]] == $i[1]) $e = 1;
  }

  if($e == 1) return true;
  else return false;

}

function OnMultiMatchContained($n,$h){

$na = OnToArray($n);

  foreach($na as $v){
   if(strpos($h,trim("$v")) !== false) $c = 1;
  }

  if($c == 1) return true;
  else return false;
}