, Copyright 2006. ## LICENSE: You can redistribute this software 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. //REQUIRED PMWIKI PARAMETERS $HandleActions['zap'] = 'ZAPengine'; $PageAttributes['passwdzap'] = 'Set ZAP forms password: '; SDV($DefaultPasswords['zap'],''); # PRESERVE POST VALUES foreach ($_POST as $k=>$v) { if(!is_array($v)) $InputValues[$k] = htmlspecialchars($v); } ####################################### ### Main ZAP ENGINE ### ####################################### function ZAPengine() { ## ZAP INITIALIZATION // Is there some reason not to declare some of these global? Someone suggested it might be a security vulnerability. ??? global $pagename, $ScriptUrl, $msg, $msg_form, $ZAParray, $PCache, $Version, $ZAPfieldlabel; if (substr($Version, 0, 10) != "pmwiki-2.2") ZAPabort('version', "ZAP requires a later version of PmWiki. Please upgrade to the latest beta. Thank you! "); $pagename = ResolvePageName($pagename); $ZAParray = ZAPsecure(); $msg['submit'] = "Form submitted. "; if (!isset($ZAParray['nextpage'])) $ZAParray['nextpage'] = $pagename; if (!isset($ZAParray['datapage'])) $ZAParray['datapage'] = $pagename; // print_r($ZAParray);die(); ## ZAP FORMS PROCESSING COMMANDS foreach ($ZAParray as $field => $value) { $value = preg_replace('/\\{(\\w+)\\}/e', 'ZAPfieldreplace("$1", $ZAParray)', $ZAParray[$field]); if (($field == "msg") || (substr($field, 0, 4) == "msg_")) { if (strlen($field) == 3) $msg_form['clear'] = $value . " "; else $msg_form[substr($field, 4)] = $value . " "; continue; } if (($field == "warn") && ($value != "")) ZAPabort($field, $value); if ($field == "passdata") { $f = explode(",", $value); foreach ($f as $ff) { if ((isset($ZAParray[$ff])) && ($ZAParray[$ff] != '')) $passdata .= "?$ff=$ZAParray[$ff]"; } continue; } if (substr($field, 0, 8) == "savedata") ZAPsave($ZAParray['datapage'], $value, '', 'Fields have been saved. '); if (substr($field, 0, 9) == "erasedata") ZAPsave($ZAParray['datapage'], $value, '', 'Fields have been erased. '); if (substr($field, -4, 4) == "list") { $value = trim($value); $value = trim($value, "\x7f..\xff\x0..\x1f"); if ((substr($value, 0, 1) == "+") || (substr($value, 0, 1) == "-")) { $current = PageTextVar($ZAParray['datapage'], $field); $value = ZAPcsv($value, $current); } else $value = ZAPcsv($value); } if (substr($field, -4, 4) == "page") $value = ZAPpageshortcuts($value); if (substr($field, 0, 4) == "link") $value = "$ScriptUrl?n=$value"; if (substr($field, 0, 8) == "required") { $f = explode(",", $value); foreach ($f as $ff) { if ($ZAParray[$ff] == "") { if (isset($ZAPfieldlabel[$ff])) $out = $out . "$ZAPfieldlabel[$ff] "; else $out = $out . "$ff "; } } if ($out != '') { $out = substr($out, 0, -1); ZAPabort('required', "The following fields are missing: $out. "); } continue; } ## ZAP COMMANDS YOU CANNOT POST if ((substr($field, 0, 8) == "datapage") || (substr($field, 0, 6) == "target")) { if (isset($_POST[$field])) ZAPabort('post', "The \"$field\" field cannot be processed this way. "); $value = ZAPpageshortcuts($value); $ZAParray['datapage'] = $value; } if ((substr($field, 0, 2) == "if") || (substr($field, 0, 8) == "validate")) { if (isset($_POST[$field])) ZAPabort('post', "The \"$field\" field cannot be processed this way. "); $ZAPcond = substr($value, 0, strpos($value, " ? ")); $value = substr($value, strpos($value, " ? ") + 3); $ZAPacts[0] = ''; if (! strpos($value, " : ")) $ZAPacts[1] = explode(" , ", substr($value, strpos($value, " ? "))); else { $ZAPacts[1] = explode(" , ", substr($value, 0, strpos($value, " : "))); //true $ZAPacts[0] = explode(" , ", substr($value, strpos($value, " : ") + 3)); //false } if (substr($field, 0, 2) == "if") { if (CondText($pagename, "! $ZAPcond", true)) $c = "1"; else $c = "0"; } else { $valfield = substr($field, 8); if (substr($valfield, 0, 1) == '_') $valfield = substr($valfield, 1); if (!preg_match($ZAPcond, $ZAParray[$valfield])) $c = "0"; else $c = "1"; } if (!is_array($ZAPacts[$c])) $ZAPacts[$c] = array($ZAPacts[$c]); foreach ($ZAPacts[$c] as $act) { $f = substr($act, 0, strpos($act, "=")); $ZAParray[$f] = substr($act, strpos($act, "=") + 1); } continue; } ## ZAP PLUGIN EXTENSIONS if (strpos($field, "_")) $func = substr($field, 0, strpos($field, "_")); else ($func = $field); if ((function_exists("ZAPX$func")) && ($func == strtolower($func))) { if (isset($_POST[$field])) ZAPabort('post', "The \"$field\" field cannot be processed as a POST value. "); $ZAPfunc = "ZAPX$func"; if (ZAPauth($func, $pagename, 'Commands') == true) $value = $ZAPfunc($value, $field); else ZAPabort('not_enabled', "The $func command is not enabled for this page. "); } $ZAParray[$field] = $value; } ## ZAP CLOSING STEPS unset($PCache[$pagename]); if (($ZAParray['nextpage'] != $pagename) || (isset($passdata))) Redirect(FmtPageName($ZAParray['nextpage'] . $passdata, $pagename)); ZAPmessages(); } ####################################### ### ZAP UTILITY FUNCTIONS ### ####################################### ## Produces message output from msg array. Each element in the array is checked for a replacement at Site.ZAPMessages ## (can be used for sitewide customization or easy internationalizations) and then checked for a form defined substitute. ## That is, if msg_submit is set to a value in the form it will override the default or config values for that entry. ## Setting a msg field in the form will clear the entire message array and reset to the single value in the msg field. function ZAPmessages() { global $msg, $msg_form, $pagename, $MessagesFmt; foreach ($msg as $mi => $mv) { if (ZAPconfig($mi, $mv, "Site.ZAPMessages") != '') $msg[$mi] = ZAPconfig($mi, $mv, "Site.ZAPMessages"); } if (is_array($msg_form)) $msg = array_merge($msg, $msg_form); if (isset($msg_form['clear'])) $m = $msg_form['clear']; else $m = implode(' ', $msg); $MessagesFmt[] = "
$[$m]
"; HandleBrowse($pagename); die(); } ## Adds a message to the message array, and shut down processing immediately. function ZAPabort($id, $m) { global $msg; $msg['id'] = $m; ZAPmessages(); } ## Can be used to save or erase Text Vars on a page. function ZAPsave($page, $value, $text='', $message='') { global $pagename, $ZAParray, $msg; if ((!ZAPauth($pagename, $page, 'Targets')) && ($pagename != $page)) ZAPabort('save', "You are not authorized to write to this page. "); $oldpage = ReadPage($page); $newpage = $oldpage; // Shouldn't savedata escape page text for these kinds of functions for directives? Or leave to functions... if ($text != '') $newpage['text'] = $text; else $newpage['text'] = $oldpage['text']; if ($value != '') { $escapein = array('(:', ':)', '$'); $escapeout = array('( :', ': )', '-$-'); $f = explode(",", $value); // It would be better and easier to use the -field syntax and get rid of erasedata. Maybe one message... 'Data fields updated' foreach ($f as $ff) { $ff = trim($ff); if ($message == 'Fields have been erased. ') { // Make the ending \n optional using ? in the pattern for cleaner replacements. // Make sure all new fields get save to a new line. Double check this is working clean. $newpage['text'] = preg_replace('/\\(:'.$ff.': (.*?):\\)\n/s', '', $newpage['text']); } else { $v = str_replace($escapein, $escapeout, $ZAParray[$ff]); if (strpos($newpage[text], "(:$ff: ") !== false) $newpage['text'] = preg_replace('/\\(:'.$ff.': (.*?):\\)/s', "(:$ff: $v:)", $newpage['text'], 1); else $newpage['text'] .= "(:$ff: $v:)\n"; $newpage['text'] = str_replace('-$-', '$', $newpage['text']); } } } $msg['savemessage'] = $message; UpdatePage($page, $oldpage, $newpage); return; } ## Used for ZAP's list and group management functions. To add or remove items from list just set field equal to +Item,-Item. ## If no $current is supplied, it trims each items and returns $value. function ZAPcsv($value, $current='') { if ($current != '') { $list = ",,$current,,"; $i = explode(",", $value); foreach ($i as $ii) { $ii = trim($ii); $flag = substr($ii, 0, 1); $item = trim(substr($ii, 1)); if (($flag == "-") && (strpos($list, ",$item,"))) $list = str_replace(",$item,", ",", $list); if (($flag == "+") && (! strpos($list, ",$item,"))) $list = substr($list, 0, -1) . $item . ",,"; } $value = substr($list, 2, -2); } $i = explode(",", $value); foreach ($i as $ii) { $out[] = trim($ii); } $value = implode(",", $out); return $value; } ## Retrieves markup from a template for use in new pages, pages insertions, emails, etc. (type is defined by $x) ## First it looks for appropriate field in the form (if $x = email, emailtemplate) for the template's page name. ## If not set, it looks for the default location relevant to the current page, ie Group.Name-template. ## Finally markup content is returned, or false if no value found or user not authorized to read the template. function ZAPtemplate($x) { global $pagename, $ZAParray; if (isset($ZAParray[$x . "template"])) $xx = ZAPpageshortcuts($ZAParray[$x . "template"]); else $xx = $pagename . "-template"; if ((PageExists($xx)) && (CondAuth($xx, 'read'))) { $page = ReadPage($xx); $r = $page['text']; } else return false; $r = preg_replace('/\\{\\$\\$(\\w+)\\}/e', 'ZAPfieldreplace("$1", $ZAParray)', $r); return $r; } ## Used to perform field replacements in the templating engine above and other places in the code. function ZAPfieldreplace($x, $ZAParray) { if (isset($ZAParray[$x])) return ($ZAParray[$x]); return "\{$x}"; } ## Used in nearly all functions involving page names allowing several useful shortcuts. See docs for syntax. ## Also verifies that pages cannot be set to the site group unless enabled via a special config file setting. function ZAPpageshortcuts($v) { global $pagename, $FmtPV, $EnableZAPsiteEdits, $SiteGroup; if (strpos($v, "/")) $v = str_replace("/", ".", $v); if (! strpos($v, ".")) return $pagename; $vv[0] = substr($v, 0, strpos($v, ".")); $vv[1] = substr($v, strpos($v, ".") + 1); if (strpos($vv[1], "?")) { $vv[2] = substr($vv[1], strpos($vv[1], "?")); $vv[1] = substr($vv[1], 0, strpos($vv[1], "?")); } if ((($vv[0] == $SiteGroup) || ($vv[0] == "Site")) && ($EnableZAPsiteEdits != true)) ZAPabort('site', "Group $SiteGroup is blocked on this system. "); $pn = explode(".", $pagename); $rr1 = array('*','^'); $rr2 = array($pn[0],$pn[1]); $vv[0] = str_replace($rr1, $rr2, $vv[0]); $time = time(); // Should there be some way to make these shortcuts configurable--so you can put some of this in extensions? $order = substr($FmtPV['$orderpage'], 1, -1); $rr1 = array('*','^','@','$','+','~','!'); $rr2 = array($pn[1],$pn[0],$GLOBALS[Author],$order,$time,"Profiles","Categories"); $vv[0] = str_replace($rr1, $rr2, $vv[0]); $vv[1] = str_replace($rr1, $rr2, $vv[1]); if (substr($vv[1], -1) == "#") $vv[1] = substr($vv[1], 0, -1) . ZAPthread($vv[0]); $v = $vv[0] . "." . $vv[1]; if (strpos($v, "#") != false) $v = MakePageName($pagename, substr($v, 0, strpos($v, "#"))) . substr($v, strpos($v, "#")); else $v = MakePageName($pagename, $v) . $vv[2]; return $v; } ## Used to find the highest numbered page in a group and return that number plus one. SDV($ZAPthreadstart,'1000'); function ZAPthread($g) { global $ZAPthreadstart; $e = $ZAPthreadstart - 1; $gg = explode(",", $g); foreach($gg as $ggg) { foreach(ListPages("/^$ggg\\.\\d/") as $n) { $n = substr($n,strlen($ggg)+1); if (! ereg("^[0-9]+$", $n)) continue; $e = max($e,$n); } } $e = $e + 1; return $e; } ## This function is used to verify that a form is properly submitted (to prevent various potential security risks) ## Basically, it checks that a required session variable was set. Then, sets up the $ZAParray variable for processing. function ZAPsecure() { global $pagename; if(!CondAuth($pagename, "zap")) ZAPabort('submit', "You are not authorized to submit this form. "); $formpage = str_replace(".", "", $pagename); $formkey = $_REQUEST['ZAPkey']; session_start(); if ($_SESSION['ZAP']["$formpage$formkey"]['secure'] != "set") ZAPabort('key', "An error occurred. Form could not be processed."); $ZAParray = $_SESSION['ZAP']["$formpage$formkey"]; foreach($_SESSION['ZAP'] as $i => $ii) unset($_SESSION['ZAP'][$i]); foreach ($_POST as $field => $value) { if (is_array($value)) $value = implode(",", $value); $_POST[$field] = stripmagic($value); } foreach($_POST as $f => $v) { // These escapes are to protect hijacking the if/validate commands. Not sure they are needed... Hmmm... $escapein = array(' ? ', ' : ', ' , '); $escapeout = array(' ? ', ' : ', ' , '); if (!isset($ZAParray[$f])) $ZAParray[$f] = str_replace($escapein, $escapeout, $v); } return $ZAParray; } ## This function allows you to retrieve a text var value (field $var on page $page) if defined. If not the default value ($def) is returned. function ZAPconfig($var, $def, $page) { $value = PageTextVar($page, $var); if ($value == '') { $page = ReadPage($page); $value = preg_match('/'.$value.'\\=\\"(.*?)\\"/s', $page['text'], $v); $value = $v[1]; } if ($value == '') $value = $def; return $value; } ## This function is used to check various kinds of permissions in ZAP--namely commands and targetss ## ZAPauth('edit', 'Test.Main', 'Commands') will verify whether or not the edit command is allowed for page Test.Main ## ZAPauth('Test.One', 'Test.Two', 'Targets') verifies whether a form on Test.One can write to Test.Two ## The permissiable values are all set on Site.ZAPCommands or Site.ZAPTargets as normal PTV's function ZAPauth($var, $p, $check) { global $SiteGroup; if (! PageExists("$SiteGroup.ZAP$check")) return true; if ($check == 'Targets') $var = str_replace(".", "_", $var); $authlist = PageTextVar("$SiteGroup.ZAP$check", $var); if (($authlist == '') && ($check == "Targets")) $authlist = PageTextVar("$SiteGroup.ZAP$check", substr($var, 0, strpos($var, "_"))); if ($authlist == '') return false; $list = explode(",", $authlist); foreach ($list as $a) { $page = $p; if (substr($a, -1) == "*") $page = substr($page, 0, strlen($a) - 1) . "*"; elseif (strpos($a, '.') == NULL) $page = substr($page, 0, strpos($page, '.')); if ($a == $page) return true; } return false; } ####################################### ### ZAP MARKUPS ### ####################################### ## This markup sets up a ZAP form with a special secuity session variable to verify the form was setup properly Markup('zapform', ''); function ZAPform($d) { if (isset($_REQUEST['q'])) return ''; global $pagename, $ScriptUrl, $EnablePathInfo; if (strpos($d, "upload")) $u = "enctype=multipart/form-data "; $arg = ParseArgs($d); if (isset ($arg['action'])) $a = "action=$arg[action] method=post "; else $a = "action=$ScriptUrl".($EnablePathInfo==1 ? "/".str_replace(".","/",$pagename) : "?n=$pagename")." method=post "; if (isset($arg['key'])) $formkey = "name=$arg[key] "; else $formkey = ''; ZAPinput("secure=set $arg[key]"); return "(:input form $formkey$u$a:)(:input hidden action zap:)(:input hidden ZAPkey \"$arg[key]\":)"; } ## Input values can be sent directly to the ZAP array via session values using this markup. ## Values submitted this way cannot be overwritten by post values and are thus safer. Required syntax for most ZAP commands. Markup('zapinput', 'inline', '/\(:zap (.*?):\)/ei', "ZAPinput('$1')"); function ZAPinput($x) { if (isset($_REQUEST['q'])) return ''; global $pagename; // Do I need stripslashes or magic slashes? $x = stripslashes($x); $arg = ParseArgs($x); foreach ($arg['#'] as $i => $ii) { // Is this set up right with stripslashes now? What about just " or '? Double check this... if (substr($ii, 0, 2) == '\"') $arg['#'][$i] = substr($arg['#'][$i], 2, -2); } // What if entered key=name to input. Shouldn't it be made to accept that? $formkey = $arg['#'][3]; $f = $arg['#'][0]; $v = $arg['#'][1]; if ($f == '') { $f = $v; $v = ''; } $formpage = trim(str_replace(".", "", $pagename)); @session_start(); $_SESSION['ZAP']["$formpage$formkey"][$f] = $v; session_write_close(); return; } ## Put this markup on a page and the specified form will be submitted as soon as the page is loaded ## action=edit or stop will suspend submission. POST values will be ignored in auto submit forms. // Actually, better check. I think hidden and default POST values will be used... If so, that's OK, of course. Markup('zapsubmit', '>inline', '/\(:zapsubmit(.*?):\)/ei', "ZAPsubmit('$1')"); function ZAPsubmit($formkey) { if (isset($_REQUEST['q'])) return ''; if (($_GET['action'] == "edit") || ($_GET['action'] == "stop")) return ''; global $ZAParray, $pagename, $PageUrl; $formpage = trim(str_replace(".", "", $pagename)); $formkey = trim($formkey); if (($_SESSION['ZAP']["$formpage$formkey"]['nextpage'] == $pagename) || ($_SESSION['ZAP']["$formpage$formkey"]['nextpage'] == '')) return "ZAPsubmit error. Field nextpage required. "; $_POST['ZAPkey'] = trim($key); ZAPengine(); return; } ## This markup allows you to create a link that will submit a zap form when clicked ## POST values can be used in link submit forms. Markup('zaplink','>inline','/\(:zaplink(.*?):\)/ei', "Keep(ZAPlink('$1'))"); function ZAPlink($args) { if (isset($_REQUEST['q'])) return ''; global $pagename, $ScriptUrl; $arg = ParseArgs($args); if (!isset($arg['key'])) $arg['key'] = ''; if (!isset($arg['label'])) $arg['label'] = $pagename; if (!isset($arg['linkpage'])) $arg['linkpage'] = $pagename; else $arg['linkpage'] = ZAPpageshortcuts($arg['linkpage']); $arg['secure'] = "set"; $formkey = trim($arg['key']); $formpage = trim(str_replace(".", "", $pagename)); session_start(); $_SESSION['ZAP']["$formpage$formkey"]['key'] = $arg['key']; $_SESSION['ZAP']["$formpage$formkey"]['label'] = $arg['label']; $_SESSION['ZAP']["$formpage$formkey"]['linkpage'] = $arg['linkpage']; $_SESSION['ZAP']["$formpage$formkey"]['secure'] = "set"; session_write_close(); return "{$arg['label']}"; } ## Automatically converts GET values to page variables for use on page ## Cannot override existing page variables. (:zapget:) markup no longer needed. Markup('zapget', '<{$var}', '/\(:zapget:\)/e', ''); foreach ($_GET as $g => $gg) if ((!isset($FmtPV["$$g"])) && ($gg != "")) $FmtPV["$$g"] = "'" . $gg . "'"; // Check these out and fix the ZAP toolbox to work with the new PmWiki defaut. // You need a keep function still--but change to a markup expression that can be nested in the source markup. // But be careful or users could unescape their own directives. Not good. How will you solve??? // Can't put (: :) inside the default input markup so you have a problem. You need to get ZAPwiki done... ## These markups will soon be deprecated now that Pm is slowly bringing his own markup out for this... Markup('textarea', 'inline', '/\\(:textarea (.*?):\\)/e', "Keep(''); Markup('zapkeep', '