User:Jeblad/Qualified access/QualifiedAccess.php
From MediaWiki.org
<?php // QualifiedAccess MediaWiki extension. // Make a packet of information that can be used for qualification of // an external access. // Copyright (C) 2008 - John Erling Blad. // // 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 # Alert the user that this is not a valid entry point to MediaWiki if they try to access the skin file directly. if (!defined('MEDIAWIKI')) { echo <<<EOT To install qualified access extension, put the following line in LocalSettings.php: require_once( "{$IP}/extensions/QualifiedAccess/QualifiedAccess.php" ); EOT; exit( 1 ); } #---------------------------------------------------------------------------- # Extension initialization #---------------------------------------------------------------------------- // Features are specific keywords that turns on access control on the specific keywords $wgQualifiedAccessFeature = array(); $wgQualifiedAccessFeature['rights'] = array(); // Distinguish on user rights $wgQualifiedAccessFeature['rights']['read'] = true; $wgQualifiedAccessFeature['rights']['edit'] = true; $wgQualifiedAccessFeature['rights']['delete'] = false; $wgQualifiedAccessFeature['groups'] = array(); // Distinguish on user groups $wgQualifiedAccessFeature['groups']['autoconfirmed'] = true; $wgQualifiedAccessFeature['groups']['user'] = true; $wgQualifiedAccessFeature['groups']['patroller'] = true; $wgQualifiedAccessFeature['groups']['sysop'] = true; $wgQualifiedAccessFeature['username'] = true; // Report users username $wgQualifiedAccessFeature['realname'] = true; // Report users realname $wgQualifiedAccessFeature['editcount'] = true; // Report class of user contribution $wgQualifiedAccessFeature['registration'] = true; // Report age of user account $wgQualifiedAccessFeature['test'] = false; // Use the test attribute //$wgQualifiedAccessTest = true; // Enable the test attribute $wgQualifiedAccessMethod = array(); // Default method for the access to the service $wgQualifiedAccessMethod['post'] = true; $wgQualifiedAccessMethod['get'] = true; $wgQualifiedAccessDefault = array(); $wgQualifiedAccessDefault['method'] = 'post'; // Default method for the access to the service $wgQualifiedAccessDefault['keyserver'] = 'hkp://subkeys.pgp.net'; $wgQualifiedAccessDefault['format'] = '<dl>,<dt>,</dt>,<dd>,</dd>,</dl>'; $wgQualifiedAccessDefault['separator'] = ', '; $wgQualifiedAccessDefault['test'] = ''; $wgQualifiedAccessDefault['time'] = 'Y-m-d\TH:i:s\Z'; $wgQualifiedAccessCommand = '/usr/bin/gpg'; // Command to do the encryption and/or signing operation $wgQualifiedAccessOptions = array(); $wgQualifiedAccessOptions['default-key'] = $wgPasswordSender; $wgQualifiedAccessOptions['local-user'] = 'webmaster@yellowiki.net'; $wgQualifiedAccessOptions['keyserver'] = array(); $wgQualifiedAccessOptions['keyserver']['auto-key-retrieve'] = true; $wgQualifiedAccessOptions['keyserver']['honor-keyserver-url'] = true; $wgQualifiedAccessOptions['keyserver']['honor-pka-record'] = true; $wgQualifiedAccessOptions['keyserver']['timeout'] = 5; $wgQualifiedAccessOptions['greeting'] = false; $wgQualifiedAccessOptions['default-keyring'] = false; $wgQualifiedAccessOptions['keyring'] = 'pubring.gpg'; $wgQualifiedAccessOptions['secret-keyring'] = 'secring.gpg'; $wgQualifiedAccessOptions['options'] = '/var/gnupg/gpg.conf'; $wgQualifiedAccessOptions['homedir'] = '/var/gnupg'; // Basic credits $wgExtensionCredits['specialpage'][] = array( 'author' => 'John Erling Blad', 'name' => 'Qualified access', 'url' => 'http://www.mediawiki.org/wiki/Extension:QualifiedAccess', 'description' => 'Let users with sufficient qualifications access external services like news archives.', 'version'=>'0.2' ); // Extend the language $wgHooks['LanguageGetMagic'][] = 'efQualifiedAccessMagic'; // Tidy our protected code $wgHooks['ParserAfterTidy'][] = 'efQualifiedAccessTidy'; // Set up the bulk of the extension $wgExtensionFunctions[] = 'efQualifiedAccessExtension'; // Register the special page $wgSpecialPages['QualifiedAccess'] = 'QualifiedAccess'; // Store for localy generated content // Not quite sure abot this, and if there are any security implications $efQualifiedAccessArgs = null; $efQualifiedAccessMarks = array(); // Extend the language as necessary function efQualifiedAccessMagic( &$magicWords, $langCode ) { $magicWords['qaccess'] = array( 0, 'qaccess' ); return true; } // Add the extra files, localizations, etc function efQualifiedAccessExtension() { global $wgParser; global $wgMessageCache, $wgQualifiedAccessMessages; # Internationalization require( dirname( __FILE__ ) . '/QualifiedAccess.i18n.php' ); require( dirname( __FILE__ ) . '/QualifiedAccess_body.php' ); foreach ( $wgQualifiedAccessMessages as $lang => $langMessages ) { $wgMessageCache->addMessages( $langMessages, $lang ); } $wgParser->setHook( "qaccess", "efRenderQualifiedAccess" ); } // Tidy additional text. function efQualifiedAccessTidy(&$parser, &$text) { global $efQualifiedAccessMarks; for ($i=0;$i<count($efQualifiedAccessMarks);$i++) $text = preg_replace('/xx-qaccess-'.$i.'-xx/', $efQualifiedAccessMarks[$i], $text); return true; } // Processes the <qaccess> extension tag. function efRenderQualifiedAccess( $input, $argv, $parser ) { global $wgUser; global $wgTitle; global $wgMessageCache; global $wgSitename; global $wgQualifiedAccessFeature; global $wgQualifiedAccessMethod; global $wgQualifiedAccessTest; global $wgQualifiedAccessHash; global $wgQualifiedAccessMode; global $efQualifiedAccessArgs; global $wgQualifiedAccessOptions; global $wgQualifiedAccessDefault; global $wgQualifiedAccessCommand; global $efQualifiedAccessMarks; global $wgPasswordSender; $out = array(); // Reorganize and clean up the format string $format = ($argv['format'] ? $argv['format'] : $wgQualifiedAccessDefault['format']); $format = str_replace(array('²{', '}²', '²[', ']²', '¦', '\n', '¶'), array('{{', '}}', '[[', ']]', '|', "\n", "\n"), $format); $format = split(',', $format); foreach ($format as $item) { $item = htmlspecialchars($item); } if (count($format) != 6) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-format-error' ), array( $title ) ) ) . '</div>'; // Reorganize and clean up the separator string $separator = $argv['separator'] ? $argv['separator'] : $wgQualifiedAccessDefault['separator']; $separator = htmlspecialchars($separator); // Don't have to clean up this as it is used in pattern matches later on $mode = $argv['mode'] ? $argv['mode'] : $wgQualifiedAccessDefault['mode']; // Reorganize and clean up the keyserver string $keyserver = $argv['keyserver'] ? $argv['keyserver'] : $wgQualifiedAccessDefault['keyserver']; $keyserver = htmlspecialchars(trim($keyserver)); // Not much we can do with this except clean it up $kid = htmlspecialchars(trim($argv['key'])); // Not much we can do with this except clean it up $editlimit = intval($argv['editlimit']); // Reorganize and clean it up $timeformat = $argv['time'] ? $argv['time'] : $wgQualifiedAccessDefault['time']; $now = htmlspecialchars(gmdate( $timeformat, time() )); // handle title and service, they are one way aliases $title = trim($argv['title']); $service = trim($argv['service']); if (!strlen($title) && strlen($service)) $title = $service; if (strlen($title) && !strlen($service)) $service = $title; $title = htmlspecialchars(ucfirst($title)); $service = htmlspecialchars(strtolower($service)); // handle features $features = array(); $values = explode(',', $argv['features']); foreach ($values as $item) { $item = trim($item); if (!is_bool($wgQualifiedAccessFeature[$item]) || !$wgQualifiedAccessFeature[$item]) continue; if (strlen($item)>0) $features[$item] = true; } // handle rights $rights = array(); $values = explode(',', $argv['rights']); foreach ($values as $item) { $item = trim($item); if (!is_bool($wgQualifiedAccessFeature['rights'][$item]) || !$wgQualifiedAccessFeature['rights'][$item]) continue; if (strlen($item)>0) array_push($rights, $item); } // handle groups $groups = array(); $values = explode(',', $argv['groups']); foreach ($values as $item) { $item = trim($item); if (!is_bool($wgQualifiedAccessFeature['groups'][$item]) || !$wgQualifiedAccessFeature['groups'][$item]) continue; if (strlen($item)>0) array_push($groups, $item); } // what kind of page is this $special = -1 == $wgTitle->getNamespace(); // a few vars $packet = ''; $found = array(); // build regexp for resources $resources = array(); $values = explode(',', $argv['resources']); foreach ($values as $item) { $item = preg_quote(trim($item)); if (strlen($item)>0) array_push($resources, $item); } if (count($resources)) $mres = '[^(' . join($resources, '|') . ')]i'; // process a special page if ($special) { // if necessary, run the test if ($wgQualifiedAccessFeature['test']) { $test = $argv['test'] ? $argv['test'] : $wgQualifiedAccessDefault['test']; $test = trim($test); if (strlen($test)) { $test = $parser->internalParse($test); if (strlen($test)) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-test-result' ), array( $title ) ) ) . '</div>'; } } // check blocked status if ($wgUser->isBlocked()) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-blocked' ), array( $title ) ) ) . '</div>'; // check blocked status if ($editlimit > $wgUser->getEditCount()) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-editlimit' ), array( $title ) ) ) . '</div>'; // get site name $packet .= "sitename: $wgSitename"; // report time $packet .= "\ntime: $now"; // get ip address $packet .= "\nipaddress: " . wfGetIp(); // set the action $action = htmlspecialchars(trim($argv['action'])); if (!strlen($action)) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-action-error' ), array( $title ) ) ) . '</div>'; // set the method $method = htmlspecialchars(trim(strtolower($method))); $method = $wgQualifiedAccessMethod[$method] ? $method : $wgQualifiedAccessDefault['method']; if (!strlen($action)) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-method-error' ), array( $title ) ) ) . '</div>'; // try to identify resources foreach ($efQualifiedAccessArgs as $key => $value) { if (preg_match('/^\d+$/',$key) || $key == 'resource' || $key == 'doi' || $key == 'issn') { if (!isset($value) || $value == '') continue; $found[ $key == 'doi' || $key == 'issn' ? "$key $value" : $value ]++; } } } $output = $format[0]; $n = 0; $m = 0; if (!$special) { // report key server $output .= $format[1] . wfMsg( 'qaccess-key-server' ) . $format[2] . $format[3] . $keyserver . $format[4]; // report key id $output .= $format[1] . wfMsg( 'qaccess-key-id' ) . $format[2] . $format[3] . $kid . $format[4]; // report service if ($service) $output .= $format[1] . wfMsg( 'qaccess-service' ) . $format[2] . $format[3] . $service . $format[4]; // report edit limit if ($editlimit) $output .= $format[1] . wfMsg( 'qaccess-editlimit' ) . $format[2] . $format[3] . $editlimit . $format[4]; } if ($special) { // check service match if ($argv['service']) { foreach ($efQualifiedAccessArgs as $key => $value) { if (preg_match('/^\d+$/',$key) || $key == 'service') { if (!isset($value) || $value == '') continue; $m++; if ($service == strtolower($value)) { $found[ $key == 'doi' || $key == 'issn' ? "$key $value" : $value ]--; $n++; break; } } } } // check resource match if ($argv['resources']) { foreach ($efQualifiedAccessArgs as $key => $value) { if (preg_match('/^\d+$/',$key) || $key == 'resource' || $key == 'doi' || $key == 'issn') { if (!isset($value) || $value == '') continue; $m++; if (preg_match($mres, $value)) { if (($key == 'doi') || ($key == 'issn')) { $output .= $format[1] . wfMsg( 'qaccess-resource' ) . $format[2] . $format[3] . "$key $value" . $format[4]; $packet .= "\nresource: $key $value"; $found[ "$key $value" ]--; } else { $output .= $format[1] . wfMsg( 'qaccess-resource' ) . $format[2] . $format[3] . $value . $format[4]; $packet .= "\nresource: $value"; $found[ $value ]--; } $n++; } } } } // still in sync? if (($argv['service'] || $argv['resources']) && $m && !$n) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notfound-resourceorservice' ), array( $title ) ) ) . '</div>'; // additional resources foreach ($found as $key => $value) if (0<$found[$key]) { $output .= $format[1] . wfMsg( 'qaccess-resource' ) . $format[2] . $format[3] . $key . $format[4]; $packet .= "\nresource: " . $key; } } // process a special page if ($special) { // get user name if ($features['username']) { $output .= $format[1] . wfMsg( 'qaccess-username' ) . $format[2] . $format[3] . $wgUser->getName() . $format[4]; $packet .= "\nusername: " . $wgUser->getName(); } // get real name if ($features['realname']) { $output .= $format[1] . wfMsg( 'qaccess-realname' ) . $format[2] . $format[3] . $wgUser->getRealName() . $format[4]; $packet .= "\nrealname: " . $wgUser->getRealName(); } // get edit count if ($features['editcount']) { $n = $wgUser->getEditCount(); if (10<$n) $n = round($n, -intval(log10($n))); $output .= $format[1] . wfMsg( 'qaccess-editcount' ) . $format[2] . $format[3] . $n . $format[4]; $packet .= "\neditcount: " . $n; } // get registration if ($features['registration']) { $then = $wgUser->getRegistration(); $weeks = round((time() - strtotime($then))/604800); $months = round((time() - strtotime($then))/2629744); if ($months<=3) { $output .= $format[1] . wfMsg( 'qaccess-registration' ) . $format[2] . $format[3] . wfMsgReplaceArgs( wfMsg( 'qaccess-registration-weeks' ), array( substr($then, 0, 4), $months, $weeks ) ) . $format[4]; $packet .= "\nregistration: " . substr($then, 0, 4) . " ($weeks weeks)"; } elseif ($months<=12) { $output .= $format[1] . wfMsg( 'qaccess-registration' ) . $format[2] . $format[3] . wfMsgReplaceArgs( wfMsg( 'qaccess-registration-months' ), array( substr($then, 0, 4), $months, $weeks ) ) . $format[4]; $packet .= "\nregistration: " . substr($then, 0, 4) . " ($months months)"; } else { $output .= $format[1] . wfMsg( 'qaccess-registration' ) . $format[2] . $format[3] . wfMsgReplaceArgs( wfMsg( 'qaccess-registration-years' ), array( substr($then, 0, 4), $months, $weeks ) ) . $format[4]; $packet .= "\nregistration: " . substr($then, 0, 4); } } } // check features $out = ''; if (!$special) { foreach ($features as $key => $value) { if (isset($wgQualifiedAccessFeature[$key])) { if (0<strlen($out)) $out .= $separator; $out .= wfMsg( 'qaccess-' . $key ); } } if (0<strlen($out)) $output .= $format[1] . wfMsg( 'qaccess-features' ) . $format[2] . $format[3] . $out . $format[4]; } // check groups $out = ''; if ($special && $argv['groups']) { $usergroups = array(); foreach ($wgUser->getEffectiveGroups() as $item) { $usergroups[$item] = true; } foreach ($groups as $item) { if ($usergroups[$item]) { if (0<strlen($out)) $out .= $separator; $out .= wfMsg( 'qaccess-' . $item ); $packet .= "\ngroup: " . $item; } } } elseif (!$special && $argv['groups']) { foreach ($groups as $item) { if (isset($wgQualifiedAccessFeature['groups'][$item])) { if (0<strlen($out)) $out .= $separator; $out .= wfMsg( 'qaccess-' . $item ); } } } if ($argv['groups']) { if (0<strlen($out)) $output .= $format[1] . wfMsg( 'qaccess-groups' ) . $format[2] . $format[3] . $out . $format[4]; elseif ($special) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-groups' ), array( $title ) ) ) . '</div>'; } // check rights $out = ''; if ($special && $argv['rights']) { $userrights = array(); foreach ($wgUser->getRights() as $item) { $userrights[$item] = true; } foreach ($rights as $item) { if ($userrights[$item]) { if (0<strlen($out)) $out .= $separator; $out .= wfMsg( 'qaccess-' . $item ); $packet .= "\nright: " . $item; } } } if (!$special && $argv['rights']) { foreach ($rights as $item) { if (isset($wgQualifiedAccessFeature['rights'][$item])) { if (0<strlen($out)) $out .= $separator; $out .= wfMsg( 'qaccess-' . $item ); } } } if ($argv['rights']) { if (0<strlen($out)) $output .= $format[1] . wfMsg( 'qaccess-rights' ) . $format[2] . $format[3] . $out . $format[4]; elseif ($special) return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-notavailable-rights' ), array( $title ) ) ) . '</div>'; } $output .= $format[5]; if ($special) { // build the packet $cmd = $wgQualifiedAccessCommand . ' '; foreach ($wgQualifiedAccessOptions as $key => $value) { if (is_int($key) && is_string($value)) $cmd .= "--$value "; elseif (is_int($key)) continue; elseif (is_null($value)) $cmd .= "--$key "; elseif (is_bool($value)) $cmd .= "--" . ($value ? '' : 'no-') . "$key "; elseif (is_string($value) || is_int($v)) $cmd .= "--$key '$value' "; elseif (is_array($value)) { foreach ($value as $k => $v) { if (is_int($k) && is_string($v)) $cmd .= "--$key $v "; elseif (is_int($k)) continue; elseif (is_null($v)) $cmd .= "--$key $k "; elseif (is_bool($v)) $cmd .= "--$key " . ($value ? '' : 'no-') . "$k "; elseif (is_string($v) || is_int($v)) $cmd .= "--$key '$k=$v' "; } } } if (preg_match('/(encrypt)/i', $mode)) { $cmd .= "--encrypt --sign "; $cmd .= "--recipient " . escapeshellarg($kid) . " "; $cmd .= "--keyserver " . escapeshellarg($keyserver) . " "; } elseif (preg_match('/(armour)/i', $mode)) $cmd .= "--sign --armour "; else $cmd .= "--clearsign "; $desc = array( 0 => array("pipe", "r"), // stdin 1 => array("pipe", "w"), // stdout 2 => array("pipe", "w"), // stderr ); $enc = ''; $env = array(); $process = proc_open($cmd, $desc, $pipes, $wgQualifiedAccessOptions['homedir'], $env); if (is_resource($process)) { fwrite($pipes[0], $packet); fclose($pipes[0]); $enc = stream_get_contents($pipes[1]); fclose($pipes[1]); $err = stream_get_contents($pipes[2]); fclose($pipes[2]); $ret = proc_close($process); // if we gets anything on the error pipe we assume we have an error and terminates if (strlen($err)) { return '<div class="qaccess-msg">' . $parser->internalParse( wfMsgReplaceArgs( wfMsg( 'qaccess-gpg-error' ), array( $title, $cmd, $err ) ) ) . '</div>'; } } // keep the result for later, we have to bypass tidy $marks = count($efQualifiedAccessMarks); $efQualifiedAccessMarks[$marks] = $enc; // make a sufficient large textarea, note that this will typically have display:none $lines = substr_count($enc, "\n"); // return our form with the report when we are in a special page return '<form action="' . $action . '" method="' . $method . '">' . '<fieldset><legend>' . $title . '</legend>' . '<div class="data" style="float:right;width:35%">' . $output . '<input class="submit" type="submit" value="' . wfMsgReplaceArgs( wfMsg( 'qaccess-submit' ), array( $title ) ) . '" title="' . wfMsg( 'qaccess-submit-warning' ) . '">' . '</div>' . $parser->internalParse("__NOEDITSECTION__" . $input) . '<textarea rows="' . $lines . '" cols="64" readonly>' . 'xx-qaccess-'.$marks.'-xx' . '</textarea>' . '</fieldset></form>'; } else { // we are not in a form, return a description return '<fieldset><legend>' . $title . '</legend>' . '<div class="data" style="float:right;width:35%">' . $output . '</div>' . $parser->internalParse(" __NOEDITSECTION__" . $input) . '</fieldset>'; } }