Date of publication:
Date:
"; }
die($ownsettingserror . PHP_EOL . "Haal bovenstaande settings weg uit je ownsettings.php" . PHP_EOL);
} # if
# Controleer op oud type quicklinks (zonder security)
foreach($settings['quicklinks'] as $link) {
if (count($link) < 5) {
die("Quicklinks moeten voortaan ook een security check bevatten, wijzig je quicklinks in je (own)settings.php (zie settings.php voor een voorbeeld)");
} # if
} # foreach
# Controleer op oud type quicklinks (zonder preference link)
foreach($settings['quicklinks'] as $link) {
if (count($link) < 6) {
die("Quicklinks moeten voortaan ook een voorkeuren check bevatten, wijzig je quicklinks in je (own)settings.php (zie settings.php voor een voorbeeld)");
} # if
} # foreach
debian/db-update/v20111210/Math/ 0000755 0000000 0000000 00000000000 12267045571 013012 5 ustar debian/db-update/v20111210/Math/BigInteger.php 0000644 0000000 0000000 00000355276 12267045571 015564 0 ustar > and << cannot be used, nor can the modulo operator %,
* which only supports integers. Although this fact will slow this library down, the fact that such a high
* base is being used should more than compensate.
*
* When PHP version 6 is officially released, we'll be able to use 64-bit integers. This should, once again,
* allow bitwise operators, and will increase the maximum possible base to 2**31 (or 2**62 for addition /
* subtraction).
*
* Numbers are stored in {@link http://en.wikipedia.org/wiki/Endianness little endian} format. ie.
* (new Math_BigInteger(pow(2, 26)))->value = array(0, 1)
*
* Useful resources are as follows:
*
* - {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf Handbook of Applied Cryptography (HAC)}
* - {@link http://math.libtomcrypt.com/files/tommath.pdf Multi-Precision Math (MPM)}
* - Java's BigInteger classes. See /j2se/src/share/classes/java/math in jdk-1_5_0-src-jrl.zip
*
* Here's an example of how to use this library:
*
* add($b);
*
* echo $c->toString(); // outputs 5
* ?>
*
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Math
* @package Math_BigInteger
* @author Jim Wigginton
* @copyright MMVI Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $
* @link http://pear.php.net/package/Math_BigInteger
*/
/**#@+
* Reduction constants
*
* @access private
* @see Math_BigInteger::_reduce()
*/
/**
* @see Math_BigInteger::_montgomery()
* @see Math_BigInteger::_prepMontgomery()
*/
define('MATH_BIGINTEGER_MONTGOMERY', 0);
/**
* @see Math_BigInteger::_barrett()
*/
define('MATH_BIGINTEGER_BARRETT', 1);
/**
* @see Math_BigInteger::_mod2()
*/
define('MATH_BIGINTEGER_POWEROF2', 2);
/**
* @see Math_BigInteger::_remainder()
*/
define('MATH_BIGINTEGER_CLASSIC', 3);
/**
* @see Math_BigInteger::__clone()
*/
define('MATH_BIGINTEGER_NONE', 4);
/**#@-*/
/**#@+
* Array constants
*
* Rather than create a thousands and thousands of new Math_BigInteger objects in repeated function calls to add() and
* multiply() or whatever, we'll just work directly on arrays, taking them in as parameters and returning them.
*
* @access private
*/
/**
* $result[MATH_BIGINTEGER_VALUE] contains the value.
*/
define('MATH_BIGINTEGER_VALUE', 0);
/**
* $result[MATH_BIGINTEGER_SIGN] contains the sign.
*/
define('MATH_BIGINTEGER_SIGN', 1);
/**#@-*/
/**#@+
* @access private
* @see Math_BigInteger::_montgomery()
* @see Math_BigInteger::_barrett()
*/
/**
* Cache constants
*
* $cache[MATH_BIGINTEGER_VARIABLE] tells us whether or not the cached data is still valid.
*/
define('MATH_BIGINTEGER_VARIABLE', 0);
/**
* $cache[MATH_BIGINTEGER_DATA] contains the cached data.
*/
define('MATH_BIGINTEGER_DATA', 1);
/**#@-*/
/**#@+
* Mode constants.
*
* @access private
* @see Math_BigInteger::Math_BigInteger()
*/
/**
* To use the pure-PHP implementation
*/
define('MATH_BIGINTEGER_MODE_INTERNAL', 1);
/**
* To use the BCMath library
*
* (if enabled; otherwise, the internal implementation will be used)
*/
define('MATH_BIGINTEGER_MODE_BCMATH', 2);
/**
* To use the GMP library
*
* (if present; otherwise, either the BCMath or the internal implementation will be used)
*/
define('MATH_BIGINTEGER_MODE_GMP', 3);
/**#@-*/
/**
* The largest digit that may be used in addition / subtraction
*
* (we do pow(2, 52) instead of using 4503599627370496, directly, because some PHP installations
* will truncate 4503599627370496)
*
* @access private
*/
define('MATH_BIGINTEGER_MAX_DIGIT52', pow(2, 52));
/**
* Karatsuba Cutoff
*
* At what point do we switch between Karatsuba multiplication and schoolbook long multiplication?
*
* @access private
*/
define('MATH_BIGINTEGER_KARATSUBA_CUTOFF', 25);
/**
* Pure-PHP arbitrary precision integer arithmetic library. Supports base-2, base-10, base-16, and base-256
* numbers.
*
* @author Jim Wigginton
* @version 1.0.0RC4
* @access public
* @package Math_BigInteger
*/
class Math_BigInteger {
/**
* Holds the BigInteger's value.
*
* @var Array
* @access private
*/
var $value;
/**
* Holds the BigInteger's magnitude.
*
* @var Boolean
* @access private
*/
var $is_negative = false;
/**
* Random number generator function
*
* @see setRandomGenerator()
* @access private
*/
var $generator = 'mt_rand';
/**
* Precision
*
* @see setPrecision()
* @access private
*/
var $precision = -1;
/**
* Precision Bitmask
*
* @see setPrecision()
* @access private
*/
var $bitmask = false;
/**
* Mode independant value used for serialization.
*
* If the bcmath or gmp extensions are installed $this->value will be a non-serializable resource, hence the need for
* a variable that'll be serializable regardless of whether or not extensions are being used. Unlike $this->value,
* however, $this->hex is only calculated when $this->__sleep() is called.
*
* @see __sleep()
* @see __wakeup()
* @var String
* @access private
*/
var $hex;
/**
* Converts base-2, base-10, base-16, and binary strings (eg. base-256) to BigIntegers.
*
* If the second parameter - $base - is negative, then it will be assumed that the number's are encoded using
* two's compliment. The sole exception to this is -10, which is treated the same as 10 is.
*
* Here's an example:
*
* toString(); // outputs 50
* ?>
*
*
* @param optional $x base-10 number or base-$base number if $base set.
* @param optional integer $base
* @return Math_BigInteger
* @access public
*/
function Math_BigInteger($x = 0, $base = 10)
{
if ( !defined('MATH_BIGINTEGER_MODE') ) {
switch (true) {
case extension_loaded('gmp'):
define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_GMP);
break;
case extension_loaded('bcmath'):
define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_BCMATH);
break;
default:
define('MATH_BIGINTEGER_MODE', MATH_BIGINTEGER_MODE_INTERNAL);
}
}
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
if (is_resource($x) && get_resource_type($x) == 'GMP integer') {
$this->value = $x;
return;
}
$this->value = gmp_init(0);
break;
case MATH_BIGINTEGER_MODE_BCMATH:
$this->value = '0';
break;
default:
$this->value = array();
}
if (empty($x)) {
return;
}
switch ($base) {
case -256:
if (ord($x[0]) & 0x80) {
$x = ~$x;
$this->is_negative = true;
}
case 256:
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$sign = $this->is_negative ? '-' : '';
$this->value = gmp_init($sign . '0x' . bin2hex($x));
break;
case MATH_BIGINTEGER_MODE_BCMATH:
// round $len to the nearest 4 (thanks, DavidMJ!)
$len = (strlen($x) + 3) & 0xFFFFFFFC;
$x = str_pad($x, $len, chr(0), STR_PAD_LEFT);
for ($i = 0; $i < $len; $i+= 4) {
$this->value = bcmul($this->value, '4294967296', 0); // 4294967296 == 2**32
$this->value = bcadd($this->value, 0x1000000 * ord($x[$i]) + ((ord($x[$i + 1]) << 16) | (ord($x[$i + 2]) << 8) | ord($x[$i + 3])), 0);
}
if ($this->is_negative) {
$this->value = '-' . $this->value;
}
break;
// converts a base-2**8 (big endian / msb) number to base-2**26 (little endian / lsb)
default:
while (strlen($x)) {
$this->value[] = $this->_bytes2int($this->_base256_rshift($x, 26));
}
}
if ($this->is_negative) {
if (MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL) {
$this->is_negative = false;
}
$temp = $this->add(new Math_BigInteger('-1'));
$this->value = $temp->value;
}
break;
case 16:
case -16:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^(?:0x)?([A-Fa-f0-9]*).*#', '$1', $x);
$is_negative = false;
if ($base < 0 && hexdec($x[0]) >= 8) {
$this->is_negative = $is_negative = true;
$x = bin2hex(~pack('H*', $x));
}
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = $this->is_negative ? '-0x' . $x : '0x' . $x;
$this->value = gmp_init($temp);
$this->is_negative = false;
break;
case MATH_BIGINTEGER_MODE_BCMATH:
$x = ( strlen($x) & 1 ) ? '0' . $x : $x;
$temp = new Math_BigInteger(pack('H*', $x), 256);
$this->value = $this->is_negative ? '-' . $temp->value : $temp->value;
$this->is_negative = false;
break;
default:
$x = ( strlen($x) & 1 ) ? '0' . $x : $x;
$temp = new Math_BigInteger(pack('H*', $x), 256);
$this->value = $temp->value;
}
if ($is_negative) {
$temp = $this->add(new Math_BigInteger('-1'));
$this->value = $temp->value;
}
break;
case 10:
case -10:
$x = preg_replace('#^(-?[0-9]*).*#', '$1', $x);
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$this->value = gmp_init($x);
break;
case MATH_BIGINTEGER_MODE_BCMATH:
// explicitly casting $x to a string is necessary, here, since doing $x[0] on -1 yields different
// results then doing it on '-1' does (modInverse does $x[0])
$this->value = (string) $x;
break;
default:
$temp = new Math_BigInteger();
// array(10000000) is 10**7 in base-2**26. 10**7 is the closest to 2**26 we can get without passing it.
$multiplier = new Math_BigInteger();
$multiplier->value = array(10000000);
if ($x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = str_pad($x, strlen($x) + (6 * strlen($x)) % 7, 0, STR_PAD_LEFT);
while (strlen($x)) {
$temp = $temp->multiply($multiplier);
$temp = $temp->add(new Math_BigInteger($this->_int2bytes(substr($x, 0, 7)), 256));
$x = substr($x, 7);
}
$this->value = $temp->value;
}
break;
case 2: // base-2 support originally implemented by Lluis Pamies - thanks!
case -2:
if ($base > 0 && $x[0] == '-') {
$this->is_negative = true;
$x = substr($x, 1);
}
$x = preg_replace('#^([01]*).*#', '$1', $x);
$x = str_pad($x, strlen($x) + (3 * strlen($x)) % 4, 0, STR_PAD_LEFT);
$str = '0x';
while (strlen($x)) {
$part = substr($x, 0, 4);
$str.= dechex(bindec($part));
$x = substr($x, 4);
}
if ($this->is_negative) {
$str = '-' . $str;
}
$temp = new Math_BigInteger($str, 8 * $base); // ie. either -16 or +16
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
break;
default:
// base not supported, so we'll let $this == 0
}
}
/**
* Converts a BigInteger to a byte string (eg. base-256).
*
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
*
* toBytes(); // outputs chr(65)
* ?>
*
*
* @param Boolean $twos_compliment
* @return String
* @access public
* @internal Converts a base-2**26 number to base-2**8
*/
function toBytes($twos_compliment = false)
{
if ($twos_compliment) {
$comparison = $this->compare(new Math_BigInteger());
if ($comparison == 0) {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}
$temp = $comparison < 0 ? $this->add(new Math_BigInteger(1)) : $this->copy();
$bytes = $temp->toBytes();
if (empty($bytes)) { // eg. if the number we're trying to convert is -1
$bytes = chr(0);
}
if (ord($bytes[0]) & 0x80) {
$bytes = chr(0) . $bytes;
}
return $comparison < 0 ? ~$bytes : $bytes;
}
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
if (gmp_cmp($this->value, gmp_init(0)) == 0) {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}
$temp = gmp_strval(gmp_abs($this->value), 16);
$temp = ( strlen($temp) & 1 ) ? '0' . $temp : $temp;
$temp = pack('H*', $temp);
return $this->precision > 0 ?
substr(str_pad($temp, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($temp, chr(0));
case MATH_BIGINTEGER_MODE_BCMATH:
if ($this->value === '0') {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}
$value = '';
$current = $this->value;
if ($current[0] == '-') {
$current = substr($current, 1);
}
while (bccomp($current, '0', 0) > 0) {
$temp = bcmod($current, '16777216');
$value = chr($temp >> 16) . chr($temp >> 8) . chr($temp) . $value;
$current = bcdiv($current, '16777216', 0);
}
return $this->precision > 0 ?
substr(str_pad($value, $this->precision >> 3, chr(0), STR_PAD_LEFT), -($this->precision >> 3)) :
ltrim($value, chr(0));
}
if (!count($this->value)) {
return $this->precision > 0 ? str_repeat(chr(0), ($this->precision + 1) >> 3) : '';
}
$result = $this->_int2bytes($this->value[count($this->value) - 1]);
$temp = $this->copy();
for ($i = count($temp->value) - 2; $i >= 0; --$i) {
$temp->_base256_lshift($result, 26);
$result = $result | str_pad($temp->_int2bytes($temp->value[$i]), strlen($result), chr(0), STR_PAD_LEFT);
}
return $this->precision > 0 ?
str_pad(substr($result, -(($this->precision + 7) >> 3)), ($this->precision + 7) >> 3, chr(0), STR_PAD_LEFT) :
$result;
}
/**
* Converts a BigInteger to a hex string (eg. base-16)).
*
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
*
* toHex(); // outputs '41'
* ?>
*
*
* @param Boolean $twos_compliment
* @return String
* @access public
* @internal Converts a base-2**26 number to base-2**8
*/
function toHex($twos_compliment = false)
{
return bin2hex($this->toBytes($twos_compliment));
}
/**
* Converts a BigInteger to a bit string (eg. base-2).
*
* Negative numbers are saved as positive numbers, unless $twos_compliment is set to true, at which point, they're
* saved as two's compliment.
*
* Here's an example:
*
* toBits(); // outputs '1000001'
* ?>
*
*
* @param Boolean $twos_compliment
* @return String
* @access public
* @internal Converts a base-2**26 number to base-2**2
*/
function toBits($twos_compliment = false)
{
$hex = $this->toHex($twos_compliment);
$bits = '';
for ($i = 0; $i < strlen($hex); $i+=8) {
$bits.= str_pad(decbin(hexdec(substr($hex, $i, 8))), 32, '0', STR_PAD_LEFT);
}
return $this->precision > 0 ? substr($bits, -$this->precision) : ltrim($bits, '0');
}
/**
* Converts a BigInteger to a base-10 number.
*
* Here's an example:
*
* toString(); // outputs 50
* ?>
*
*
* @return String
* @access public
* @internal Converts a base-2**26 number to base-10**7 (which is pretty much base-10)
*/
function toString()
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
return gmp_strval($this->value);
case MATH_BIGINTEGER_MODE_BCMATH:
if ($this->value === '0') {
return '0';
}
return ltrim($this->value, '0');
}
if (!count($this->value)) {
return '0';
}
$temp = $this->copy();
$temp->is_negative = false;
$divisor = new Math_BigInteger();
$divisor->value = array(10000000); // eg. 10**7
$result = '';
while (count($temp->value)) {
list($temp, $mod) = $temp->divide($divisor);
$result = str_pad(isset($mod->value[0]) ? $mod->value[0] : '', 7, '0', STR_PAD_LEFT) . $result;
}
$result = ltrim($result, '0');
if (empty($result)) {
$result = '0';
}
if ($this->is_negative) {
$result = '-' . $result;
}
return $result;
}
/**
* Copy an object
*
* PHP5 passes objects by reference while PHP4 passes by value. As such, we need a function to guarantee
* that all objects are passed by value, when appropriate. More information can be found here:
*
* {@link http://php.net/language.oop5.basic#51624}
*
* @access public
* @see __clone()
* @return Math_BigInteger
*/
function copy()
{
$temp = new Math_BigInteger();
$temp->value = $this->value;
$temp->is_negative = $this->is_negative;
$temp->generator = $this->generator;
$temp->precision = $this->precision;
$temp->bitmask = $this->bitmask;
return $temp;
}
/**
* __toString() magic method
*
* Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call
* toString().
*
* @access public
* @internal Implemented per a suggestion by Techie-Michael - thanks!
*/
function __toString()
{
return $this->toString();
}
/**
* __clone() magic method
*
* Although you can call Math_BigInteger::__toString() directly in PHP5, you cannot call Math_BigInteger::__clone()
* directly in PHP5. You can in PHP4 since it's not a magic method, but in PHP5, you have to call it by using the PHP5
* only syntax of $y = clone $x. As such, if you're trying to write an application that works on both PHP4 and PHP5,
* call Math_BigInteger::copy(), instead.
*
* @access public
* @see copy()
* @return Math_BigInteger
*/
function __clone()
{
return $this->copy();
}
/**
* __sleep() magic method
*
* Will be called, automatically, when serialize() is called on a Math_BigInteger object.
*
* @see __wakeup()
* @access public
*/
function __sleep()
{
$this->hex = $this->toHex(true);
$vars = array('hex');
if ($this->generator != 'mt_rand') {
$vars[] = 'generator';
}
if ($this->precision > 0) {
$vars[] = 'precision';
}
return $vars;
}
/**
* __wakeup() magic method
*
* Will be called, automatically, when unserialize() is called on a Math_BigInteger object.
*
* @see __sleep()
* @access public
*/
function __wakeup()
{
$temp = new Math_BigInteger($this->hex, -16);
$this->value = $temp->value;
$this->is_negative = $temp->is_negative;
$this->setRandomGenerator($this->generator);
if ($this->precision > 0) {
// recalculate $this->bitmask
$this->setPrecision($this->precision);
}
}
/**
* Adds two BigIntegers.
*
* Here's an example:
*
* add($b);
*
* echo $c->toString(); // outputs 30
* ?>
*
*
* @param Math_BigInteger $y
* @return Math_BigInteger
* @access public
* @internal Performs base-2**52 addition
*/
function add($y)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_add($this->value, $y->value);
return $this->_normalize($temp);
case MATH_BIGINTEGER_MODE_BCMATH:
$temp = new Math_BigInteger();
$temp->value = bcadd($this->value, $y->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_add($this->value, $this->is_negative, $y->value, $y->is_negative);
$result = new Math_BigInteger();
$result->value = $temp[MATH_BIGINTEGER_VALUE];
$result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
return $this->_normalize($result);
}
/**
* Performs addition.
*
* @param Array $x_value
* @param Boolean $x_negative
* @param Array $y_value
* @param Boolean $y_negative
* @return Array
* @access private
*/
function _add($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return array(
MATH_BIGINTEGER_VALUE => $y_value,
MATH_BIGINTEGER_SIGN => $y_negative
);
} else if ($y_size == 0) {
return array(
MATH_BIGINTEGER_VALUE => $x_value,
MATH_BIGINTEGER_SIGN => $x_negative
);
}
// subtract, if appropriate
if ( $x_negative != $y_negative ) {
if ( $x_value == $y_value ) {
return array(
MATH_BIGINTEGER_VALUE => array(),
MATH_BIGINTEGER_SIGN => false
);
}
$temp = $this->_subtract($x_value, false, $y_value, false);
$temp[MATH_BIGINTEGER_SIGN] = $this->_compare($x_value, false, $y_value, false) > 0 ?
$x_negative : $y_negative;
return $temp;
}
if ($x_size < $y_size) {
$size = $x_size;
$value = $y_value;
} else {
$size = $y_size;
$value = $x_value;
}
$value[] = 0; // just in case the carry adds an extra digit
$carry = 0;
for ($i = 0, $j = 1; $j < $size; $i+=2, $j+=2) {
$sum = $x_value[$j] * 0x4000000 + $x_value[$i] + $y_value[$j] * 0x4000000 + $y_value[$i] + $carry;
$carry = $sum >= MATH_BIGINTEGER_MAX_DIGIT52; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
$sum = $carry ? $sum - MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
$temp = (int) ($sum / 0x4000000);
$value[$i] = (int) ($sum - 0x4000000 * $temp); // eg. a faster alternative to fmod($sum, 0x4000000)
$value[$j] = $temp;
}
if ($j == $size) { // ie. if $y_size is odd
$sum = $x_value[$i] + $y_value[$i] + $carry;
$carry = $sum >= 0x4000000;
$value[$i] = $carry ? $sum - 0x4000000 : $sum;
++$i; // ie. let $i = $j since we've just done $value[$i]
}
if ($carry) {
for (; $value[$i] == 0x3FFFFFF; ++$i) {
$value[$i] = 0;
}
++$value[$i];
}
return array(
MATH_BIGINTEGER_VALUE => $this->_trim($value),
MATH_BIGINTEGER_SIGN => $x_negative
);
}
/**
* Subtracts two BigIntegers.
*
* Here's an example:
*
* subtract($b);
*
* echo $c->toString(); // outputs -10
* ?>
*
*
* @param Math_BigInteger $y
* @return Math_BigInteger
* @access public
* @internal Performs base-2**52 subtraction
*/
function subtract($y)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_sub($this->value, $y->value);
return $this->_normalize($temp);
case MATH_BIGINTEGER_MODE_BCMATH:
$temp = new Math_BigInteger();
$temp->value = bcsub($this->value, $y->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_subtract($this->value, $this->is_negative, $y->value, $y->is_negative);
$result = new Math_BigInteger();
$result->value = $temp[MATH_BIGINTEGER_VALUE];
$result->is_negative = $temp[MATH_BIGINTEGER_SIGN];
return $this->_normalize($result);
}
/**
* Performs subtraction.
*
* @param Array $x_value
* @param Boolean $x_negative
* @param Array $y_value
* @param Boolean $y_negative
* @return Array
* @access private
*/
function _subtract($x_value, $x_negative, $y_value, $y_negative)
{
$x_size = count($x_value);
$y_size = count($y_value);
if ($x_size == 0) {
return array(
MATH_BIGINTEGER_VALUE => $y_value,
MATH_BIGINTEGER_SIGN => !$y_negative
);
} else if ($y_size == 0) {
return array(
MATH_BIGINTEGER_VALUE => $x_value,
MATH_BIGINTEGER_SIGN => $x_negative
);
}
// add, if appropriate (ie. -$x - +$y or +$x - -$y)
if ( $x_negative != $y_negative ) {
$temp = $this->_add($x_value, false, $y_value, false);
$temp[MATH_BIGINTEGER_SIGN] = $x_negative;
return $temp;
}
$diff = $this->_compare($x_value, $x_negative, $y_value, $y_negative);
if ( !$diff ) {
return array(
MATH_BIGINTEGER_VALUE => array(),
MATH_BIGINTEGER_SIGN => false
);
}
// switch $x and $y around, if appropriate.
if ( (!$x_negative && $diff < 0) || ($x_negative && $diff > 0) ) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_negative = !$x_negative;
$x_size = count($x_value);
$y_size = count($y_value);
}
// at this point, $x_value should be at least as big as - if not bigger than - $y_value
$carry = 0;
for ($i = 0, $j = 1; $j < $y_size; $i+=2, $j+=2) {
$sum = $x_value[$j] * 0x4000000 + $x_value[$i] - $y_value[$j] * 0x4000000 - $y_value[$i] - $carry;
$carry = $sum < 0; // eg. floor($sum / 2**52); only possible values (in any base) are 0 and 1
$sum = $carry ? $sum + MATH_BIGINTEGER_MAX_DIGIT52 : $sum;
$temp = (int) ($sum / 0x4000000);
$x_value[$i] = (int) ($sum - 0x4000000 * $temp);
$x_value[$j] = $temp;
}
if ($j == $y_size) { // ie. if $y_size is odd
$sum = $x_value[$i] - $y_value[$i] - $carry;
$carry = $sum < 0;
$x_value[$i] = $carry ? $sum + 0x4000000 : $sum;
++$i;
}
if ($carry) {
for (; !$x_value[$i]; ++$i) {
$x_value[$i] = 0x3FFFFFF;
}
--$x_value[$i];
}
return array(
MATH_BIGINTEGER_VALUE => $this->_trim($x_value),
MATH_BIGINTEGER_SIGN => $x_negative
);
}
/**
* Multiplies two BigIntegers
*
* Here's an example:
*
* multiply($b);
*
* echo $c->toString(); // outputs 200
* ?>
*
*
* @param Math_BigInteger $x
* @return Math_BigInteger
* @access public
*/
function multiply($x)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_mul($this->value, $x->value);
return $this->_normalize($temp);
case MATH_BIGINTEGER_MODE_BCMATH:
$temp = new Math_BigInteger();
$temp->value = bcmul($this->value, $x->value, 0);
return $this->_normalize($temp);
}
$temp = $this->_multiply($this->value, $this->is_negative, $x->value, $x->is_negative);
$product = new Math_BigInteger();
$product->value = $temp[MATH_BIGINTEGER_VALUE];
$product->is_negative = $temp[MATH_BIGINTEGER_SIGN];
return $this->_normalize($product);
}
/**
* Performs multiplication.
*
* @param Array $x_value
* @param Boolean $x_negative
* @param Array $y_value
* @param Boolean $y_negative
* @return Array
* @access private
*/
function _multiply($x_value, $x_negative, $y_value, $y_negative)
{
//if ( $x_value == $y_value ) {
// return array(
// MATH_BIGINTEGER_VALUE => $this->_square($x_value),
// MATH_BIGINTEGER_SIGN => $x_sign != $y_value
// );
//}
$x_length = count($x_value);
$y_length = count($y_value);
if ( !$x_length || !$y_length ) { // a 0 is being multiplied
return array(
MATH_BIGINTEGER_VALUE => array(),
MATH_BIGINTEGER_SIGN => false
);
}
return array(
MATH_BIGINTEGER_VALUE => min($x_length, $y_length) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
$this->_trim($this->_regularMultiply($x_value, $y_value)) :
$this->_trim($this->_karatsuba($x_value, $y_value)),
MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
);
}
/**
* Performs long multiplication on two BigIntegers
*
* Modeled after 'multiply' in MutableBigInteger.java.
*
* @param Array $x_value
* @param Array $y_value
* @return Array
* @access private
*/
function _regularMultiply($x_value, $y_value)
{
$x_length = count($x_value);
$y_length = count($y_value);
if ( !$x_length || !$y_length ) { // a 0 is being multiplied
return array();
}
if ( $x_length < $y_length ) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_length = count($x_value);
$y_length = count($y_value);
}
$product_value = $this->_array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary adds,
// since on the outermost loops first pass, $product->value[$k] is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
$carry = (int) ($temp / 0x4000000);
$product_value[$j] = (int) ($temp - 0x4000000 * $carry);
}
$product_value[$j] = $carry;
// the above for loop is what the previous comment was talking about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
$carry = (int) ($temp / 0x4000000);
$product_value[$k] = (int) ($temp - 0x4000000 * $carry);
}
$product_value[$k] = $carry;
}
return $product_value;
}
/**
* Performs Karatsuba multiplication on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=120 MPM 5.2.3}.
*
* @param Array $x_value
* @param Array $y_value
* @return Array
* @access private
*/
function _karatsuba($x_value, $y_value)
{
$m = min(count($x_value) >> 1, count($y_value) >> 1);
if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
return $this->_regularMultiply($x_value, $y_value);
}
$x1 = array_slice($x_value, $m);
$x0 = array_slice($x_value, 0, $m);
$y1 = array_slice($y_value, $m);
$y0 = array_slice($y_value, 0, $m);
$z2 = $this->_karatsuba($x1, $y1);
$z0 = $this->_karatsuba($x0, $y0);
$z1 = $this->_add($x1, false, $x0, false);
$temp = $this->_add($y1, false, $y0, false);
$z1 = $this->_karatsuba($z1[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
$xy = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
$xy = $this->_add($xy[MATH_BIGINTEGER_VALUE], $xy[MATH_BIGINTEGER_SIGN], $z0, false);
return $xy[MATH_BIGINTEGER_VALUE];
}
/**
* Performs squaring
*
* @param Array $x
* @return Array
* @access private
*/
function _square($x = false)
{
return count($x) < 2 * MATH_BIGINTEGER_KARATSUBA_CUTOFF ?
$this->_trim($this->_baseSquare($x)) :
$this->_trim($this->_karatsubaSquare($x));
}
/**
* Performs traditional squaring on two BigIntegers
*
* Squaring can be done faster than multiplying a number by itself can be. See
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=7 HAC 14.2.4} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=141 MPM 5.3} for more information.
*
* @param Array $value
* @return Array
* @access private
*/
function _baseSquare($value)
{
if ( empty($value) ) {
return array();
}
$square_value = $this->_array_repeat(0, 2 * count($value));
for ($i = 0, $max_index = count($value) - 1; $i <= $max_index; ++$i) {
$i2 = $i << 1;
$temp = $square_value[$i2] + $value[$i] * $value[$i];
$carry = (int) ($temp / 0x4000000);
$square_value[$i2] = (int) ($temp - 0x4000000 * $carry);
// note how we start from $i+1 instead of 0 as we do in multiplication.
for ($j = $i + 1, $k = $i2 + 1; $j <= $max_index; ++$j, ++$k) {
$temp = $square_value[$k] + 2 * $value[$j] * $value[$i] + $carry;
$carry = (int) ($temp / 0x4000000);
$square_value[$k] = (int) ($temp - 0x4000000 * $carry);
}
// the following line can yield values larger 2**15. at this point, PHP should switch
// over to floats.
$square_value[$i + $max_index + 1] = $carry;
}
return $square_value;
}
/**
* Performs Karatsuba "squaring" on two BigIntegers
*
* See {@link http://en.wikipedia.org/wiki/Karatsuba_algorithm Karatsuba algorithm} and
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=151 MPM 5.3.4}.
*
* @param Array $value
* @return Array
* @access private
*/
function _karatsubaSquare($value)
{
$m = count($value) >> 1;
if ($m < MATH_BIGINTEGER_KARATSUBA_CUTOFF) {
return $this->_baseSquare($value);
}
$x1 = array_slice($value, $m);
$x0 = array_slice($value, 0, $m);
$z2 = $this->_karatsubaSquare($x1);
$z0 = $this->_karatsubaSquare($x0);
$z1 = $this->_add($x1, false, $x0, false);
$z1 = $this->_karatsubaSquare($z1[MATH_BIGINTEGER_VALUE]);
$temp = $this->_add($z2, false, $z0, false);
$z1 = $this->_subtract($z1, false, $temp[MATH_BIGINTEGER_VALUE], false);
$z2 = array_merge(array_fill(0, 2 * $m, 0), $z2);
$z1[MATH_BIGINTEGER_VALUE] = array_merge(array_fill(0, $m, 0), $z1[MATH_BIGINTEGER_VALUE]);
$xx = $this->_add($z2, false, $z1[MATH_BIGINTEGER_VALUE], $z1[MATH_BIGINTEGER_SIGN]);
$xx = $this->_add($xx[MATH_BIGINTEGER_VALUE], $xx[MATH_BIGINTEGER_SIGN], $z0, false);
return $xx[MATH_BIGINTEGER_VALUE];
}
/**
* Divides two BigIntegers.
*
* Returns an array whose first element contains the quotient and whose second element contains the
* "common residue". If the remainder would be positive, the "common residue" and the remainder are the
* same. If the remainder would be negative, the "common residue" is equal to the sum of the remainder
* and the divisor (basically, the "common residue" is the first positive modulo).
*
* Here's an example:
*
* divide($b);
*
* echo $quotient->toString(); // outputs 0
* echo "\r\n";
* echo $remainder->toString(); // outputs 10
* ?>
*
*
* @param Math_BigInteger $y
* @return Array
* @access public
* @internal This function is based off of {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=9 HAC 14.20}.
*/
function divide($y)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$quotient = new Math_BigInteger();
$remainder = new Math_BigInteger();
list($quotient->value, $remainder->value) = gmp_div_qr($this->value, $y->value);
if (gmp_sign($remainder->value) < 0) {
$remainder->value = gmp_add($remainder->value, gmp_abs($y->value));
}
return array($this->_normalize($quotient), $this->_normalize($remainder));
case MATH_BIGINTEGER_MODE_BCMATH:
$quotient = new Math_BigInteger();
$remainder = new Math_BigInteger();
$quotient->value = bcdiv($this->value, $y->value, 0);
$remainder->value = bcmod($this->value, $y->value);
if ($remainder->value[0] == '-') {
$remainder->value = bcadd($remainder->value, $y->value[0] == '-' ? substr($y->value, 1) : $y->value, 0);
}
return array($this->_normalize($quotient), $this->_normalize($remainder));
}
if (count($y->value) == 1) {
list($q, $r) = $this->_divide_digit($this->value, $y->value[0]);
$quotient = new Math_BigInteger();
$remainder = new Math_BigInteger();
$quotient->value = $q;
$remainder->value = array($r);
$quotient->is_negative = $this->is_negative != $y->is_negative;
return array($this->_normalize($quotient), $this->_normalize($remainder));
}
static $zero;
if ( !isset($zero) ) {
$zero = new Math_BigInteger();
}
$x = $this->copy();
$y = $y->copy();
$x_sign = $x->is_negative;
$y_sign = $y->is_negative;
$x->is_negative = $y->is_negative = false;
$diff = $x->compare($y);
if ( !$diff ) {
$temp = new Math_BigInteger();
$temp->value = array(1);
$temp->is_negative = $x_sign != $y_sign;
return array($this->_normalize($temp), $this->_normalize(new Math_BigInteger()));
}
if ( $diff < 0 ) {
// if $x is negative, "add" $y.
if ( $x_sign ) {
$x = $y->subtract($x);
}
return array($this->_normalize(new Math_BigInteger()), $this->_normalize($x));
}
// normalize $x and $y as described in HAC 14.23 / 14.24
$msb = $y->value[count($y->value) - 1];
for ($shift = 0; !($msb & 0x2000000); ++$shift) {
$msb <<= 1;
}
$x->_lshift($shift);
$y->_lshift($shift);
$y_value = &$y->value;
$x_max = count($x->value) - 1;
$y_max = count($y->value) - 1;
$quotient = new Math_BigInteger();
$quotient_value = &$quotient->value;
$quotient_value = $this->_array_repeat(0, $x_max - $y_max + 1);
static $temp, $lhs, $rhs;
if (!isset($temp)) {
$temp = new Math_BigInteger();
$lhs = new Math_BigInteger();
$rhs = new Math_BigInteger();
}
$temp_value = &$temp->value;
$rhs_value = &$rhs->value;
// $temp = $y << ($x_max - $y_max-1) in base 2**26
$temp_value = array_merge($this->_array_repeat(0, $x_max - $y_max), $y_value);
while ( $x->compare($temp) >= 0 ) {
// calculate the "common residue"
++$quotient_value[$x_max - $y_max];
$x = $x->subtract($temp);
$x_max = count($x->value) - 1;
}
for ($i = $x_max; $i >= $y_max + 1; --$i) {
$x_value = &$x->value;
$x_window = array(
isset($x_value[$i]) ? $x_value[$i] : 0,
isset($x_value[$i - 1]) ? $x_value[$i - 1] : 0,
isset($x_value[$i - 2]) ? $x_value[$i - 2] : 0
);
$y_window = array(
$y_value[$y_max],
( $y_max > 0 ) ? $y_value[$y_max - 1] : 0
);
$q_index = $i - $y_max - 1;
if ($x_window[0] == $y_window[0]) {
$quotient_value[$q_index] = 0x3FFFFFF;
} else {
$quotient_value[$q_index] = (int) (
($x_window[0] * 0x4000000 + $x_window[1])
/
$y_window[0]
);
}
$temp_value = array($y_window[1], $y_window[0]);
$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);
$rhs_value = array($x_window[2], $x_window[1], $x_window[0]);
while ( $lhs->compare($rhs) > 0 ) {
--$quotient_value[$q_index];
$lhs->value = array($quotient_value[$q_index]);
$lhs = $lhs->multiply($temp);
}
$adjust = $this->_array_repeat(0, $q_index);
$temp_value = array($quotient_value[$q_index]);
$temp = $temp->multiply($y);
$temp_value = &$temp->value;
$temp_value = array_merge($adjust, $temp_value);
$x = $x->subtract($temp);
if ($x->compare($zero) < 0) {
$temp_value = array_merge($adjust, $y_value);
$x = $x->add($temp);
--$quotient_value[$q_index];
}
$x_max = count($x_value) - 1;
}
// unnormalize the remainder
$x->_rshift($shift);
$quotient->is_negative = $x_sign != $y_sign;
// calculate the "common residue", if appropriate
if ( $x_sign ) {
$y->_rshift($shift);
$x = $y->subtract($x);
}
return array($this->_normalize($quotient), $this->_normalize($x));
}
/**
* Divides a BigInteger by a regular integer
*
* abc / x = a00 / x + b0 / x + c / x
*
* @param Array $dividend
* @param Array $divisor
* @return Array
* @access private
*/
function _divide_digit($dividend, $divisor)
{
$carry = 0;
$result = array();
for ($i = count($dividend) - 1; $i >= 0; --$i) {
$temp = 0x4000000 * $carry + $dividend[$i];
$result[$i] = (int) ($temp / $divisor);
$carry = (int) ($temp - $divisor * $result[$i]);
}
return array($result, $carry);
}
/**
* Performs modular exponentiation.
*
* Here's an example:
*
* modPow($b, $c);
*
* echo $c->toString(); // outputs 10
* ?>
*
*
* @param Math_BigInteger $e
* @param Math_BigInteger $n
* @return Math_BigInteger
* @access public
* @internal The most naive approach to modular exponentiation has very unreasonable requirements, and
* and although the approach involving repeated squaring does vastly better, it, too, is impractical
* for our purposes. The reason being that division - by far the most complicated and time-consuming
* of the basic operations (eg. +,-,*,/) - occurs multiple times within it.
*
* Modular reductions resolve this issue. Although an individual modular reduction takes more time
* then an individual division, when performed in succession (with the same modulo), they're a lot faster.
*
* The two most commonly used modular reductions are Barrett and Montgomery reduction. Montgomery reduction,
* although faster, only works when the gcd of the modulo and of the base being used is 1. In RSA, when the
* base is a power of two, the modulo - a product of two primes - is always going to have a gcd of 1 (because
* the product of two odd numbers is odd), but what about when RSA isn't used?
*
* In contrast, Barrett reduction has no such constraint. As such, some bigint implementations perform a
* Barrett reduction after every operation in the modpow function. Others perform Barrett reductions when the
* modulo is even and Montgomery reductions when the modulo is odd. BigInteger.java's modPow method, however,
* uses a trick involving the Chinese Remainder Theorem to factor the even modulo into two numbers - one odd and
* the other, a power of two - and recombine them, later. This is the method that this modPow function uses.
* {@link http://islab.oregonstate.edu/papers/j34monex.pdf Montgomery Reduction with Even Modulus} elaborates.
*/
function modPow($e, $n)
{
$n = $this->bitmask !== false && $this->bitmask->compare($n) < 0 ? $this->bitmask : $n->abs();
if ($e->compare(new Math_BigInteger()) < 0) {
$e = $e->abs();
$temp = $this->modInverse($n);
if ($temp === false) {
return false;
}
return $this->_normalize($temp->modPow($e, $n));
}
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_powm($this->value, $e->value, $n->value);
return $this->_normalize($temp);
case MATH_BIGINTEGER_MODE_BCMATH:
$temp = new Math_BigInteger();
$temp->value = bcpowmod($this->value, $e->value, $n->value, 0);
return $this->_normalize($temp);
}
if ( empty($e->value) ) {
$temp = new Math_BigInteger();
$temp->value = array(1);
return $this->_normalize($temp);
}
if ( $e->value == array(1) ) {
list(, $temp) = $this->divide($n);
return $this->_normalize($temp);
}
if ( $e->value == array(2) ) {
$temp = new Math_BigInteger();
$temp->value = $this->_square($this->value);
list(, $temp) = $temp->divide($n);
return $this->_normalize($temp);
}
return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_BARRETT));
// is the modulo odd?
if ( $n->value[0] & 1 ) {
return $this->_normalize($this->_slidingWindow($e, $n, MATH_BIGINTEGER_MONTGOMERY));
}
// if it's not, it's even
// find the lowest set bit (eg. the max pow of 2 that divides $n)
for ($i = 0; $i < count($n->value); ++$i) {
if ( $n->value[$i] ) {
$temp = decbin($n->value[$i]);
$j = strlen($temp) - strrpos($temp, '1') - 1;
$j+= 26 * $i;
break;
}
}
// at this point, 2^$j * $n/(2^$j) == $n
$mod1 = $n->copy();
$mod1->_rshift($j);
$mod2 = new Math_BigInteger();
$mod2->value = array(1);
$mod2->_lshift($j);
$part1 = ( $mod1->value != array(1) ) ? $this->_slidingWindow($e, $mod1, MATH_BIGINTEGER_MONTGOMERY) : new Math_BigInteger();
$part2 = $this->_slidingWindow($e, $mod2, MATH_BIGINTEGER_POWEROF2);
$y1 = $mod2->modInverse($mod1);
$y2 = $mod1->modInverse($mod2);
$result = $part1->multiply($mod2);
$result = $result->multiply($y1);
$temp = $part2->multiply($mod1);
$temp = $temp->multiply($y2);
$result = $result->add($temp);
list(, $result) = $result->divide($n);
return $this->_normalize($result);
}
/**
* Performs modular exponentiation.
*
* Alias for Math_BigInteger::modPow()
*
* @param Math_BigInteger $e
* @param Math_BigInteger $n
* @return Math_BigInteger
* @access public
*/
function powMod($e, $n)
{
return $this->modPow($e, $n);
}
/**
* Sliding Window k-ary Modular Exponentiation
*
* Based on {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=27 HAC 14.85} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=210 MPM 7.7}. In a departure from those algorithims,
* however, this function performs a modular reduction after every multiplication and squaring operation.
* As such, this function has the same preconditions that the reductions being used do.
*
* @param Math_BigInteger $e
* @param Math_BigInteger $n
* @param Integer $mode
* @return Math_BigInteger
* @access private
*/
function _slidingWindow($e, $n, $mode)
{
static $window_ranges = array(7, 25, 81, 241, 673, 1793); // from BigInteger.java's oddModPow function
//static $window_ranges = array(0, 7, 36, 140, 450, 1303, 3529); // from MPM 7.3.1
$e_value = $e->value;
$e_length = count($e_value) - 1;
$e_bits = decbin($e_value[$e_length]);
for ($i = $e_length - 1; $i >= 0; --$i) {
$e_bits.= str_pad(decbin($e_value[$i]), 26, '0', STR_PAD_LEFT);
}
$e_length = strlen($e_bits);
// calculate the appropriate window size.
// $window_size == 3 if $window_ranges is between 25 and 81, for example.
for ($i = 0, $window_size = 1; $e_length > $window_ranges[$i] && $i < count($window_ranges); ++$window_size, ++$i);
$n_value = $n->value;
// precompute $this^0 through $this^$window_size
$powers = array();
$powers[1] = $this->_prepareReduce($this->value, $n_value, $mode);
$powers[2] = $this->_squareReduce($powers[1], $n_value, $mode);
// we do every other number since substr($e_bits, $i, $j+1) (see below) is supposed to end
// in a 1. ie. it's supposed to be odd.
$temp = 1 << ($window_size - 1);
for ($i = 1; $i < $temp; ++$i) {
$i2 = $i << 1;
$powers[$i2 + 1] = $this->_multiplyReduce($powers[$i2 - 1], $powers[2], $n_value, $mode);
}
$result = array(1);
$result = $this->_prepareReduce($result, $n_value, $mode);
for ($i = 0; $i < $e_length; ) {
if ( !$e_bits[$i] ) {
$result = $this->_squareReduce($result, $n_value, $mode);
++$i;
} else {
for ($j = $window_size - 1; $j > 0; --$j) {
if ( !empty($e_bits[$i + $j]) ) {
break;
}
}
for ($k = 0; $k <= $j; ++$k) {// eg. the length of substr($e_bits, $i, $j+1)
$result = $this->_squareReduce($result, $n_value, $mode);
}
$result = $this->_multiplyReduce($result, $powers[bindec(substr($e_bits, $i, $j + 1))], $n_value, $mode);
$i+=$j + 1;
}
}
$temp = new Math_BigInteger();
$temp->value = $this->_reduce($result, $n_value, $mode);
return $temp;
}
/**
* Modular reduction
*
* For most $modes this will return the remainder.
*
* @see _slidingWindow()
* @access private
* @param Array $x
* @param Array $n
* @param Integer $mode
* @return Array
*/
function _reduce($x, $n, $mode)
{
switch ($mode) {
case MATH_BIGINTEGER_MONTGOMERY:
return $this->_montgomery($x, $n);
case MATH_BIGINTEGER_BARRETT:
return $this->_barrett($x, $n);
case MATH_BIGINTEGER_POWEROF2:
$lhs = new Math_BigInteger();
$lhs->value = $x;
$rhs = new Math_BigInteger();
$rhs->value = $n;
return $x->_mod2($n);
case MATH_BIGINTEGER_CLASSIC:
$lhs = new Math_BigInteger();
$lhs->value = $x;
$rhs = new Math_BigInteger();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
case MATH_BIGINTEGER_NONE:
return $x;
default:
// an invalid $mode was provided
}
}
/**
* Modular reduction preperation
*
* @see _slidingWindow()
* @access private
* @param Array $x
* @param Array $n
* @param Integer $mode
* @return Array
*/
function _prepareReduce($x, $n, $mode)
{
if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
return $this->_prepMontgomery($x, $n);
}
return $this->_reduce($x, $n, $mode);
}
/**
* Modular multiply
*
* @see _slidingWindow()
* @access private
* @param Array $x
* @param Array $y
* @param Array $n
* @param Integer $mode
* @return Array
*/
function _multiplyReduce($x, $y, $n, $mode)
{
if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
return $this->_montgomeryMultiply($x, $y, $n);
}
$temp = $this->_multiply($x, false, $y, false);
return $this->_reduce($temp[MATH_BIGINTEGER_VALUE], $n, $mode);
}
/**
* Modular square
*
* @see _slidingWindow()
* @access private
* @param Array $x
* @param Array $n
* @param Integer $mode
* @return Array
*/
function _squareReduce($x, $n, $mode)
{
if ($mode == MATH_BIGINTEGER_MONTGOMERY) {
return $this->_montgomeryMultiply($x, $x, $n);
}
return $this->_reduce($this->_square($x), $n, $mode);
}
/**
* Modulos for Powers of Two
*
* Calculates $x%$n, where $n = 2**$e, for some $e. Since this is basically the same as doing $x & ($n-1),
* we'll just use this function as a wrapper for doing that.
*
* @see _slidingWindow()
* @access private
* @param Math_BigInteger
* @return Math_BigInteger
*/
function _mod2($n)
{
$temp = new Math_BigInteger();
$temp->value = array(1);
return $this->bitwise_and($n->subtract($temp));
}
/**
* Barrett Modular Reduction
*
* See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=14 HAC 14.3.3} /
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=165 MPM 6.2.5} for more information. Modified slightly,
* so as not to require negative numbers (initially, this script didn't support negative numbers).
*
* Employs "folding", as described at
* {@link http://www.cosic.esat.kuleuven.be/publications/thesis-149.pdf#page=66 thesis-149.pdf#page=66}. To quote from
* it, "the idea [behind folding] is to find a value x' such that x (mod m) = x' (mod m), with x' being smaller than x."
*
* Unfortunately, the "Barrett Reduction with Folding" algorithm described in thesis-149.pdf is not, as written, all that
* usable on account of (1) its not using reasonable radix points as discussed in
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=162 MPM 6.2.2} and (2) the fact that, even with reasonable
* radix points, it only works when there are an even number of digits in the denominator. The reason for (2) is that
* (x >> 1) + (x >> 1) != x / 2 + x / 2. If x is even, they're the same, but if x is odd, they're not. See the in-line
* comments for details.
*
* @see _slidingWindow()
* @access private
* @param Array $n
* @param Array $m
* @return Array
*/
function _barrett($n, $m)
{
static $cache = array(
MATH_BIGINTEGER_VARIABLE => array(),
MATH_BIGINTEGER_DATA => array()
);
$m_length = count($m);
// if ($this->_compare($n, $this->_square($m)) >= 0) {
if (count($n) > 2 * $m_length) {
$lhs = new Math_BigInteger();
$rhs = new Math_BigInteger();
$lhs->value = $n;
$rhs->value = $m;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
// if (m.length >> 1) + 2 <= m.length then m is too small and n can't be reduced
if ($m_length < 5) {
return $this->_regularBarrett($n, $m);
}
// n = 2 * m.length
if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
$key = count($cache[MATH_BIGINTEGER_VARIABLE]);
$cache[MATH_BIGINTEGER_VARIABLE][] = $m;
$lhs = new Math_BigInteger();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, $m_length + ($m_length >> 1));
$lhs_value[] = 1;
$rhs = new Math_BigInteger();
$rhs->value = $m;
list($u, $m1) = $lhs->divide($rhs);
$u = $u->value;
$m1 = $m1->value;
$cache[MATH_BIGINTEGER_DATA][] = array(
'u' => $u, // m.length >> 1 (technically (m.length >> 1) + 1)
'm1'=> $m1 // m.length
);
} else {
extract($cache[MATH_BIGINTEGER_DATA][$key]);
}
$cutoff = $m_length + ($m_length >> 1);
$lsd = array_slice($n, 0, $cutoff); // m.length + (m.length >> 1)
$msd = array_slice($n, $cutoff); // m.length >> 1
$lsd = $this->_trim($lsd);
$temp = $this->_multiply($msd, false, $m1, false);
$n = $this->_add($lsd, false, $temp[MATH_BIGINTEGER_VALUE], false); // m.length + (m.length >> 1) + 1
if ($m_length & 1) {
return $this->_regularBarrett($n[MATH_BIGINTEGER_VALUE], $m);
}
// (m.length + (m.length >> 1) + 1) - (m.length - 1) == (m.length >> 1) + 2
$temp = array_slice($n[MATH_BIGINTEGER_VALUE], $m_length - 1);
// if even: ((m.length >> 1) + 2) + (m.length >> 1) == m.length + 2
// if odd: ((m.length >> 1) + 2) + (m.length >> 1) == (m.length - 1) + 2 == m.length + 1
$temp = $this->_multiply($temp, false, $u, false);
// if even: (m.length + 2) - ((m.length >> 1) + 1) = m.length - (m.length >> 1) + 1
// if odd: (m.length + 1) - ((m.length >> 1) + 1) = m.length - (m.length >> 1)
$temp = array_slice($temp[MATH_BIGINTEGER_VALUE], ($m_length >> 1) + 1);
// if even: (m.length - (m.length >> 1) + 1) + m.length = 2 * m.length - (m.length >> 1) + 1
// if odd: (m.length - (m.length >> 1)) + m.length = 2 * m.length - (m.length >> 1)
$temp = $this->_multiply($temp, false, $m, false);
// at this point, if m had an odd number of digits, we'd be subtracting a 2 * m.length - (m.length >> 1) digit
// number from a m.length + (m.length >> 1) + 1 digit number. ie. there'd be an extra digit and the while loop
// following this comment would loop a lot (hence our calling _regularBarrett() in that situation).
$result = $this->_subtract($n[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false) >= 0) {
$result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $m, false);
}
return $result[MATH_BIGINTEGER_VALUE];
}
/**
* (Regular) Barrett Modular Reduction
*
* For numbers with more than four digits Math_BigInteger::_barrett() is faster. The difference between that and this
* is that this function does not fold the denominator into a smaller form.
*
* @see _slidingWindow()
* @access private
* @param Array $x
* @param Array $n
* @return Array
*/
function _regularBarrett($x, $n)
{
static $cache = array(
MATH_BIGINTEGER_VARIABLE => array(),
MATH_BIGINTEGER_DATA => array()
);
$n_length = count($n);
if (count($x) > 2 * $n_length) {
$lhs = new Math_BigInteger();
$rhs = new Math_BigInteger();
$lhs->value = $x;
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
$key = count($cache[MATH_BIGINTEGER_VARIABLE]);
$cache[MATH_BIGINTEGER_VARIABLE][] = $n;
$lhs = new Math_BigInteger();
$lhs_value = &$lhs->value;
$lhs_value = $this->_array_repeat(0, 2 * $n_length);
$lhs_value[] = 1;
$rhs = new Math_BigInteger();
$rhs->value = $n;
list($temp, ) = $lhs->divide($rhs); // m.length
$cache[MATH_BIGINTEGER_DATA][] = $temp->value;
}
// 2 * m.length - (m.length - 1) = m.length + 1
$temp = array_slice($x, $n_length - 1);
// (m.length + 1) + m.length = 2 * m.length + 1
$temp = $this->_multiply($temp, false, $cache[MATH_BIGINTEGER_DATA][$key], false);
// (2 * m.length + 1) - (m.length - 1) = m.length + 2
$temp = array_slice($temp[MATH_BIGINTEGER_VALUE], $n_length + 1);
// m.length + 1
$result = array_slice($x, 0, $n_length + 1);
// m.length + 1
$temp = $this->_multiplyLower($temp, false, $n, false, $n_length + 1);
// $temp == array_slice($temp->_multiply($temp, false, $n, false)->value, 0, $n_length + 1)
if ($this->_compare($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]) < 0) {
$corrector_value = $this->_array_repeat(0, $n_length + 1);
$corrector_value[] = 1;
$result = $this->_add($result, false, $corrector, false);
$result = $result[MATH_BIGINTEGER_VALUE];
}
// at this point, we're subtracting a number with m.length + 1 digits from another number with m.length + 1 digits
$result = $this->_subtract($result, false, $temp[MATH_BIGINTEGER_VALUE], $temp[MATH_BIGINTEGER_SIGN]);
while ($this->_compare($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false) > 0) {
$result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], $result[MATH_BIGINTEGER_SIGN], $n, false);
}
return $result[MATH_BIGINTEGER_VALUE];
}
/**
* Performs long multiplication up to $stop digits
*
* If you're going to be doing array_slice($product->value, 0, $stop), some cycles can be saved.
*
* @see _regularBarrett()
* @param Array $x_value
* @param Boolean $x_negative
* @param Array $y_value
* @param Boolean $y_negative
* @return Array
* @access private
*/
function _multiplyLower($x_value, $x_negative, $y_value, $y_negative, $stop)
{
$x_length = count($x_value);
$y_length = count($y_value);
if ( !$x_length || !$y_length ) { // a 0 is being multiplied
return array(
MATH_BIGINTEGER_VALUE => array(),
MATH_BIGINTEGER_SIGN => false
);
}
if ( $x_length < $y_length ) {
$temp = $x_value;
$x_value = $y_value;
$y_value = $temp;
$x_length = count($x_value);
$y_length = count($y_value);
}
$product_value = $this->_array_repeat(0, $x_length + $y_length);
// the following for loop could be removed if the for loop following it
// (the one with nested for loops) initially set $i to 0, but
// doing so would also make the result in one set of unnecessary adds,
// since on the outermost loops first pass, $product->value[$k] is going
// to always be 0
$carry = 0;
for ($j = 0; $j < $x_length; ++$j) { // ie. $i = 0, $k = $i
$temp = $x_value[$j] * $y_value[0] + $carry; // $product_value[$k] == 0
$carry = (int) ($temp / 0x4000000);
$product_value[$j] = (int) ($temp - 0x4000000 * $carry);
}
if ($j < $stop) {
$product_value[$j] = $carry;
}
// the above for loop is what the previous comment was talking about. the
// following for loop is the "one with nested for loops"
for ($i = 1; $i < $y_length; ++$i) {
$carry = 0;
for ($j = 0, $k = $i; $j < $x_length && $k < $stop; ++$j, ++$k) {
$temp = $product_value[$k] + $x_value[$j] * $y_value[$i] + $carry;
$carry = (int) ($temp / 0x4000000);
$product_value[$k] = (int) ($temp - 0x4000000 * $carry);
}
if ($k < $stop) {
$product_value[$k] = $carry;
}
}
return array(
MATH_BIGINTEGER_VALUE => $this->_trim($product_value),
MATH_BIGINTEGER_SIGN => $x_negative != $y_negative
);
}
/**
* Montgomery Modular Reduction
*
* ($x->_prepMontgomery($n))->_montgomery($n) yields $x % $n.
* {@link http://math.libtomcrypt.com/files/tommath.pdf#page=170 MPM 6.3} provides insights on how this can be
* improved upon (basically, by using the comba method). gcd($n, 2) must be equal to one for this function
* to work correctly.
*
* @see _prepMontgomery()
* @see _slidingWindow()
* @access private
* @param Array $x
* @param Array $n
* @return Array
*/
function _montgomery($x, $n)
{
static $cache = array(
MATH_BIGINTEGER_VARIABLE => array(),
MATH_BIGINTEGER_DATA => array()
);
if ( ($key = array_search($n, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
$key = count($cache[MATH_BIGINTEGER_VARIABLE]);
$cache[MATH_BIGINTEGER_VARIABLE][] = $x;
$cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($n);
}
$k = count($n);
$result = array(MATH_BIGINTEGER_VALUE => $x);
for ($i = 0; $i < $k; ++$i) {
$temp = $result[MATH_BIGINTEGER_VALUE][$i] * $cache[MATH_BIGINTEGER_DATA][$key];
$temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
$temp = $this->_regularMultiply(array($temp), $n);
$temp = array_merge($this->_array_repeat(0, $i), $temp);
$result = $this->_add($result[MATH_BIGINTEGER_VALUE], false, $temp, false);
}
$result[MATH_BIGINTEGER_VALUE] = array_slice($result[MATH_BIGINTEGER_VALUE], $k);
if ($this->_compare($result, false, $n, false) >= 0) {
$result = $this->_subtract($result[MATH_BIGINTEGER_VALUE], false, $n, false);
}
return $result[MATH_BIGINTEGER_VALUE];
}
/**
* Montgomery Multiply
*
* Interleaves the montgomery reduction and long multiplication algorithms together as described in
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=13 HAC 14.36}
*
* @see _prepMontgomery()
* @see _montgomery()
* @access private
* @param Array $x
* @param Array $y
* @param Array $m
* @return Array
*/
function _montgomeryMultiply($x, $y, $m)
{
$temp = $this->_multiply($x, false, $y, false);
return $this->_montgomery($temp[MATH_BIGINTEGER_VALUE], $m);
static $cache = array(
MATH_BIGINTEGER_VARIABLE => array(),
MATH_BIGINTEGER_DATA => array()
);
if ( ($key = array_search($m, $cache[MATH_BIGINTEGER_VARIABLE])) === false ) {
$key = count($cache[MATH_BIGINTEGER_VARIABLE]);
$cache[MATH_BIGINTEGER_VARIABLE][] = $m;
$cache[MATH_BIGINTEGER_DATA][] = $this->_modInverse67108864($m);
}
$n = max(count($x), count($y), count($m));
$x = array_pad($x, $n, 0);
$y = array_pad($y, $n, 0);
$m = array_pad($m, $n, 0);
$a = array(MATH_BIGINTEGER_VALUE => $this->_array_repeat(0, $n + 1));
for ($i = 0; $i < $n; ++$i) {
$temp = $a[MATH_BIGINTEGER_VALUE][0] + $x[$i] * $y[0];
$temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
$temp = $temp * $cache[MATH_BIGINTEGER_DATA][$key];
$temp = (int) ($temp - 0x4000000 * ((int) ($temp / 0x4000000)));
$temp = $this->_add($this->_regularMultiply(array($x[$i]), $y), false, $this->_regularMultiply(array($temp), $m), false);
$a = $this->_add($a[MATH_BIGINTEGER_VALUE], false, $temp[MATH_BIGINTEGER_VALUE], false);
$a[MATH_BIGINTEGER_VALUE] = array_slice($a[MATH_BIGINTEGER_VALUE], 1);
}
if ($this->_compare($a[MATH_BIGINTEGER_VALUE], false, $m, false) >= 0) {
$a = $this->_subtract($a[MATH_BIGINTEGER_VALUE], false, $m, false);
}
return $a[MATH_BIGINTEGER_VALUE];
}
/**
* Prepare a number for use in Montgomery Modular Reductions
*
* @see _montgomery()
* @see _slidingWindow()
* @access private
* @param Array $x
* @param Array $n
* @return Array
*/
function _prepMontgomery($x, $n)
{
$lhs = new Math_BigInteger();
$lhs->value = array_merge($this->_array_repeat(0, count($n)), $x);
$rhs = new Math_BigInteger();
$rhs->value = $n;
list(, $temp) = $lhs->divide($rhs);
return $temp->value;
}
/**
* Modular Inverse of a number mod 2**26 (eg. 67108864)
*
* Based off of the bnpInvDigit function implemented and justified in the following URL:
*
* {@link http://www-cs-students.stanford.edu/~tjw/jsbn/jsbn.js}
*
* The following URL provides more info:
*
* {@link http://groups.google.com/group/sci.crypt/msg/7a137205c1be7d85}
*
* As for why we do all the bitmasking... strange things can happen when converting from floats to ints. For
* instance, on some computers, var_dump((int) -4294967297) yields int(-1) and on others, it yields
* int(-2147483648). To avoid problems stemming from this, we use bitmasks to guarantee that ints aren't
* auto-converted to floats. The outermost bitmask is present because without it, there's no guarantee that
* the "residue" returned would be the so-called "common residue". We use fmod, in the last step, because the
* maximum possible $x is 26 bits and the maximum $result is 16 bits. Thus, we have to be able to handle up to
* 40 bits, which only 64-bit floating points will support.
*
* Thanks to Pedro Gimeno Fortea for input!
*
* @see _montgomery()
* @access private
* @param Array $x
* @return Integer
*/
function _modInverse67108864($x) // 2**26 == 67108864
{
$x = -$x[0];
$result = $x & 0x3; // x**-1 mod 2**2
$result = ($result * (2 - $x * $result)) & 0xF; // x**-1 mod 2**4
$result = ($result * (2 - ($x & 0xFF) * $result)) & 0xFF; // x**-1 mod 2**8
$result = ($result * ((2 - ($x & 0xFFFF) * $result) & 0xFFFF)) & 0xFFFF; // x**-1 mod 2**16
$result = fmod($result * (2 - fmod($x * $result, 0x4000000)), 0x4000000); // x**-1 mod 2**26
return $result & 0x3FFFFFF;
}
/**
* Calculates modular inverses.
*
* Say you have (30 mod 17 * x mod 17) mod 17 == 1. x can be found using modular inverses.
*
* Here's an example:
*
* modInverse($b);
* echo $c->toString(); // outputs 4
*
* echo "\r\n";
*
* $d = $a->multiply($c);
* list(, $d) = $d->divide($b);
* echo $d; // outputs 1 (as per the definition of modular inverse)
* ?>
*
*
* @param Math_BigInteger $n
* @return mixed false, if no modular inverse exists, Math_BigInteger, otherwise.
* @access public
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=21 HAC 14.64} for more information.
*/
function modInverse($n)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_invert($this->value, $n->value);
return ( $temp->value === false ) ? false : $this->_normalize($temp);
}
static $zero, $one;
if (!isset($zero)) {
$zero = new Math_BigInteger();
$one = new Math_BigInteger(1);
}
// $x mod $n == $x mod -$n.
$n = $n->abs();
if ($this->compare($zero) < 0) {
$temp = $this->abs();
$temp = $temp->modInverse($n);
return $negated === false ? false : $this->_normalize($n->subtract($temp));
}
extract($this->extendedGCD($n));
if (!$gcd->equals($one)) {
return false;
}
$x = $x->compare($zero) < 0 ? $x->add($n) : $x;
return $this->compare($zero) < 0 ? $this->_normalize($n->subtract($x)) : $this->_normalize($x);
}
/**
* Calculates the greatest common divisor and Bézout's identity.
*
* Say you have 693 and 609. The GCD is 21. Bézout's identity states that there exist integers x and y such that
* 693*x + 609*y == 21. In point of fact, there are actually an infinite number of x and y combinations and which
* combination is returned is dependant upon which mode is in use. See
* {@link http://en.wikipedia.org/wiki/B%C3%A9zout%27s_identity Bézout's identity - Wikipedia} for more information.
*
* Here's an example:
*
* extendedGCD($b));
*
* echo $gcd->toString() . "\r\n"; // outputs 21
* echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
* ?>
*
*
* @param Math_BigInteger $n
* @return Math_BigInteger
* @access public
* @internal Calculates the GCD using the binary xGCD algorithim described in
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap14.pdf#page=19 HAC 14.61}. As the text above 14.61 notes,
* the more traditional algorithim requires "relatively costly multiple-precision divisions".
*/
function extendedGCD($n)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
extract(gmp_gcdext($this->value, $n->value));
return array(
'gcd' => $this->_normalize(new Math_BigInteger($g)),
'x' => $this->_normalize(new Math_BigInteger($s)),
'y' => $this->_normalize(new Math_BigInteger($t))
);
case MATH_BIGINTEGER_MODE_BCMATH:
// it might be faster to use the binary xGCD algorithim here, as well, but (1) that algorithim works
// best when the base is a power of 2 and (2) i don't think it'd make much difference, anyway. as is,
// the basic extended euclidean algorithim is what we're using.
$u = $this->value;
$v = $n->value;
$a = '1';
$b = '0';
$c = '0';
$d = '1';
while (bccomp($v, '0', 0) != 0) {
$q = bcdiv($u, $v, 0);
$temp = $u;
$u = $v;
$v = bcsub($temp, bcmul($v, $q, 0), 0);
$temp = $a;
$a = $c;
$c = bcsub($temp, bcmul($a, $q, 0), 0);
$temp = $b;
$b = $d;
$d = bcsub($temp, bcmul($b, $q, 0), 0);
}
return array(
'gcd' => $this->_normalize(new Math_BigInteger($u)),
'x' => $this->_normalize(new Math_BigInteger($a)),
'y' => $this->_normalize(new Math_BigInteger($b))
);
}
$y = $n->copy();
$x = $this->copy();
$g = new Math_BigInteger();
$g->value = array(1);
while ( !(($x->value[0] & 1)|| ($y->value[0] & 1)) ) {
$x->_rshift(1);
$y->_rshift(1);
$g->_lshift(1);
}
$u = $x->copy();
$v = $y->copy();
$a = new Math_BigInteger();
$b = new Math_BigInteger();
$c = new Math_BigInteger();
$d = new Math_BigInteger();
$a->value = $d->value = $g->value = array(1);
$b->value = $c->value = array();
while ( !empty($u->value) ) {
while ( !($u->value[0] & 1) ) {
$u->_rshift(1);
if ( (!empty($a->value) && ($a->value[0] & 1)) || (!empty($b->value) && ($b->value[0] & 1)) ) {
$a = $a->add($y);
$b = $b->subtract($x);
}
$a->_rshift(1);
$b->_rshift(1);
}
while ( !($v->value[0] & 1) ) {
$v->_rshift(1);
if ( (!empty($d->value) && ($d->value[0] & 1)) || (!empty($c->value) && ($c->value[0] & 1)) ) {
$c = $c->add($y);
$d = $d->subtract($x);
}
$c->_rshift(1);
$d->_rshift(1);
}
if ($u->compare($v) >= 0) {
$u = $u->subtract($v);
$a = $a->subtract($c);
$b = $b->subtract($d);
} else {
$v = $v->subtract($u);
$c = $c->subtract($a);
$d = $d->subtract($b);
}
}
return array(
'gcd' => $this->_normalize($g->multiply($v)),
'x' => $this->_normalize($c),
'y' => $this->_normalize($d)
);
}
/**
* Calculates the greatest common divisor
*
* Say you have 693 and 609. The GCD is 21.
*
* Here's an example:
*
* extendedGCD($b);
*
* echo $gcd->toString() . "\r\n"; // outputs 21
* ?>
*
*
* @param Math_BigInteger $n
* @return Math_BigInteger
* @access public
*/
function gcd($n)
{
extract($this->extendedGCD($n));
return $gcd;
}
/**
* Absolute value.
*
* @return Math_BigInteger
* @access public
*/
function abs()
{
$temp = new Math_BigInteger();
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp->value = gmp_abs($this->value);
break;
case MATH_BIGINTEGER_MODE_BCMATH:
$temp->value = (bccomp($this->value, '0', 0) < 0) ? substr($this->value, 1) : $this->value;
break;
default:
$temp->value = $this->value;
}
return $temp;
}
/**
* Compares two numbers.
*
* Although one might think !$x->compare($y) means $x != $y, it, in fact, means the opposite. The reason for this is
* demonstrated thusly:
*
* $x > $y: $x->compare($y) > 0
* $x < $y: $x->compare($y) < 0
* $x == $y: $x->compare($y) == 0
*
* Note how the same comparison operator is used. If you want to test for equality, use $x->equals($y).
*
* @param Math_BigInteger $x
* @return Integer < 0 if $this is less than $x; > 0 if $this is greater than $x, and 0 if they are equal.
* @access public
* @see equals()
* @internal Could return $this->subtract($x), but that's not as fast as what we do do.
*/
function compare($y)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
return gmp_cmp($this->value, $y->value);
case MATH_BIGINTEGER_MODE_BCMATH:
return bccomp($this->value, $y->value, 0);
}
return $this->_compare($this->value, $this->is_negative, $y->value, $y->is_negative);
}
/**
* Compares two numbers.
*
* @param Array $x_value
* @param Boolean $x_negative
* @param Array $y_value
* @param Boolean $y_negative
* @return Integer
* @see compare()
* @access private
*/
function _compare($x_value, $x_negative, $y_value, $y_negative)
{
if ( $x_negative != $y_negative ) {
return ( !$x_negative && $y_negative ) ? 1 : -1;
}
$result = $x_negative ? -1 : 1;
if ( count($x_value) != count($y_value) ) {
return ( count($x_value) > count($y_value) ) ? $result : -$result;
}
$size = max(count($x_value), count($y_value));
$x_value = array_pad($x_value, $size, 0);
$y_value = array_pad($y_value, $size, 0);
for ($i = count($x_value) - 1; $i >= 0; --$i) {
if ($x_value[$i] != $y_value[$i]) {
return ( $x_value[$i] > $y_value[$i] ) ? $result : -$result;
}
}
return 0;
}
/**
* Tests the equality of two numbers.
*
* If you need to see if one number is greater than or less than another number, use Math_BigInteger::compare()
*
* @param Math_BigInteger $x
* @return Boolean
* @access public
* @see compare()
*/
function equals($x)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
return gmp_cmp($this->value, $x->value) == 0;
default:
return $this->value === $x->value && $this->is_negative == $x->is_negative;
}
}
/**
* Set Precision
*
* Some bitwise operations give different results depending on the precision being used. Examples include left
* shift, not, and rotates.
*
* @param Math_BigInteger $x
* @access public
* @return Math_BigInteger
*/
function setPrecision($bits)
{
$this->precision = $bits;
if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ) {
$this->bitmask = new Math_BigInteger(chr((1 << ($bits & 0x7)) - 1) . str_repeat(chr(0xFF), $bits >> 3), 256);
} else {
$this->bitmask = new Math_BigInteger(bcpow('2', $bits, 0));
}
$temp = $this->_normalize($this);
$this->value = $temp->value;
}
/**
* Logical And
*
* @param Math_BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
* @return Math_BigInteger
*/
function bitwise_and($x)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_and($this->value, $x->value);
return $this->_normalize($temp);
case MATH_BIGINTEGER_MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new Math_BigInteger($left & $right, 256));
}
$result = $this->copy();
$length = min(count($x->value), count($this->value));
$result->value = array_slice($result->value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i] = $result->value[$i] & $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Or
*
* @param Math_BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
* @return Math_BigInteger
*/
function bitwise_or($x)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_or($this->value, $x->value);
return $this->_normalize($temp);
case MATH_BIGINTEGER_MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new Math_BigInteger($left | $right, 256));
}
$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->value = array_pad($result->value, 0, $length);
$x->value = array_pad($x->value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i] = $this->value[$i] | $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Exclusive-Or
*
* @param Math_BigInteger $x
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
* @return Math_BigInteger
*/
function bitwise_xor($x)
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
$temp = new Math_BigInteger();
$temp->value = gmp_xor($this->value, $x->value);
return $this->_normalize($temp);
case MATH_BIGINTEGER_MODE_BCMATH:
$left = $this->toBytes();
$right = $x->toBytes();
$length = max(strlen($left), strlen($right));
$left = str_pad($left, $length, chr(0), STR_PAD_LEFT);
$right = str_pad($right, $length, chr(0), STR_PAD_LEFT);
return $this->_normalize(new Math_BigInteger($left ^ $right, 256));
}
$length = max(count($this->value), count($x->value));
$result = $this->copy();
$result->value = array_pad($result->value, 0, $length);
$x->value = array_pad($x->value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$result->value[$i] = $this->value[$i] ^ $x->value[$i];
}
return $this->_normalize($result);
}
/**
* Logical Not
*
* @access public
* @internal Implemented per a request by Lluis Pamies i Juarez
* @return Math_BigInteger
*/
function bitwise_not()
{
// calculuate "not" without regard to $this->precision
// (will always result in a smaller number. ie. ~1 isn't 1111 1110 - it's 0)
$temp = $this->toBytes();
$pre_msb = decbin(ord($temp[0]));
$temp = ~$temp;
$msb = decbin(ord($temp[0]));
if (strlen($msb) == 8) {
$msb = substr($msb, strpos($msb, '0'));
}
$temp[0] = chr(bindec($msb));
// see if we need to add extra leading 1's
$current_bits = strlen($pre_msb) + 8 * strlen($temp) - 8;
$new_bits = $this->precision - $current_bits;
if ($new_bits <= 0) {
return $this->_normalize(new Math_BigInteger($temp, 256));
}
// generate as many leading 1's as we need to.
$leading_ones = chr((1 << ($new_bits & 0x7)) - 1) . str_repeat(chr(0xFF), $new_bits >> 3);
$this->_base256_lshift($leading_ones, $current_bits);
$temp = str_pad($temp, ceil($this->bits / 8), chr(0), STR_PAD_LEFT);
return $this->_normalize(new Math_BigInteger($leading_ones | $temp, 256));
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits, effectively dividing by 2**$shift.
*
* @param Integer $shift
* @return Math_BigInteger
* @access public
* @internal The only version that yields any speed increases is the internal version.
*/
function bitwise_rightShift($shift)
{
$temp = new Math_BigInteger();
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
static $two;
if (!isset($two)) {
$two = gmp_init('2');
}
$temp->value = gmp_div_q($this->value, gmp_pow($two, $shift));
break;
case MATH_BIGINTEGER_MODE_BCMATH:
$temp->value = bcdiv($this->value, bcpow('2', $shift, 0), 0);
break;
default: // could just replace _lshift with this, but then all _lshift() calls would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->_rshift($shift);
}
return $this->_normalize($temp);
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits, effectively multiplying by 2**$shift.
*
* @param Integer $shift
* @return Math_BigInteger
* @access public
* @internal The only version that yields any speed increases is the internal version.
*/
function bitwise_leftShift($shift)
{
$temp = new Math_BigInteger();
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
static $two;
if (!isset($two)) {
$two = gmp_init('2');
}
$temp->value = gmp_mul($this->value, gmp_pow($two, $shift));
break;
case MATH_BIGINTEGER_MODE_BCMATH:
$temp->value = bcmul($this->value, bcpow('2', $shift, 0), 0);
break;
default: // could just replace _rshift with this, but then all _lshift() calls would need to be rewritten
// and I don't want to do that...
$temp->value = $this->value;
$temp->_lshift($shift);
}
return $this->_normalize($temp);
}
/**
* Logical Left Rotate
*
* Instead of the top x bits being dropped they're appended to the shifted bit string.
*
* @param Integer $shift
* @return Math_BigInteger
* @access public
*/
function bitwise_leftRotate($shift)
{
$bits = $this->toBytes();
if ($this->precision > 0) {
$precision = $this->precision;
if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
$mask = $this->bitmask->subtract(new Math_BigInteger(1));
$mask = $mask->toBytes();
} else {
$mask = $this->bitmask->toBytes();
}
} else {
$temp = ord($bits[0]);
for ($i = 0; $temp >> $i; ++$i);
$precision = 8 * strlen($bits) - 8 + $i;
$mask = chr((1 << ($precision & 0x7)) - 1) . str_repeat(chr(0xFF), $precision >> 3);
}
if ($shift < 0) {
$shift+= $precision;
}
$shift%= $precision;
if (!$shift) {
return $this->copy();
}
$left = $this->bitwise_leftShift($shift);
$left = $left->bitwise_and(new Math_BigInteger($mask, 256));
$right = $this->bitwise_rightShift($precision - $shift);
$result = MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_BCMATH ? $left->bitwise_or($right) : $left->add($right);
return $this->_normalize($result);
}
/**
* Logical Right Rotate
*
* Instead of the bottom x bits being dropped they're prepended to the shifted bit string.
*
* @param Integer $shift
* @return Math_BigInteger
* @access public
*/
function bitwise_rightRotate($shift)
{
return $this->bitwise_leftRotate(-$shift);
}
/**
* Set random number generator function
*
* $generator should be the name of a random generating function whose first parameter is the minimum
* value and whose second parameter is the maximum value. If this function needs to be seeded, it should
* be seeded prior to calling Math_BigInteger::random() or Math_BigInteger::randomPrime()
*
* If the random generating function is not explicitly set, it'll be assumed to be mt_rand().
*
* @see random()
* @see randomPrime()
* @param optional String $generator
* @access public
*/
function setRandomGenerator($generator)
{
$this->generator = $generator;
}
/**
* Generate a random number
*
* @param optional Integer $min
* @param optional Integer $max
* @return Math_BigInteger
* @access public
*/
function random($min = false, $max = false)
{
if ($min === false) {
$min = new Math_BigInteger(0);
}
if ($max === false) {
$max = new Math_BigInteger(0x7FFFFFFF);
}
$compare = $max->compare($min);
if (!$compare) {
return $this->_normalize($min);
} else if ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
$generator = $this->generator;
$max = $max->subtract($min);
$max = ltrim($max->toBytes(), chr(0));
$size = strlen($max) - 1;
$random = '';
$bytes = $size & 1;
for ($i = 0; $i < $bytes; ++$i) {
$random.= chr($generator(0, 255));
}
$blocks = $size >> 1;
for ($i = 0; $i < $blocks; ++$i) {
// mt_rand(-2147483648, 0x7FFFFFFF) always produces -2147483648 on some systems
$random.= pack('n', $generator(0, 0xFFFF));
}
$temp = new Math_BigInteger($random, 256);
if ($temp->compare(new Math_BigInteger(substr($max, 1), 256)) > 0) {
$random = chr($generator(0, ord($max[0]) - 1)) . $random;
} else {
$random = chr($generator(0, ord($max[0]) )) . $random;
}
$random = new Math_BigInteger($random, 256);
return $this->_normalize($random->add($min));
}
/**
* Generate a random prime number.
*
* If there's not a prime within the given range, false will be returned. If more than $timeout seconds have elapsed,
* give up and return false.
*
* @param optional Integer $min
* @param optional Integer $max
* @param optional Integer $timeout
* @return Math_BigInteger
* @access public
* @internal See {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=15 HAC 4.44}.
*/
function randomPrime($min = false, $max = false, $timeout = false)
{
$compare = $max->compare($min);
if (!$compare) {
return $min;
} else if ($compare < 0) {
// if $min is bigger then $max, swap $min and $max
$temp = $max;
$max = $min;
$min = $temp;
}
// gmp_nextprime() requires PHP 5 >= 5.2.0 per .
if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_GMP && function_exists('gmp_nextprime') ) {
// we don't rely on Math_BigInteger::random()'s min / max when gmp_nextprime() is being used since this function
// does its own checks on $max / $min when gmp_nextprime() is used. When gmp_nextprime() is not used, however,
// the same $max / $min checks are not performed.
if ($min === false) {
$min = new Math_BigInteger(0);
}
if ($max === false) {
$max = new Math_BigInteger(0x7FFFFFFF);
}
$x = $this->random($min, $max);
$x->value = gmp_nextprime($x->value);
if ($x->compare($max) <= 0) {
return $x;
}
$x->value = gmp_nextprime($min->value);
if ($x->compare($max) <= 0) {
return $x;
}
return false;
}
static $one, $two;
if (!isset($one)) {
$one = new Math_BigInteger(1);
$two = new Math_BigInteger(2);
}
$start = time();
$x = $this->random($min, $max);
if ($x->equals($two)) {
return $x;
}
$x->_make_odd();
if ($x->compare($max) > 0) {
// if $x > $max then $max is even and if $min == $max then no prime number exists between the specified range
if ($min->equals($max)) {
return false;
}
$x = $min->copy();
$x->_make_odd();
}
$initial_x = $x->copy();
while (true) {
if ($timeout !== false && time() - $start > $timeout) {
return false;
}
if ($x->isPrime()) {
return $x;
}
$x = $x->add($two);
if ($x->compare($max) > 0) {
$x = $min->copy();
if ($x->equals($two)) {
return $x;
}
$x->_make_odd();
}
if ($x->equals($initial_x)) {
return false;
}
}
}
/**
* Make the current number odd
*
* If the current number is odd it'll be unchanged. If it's even, one will be added to it.
*
* @see randomPrime()
* @access private
*/
function _make_odd()
{
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
gmp_setbit($this->value, 0);
break;
case MATH_BIGINTEGER_MODE_BCMATH:
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
$this->value = bcadd($this->value, '1');
}
break;
default:
$this->value[0] |= 1;
}
}
/**
* Checks a numer to see if it's prime
*
* Assuming the $t parameter is not set, this function has an error rate of 2**-80. The main motivation for the
* $t parameter is distributability. Math_BigInteger::randomPrime() can be distributed accross multiple pageloads
* on a website instead of just one.
*
* @param optional Integer $t
* @return Boolean
* @access public
* @internal Uses the
* {@link http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test Miller-Rabin primality test}. See
* {@link http://www.cacr.math.uwaterloo.ca/hac/about/chap4.pdf#page=8 HAC 4.24}.
*/
function isPrime($t = false)
{
$length = strlen($this->toBytes());
if (!$t) {
// see HAC 4.49 "Note (controlling the error probability)"
if ($length >= 163) { $t = 2; } // floor(1300 / 8)
else if ($length >= 106) { $t = 3; } // floor( 850 / 8)
else if ($length >= 81 ) { $t = 4; } // floor( 650 / 8)
else if ($length >= 68 ) { $t = 5; } // floor( 550 / 8)
else if ($length >= 56 ) { $t = 6; } // floor( 450 / 8)
else if ($length >= 50 ) { $t = 7; } // floor( 400 / 8)
else if ($length >= 43 ) { $t = 8; } // floor( 350 / 8)
else if ($length >= 37 ) { $t = 9; } // floor( 300 / 8)
else if ($length >= 31 ) { $t = 12; } // floor( 250 / 8)
else if ($length >= 25 ) { $t = 15; } // floor( 200 / 8)
else if ($length >= 18 ) { $t = 18; } // floor( 150 / 8)
else { $t = 27; }
}
// ie. gmp_testbit($this, 0)
// ie. isEven() or !isOdd()
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
return gmp_prob_prime($this->value, $t) != 0;
case MATH_BIGINTEGER_MODE_BCMATH:
if ($this->value === '2') {
return true;
}
if ($this->value[strlen($this->value) - 1] % 2 == 0) {
return false;
}
break;
default:
if ($this->value == array(2)) {
return true;
}
if (~$this->value[0] & 1) {
return false;
}
}
static $primes, $zero, $one, $two;
if (!isset($primes)) {
$primes = array(
3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59,
61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137,
139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227,
229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313,
317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419,
421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509,
521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617,
619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727,
733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829,
839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947,
953, 967, 971, 977, 983, 991, 997
);
if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
for ($i = 0; $i < count($primes); ++$i) {
$primes[$i] = new Math_BigInteger($primes[$i]);
}
}
$zero = new Math_BigInteger();
$one = new Math_BigInteger(1);
$two = new Math_BigInteger(2);
}
if ($this->equals($one)) {
return false;
}
// see HAC 4.4.1 "Random search for probable primes"
if ( MATH_BIGINTEGER_MODE != MATH_BIGINTEGER_MODE_INTERNAL ) {
foreach ($primes as $prime) {
list(, $r) = $this->divide($prime);
if ($r->equals($zero)) {
return $this->equals($prime);
}
}
} else {
$value = $this->value;
foreach ($primes as $prime) {
list(, $r) = $this->_divide_digit($value, $prime);
if (!$r) {
return count($value) == 1 && $value[0] == $prime;
}
}
}
$n = $this->copy();
$n_1 = $n->subtract($one);
$n_2 = $n->subtract($two);
$r = $n_1->copy();
$r_value = $r->value;
// ie. $s = gmp_scan1($n, 0) and $r = gmp_div_q($n, gmp_pow(gmp_init('2'), $s));
if ( MATH_BIGINTEGER_MODE == MATH_BIGINTEGER_MODE_BCMATH ) {
$s = 0;
// if $n was 1, $r would be 0 and this would be an infinite loop, hence our $this->equals($one) check earlier
while ($r->value[strlen($r->value) - 1] % 2 == 0) {
$r->value = bcdiv($r->value, '2', 0);
++$s;
}
} else {
for ($i = 0, $r_length = count($r_value); $i < $r_length; ++$i) {
$temp = ~$r_value[$i] & 0xFFFFFF;
for ($j = 1; ($temp >> $j) & 1; ++$j);
if ($j != 25) {
break;
}
}
$s = 26 * $i + $j - 1;
$r->_rshift($s);
}
for ($i = 0; $i < $t; ++$i) {
$a = $this->random($two, $n_2);
$y = $a->modPow($r, $n);
if (!$y->equals($one) && !$y->equals($n_1)) {
for ($j = 1; $j < $s && !$y->equals($n_1); ++$j) {
$y = $y->modPow($two, $n);
if ($y->equals($one)) {
return false;
}
}
if (!$y->equals($n_1)) {
return false;
}
}
}
return true;
}
/**
* Logical Left Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param Integer $shift
* @access private
*/
function _lshift($shift)
{
if ( $shift == 0 ) {
return;
}
$num_digits = (int) ($shift / 26);
$shift %= 26;
$shift = 1 << $shift;
$carry = 0;
for ($i = 0; $i < count($this->value); ++$i) {
$temp = $this->value[$i] * $shift + $carry;
$carry = (int) ($temp / 0x4000000);
$this->value[$i] = (int) ($temp - $carry * 0x4000000);
}
if ( $carry ) {
$this->value[] = $carry;
}
while ($num_digits--) {
array_unshift($this->value, 0);
}
}
/**
* Logical Right Shift
*
* Shifts BigInteger's by $shift bits.
*
* @param Integer $shift
* @access private
*/
function _rshift($shift)
{
if ($shift == 0) {
return;
}
$num_digits = (int) ($shift / 26);
$shift %= 26;
$carry_shift = 26 - $shift;
$carry_mask = (1 << $shift) - 1;
if ( $num_digits ) {
$this->value = array_slice($this->value, $num_digits);
}
$carry = 0;
for ($i = count($this->value) - 1; $i >= 0; --$i) {
$temp = $this->value[$i] >> $shift | $carry;
$carry = ($this->value[$i] & $carry_mask) << $carry_shift;
$this->value[$i] = $temp;
}
$this->value = $this->_trim($this->value);
}
/**
* Normalize
*
* Removes leading zeros and truncates (if necessary) to maintain the appropriate precision
*
* @param Math_BigInteger
* @return Math_BigInteger
* @see _trim()
* @access private
*/
function _normalize($result)
{
$result->precision = $this->precision;
$result->bitmask = $this->bitmask;
switch ( MATH_BIGINTEGER_MODE ) {
case MATH_BIGINTEGER_MODE_GMP:
if (!empty($result->bitmask->value)) {
$result->value = gmp_and($result->value, $result->bitmask->value);
}
return $result;
case MATH_BIGINTEGER_MODE_BCMATH:
if (!empty($result->bitmask->value)) {
$result->value = bcmod($result->value, $result->bitmask->value);
}
return $result;
}
$value = &$result->value;
if ( !count($value) ) {
return $result;
}
$value = $this->_trim($value);
if (!empty($result->bitmask->value)) {
$length = min(count($value), count($this->bitmask->value));
$value = array_slice($value, 0, $length);
for ($i = 0; $i < $length; ++$i) {
$value[$i] = $value[$i] & $this->bitmask->value[$i];
}
}
return $result;
}
/**
* Trim
*
* Removes leading zeros
*
* @return Math_BigInteger
* @access private
*/
function _trim($value)
{
for ($i = count($value) - 1; $i >= 0; --$i) {
if ( $value[$i] ) {
break;
}
unset($value[$i]);
}
return $value;
}
/**
* Array Repeat
*
* @param $input Array
* @param $multiplier mixed
* @return Array
* @access private
*/
function _array_repeat($input, $multiplier)
{
return ($multiplier) ? array_fill(0, $multiplier, $input) : array();
}
/**
* Logical Left Shift
*
* Shifts binary strings $shift bits, essentially multiplying by 2**$shift.
*
* @param $x String
* @param $shift Integer
* @return String
* @access private
*/
function _base256_lshift(&$x, $shift)
{
if ($shift == 0) {
return;
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$carry = 0;
for ($i = strlen($x) - 1; $i >= 0; --$i) {
$temp = ord($x[$i]) << $shift | $carry;
$x[$i] = chr($temp);
$carry = $temp >> 8;
}
$carry = ($carry != 0) ? chr($carry) : '';
$x = $carry . $x . str_repeat(chr(0), $num_bytes);
}
/**
* Logical Right Shift
*
* Shifts binary strings $shift bits, essentially dividing by 2**$shift and returning the remainder.
*
* @param $x String
* @param $shift Integer
* @return String
* @access private
*/
function _base256_rshift(&$x, $shift)
{
if ($shift == 0) {
$x = ltrim($x, chr(0));
return '';
}
$num_bytes = $shift >> 3; // eg. floor($shift/8)
$shift &= 7; // eg. $shift % 8
$remainder = '';
if ($num_bytes) {
$start = $num_bytes > strlen($x) ? -strlen($x) : -$num_bytes;
$remainder = substr($x, $start);
$x = substr($x, 0, -$num_bytes);
}
$carry = 0;
$carry_shift = 8 - $shift;
for ($i = 0; $i < strlen($x); ++$i) {
$temp = (ord($x[$i]) >> $shift) | $carry;
$carry = (ord($x[$i]) << $carry_shift) & 0xFF;
$x[$i] = chr($temp);
}
$x = ltrim($x, chr(0));
$remainder = chr($carry >> $carry_shift) . $remainder;
return ltrim($remainder, chr(0));
}
// one quirk about how the following functions are implemented is that PHP defines N to be an unsigned long
// at 32-bits, while java's longs are 64-bits.
/**
* Converts 32-bit integers to bytes.
*
* @param Integer $x
* @return String
* @access private
*/
function _int2bytes($x)
{
return ltrim(pack('N', $x), chr(0));
}
/**
* Converts bytes to 32-bit integers
*
* @param String $x
* @return Integer
* @access private
*/
function _bytes2int($x)
{
$temp = unpack('Nint', str_pad($x, 4, chr(0), STR_PAD_LEFT));
return $temp['int'];
}
} debian/db-update/v20111210/lib/ 0000755 0000000 0000000 00000000000 12267045571 012667 5 ustar debian/db-update/v20111210/lib/SpotTiming.php 0000644 0000000 0000000 00000002611 12267045571 015475 0 ustar microtime(true));
} # start
static function stop($name, $extra = '') {
if (self::$_disabled) return;
self::$_inflight[$name]['stop'] = microtime(true);
self::$_inflight[$name]['extra'] = $extra;
self::$_inflight[$name]['level'] = self::$_curlevel;
self::$_curlevel--;
self::$_timings[] = array_merge(self::$_inflight[$name], array('name' => $name));
unset(self::$_inflight[$name]);
} # stop
static function display() {
if (self::$_disabled) return;
echo 'Name Time Extra ';
foreach(array_reverse(self::$_timings) as $values) {
echo '' . str_pad('', $values['level'], '.') . $values['name'] . ' ' . ($values['stop'] - $values['start']) . ' ' . serialize($values['extra']) . ' ' . PHP_EOL;
} # foreach
echo '
';
} # display()
} # class SpotTiming debian/db-update/v20111210/lib/SpotSecurity.php 0000644 0000000 0000000 00000013710 12267045571 016057 0 ustar "Display overview of spots",
1 => "Log in",
2 => "Perform a search",
3 => "View spot in detail",
4 => "Retrieve NZB file",
5 => "Download manager integration",
6 => "Mark spots as read",
7 => "View image of spot",
8 => "RSS feed",
9 => "Static resources",
10 => "Create new user",
11 => "Change own preferences",
12 => "Change own user settings",
13 => "List all users",
14 => "Post comments to a spot",
15 => "Log out",
16 => "Use downloads manager API ",
17 => "Keep watchlist",
18 => "Keep downloadlist",
19 => "Keep seenlist",
20 => "Show new spotcount in list of filters",
21 => "Display Retrieve new spots button",
22 => "Display comments of a spot",
23 => "Let user choose their template",
24 => "Use Spotweb using an API key",
25 => "Change other users",
26 => "Display total amount of spots",
27 => "Delete users",
28 => "Change users' group membeship",
29 => "Display users' group membership",
30 => "Change security groups",
31 => "Send notifications (per service)",
32 => "Send notifications (per type)",
33 => "Let users create their own CSS",
34 => "Create own Spot filters",
35 => "Set a set of filters as default for new users",
36 => "Report a spot as spam",
37 => "Post a new spot",
38 => "Blacklist a spotter",
39 => "Display statistics",
40 => "Display Spotweb's changelog",
41 => "Change settings"
);
/*
* Audit levels
*/
const spot_secaudit_none = 0;
const spot_secaudit_failure = 1;
const spot_secaudit_all = 2;
function __construct(SpotDb $db, SpotSettings $settings, array $user, $ipaddr) {
$this->_db = $db;
$this->_user = $user;
$this->_settings = $settings;
$this->_failAudit = ($settings->get('auditlevel') == SpotSecurity::spot_secaudit_failure);
$this->_allAudit = ($settings->get('auditlevel') == SpotSecurity::spot_secaudit_all);
if (($this->_failAudit) || ($this->_allAudit)) {
$this->_spotAudit = new SpotAudit($db, $settings, $user, $ipaddr);
} # if
$this->_permissions = $db->getPermissions($user['userid']);
} # ctor
function allowed($perm, $object) {
$allowed = isset($this->_permissions[$perm][$object]) && $this->_permissions[$perm][$object];
# We check for auditing in SpotSecurity to prevent the overhead
# of a function call for each security check
if (($this->_allAudit) || ((!$allowed) && ($this->_failAudit))) {
$this->_spotAudit->audit($perm, $object, $allowed);
} # if
return $allowed;
} # allowed
function fatalPermCheck($perm, $object) {
if (!$this->allowed($perm, $object)) {
throw new PermissionDeniedException($perm, $object);
} # if
} # fatalPermCheck
function toHuman($perm) {
return $this->_secHumanReadable[$perm];
} # toHuman
function getAllPermissions() {
return $this->_secHumanReadable;
} # getAllPermissions
function securityValid() {
# SPOTWEB_SECURITY_VERSION is gedefinieerd bovenin dit bestand
return ($this->_settings->get('securityversion') == SPOTWEB_SECURITY_VERSION);
} # securityValid
} # class SpotSecurity
debian/db-update/v20111210/lib/notifications/ 0000755 0000000 0000000 00000000000 12267045571 015540 5 ustar debian/db-update/v20111210/lib/notifications/Notifications_Factory.php 0000644 0000000 0000000 00000002235 12267045571 022553 0 ustar _db = $db;
$this->_settings = $settings;
} # ctor
function update() {
$this->createSecurityGroups();
$this->createAnonymous();
$this->createAdmin();
$this->updateUserPreferences();
$this->updateSecurityGroupMembership();
$this->updateUserFilters();
# Fix for a bug introduced by me (3nov2011)
if ($this->_settings->get('securityversion') === 'true') {
# Reinsert the users' membership of groups
$dbCon = $this->_db->getDbHandle();
$dbCon->rawExec("INSERT INTO usergroups(userid, groupid, prio) VALUES(1, 1, 1)");
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(2, 1, 1)");
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(2, 2, 2)");
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(2, 3, 3)");
# now for all users, readd them to the default 'authenticated users' membership
$userList = $this->_db->listUsers("", 0, 9999999);
foreach($userList['list'] as $user) {
if ($user['userid'] > 2) {
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(" . $user['userid'] . ", 1, 1)");
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(" . $user['userid'] . ", 2, 2)");
} # if
} # foreach
} # if
$this->updateSecurityVersion();
} # update()
/*
* Creates the anonymous user
*/
function createAnonymous() {
# if we already have an anonymous user, exit
$anonUser = $this->_db->getUser(1);
if ($anonUser !== false) {
if ($anonUser['username'] != 'anonymous') {
throw new Exception("Anonymous user is not anonymous anymore. Database logical corrupted, unable to continue");
} # if
return ;
} # if
# DB connectie
$dbCon = $this->_db->getDbHandle();
# Maak een apikey aan. Deze kan niet worden gebruikt, maar is bij voorkeur niet leeg
$apikey = md5('anonymous');
# Create the dummy 'anonymous' user
$anonymous_user = array(
# 'userid' => 1, <= Moet 1 zijn voor de anonymous user
'username' => 'anonymous',
'firstname' => 'Jane',
'passhash' => '',
'lastname' => 'Doe',
'mail' => 'john@example.com',
'apikey' => $apikey,
'lastlogin' => 0,
'lastvisit' => 0,
'deleted' => 0);
$this->_db->addUser($anonymous_user);
# update handmatig het userid
$currentId = $dbCon->singleQuery("SELECT id FROM users WHERE username = 'anonymous'");
$dbCon->exec("UPDATE users SET id = 1 WHERE username = 'anonymous'");
$dbCon->exec("UPDATE usersettings SET userid = 1 WHERE userid = '%s'", Array( (int) $currentId));
# Geef de anonieme user de anonymous group
$dbCon->rawExec("INSERT INTO usergroups(userid, groupid, prio) VALUES(1, 1, 1)");
} # createAnonymous
/*
* Create the admin user
*/
function createAdmin() {
# if we already have an admin user, exit
$adminUser = $this->_db->getUser(2);
if ($adminUser !== false) {
return ;
} # if
# DB connectie
$dbCon = $this->_db->getDbHandle();
# Vraag de password salt op
$passSalt = $this->_settings->get('pass_salt');
# Bereken het password van de dummy admin user
$adminPwdHash = sha1(strrev(substr($passSalt, 1, 3)) . 'admin' . $passSalt);
# Maak een apikey aan. Deze kan niet worden gebruikt, maar is bij voorkeur niet leeg
$apikey = md5('admin');
# Create the dummy 'admin' user
$admin_user = array(
# 'userid' => 2,
'username' => 'admin',
'firstname' => 'admin',
'passhash' => $adminPwdHash,
'lastname' => 'user',
'mail' => 'spotwebadmin@example.com',
'apikey' => $apikey,
'lastlogin' => 0,
'lastvisit' => 0,
'deleted' => 0);
$this->_db->addUser($admin_user);
# update handmatig het userid
$currentId = $dbCon->singleQuery("SELECT id FROM users WHERE username = 'admin'");
$dbCon->exec("UPDATE users SET id = 2 WHERE username = 'admin'");
$dbCon->exec("UPDATE usersettings SET userid = 2 WHERE userid = '%s'", Array( (int) $currentId));
# Geef user 2 (de admin user, naar we van uit gaan) de anon, auth en admin group
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(2, 1, 1)");
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(2, 2, 2)");
$dbCon->rawExec("INSERT INTO usergroups(userid,groupid,prio) VALUES(2, 3, 3)");
} # createAdmin
/*
* Update all users preferences
*/
function updateUserPreferences() {
$userList = $this->_db->listUsers("", 0, 9999999);
# loop through every user and fix it
foreach($userList['list'] as $user) {
# Omdat we vanuit listUsers() niet alle velden meekrijgen
# vragen we opnieuw het user record op
$user = $this->_db->getUser($user['userid']);
# set the users' preferences
$this->setSettingIfNot($user['prefs'], 'perpage', 25);
$this->setSettingIfNot($user['prefs'], 'date_formatting', 'human');
$this->setSettingIfNot($user['prefs'], 'template', 'we1rdo');
$this->setSettingIfNot($user['prefs'], 'count_newspots', true);
$this->setSettingIfNot($user['prefs'], 'keep_seenlist', true);
$this->setSettingIfNot($user['prefs'], 'auto_markasread', true);
$this->setSettingIfNot($user['prefs'], 'keep_downloadlist', true);
$this->setSettingIfNot($user['prefs'], 'keep_watchlist', true);
$this->setSettingIfNot($user['prefs'], 'nzb_search_engine', 'nzbindex');
$this->setSettingIfNot($user['prefs'], 'show_filesize', true);
$this->setSettingIfNot($user['prefs'], 'show_reportcount', true);
$this->setSettingIfNot($user['prefs'], 'show_nzbbutton', true);
$this->setSettingIfNot($user['prefs'], 'show_multinzb', true);
$this->setSettingIfNot($user['prefs'], 'customcss', '');
$this->setSettingIfNot($user['prefs'], 'newspotdefault_tag', $user['username']);
$this->setSettingIfNot($user['prefs'], 'newspotdefault_body', '');
$this->setSettingIfNot($user['prefs'], 'user_language', 'nl_NL');
$this->setSettingIfNot($user['prefs'], 'show_avatars', true);
$this->setSettingIfNot($user['prefs'], 'usemailaddress_for_gravatar', true);
$this->setSettingIfNot($user['prefs']['nzbhandling'], 'action', 'disable');
$this->setSettingIfNot($user['prefs']['nzbhandling'], 'local_dir', '/tmp');
$this->setSettingIfNot($user['prefs']['nzbhandling'], 'prepare_action', 'merge');
$this->setSettingIfNot($user['prefs']['nzbhandling'], 'command', '');
$this->setSettingIfNot($user['prefs']['nzbhandling']['sabnzbd'], 'url', '');
$this->setSettingIfNot($user['prefs']['nzbhandling']['sabnzbd'], 'apikey', '');
$this->setSettingIfNot($user['prefs']['nzbhandling']['nzbget'], 'host', '');
$this->setSettingIfNot($user['prefs']['nzbhandling']['nzbget'], 'port', '');
$this->setSettingIfNot($user['prefs']['nzbhandling']['nzbget'], 'username', '');
$this->setSettingIfNot($user['prefs']['nzbhandling']['nzbget'], 'password', '');
$this->setSettingIfNot($user['prefs']['nzbhandling']['nzbget'], 'timeout', 15);
$this->setSettingIfNot($user['prefs']['notifications']['boxcar'], 'email', '');
$this->setSettingIfNot($user['prefs']['notifications']['growl'], 'host', '');
$this->setSettingIfNot($user['prefs']['notifications']['growl'], 'password', '');
$this->setSettingIfNot($user['prefs']['notifications']['nma'], 'api', '');
$this->setSettingIfNot($user['prefs']['notifications']['notifo'], 'username', '');
$this->setSettingIfNot($user['prefs']['notifications']['notifo'], 'api', '');
$this->setSettingIfNot($user['prefs']['notifications']['prowl'], 'apikey', '');
$this->setSettingIfNot($user['prefs']['notifications']['twitter'], 'screen_name', '');
$this->setSettingIfNot($user['prefs']['notifications']['twitter'], 'request_token', '');
$this->setSettingIfNot($user['prefs']['notifications']['twitter'], 'request_token_secret', '');
$this->setSettingIfNot($user['prefs']['notifications']['twitter'], 'access_token', '');
$this->setSettingIfNot($user['prefs']['notifications']['twitter'], 'access_token_secret', '');
$notifProviders = Notifications_Factory::getActiveServices();
foreach ($notifProviders as $notifProvider) {
$this->setSettingIfNot($user['prefs']['notifications'][$notifProvider], 'enabled', false);
$this->setSettingIfNot($user['prefs']['notifications'][$notifProvider]['events'], 'watchlist_handled', false);
$this->setSettingIfNot($user['prefs']['notifications'][$notifProvider]['events'], 'nzb_handled', false);
$this->setSettingIfNot($user['prefs']['notifications'][$notifProvider]['events'], 'retriever_finished', false);
$this->setSettingIfNot($user['prefs']['notifications'][$notifProvider]['events'], 'report_posted', false);
$this->setSettingIfNot($user['prefs']['notifications'][$notifProvider]['events'], 'spot_posted', false);
$this->setSettingIfNot($user['prefs']['notifications'][$notifProvider]['events'], 'user_added', false);
} // foreach
# make sure a sort preference is defined. An empty field means relevancy
$this->setSettingIfNot($user['prefs'], 'defaultsortfield', '');
# oude settings verwijderen
$this->unsetSetting($user['prefs'], 'search_url');
$this->unsetSetting($user['prefs']['notifications'], 'libnotify');
# controleren dat de user een geldige RSA key heeft
if ($user['userid'] > 2) {
$rsaKey = $this->_db->getUserPrivateRsaKey($user['userid']);
if (empty($rsaKey)) {
# Creer een private en public key paar voor deze user
$spotSigning = new SpotSigning();
$userKey = $spotSigning->createPrivateKey($this->_settings->get('openssl_cnf_path'));
$this->_db->setUserRsaKeys($user['userid'], $userKey['public'], $userKey['private']);
} # if
} # if
# update the user record in the database
$this->_db->setUser($user);
} # foreach
} # update()
/*
* Creeer de default security groepen
*/
function createSecurityGroups() {
# DB connectie
$dbCon = $this->_db->getDbHandle();
if ($this->_settings->get('securityversion') < 0.01) {
/* Truncate de huidige permissies */
$dbCon->rawExec("DELETE FROM securitygroups");
/* Creeer de security groepen */
$dbCon->rawExec("INSERT INTO securitygroups(id,name) VALUES(1, 'Anonymous users')");
$dbCon->rawExec("INSERT INTO securitygroups(id,name) VALUES(2, 'Authenticated users')");
$dbCon->rawExec("INSERT INTO securitygroups(id,name) VALUES(3, 'Administrators')");
} # if
} # createSecurityGroups
/*
* Update de 'default' security groepen hun rechten
*/
function updateSecurityGroupMembership() {
# DB connectie
$dbCon = $this->_db->getDbHandle();
if ($this->_settings->get('securityversion') < 0.01) {
/* Truncate de huidige permissies */
$dbCon->rawExec("DELETE FROM grouppermissions");
/* Default permissions for anonymous users */
$anonPerms = array(SpotSecurity::spotsec_view_spots_index, SpotSecurity::spotsec_perform_login, SpotSecurity::spotsec_perform_search,
SpotSecurity::spotsec_view_spotdetail, SpotSecurity::spotsec_retrieve_nzb, SpotSecurity::spotsec_view_spotimage,
SpotSecurity::spotsec_view_statics, SpotSecurity::spotsec_create_new_user, SpotSecurity::spotsec_view_comments,
SpotSecurity::spotsec_view_spotcount_total);
foreach($anonPerms as $anonPerm) {
$dbCon->rawExec("INSERT INTO grouppermissions(groupid,permissionid) VALUES(1, " . $anonPerm . ")");
} # foreach
/* Default permissions for authenticated users */
$authedPerms = array(SpotSecurity::spotsec_download_integration, SpotSecurity::spotsec_mark_spots_asread, SpotSecurity::spotsec_view_rssfeed,
SpotSecurity::spotsec_edit_own_userprefs, SpotSecurity::spotsec_edit_own_user, SpotSecurity::spotsec_post_comment,
SpotSecurity::spotsec_perform_logout, SpotSecurity::spotsec_use_sabapi, SpotSecurity::spotsec_keep_own_watchlist,
SpotSecurity::spotsec_keep_own_downloadlist, SpotSecurity::spotsec_keep_own_seenlist, SpotSecurity::spotsec_view_spotcount_filtered,
SpotSecurity::spotsec_select_template, SpotSecurity::spotsec_consume_api);
foreach($authedPerms as $authedPerm) {
$dbCon->rawExec("INSERT INTO grouppermissions(groupid,permissionid) VALUES(2, " . $authedPerm . ")");
} # foreach
/* Default permissions for administrative users */
$adminPerms = array(SpotSecurity::spotsec_list_all_users, SpotSecurity::spotsec_retrieve_spots, SpotSecurity::spotsec_edit_other_users);
foreach($adminPerms as $adminPerm) {
$dbCon->rawExec("INSERT INTO grouppermissions(groupid,permissionid) VALUES(3, " . $adminPerm . ")");
} # foreach
} # if
# We voegen nog extra security toe voor de logged in user, deze mag gebruik
# maken van een aantal paginas via enkel api authenticatie
if ($this->_settings->get('securityversion') < 0.02) {
$dbCon->rawExec("DELETE FROM grouppermissions WHERE permissionid = " . SpotSecurity::spotsec_consume_api . " AND
objectid in ('rss', 'newznabapi', 'getnzb', 'getspot')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_consume_api . ", 'rss')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_consume_api . ", 'newznabapi')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_consume_api . ", 'getnzb')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_consume_api . ", 'getspot')");
} # if
# We voegen nog extra security toe voor de logged in user, deze mag gebruik
# maken van een aantal download integration settings. De admin user mag ze van
# allemaal (tot nu toe bekent) gebruik maken.
if ($this->_settings->get('securityversion') < 0.03) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_download_integration . ", 'disable')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_download_integration . ", 'client-sabnzbd')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(3, " . SpotSecurity::spotsec_download_integration . ", 'push-sabnzbd')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(3, " . SpotSecurity::spotsec_download_integration . ", 'save')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(3, " . SpotSecurity::spotsec_download_integration . ", 'runcommand')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(3, " . SpotSecurity::spotsec_download_integration . ", 'nzbget')");
} # if
# We voegen nog extra security toe voor de admin user, deze mag users wissen en
# groepen van users aanpassen
if ($this->_settings->get('securityversion') < 0.06) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(3, " . SpotSecurity::spotsec_delete_user . ")");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(3, " . SpotSecurity::spotsec_edit_groupmembership . ")");
} # if
# We voegen nog extra security toe voor de admin user, deze mag group membership van
# een user tonen, en securitygroepen inhoudelijk wijzigen
if ($this->_settings->get('securityversion') < 0.07) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(3, " . SpotSecurity::spotsec_display_groupmembership . ")");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(3, " . SpotSecurity::spotsec_edit_securitygroups . ")");
} # if
# We voegen nog extra security toe voor notificaties
if ($this->_settings->get('securityversion') < 0.08) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_services . ")");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_services . ", 'email')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(3, " . SpotSecurity::spotsec_send_notifications_services . ", 'growl')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_services . ", 'notifo')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_services . ", 'prowl')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_types . ")");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_types . ", 'nzb_handled')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(3, " . SpotSecurity::spotsec_send_notifications_types . ", 'retriever_finished')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(3, " . SpotSecurity::spotsec_send_notifications_types . ", 'user_added')");
} # if
# We voegen nog extra security toe voor custom stylesheets
if ($this->_settings->get('securityversion') < 0.09) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_allow_custom_stylesheet . ")");
} # if
# We voegen nog extra security toe voor watchlist notificaties en een vergeten NZB download
if ($this->_settings->get('securityversion') < 0.10) {
$dbCon->rawExec("DELETE FROM grouppermissions WHERE permissionid = " . SpotSecurity::spotsec_send_notifications_services . " AND objectid = 'libnotify'");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_types . ", 'watchlist_handled')");
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_consume_api . ", 'getnzbmobile')");
} # if
# Twitter toegevoegd
if ($this->_settings->get('securityversion') < 0.11) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_services . ", 'twitter')");
} # if
# Zelf filters kunnen wijzigen
if ($this->_settings->get('securityversion') < 0.12) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_keep_own_filters . ")");
} # if
# Filters als default in kunnen stellen voor de anonymous user
if ($this->_settings->get('securityversion') < 0.13) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(3, " . SpotSecurity::spotsec_set_filters_as_default . ")");
} # if
# Downloads kunnen wissen is een apart recht geworden (issue #935)
if ($this->_settings->get('securityversion') < 0.14) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid,objectid) VALUES(3, " . SpotSecurity::spotsec_keep_own_downloadlist . ", 'erasedls')");
} # if
# Spam reporting toegevoegd
if ($this->_settings->get('securityversion') < 0.15) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_report_spam . ")");
} # if
# Nieuwe spot posten toegevoegd
if ($this->_settings->get('securityversion') < 0.16) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_post_spot . ")");
} # if
# Notify My Android toegevoegd
if ($this->_settings->get('securityversion') < 0.17) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_services . ", 'nma')");
} # if
# Notificatie bij Spot Posten
if ($this->_settings->get('securityversion') < 0.18) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_types . ", 'spot_posted')");
} # if
# Notificatie bij Report Posten
if ($this->_settings->get('securityversion') < 0.19) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_types . ", 'report_posted')");
} # if
# Spotters kunnen blacklisten
if ($this->_settings->get('securityversion') < 0.20) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_blacklist_spotter . ")");
} # if
# Anonymous users mag welcomemail versturen
if ($this->_settings->get('securityversion') < 0.21) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(1, " . SpotSecurity::spotsec_send_notifications_services . ", 'welcomemail')");
} # if
# Boxcar toegevoegd
if ($this->_settings->get('securityversion') < 0.22) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid, objectid) VALUES(2, " . SpotSecurity::spotsec_send_notifications_services . ", 'boxcar')");
} # if
# Statistieken toegevoegd
if ($this->_settings->get('securityversion') < 0.23) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(2, " . SpotSecurity::spotsec_view_statistics . ")");
} # if
# Showing of avatars is an security right so administrators could globally disable this
if ($this->_settings->get('securityversion') < 0.24) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid,objectid) VALUES(1, " . SpotSecurity::spotsec_view_spotimage . ", 'avatar')");
} # if
# Showing the Spotweb updates is a security setting because it compares the current Spotweb version with the latest one it could be seen as
# information disclosure
if ($this->_settings->get('securityversion') < 0.25) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(3, " . SpotSecurity::spotsec_view_spotweb_updates . ")");
} # if
# Settings
if ($this->_settings->get('securityversion') < 0.26) {
$dbCon->rawExec("INSERT IGNORE INTO grouppermissions(groupid,permissionid) VALUES(3, " . SpotSecurity::spotsec_edit_settings . ")");
} # if
} # updateSecurityGroups
/*
* Update user filters
*/
function updateUserFilters() {
if (($this->_settings->get('securityversion') < 0.12)) {
# DB connectie
$dbCon = $this->_db->getDbHandle();
# delete all existing filters
$dbCon->rawExec("DELETE FROM filters WHERE filtertype = 'filter'");
$userList = $this->_db->listUsers("", 0, 9999999);
# loop through every user and fix it
foreach($userList['list'] as $user) {
/* Beeld */
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Beeld', 'film', 0, 0, 'cat0_z0')");
$beeldFilterId = $dbCon->lastInsertId('filters');
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'DivX', 'divx', 0, " . $beeldFilterId . ", 'cat0_z0_a0')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'WMV', 'wmv', 1, " . $beeldFilterId . ", 'cat0_z0_a1')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'MPEG', 'mpg', 2, " . $beeldFilterId . ", 'cat0_z0_a2')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'DVD', 'dvd', 3, " . $beeldFilterId . ", 'cat0_z0_a3,cat0_z0_a10')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'HD', 'hd', 4, " . $beeldFilterId . ", 'cat0_z0_a4,cat0_z0_a6,cat0_z0_a7,cat0_z0_a8,cat0_z0_a9')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Series', 'tv', 5, " . $beeldFilterId . ", 'cat0_z1')");
/* Boeken */
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Boeken', 'book', 6, " . $beeldFilterId . ", 'cat0_z2')");
$boekenFilterId = $dbCon->lastInsertId('filters');
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Nederlands', 'book', 0, " . $boekenFilterId . ", 'cat0_z2_c11')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Engels', 'book', 1, " . $boekenFilterId . ", 'cat0_z2_c10')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Anders', 'book', 2, " . $boekenFilterId . ", 'cat0_z2,~cat0_z2_c10,~cat0_z2_c11')");
/* Erotiek */
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Erotiek', 'female', 7, " . $beeldFilterId. ", 'cat0_z3')");
$erotiekFilterId = $dbCon->lastInsertId('filters');
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Hetero', 'female', 0, " . $erotiekFilterId . ", 'cat0_z3_d75,cat0_z3_d23')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Homo', 'female', 1, " . $erotiekFilterId . ", 'cat0_z3_d74,cat0_z3_d24')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Lesbo', 'female', 2, " . $erotiekFilterId . ", 'cat0_z3_d73,cat0_z3_d25')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Bi', 'female', 3, " . $erotiekFilterId . ", 'cat0_z3_d72,cat0_z3_d26')");
/* Muziek */
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Muziek', 'music', 2, 0, 'cat1')");
$muziekFilterId = $dbCon->lastInsertId('filters');
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Compressed', 'music', 0, " . $muziekFilterId . ", 'cat1_a0,cat1_a3,cat1_a5,cat1_a6')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Lossless', 'music', 1, " . $muziekFilterId . ", 'cat1_a2,cat1_a4,cat1_a7,cat1_a8')");
/* Spellen */
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Spellen', 'controller', 3, 0, 'cat2')");
$gameFilterId = $dbCon->lastInsertId('filters');
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Windows', 'windows', 0, " . $gameFilterId . ", 'cat2_a0')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Mac / Linux', 'linux', 1, " . $gameFilterId . ", 'cat2_a1,cat2_a2')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Playstation', 'playstation', 2, " . $gameFilterId . ", 'cat2_a3,cat2_a4,cat2_a5,cat2_a12')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'XBox', 'xbox', 3, " . $gameFilterId . ", 'cat2_a6,cat2_a7')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Nintendo', 'nintendo_ds', 4, " . $gameFilterId . ", 'cat2_a8,cat2_a9,cat2_a10,cat2_a11')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Smartphone / PDA', 'pda', 5, " . $gameFilterId . ", 'cat2_a13,cat2_a14,cat2_a15')");
/* Applicaties */
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Applicaties', 'application', 4, 0, 'cat3')");
$appFilterId = $dbCon->lastInsertId('filters');
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Windows', 'vista', 0, " . $appFilterId . ", 'cat3_a0')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'Mac / Linux / OS2', 'linux', 1, " . $appFilterId . ", 'cat3_a1,cat3_a2,cat3_a3')");
$dbCon->rawExec("INSERT INTO filters(userid,filtertype,title,icon,torder,tparent,tree) VALUES(" . $user['userid'] . ", 'filter', 'PDA / Navigatie', 'pda', 2, " . $appFilterId . ", 'cat3_a4,cat3_a5,cat3_a6,cat3_a7')");
} # foreach
} # if
} # updateUserFilters
/*
* Update de huidige versie van de settings
*/
function updateSecurityVersion() {
# Lelijke truc om de class autoloader de SpotSecurity klasse te laten laden
if (SpotSecurity::spotsec_perform_login == 0) { } ;
$this->_settings->set('securityversion', SPOTWEB_SECURITY_VERSION);
} # updateSecurityVersion
/*
* Set een setting alleen als hij nog niet bestaat
*/
function setSettingIfNot(&$pref, $name, $value) {
if (isset($pref[$name])) {
return ;
} # if
$pref[$name] = $value;
} # setSettingIfNot
/*
* Verwijdert een gekozen setting
*/
function unsetSetting(&$pref, $name) {
if (!isset($pref[$name])) {
return ;
} # if
unset($pref[$name]);
} # setSettingIfNot
} # SpotUserUpgrader
debian/db-update/v20111210/lib/SpotDb.php 0000644 0000000 0000000 00000252434 12267045571 014605 0 ustar _dbsettings = $db;
} # __ctor
/*
* Open connectie naar de database (basically factory), de 'engine' wordt uit de
* settings gehaald die mee worden gegeven in de ctor.
*/
function connect() {
SpotTiming::start(__FUNCTION__);
/*
* Erase username/password so it won't show up in any stacktrace
*/
# SQlite heeft geen username gedefinieerd
if (isset($this->_dbsettings['user'])) {
$tmpUser = $this->_dbsettings['user'];
$this->_dbsettings['user'] = '*FILTERED*';
} # if
# en ook geen pass
if (isset($this->_dbsettings['pass'])) {
$tmpPass = $this->_dbsettings['pass'];
$this->_dbsettings['pass'] = '*FILTERED*';
} # if
switch ($this->_dbsettings['engine']) {
case 'mysql' : $this->_conn = new dbeng_mysql($this->_dbsettings['host'],
$tmpUser,
$tmpPass,
$this->_dbsettings['dbname']);
break;
case 'pdo_mysql': $this->_conn = new dbeng_pdo_mysql($this->_dbsettings['host'],
$tmpUser,
$tmpPass,
$this->_dbsettings['dbname']);
break;
case 'pdo_pgsql' : $this->_conn = new dbeng_pdo_pgsql($this->_dbsettings['host'],
$tmpUser,
$tmpPass,
$this->_dbsettings['dbname']);
break;
case 'pdo_sqlite': $this->_conn = new dbeng_pdo_sqlite($this->_dbsettings['path']);
break;
default : throw new Exception('Unknown DB engine specified, please choose pdo_sqlite, mysql or pdo_mysql');
} # switch
$this->_conn->connect();
SpotTiming::stop(__FUNCTION__);
} # connect
/*
* Geeft het database connectie object terug
*/
function getDbHandle() {
return $this->_conn;
} # getDbHandle
/*
* Haalt alle settings op uit de database
*/
function getAllSettings() {
$dbSettings = $this->_conn->arrayQuery('SELECT name,value,serialized FROM settings');
$tmpSettings = array();
foreach($dbSettings as $item) {
if ($item['serialized']) {
$item['value'] = unserialize($item['value']);
} # if
$tmpSettings[$item['name']] = $item['value'];
} # foreach
return $tmpSettings;
} # getAllSettings
function getMaxPacketSize() {
if ($this->_maxPacketSize == null) {
$packet = -1;
switch ($this->_dbsettings['engine']) {
case 'mysql' :
case 'pdo_mysql' : $packet = $this->_conn->arrayQuery("SHOW VARIABLES LIKE 'max_allowed_packet'");
$packet = $packet[0]['Value'];
break;
} # switch
$this->_maxPacketSize = $packet;
} # if
return $this->_maxPacketSize;
} # getMaxPacketSize
/*
* Controleer of een messageid niet al eerder gebruikt is door ons om hier
* te posten
*/
function isNewSpotMessageIdUnique($messageid) {
/*
* We use a union between our own messageids and the messageids we already
* know to prevent a user from spamming the spotweb system by using existing
* but valid spots
*/
$tmpResult = $this->_conn->singleQuery("SELECT messageid FROM commentsposted WHERE messageid = '%s'
UNION
SELECT messageid FROM spots WHERE messageid = '%s'",
Array($messageid, $messageid));
return (empty($tmpResult));
} # isNewSpotMessageIdUnique
/*
* Add the posted spot to the database
*/
function addPostedSpot($userId, $spot, $fullXml) {
$this->_conn->modify(
"INSERT INTO spotsposted(ouruserid, messageid, stamp, title, tag, category, subcats, fullxml)
VALUES(%d, '%s', %d, '%s', '%s', %d, '%s', '%s')",
Array((int) $userId,
$spot['newmessageid'],
(int) time(),
$spot['title'],
$spot['tag'],
(int) $spot['category'],
implode(',', $spot['subcatlist']),
$fullXml));
} # addPostedSpot
/*
* Controleer of een messageid niet al eerder gebruikt is door ons om hier
* te posten
*/
function isCommentMessageIdUnique($messageid) {
$tmpResult = $this->_conn->singleQuery("SELECT messageid FROM commentsposted WHERE messageid = '%s'",
Array($messageid));
return (empty($tmpResult));
} # isCommentMessageIdUnique
/*
* Controleer of een messageid niet al eerder gebruikt is door ons om hier
* te posten
*/
function isReportMessageIdUnique($messageid) {
$tmpResult = $this->_conn->singleQuery("SELECT messageid FROM reportsposted WHERE messageid = '%s'",
Array($messageid));
return (empty($tmpResult));
} # isReportMessageIdUnique
/*
* Controleer of een user reeds een spamreport heeft geplaatst voor de betreffende spot
*/
function isReportPlaced($messageid, $userId) {
$tmpResult = $this->_conn->singleQuery("SELECT messageid FROM reportsposted WHERE inreplyto = '%s' AND ouruserid = %d", Array($messageid, $userId));
return (!empty($tmpResult));
} #isReportPlaced
/*
* Sla het gepostte comment op van deze user
*/
function addPostedComment($userId, $comment) {
$this->_conn->modify(
"INSERT INTO commentsposted(ouruserid, messageid, inreplyto, randompart, rating, body, stamp)
VALUES('%d', '%s', '%s', '%s', '%d', '%s', %d)",
Array((int) $userId,
$comment['newmessageid'],
$comment['inreplyto'],
$comment['randomstr'],
(int) $comment['rating'],
$comment['body'],
(int) time()));
} # addPostedComment
/*
* Sla het gepostte report op van deze user
*/
function addPostedReport($userId, $report) {
$this->_conn->modify(
"INSERT INTO reportsposted(ouruserid, messageid, inreplyto, randompart, body, stamp)
VALUES('%d', '%s', '%s', '%s', '%s', %d)",
Array((int) $userId,
$report['newmessageid'],
$report['inreplyto'],
$report['randomstr'],
$report['body'],
(int) time()));
} # addPostedReport
/*
* Verwijder een setting
*/
function removeSetting($name) {
$this->_conn->exec("DELETE FROM settings WHERE name = '%s'", Array($name));
} # removeSetting
/*
* Update setting
*/
function updateSetting($name, $value) {
# zet het eventueel serialized in de database als dat nodig is
if ((is_array($value) || is_object($value))) {
$value = serialize($value);
$serialized = true;
} else {
$serialized = false;
} # if
switch ($this->_dbsettings['engine']) {
case 'mysql' :
case 'pdo_mysql' : {
$this->_conn->modify("INSERT INTO settings(name,value,serialized) VALUES ('%s', '%s', '%s') ON DUPLICATE KEY UPDATE value = '%s', serialized = %s",
Array($name, $value, $this->bool2dt($serialized), $value, $this->bool2dt($serialized)));
break;
} # mysql
default : {
$this->_conn->exec("UPDATE settings SET value = '%s', serialized = '%s' WHERE name = '%s'", Array($value, $this->bool2dt($serialized), $name));
if ($this->_conn->rows() == 0) {
$this->_conn->modify("INSERT INTO settings(name,value,serialized) VALUES('%s', '%s', '%s')", Array($name, $value, $this->bool2dt($serialized)));
} # if
break;
} # default
} # switch
} # updateSetting
/*
* Haalt een session op uit de database
*/
function getSession($sessionid, $userid) {
$tmp = $this->_conn->arrayQuery(
"SELECT s.sessionid as sessionid,
s.userid as userid,
s.hitcount as hitcount,
s.lasthit as lasthit,
s.ipaddr as ipaddr
FROM sessions AS s
WHERE (sessionid = '%s') AND (userid = %d)",
Array($sessionid,
(int) $userid));
if (!empty($tmp)) {
return $tmp[0];
} # if
return false;
} # getSession
/*
* Creert een sessie
*/
function addSession($session) {
$this->_conn->modify(
"INSERT INTO sessions(sessionid, userid, hitcount, lasthit, ipaddr)
VALUES('%s', %d, %d, %d, '%s')",
Array($session['sessionid'],
(int) $session['userid'],
(int) $session['hitcount'],
(int) $session['lasthit'],
$session['ipaddr']));
} # addSession
/*
* Haalt een session op uit de database
*/
function deleteSession($sessionid) {
$this->_conn->modify(
"DELETE FROM sessions WHERE sessionid = '%s'",
Array($sessionid));
} # deleteSession
/*
* Haalt een session op uit de database
*/
function deleteAllUserSessions($userid) {
$this->_conn->modify(
"DELETE FROM sessions WHERE userid = %d",
Array( (int) $userid));
} # deleteAllUserSessions
/*
* Haalt een session op uit de database
*/
function deleteExpiredSessions($maxLifeTime) {
$this->_conn->modify(
"DELETE FROM sessions WHERE lasthit < %d",
Array(time() - $maxLifeTime));
} # deleteExpiredSessions
/*
* Update de last hit van een session
*/
function hitSession($sessionid) {
$this->_conn->modify("UPDATE sessions
SET hitcount = hitcount + 1,
lasthit = %d
WHERE sessionid = '%s'",
Array(time(), $sessionid));
} # hitSession
/*
* Checkt of een username al bestaat
*/
function usernameExists($username) {
$tmpResult = $this->_conn->singleQuery("SELECT username FROM users WHERE username = '%s'", Array($username));
return (!empty($tmpResult));
} # usernameExists
/*
* Checkt of een emailaddress al bestaat
*/
function userEmailExists($mail) {
$tmpResult = $this->_conn->singleQuery("SELECT id FROM users WHERE mail = '%s'", Array($mail));
if (!empty($tmpResult)) {
return $tmpResult;
} # if
return false;
} # userEmailExists
/*
* Haalt een user op uit de database
*/
function getUser($userid) {
$tmp = $this->_conn->arrayQuery(
"SELECT u.id AS userid,
u.username AS username,
u.firstname AS firstname,
u.lastname AS lastname,
u.mail AS mail,
u.apikey AS apikey,
u.deleted AS deleted,
u.lastlogin AS lastlogin,
u.lastvisit AS lastvisit,
u.lastread AS lastread,
u.lastapiusage AS lastapiusage,
s.publickey AS publickey,
s.avatar AS avatar,
s.otherprefs AS prefs
FROM users AS u
JOIN usersettings s ON (u.id = s.userid)
WHERE u.id = %d AND NOT DELETED",
Array( (int) $userid ));
if (!empty($tmp)) {
# Other preferences worden serialized opgeslagen in de database
$tmp[0]['prefs'] = unserialize($tmp[0]['prefs']);
return $tmp[0];
} # if
return false;
} # getUser
/*
* Haalt een user op uit de database
*/
function listUsers($username, $pageNr, $limit) {
SpotTiming::start(__FUNCTION__);
$offset = (int) $pageNr * (int) $limit;
$tmpResult = $this->_conn->arrayQuery(
"SELECT u.id AS userid,
u.username AS username,
u.firstname AS firstname,
u.lastname AS lastname,
u.mail AS mail,
u.lastlogin AS lastlogin,
COALESCE((SELECT MAX(lasthit) FROM sessions WHERE userid = u.id), lastvisit) as lastvisit,
(SELECT ipaddr FROM sessions WHERE userid = u.id ORDER BY lasthit DESC LIMIT 1) as lastipaddr,
s.otherprefs AS prefs
FROM users AS u
JOIN usersettings s ON (u.id = s.userid)
WHERE (username LIKE '%" . $this->safe($username) . "%') AND (NOT DELETED)
LIMIT " . (int) ($limit + 1) ." OFFSET " . (int) $offset);
if (!empty($tmpResult)) {
# Other preferences worden serialized opgeslagen in de database
$tmpResultCount = count($tmpResult);
for($i = 0; $i < $tmpResultCount; $i++) {
$tmpResult[$i]['prefs'] = unserialize($tmpResult[$i]['prefs']);
} # for
} # if
# als we meer resultaten krijgen dan de aanroeper van deze functie vroeg, dan
# kunnen we er van uit gaan dat er ook nog een pagina is voor de volgende aanroep
$hasMore = (count($tmpResult) > $limit);
if ($hasMore) {
# verwijder het laatste, niet gevraagde, element
array_pop($tmpResult);
} # if
SpotTiming::stop(__FUNCTION__, array($username, $pageNr, $limit));
return array('list' => $tmpResult, 'hasmore' => $hasMore);
} # listUsers
/*
* Disable/delete een user. Echt wissen willen we niet
* omdat eventuele comments dan niet meer te traceren
* zouden zijn waardoor anti-spam maatregelen erg lastig
* worden
*/
function deleteUser($userid) {
$this->_conn->modify("UPDATE users
SET deleted = true
WHERE id = '%s'",
Array( (int) $userid));
} # deleteUser
/*
* Update de informatie over een user behalve het password
*/
function setUser($user) {
# eerst updaten we de users informatie
$this->_conn->modify("UPDATE users
SET firstname = '%s',
lastname = '%s',
mail = '%s',
apikey = '%s',
lastlogin = %d,
lastvisit = %d,
lastread = %d,
lastapiusage = %d,
deleted = '%s'
WHERE id = %d",
Array($user['firstname'],
$user['lastname'],
$user['mail'],
$user['apikey'],
(int) $user['lastlogin'],
(int) $user['lastvisit'],
(int) $user['lastread'],
(int) $user['lastapiusage'],
$this->bool2dt($user['deleted']),
(int) $user['userid']));
# daarna updaten we zijn preferences
$this->_conn->modify("UPDATE usersettings
SET otherprefs = '%s'
WHERE userid = '%s'",
Array(serialize($user['prefs']),
(int) $user['userid']));
} # setUser
/*
* Stel users' password in
*/
function setUserPassword($user) {
$this->_conn->modify("UPDATE users
SET passhash = '%s'
WHERE id = '%s'",
Array($user['passhash'],
(int) $user['userid']));
} # setUserPassword
/*
* Vul de public en private key van een user in, alle andere
* user methodes kunnen dit niet updaten omdat het altijd
* een paar moet zijn
*/
function setUserRsaKeys($userId, $publicKey, $privateKey) {
# eerst updaten we de users informatie
$this->_conn->modify("UPDATE usersettings
SET publickey = '%s',
privatekey = '%s'
WHERE userid = '%s'",
Array($publicKey, $privateKey, $userId));
} # setUserRsaKeys
/*
* Vraagt de users' private key op
*/
function getUserPrivateRsaKey($userId) {
return $this->_conn->singleQuery("SELECT privatekey FROM usersettings WHERE userid = '%s'",
Array($userId));
} # getUserPrivateRsaKey
/*
* Voeg een user toe
*/
function addUser($user) {
$this->_conn->modify("INSERT INTO users(username, firstname, lastname, passhash, mail, apikey, lastread, deleted)
VALUES('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')",
Array($user['username'],
$user['firstname'],
$user['lastname'],
$user['passhash'],
$user['mail'],
$user['apikey'],
$this->getMaxMessageTime(),
$this->bool2dt(false)));
# We vragen nu het userrecord terug op om het userid te krijgen,
# niet echt een mooie oplossing, maar we hebben blijkbaar geen
# lastInsertId() exposed in de db klasse
$user['userid'] = $this->_conn->singleQuery("SELECT id FROM users WHERE username = '%s'", Array($user['username']));
# en voeg een usersettings record in
$this->_conn->modify("INSERT INTO usersettings(userid, privatekey, publickey, otherprefs)
VALUES('%s', '', '', 'a:0:{}')",
Array((int)$user['userid']));
return $user;
} # addUser
/*
* Kan de user inloggen met opgegeven password of API key?
*
* Een userid als de user gevonden kan worden, of false voor failure
*/
function authUser($username, $passhash) {
if ($username === false) {
$tmp = $this->_conn->arrayQuery("SELECT id FROM users WHERE apikey = '%s' AND NOT DELETED", Array($passhash));
} else {
$tmp = $this->_conn->arrayQuery("SELECT id FROM users WHERE username = '%s' AND passhash = '%s' AND NOT DELETED", Array($username, $passhash));
} # if
return (empty($tmp)) ? false : $tmp[0]['id'];
} # authUser
/*
* Update of insert the maximum article id in de database.
*/
function setMaxArticleId($server, $maxarticleid) {
switch ($this->_dbsettings['engine']) {
case 'mysql' :
case 'pdo_mysql' : {
$this->_conn->modify("INSERT INTO nntp(server, maxarticleid) VALUES ('%s', '%s') ON DUPLICATE KEY UPDATE maxarticleid = '%s'",
Array($server, (int) $maxarticleid, (int) $maxarticleid));
break;
} # mysql
default : {
$this->_conn->exec("UPDATE nntp SET maxarticleid = '%s' WHERE server = '%s'", Array((int) $maxarticleid, $server));
if ($this->_conn->rows() == 0) {
$this->_conn->modify("INSERT INTO nntp(server, maxarticleid) VALUES('%s', '%s')", Array($server, (int) $maxarticleid));
} # if
break;
} # default
} # switch
} # setMaxArticleId()
/*
* Vraag het huidige articleid (van de NNTP server) op, als die nog
* niet bestaat, voeg dan een nieuw record toe en zet die op 0
*/
function getMaxArticleId($server) {
$artId = $this->_conn->singleQuery("SELECT maxarticleid FROM nntp WHERE server = '%s'", Array($server));
if ($artId == null) {
$this->setMaxArticleId($server, 0);
$artId = 0;
} # if
return $artId;
} # getMaxArticleId
/*
* Returns the highest messageid from server
*/
function getMaxMessageId($headers) {
if ($headers == 'headers') {
$msgIds = $this->_conn->arrayQuery("SELECT messageid FROM spots ORDER BY id DESC LIMIT 5000");
} elseif ($headers == 'comments') {
$msgIds = $this->_conn->arrayQuery("SELECT messageid FROM commentsxover ORDER BY id DESC LIMIT 5000");
} elseif ($headers == 'reports') {
$msgIds = $this->_conn->arrayQuery("SELECT messageid FROM reportsxover ORDER BY id DESC LIMIT 5000");
} else {
throw new Exception("getMaxMessageId() header-type value is unknown");
} # else
if ($msgIds == null) {
return array();
} # if
$tempMsgIdList = array();
$msgIdCount = count($msgIds);
for($i = 0; $i < $msgIdCount; $i++) {
$tempMsgIdList['<' . $msgIds[$i]['messageid'] . '>'] = 1;
} # for
return $tempMsgIdList;
} # func. getMaxMessageId
function getMaxMessageTime() {
$stamp = $this->_conn->singleQuery("SELECT MAX(stamp) AS stamp FROM spots");
if ($stamp == null) {
$stamp = time();
} # if
return $stamp;
} # getMaxMessageTime()
/*
* Geeft een database engine specifieke text-match (bv. fulltxt search) query onderdeel terug
*/
function createTextQuery($fieldList) {
return $this->_conn->createTextQuery($fieldList);
} # createTextQuery()
/*
* Geef terug of de huidige nntp server al bezig is volgens onze eigen database
*/
function isRetrieverRunning($server) {
$artId = $this->_conn->singleQuery("SELECT nowrunning FROM nntp WHERE server = '%s'", Array($server));
return ((!empty($artId)) && ($artId > (time() - 900)));
} # isRetrieverRunning
/*
* Geef terug of de huidige nntp server al bezig is volgens onze eigen database
*/
function setRetrieverRunning($server, $isRunning) {
if ($isRunning) {
$runTime = time();
} else {
$runTime = 0;
} # if
switch ($this->_dbsettings['engine']) {
case 'mysql' :
case 'pdo_mysql' : {
$this->_conn->modify("INSERT INTO nntp (server, nowrunning) VALUES ('%s', %d) ON DUPLICATE KEY UPDATE nowrunning = %d",
Array($server, (int) $runTime, (int) $runTime));
break;
} # mysql
default : {
$this->_conn->modify("UPDATE nntp SET nowrunning = %d WHERE server = '%s'", Array((int) $runTime, $server));
if ($this->_conn->rows() == 0) {
$this->_conn->modify("INSERT INTO nntp(server, nowrunning) VALUES('%s', %d)", Array($server, (int) $runTime));
} # if
} # default
} # switch
} # setRetrieverRunning
/*
* Remove extra spots
*/
function removeExtraSpots($messageId) {
# vraag eerst het id op
$spot = $this->getSpotHeader($messageId);
/*
* The spot might be empty because - for example, the spot
* is moderated (and hence deleted), the highest spot retrieved
* might be missing from the database because of the spam cleanup.
*
* Ignore this error
*/
if (empty($spot)) {
return ;
} # if
# en wis nu alles wat 'jonger' is dan deze spot
switch ($this->_dbsettings['engine']) {
# geen join delete omdat sqlite dat niet kan
case 'pdo_pgsql' :
case 'pdo_sqlite' : {
$this->_conn->modify("DELETE FROM spotsfull WHERE messageid IN (SELECT messageid FROM spots WHERE id > %d)", Array($spot['id']));
$this->_conn->modify("DELETE FROM spots WHERE id > %d", Array($spot['id']));
break;
} # case
default : {
$this->_conn->modify("DELETE FROM spots, spotsfull USING spots
LEFT JOIN spotsfull on spots.messageid=spotsfull.messageid
WHERE spots.id > %d", array($spot['id']));
} # default
} # switch
} # removeExtraSpots
/*
* Remove extra comments
*/
function removeExtraComments($messageId) {
# vraag eerst het id op
$commentId = $this->_conn->singleQuery("SELECT id FROM commentsxover WHERE messageid = '%s'", Array($messageId));
# als deze spot leeg is, is er iets raars aan de hand
if (empty($commentId)) {
throw new Exception("Our highest comment is not in the database!?");
} # if
# en wis nu alles wat 'jonger' is dan deze spot
$this->_conn->modify("DELETE FROM commentsxover WHERE id > %d", Array($commentId));
} # removeExtraComments
/*
* Remove extra comments
*/
function removeExtraReports($messageId) {
# vraag eerst het id op
$reportId = $this->_conn->singleQuery("SELECT id FROM reportsxover WHERE messageid = '%s'", Array($messageId));
# als deze report leeg is, is er iets raars aan de hand
if (empty($reportId)) {
throw new Exception("Our highest report is not in the database!?");
} # if
# en wis nu alles wat 'jonger' is dan deze spot
$this->_conn->modify("DELETE FROM reportsxover WHERE id > %d", Array($reportId));
} # removeExtraReports
/*
* Zet de tijd/datum wanneer retrieve voor het laatst geupdate heeft
*/
function setLastUpdate($server) {
return $this->_conn->modify("UPDATE nntp SET lastrun = '%d' WHERE server = '%s'", Array(time(), $server));
} # getLastUpdate
/*
* Geef de datum van de laatste update terug
*/
function getLastUpdate($server) {
return $this->_conn->singleQuery("SELECT lastrun FROM nntp WHERE server = '%s'", Array($server));
} # getLastUpdate
/**
* Geef het aantal spots terug dat er op dit moment in de db zit
*/
function getSpotCount($sqlFilter) {
SpotTiming::start(__FUNCTION__);
if (empty($sqlFilter)) {
$query = "SELECT COUNT(1) FROM spots AS s";
} else {
$query = "SELECT COUNT(1) FROM spots AS s
LEFT JOIN spotsfull AS f ON s.messageid = f.messageid
LEFT JOIN spotstatelist AS l ON s.messageid = l.messageid
WHERE " . $sqlFilter;
} # else
$cnt = $this->_conn->singleQuery($query);
SpotTiming::stop(__FUNCTION__, array($sqlFilter));
if ($cnt == null) {
return 0;
} else {
return $cnt;
} # if
} # getSpotCount
function getSpotCountPerHour($limit) {
$filter = ($limit) ? "WHERE stamp > " . strtotime("-1 " . $limit) : '';
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' : $rs = $this->_conn->arrayQuery("SELECT EXTRACT(HOUR FROM to_timestamp(stamp)) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;"); break;
case 'pdo_sqlite' : $rs = $this->_conn->arrayQuery("SELECT strftime('%H', time(stamp, 'unixepoch')) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;"); break;
default : $rs = $this->_conn->arrayQuery("SELECT EXTRACT(HOUR FROM FROM_UNIXTIME(stamp)) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;");
} # switch
return $rs;
} # getSpotCountPerHour
function getSpotCountPerWeekday($limit) {
$filter = ($limit) ? "WHERE stamp > " . strtotime("-1 " . $limit) : '';
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' : $rs = $this->_conn->arrayQuery("SELECT EXTRACT(DOW FROM to_timestamp(stamp)) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;"); break;
case 'pdo_sqlite' : $rs = $this->_conn->arrayQuery("SELECT strftime('%w', time(stamp, 'unixepoch')) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;"); break;
default : $rs = $this->_conn->arrayQuery("SELECT FROM_UNIXTIME(stamp,'%w') AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;");
} # switch
return $rs;
} # getSpotCountPerWeekday
function getSpotCountPerMonth($limit) {
$filter = ($limit) ? "WHERE stamp > " . strtotime("-1 " . $limit) : '';
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' : $rs = $this->_conn->arrayQuery("SELECT EXTRACT(MONTH FROM to_timestamp(stamp)) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;"); break;
case 'pdo_sqlite' : $rs = $this->_conn->arrayQuery("SELECT strftime('%m', time(stamp, 'unixepoch')) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;"); break;
default : $rs = $this->_conn->arrayQuery("SELECT EXTRACT(MONTH FROM FROM_UNIXTIME(stamp)) AS data, count(*) AS amount FROM spots " . $filter . " GROUP BY data;");
} # switch
return $rs;
} # getSpotCountPerMonth
function getSpotCountPerCategory($limit) {
$filter = ($limit) ? "WHERE stamp > " . strtotime("-1 " . $limit) : '';
$rs = $this->_conn->arrayQuery("SELECT category AS data, COUNT(category) AS amount FROM spots " . $filter . " GROUP BY data;");
return $rs;
} # getSpotCountPerCategory
function getOldestSpotTimestamp() {
$rs = $this->_conn->singleQuery("SELECT MIN(stamp) FROM spots;");
return $rs;
} # getOldestSpotTimestamp
/*
* Match set of comments
*/
function matchCommentMessageIds($hdrList) {
# We negeren commentsfull hier een beetje express, als die een
# keer ontbreken dan fixen we dat later wel.
$idList = array('comment' => array(), 'fullcomment' => array());
# geen message id's gegeven? vraag het niet eens aan de db
if (count($hdrList) == 0) {
return $idList;
} # if
# bereid de lijst voor met de queries in de where
$msgIdList = '';
foreach($hdrList as $hdr) {
$msgIdList .= "'" . substr($this->_conn->safe($hdr['Message-ID']), 1, -1) . "', ";
} # foreach
$msgIdList = substr($msgIdList, 0, -2);
# Omdat MySQL geen full joins kent, doen we het zo
$rs = $this->_conn->arrayQuery("SELECT messageid AS comment, '' AS fullcomment FROM commentsxover WHERE messageid IN (" . $msgIdList . ")
UNION
SELECT '' as comment, messageid AS fullcomment FROM commentsfull WHERE messageid IN (" . $msgIdList . ")");
# en lossen we het hier op
foreach($rs as $msgids) {
if (!empty($msgids['comment'])) {
$idList['comment'][$msgids['comment']] = 1;
} # if
if (!empty($msgids['fullcomment'])) {
$idList['fullcomment'][$msgids['fullcomment']] = 1;
} # if
} # foreach
return $idList;
} # matchCommentMessageIds
/*
* Match set of reports
*/
function matchReportMessageIds($hdrList) {
$idList = array();
# geen message id's gegeven? vraag het niet eens aan de db
if (count($hdrList) == 0) {
return $idList;
} # if
# bereid de lijst voor met de queries in de where
$msgIdList = '';
foreach($hdrList as $hdr) {
$msgIdList .= "'" . substr($this->_conn->safe($hdr['Message-ID']), 1, -1) . "', ";
} # foreach
$msgIdList = substr($msgIdList, 0, -2);
# en vraag alle comments op die we kennen
$rs = $this->_conn->arrayQuery("SELECT messageid FROM reportsxover WHERE messageid IN (" . $msgIdList . ")");
# geef hier een array terug die kant en klaar is voor array_search
foreach($rs as $msgids) {
$idList[$msgids['messageid']] = 1;
} # foreach
return $idList;
} # matchReportMessageIds
/*
* Match set of spots
*/
function matchSpotMessageIds($hdrList) {
$idList = array('spot' => array(), 'fullspot' => array());
# geen message id's gegeven? vraag het niet eens aan de db
if (count($hdrList) == 0) {
return $idList;
} # if
# bereid de lijst voor met de queries in de where
$msgIdList = '';
foreach($hdrList as $hdr) {
$msgIdList .= "'" . substr($this->_conn->safe($hdr['Message-ID']), 1, -1) . "', ";
} # foreach
$msgIdList = substr($msgIdList, 0, -2);
# Omdat MySQL geen full joins kent, doen we het zo
$rs = $this->_conn->arrayQuery("SELECT messageid AS spot, '' AS fullspot FROM spots WHERE messageid IN (" . $msgIdList . ")
UNION
SELECT '' as spot, messageid AS fullspot FROM spotsfull WHERE messageid IN (" . $msgIdList . ")");
# en lossen we het hier op
foreach($rs as $msgids) {
if (!empty($msgids['spot'])) {
$idList['spot'][$msgids['spot']] = 1;
} # if
if (!empty($msgids['fullspot'])) {
$idList['fullspot'][$msgids['fullspot']] = 1;
} # if
} # foreach
return $idList;
} # matchMessageIds
/*
* Geef alle spots terug in de database die aan $parsedSearch voldoen.
*
*/
function getSpots($ourUserId, $pageNr, $limit, $parsedSearch) {
SpotTiming::start(__FUNCTION__);
$results = array();
$offset = (int) $pageNr * (int) $limit;
# je hebt de zoek criteria (category, titel, etc)
$criteriaFilter = ' WHERE (bl.spotterid IS NULL)';
if (!empty($parsedSearch['filter'])) {
$criteriaFilter .= ' AND ' . $parsedSearch['filter'];
} # if
# er kunnen ook nog additionele velden gevraagd zijn door de filter parser
# als dat zo is, voeg die dan ook toe
$extendedFieldList = '';
foreach($parsedSearch['additionalFields'] as $additionalField) {
$extendedFieldList = ', ' . $additionalField . $extendedFieldList;
} # foreach
# ook additionele tabellen kunnen gevraagd zijn door de filter parser, die
# moeten we dan ook toevoegen
$additionalTableList = '';
foreach($parsedSearch['additionalTables'] as $additionalTable) {
$additionalTableList = ', ' . $additionalTable . $additionalTableList;
} # foreach
# zelfs additionele joinskunnen gevraagd zijn door de filter parser, die
# moeten we dan ook toevoegen
$additionalJoinList = '';
foreach($parsedSearch['additionalJoins'] as $additionalJoin) {
$additionalJoinList = ' ' . $additionalJoin['jointype'] . ' JOIN ' .
$additionalJoin['tablename'] . ' AS ' . $additionalJoin['tablealias'] .
' ON (' . $additionalJoin['joincondition'] . ') ';
} # foreach
# Nu prepareren we de sorterings lijst
$sortFields = $parsedSearch['sortFields'];
$sortList = array();
foreach($sortFields as $sortValue) {
if (!empty($sortValue)) {
# als er gevraagd is om op 'stamp' descending te sorteren, dan draaien we dit
# om en voeren de query uit reversestamp zodat we een ASCending sort doen. Dit maakt
# het voor MySQL ISAM een stuk sneller
if ((strtolower($sortValue['field']) == 's.stamp') && strtolower($sortValue['direction']) == 'desc') {
$sortValue['field'] = 's.reversestamp';
$sortValue['direction'] = 'ASC';
} # if
$sortList[] = $sortValue['field'] . ' ' . $sortValue['direction'];
} # if
} # foreach
$sortList = implode(', ', $sortList);
# en voer de query uit.
# We vragen altijd 1 meer dan de gevraagde limit zodat we ook een hasMore boolean flag
# kunnen zetten.
$tmpResult = $this->_conn->arrayQuery("SELECT s.id AS id,
s.messageid AS messageid,
s.category AS category,
s.poster AS poster,
l.download as downloadstamp,
l.watch as watchstamp,
l.seen AS seenstamp,
s.subcata AS subcata,
s.subcatb AS subcatb,
s.subcatc AS subcatc,
s.subcatd AS subcatd,
s.subcatz AS subcatz,
s.title AS title,
s.tag AS tag,
s.stamp AS stamp,
s.moderated AS moderated,
s.filesize AS filesize,
s.spotrating AS rating,
s.commentcount AS commentcount,
s.reportcount AS reportcount,
s.spotterid AS spotterid,
f.verified AS verified
" . $extendedFieldList . "
FROM spots AS s " .
$additionalTableList .
$additionalJoinList .
" LEFT JOIN spotstatelist AS l on ((s.messageid = l.messageid) AND (l.ouruserid = " . $this->safe( (int) $ourUserId) . "))
LEFT JOIN spotsfull AS f ON (s.messageid = f.messageid)
LEFT JOIN spotteridblacklist as bl ON ((bl.spotterid = s.spotterid) AND ((bl.ouruserid = " . $this->safe( (int) $ourUserId) . ") OR (bl.ouruserid = -1))) " .
$criteriaFilter . "
ORDER BY " . $sortList .
" LIMIT " . (int) ($limit + 1) ." OFFSET " . (int) $offset);
# als we meer resultaten krijgen dan de aanroeper van deze functie vroeg, dan
# kunnen we er van uit gaan dat er ook nog een pagina is voor de volgende aanroep
$hasMore = (count($tmpResult) > $limit);
if ($hasMore) {
# verwijder het laatste, niet gevraagde, element
array_pop($tmpResult);
} # if
SpotTiming::stop(__FUNCTION__, array($ourUserId, $pageNr, $limit, $criteriaFilter));
return array('list' => $tmpResult, 'hasmore' => $hasMore);
} # getSpots()
/*
* Geeft enkel de header van de spot terug
*/
function getSpotHeader($msgId) {
SpotTiming::start(__FUNCTION__);
$tmpArray = $this->_conn->arrayQuery("SELECT s.id AS id,
s.messageid AS messageid,
s.category AS category,
s.poster AS poster,
s.subcata AS subcata,
s.subcatb AS subcatb,
s.subcatc AS subcatc,
s.subcatd AS subcatd,
s.subcatz AS subcatz,
s.title AS title,
s.tag AS tag,
s.stamp AS stamp,
s.spotrating AS rating,
s.commentcount AS commentcount,
s.reportcount AS reportcount,
s.moderated AS moderated
FROM spots AS s
WHERE s.messageid = '%s'", Array($msgId));
if (empty($tmpArray)) {
return ;
} # if
SpotTiming::stop(__FUNCTION__);
return $tmpArray[0];
} # getSpotHeader
/*
* Vraag 1 specifieke spot op, als de volledig spot niet in de database zit
* geeft dit NULL terug
*/
function getFullSpot($messageId, $ourUserId) {
SpotTiming::start(__FUNCTION__);
$tmpArray = $this->_conn->arrayQuery("SELECT s.id AS id,
s.messageid AS messageid,
s.category AS category,
s.poster AS poster,
s.subcata AS subcata,
s.subcatb AS subcatb,
s.subcatc AS subcatc,
s.subcatd AS subcatd,
s.subcatz AS subcatz,
s.title AS title,
s.tag AS tag,
s.stamp AS stamp,
s.moderated AS moderated,
s.spotrating AS rating,
s.commentcount AS commentcount,
s.reportcount AS reportcount,
s.filesize AS filesize,
s.spotterid AS spotterid,
l.download AS downloadstamp,
l.watch as watchstamp,
l.seen AS seenstamp,
f.verified AS verified,
f.usersignature AS \"user-signature\",
f.userkey AS \"user-key\",
f.xmlsignature AS \"xml-signature\",
f.fullxml AS fullxml
FROM spots AS s
LEFT JOIN spotstatelist AS l on ((s.messageid = l.messageid) AND (l.ouruserid = " . $this->safe( (int) $ourUserId) . "))
JOIN spotsfull AS f ON f.messageid = s.messageid
WHERE s.messageid = '%s'", Array($messageId));
if (empty($tmpArray)) {
return ;
} # if
$tmpArray = $tmpArray[0];
# If spot is fully stored in db and is of the new type, we process it to
# make it exactly the same as when retrieved using NNTP
if (!empty($tmpArray['fullxml']) && (!empty($tmpArray['user-signature']))) {
$tmpArray['user-key'] = unserialize(base64_decode($tmpArray['user-key']));
} # if
SpotTiming::stop(__FUNCTION__, array($messageId, $ourUserId));
return $tmpArray;
} # getFullSpot()
/*
* Insert commentref,
* messageid is het werkelijke commentaar id
* nntpref is de id van de spot
*/
function addComments($comments, $fullComments = array()) {
$this->beginTransaction();
# Databases can have a maximum length of statements, so we
# split the amount of spots in chunks of 100
if ($this->_dbsettings['engine'] == 'pdo_sqlite') {
$chunks = array_chunk($comments, 1);
} else {
$chunks = array_chunk($comments, 100);
} # else
foreach($chunks as $comments) {
$insertArray = array();
foreach($comments as $comment) {
$insertArray[] = vsprintf("('%s', '%s', %d)",
Array($this->safe($comment['messageid']),
$this->safe($comment['nntpref']),
$this->safe($comment['rating'])));
} # foreach
# Actually insert the batch
if (!empty($insertArray)) {
$this->_conn->modify("INSERT INTO commentsxover(messageid, nntpref, spotrating)
VALUES " . implode(',', $insertArray), array());
} # if
} # foreach
$this->commitTransaction();
if (!empty($fullComments)) {
$this->addFullComments($fullComments);
} # if
} # addComments
/*
* Insert commentfull, gaat er van uit dat er al een commentsxover entry is
*/
function addFullComments($fullComments) {
$this->beginTransaction();
# Databases can have a maximum length of statements, so we
# split the amount of spots in chunks of 100
if ($this->_dbsettings['engine'] == 'pdo_sqlite') {
$chunks = array_chunk($fullComments, 1);
} else {
$chunks = array_chunk($fullComments, 100);
} # else
foreach($chunks as $fullComments) {
$insertArray = array();
foreach($fullComments as $comment) {
# Kap de verschillende strings af op een maximum van
# de datastructuur, de unique keys kappen we expres niet af
$comment['fromhdr'] = substr($comment['fromhdr'], 0, 127);
$insertArray[] = vsprintf("('%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', '%s')",
Array($this->safe($comment['messageid']),
$this->safe($comment['fromhdr']),
$this->safe($comment['stamp']),
$this->safe($comment['user-signature']),
$this->safe(serialize($comment['user-key'])),
$this->safe($comment['spotterid']),
$this->safe(implode("\r\n", $comment['body'])),
$this->bool2dt($comment['verified']),
$this->safe($comment['user-avatar'])));
} # foreach
# Actually insert the batch
$this->_conn->modify("INSERT INTO commentsfull(messageid, fromhdr, stamp, usersignature, userkey, spotterid, body, verified, avatar)
VALUES " . implode(',', $insertArray), array());
} # foreach
$this->commitTransaction();
} # addFullComments
/*
* Insert addReportRef,
* messageid is het werkelijke commentaar id
* nntpref is de id van de spot
*/
function addReportRefs($reportList) {
$this->beginTransaction();
# Databases can have a maximum length of statements, so we
# split the amount of spots in chunks of 100
if ($this->_dbsettings['engine'] == 'pdo_sqlite') {
$chunks = array_chunk($reportList, 1);
} else {
$chunks = array_chunk($reportList, 100);
} # else
foreach($chunks as $reportList) {
$insertArray = array();
foreach($reportList as $report) {
$insertArray[] = vsprintf("('%s', '%s', '%s', '%s')",
Array($this->safe($report['messageid']),
$this->safe($report['fromhdr']),
$this->safe($report['keyword']),
$this->safe($report['nntpref'])));
} # foreach
# Actually insert the batch
$this->_conn->modify("INSERT INTO reportsxover(messageid, fromhdr, keyword, nntpref)
VALUES " . implode(',', $insertArray), array());
} # foreach
$this->commitTransaction();
} # addReportRefs
/*
* Update een lijst van messageid's met de gemiddelde spotrating
*/
function updateSpotRating($spotMsgIdList) {
# Geen message id's gegeven? Doe niets!
if (count($spotMsgIdList) == 0) {
return;
} # if
# bereid de lijst voor met de queries in de where
$msgIdList = '';
foreach($spotMsgIdList as $spotMsgId => $v) {
$msgIdList .= "'" . $this->_conn->safe($spotMsgId) . "', ";
} # foreach
$msgIdList = substr($msgIdList, 0, -2);
# en update de spotrating
$this->_conn->modify("UPDATE spots
SET spotrating =
(SELECT AVG(spotrating) as spotrating
FROM commentsxover
WHERE
spots.messageid = commentsxover.nntpref
AND spotrating BETWEEN 1 AND 10
GROUP BY nntpref)
WHERE spots.messageid IN (" . $msgIdList . ")
");
} # updateSpotRating
/*
* Update een lijst van messageid's met het aantal niet geverifieerde comments
*/
function updateSpotCommentCount($spotMsgIdList) {
if (count($spotMsgIdList) == 0) {
return;
} # if
# bereid de lijst voor met de queries in de where
$msgIdList = '';
foreach($spotMsgIdList as $spotMsgId => $v) {
$msgIdList .= "'" . $this->_conn->safe($spotMsgId) . "', ";
} # foreach
$msgIdList = substr($msgIdList, 0, -2);
# en update de spotrating
$this->_conn->modify("UPDATE spots
SET commentcount =
(SELECT COUNT(1) as commentcount
FROM commentsxover
WHERE
spots.messageid = commentsxover.nntpref
GROUP BY nntpref)
WHERE spots.messageid IN (" . $msgIdList . ")
");
} # updateSpotCommentCount
/*
* Update een lijst van messageid's met het aantal niet geverifieerde reports
*/
function updateSpotReportCount($spotMsgIdList) {
if (count($spotMsgIdList) == 0) {
return;
} # if
# bereid de lijst voor met de queries in de where
$msgIdList = '';
foreach($spotMsgIdList as $spotMsgId => $v) {
$msgIdList .= "'" . $this->_conn->safe($spotMsgId) . "', ";
} # foreach
$msgIdList = substr($msgIdList, 0, -2);
# en update de spotrating
$this->_conn->modify("UPDATE spots
SET reportcount =
(SELECT COUNT(1) as reportcount
FROM reportsxover
WHERE
spots.messageid = reportsxover.nntpref
GROUP BY nntpref)
WHERE spots.messageid IN (" . $msgIdList . ")
");
} # updateSpotReportCount
/*
* Vraag de volledige commentaar lijst op, gaat er van uit dat er al een commentsxover entry is
*/
function getCommentsFull($userId, $nntpRef) {
SpotTiming::start(__FUNCTION__);
# en vraag de comments daadwerkelijk op
$commentList = $this->_conn->arrayQuery("SELECT c.messageid AS messageid,
(f.messageid IS NOT NULL) AS havefull,
f.fromhdr AS fromhdr,
f.stamp AS stamp,
f.usersignature AS \"user-signature\",
f.userkey AS \"user-key\",
f.spotterid AS spotterid,
f.body AS body,
f.verified AS verified,
c.spotrating AS spotrating,
c.moderated AS moderated,
f.avatar as \"user-avatar\"
FROM commentsfull f
RIGHT JOIN commentsxover c on (f.messageid = c.messageid)
LEFT JOIN spotteridblacklist as bl ON ((bl.spotterid = f.spotterid) AND ((bl.ouruserid = " . $this->safe( (int) $userId) . ") OR (bl.ouruserid = -1)))
WHERE c.nntpref = '%s' AND (bl.spotterid IS NULL)
ORDER BY c.id", array($nntpRef));
$commentListCount = count($commentList);
for($i = 0; $i < $commentListCount; $i++) {
if ($commentList[$i]['havefull']) {
$commentList[$i]['user-key'] = unserialize($commentList[$i]['user-key']);
$commentList[$i]['body'] = explode("\r\n", $commentList[$i]['body']);
} # if
} # for
SpotTiming::stop(__FUNCTION__);
return $commentList;
} # getCommentsFull
/*
* Geeft huidig database schema versie nummer terug
*/
function getSchemaVer() {
return $this->_conn->singleQuery("SELECT value FROM settings WHERE name = 'schemaversion'");
} # getSchemaVer
/*
* Removes a comment from the database
*/
function removeComment($msgId) {
$this->_conn->modify("DELETE FROM commentsfull WHERE messageid = '%s'", Array($msgId));
$this->_conn->modify("DELETE FROM commentsxover WHERE messageid = '%s'", Array($msgId));
} # removeComment
/*
* Verwijder een spot uit de db
*/
function deleteSpot($msgId) {
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' :
case 'pdo_sqlite' : {
$this->_conn->modify("DELETE FROM spots WHERE messageid = '%s'", Array($msgId));
$this->_conn->modify("DELETE FROM spotsfull WHERE messageid = '%s'", Array($msgId));
$this->_conn->modify("DELETE FROM commentsfull WHERE messageid IN (SELECT messageid FROM commentsxover WHERE nntpref= '%s')", Array($msgId));
$this->_conn->modify("DELETE FROM commentsxover WHERE nntpref = '%s'", Array($msgId));
$this->_conn->modify("DELETE FROM spotstatelist WHERE messageid = '%s'", Array($msgId));
$this->_conn->modify("DELETE FROM reportsxover WHERE nntpref = '%s'", Array($msgId));
$this->_conn->modify("DELETE FROM reportsposted WHERE inreplyto = '%s'", Array($msgId));
$this->_conn->modify("DELETE FROM cache WHERE resourceid = '%s'", Array($msgId));
break;
} # pdo_sqlite
default : {
$this->_conn->modify("DELETE FROM spots, spotsfull, commentsxover, reportsxover, spotstatelist, reportsposted, cache USING spots
LEFT JOIN spotsfull ON spots.messageid=spotsfull.messageid
LEFT JOIN commentsxover ON spots.messageid=commentsxover.nntpref
LEFT JOIN reportsxover ON spots.messageid=reportsxover.nntpref
LEFT JOIN spotstatelist ON spots.messageid=spotstatelist.messageid
LEFT JOIN reportsposted ON spots.messageid=reportsposted.inreplyto
LEFT JOIN cache ON spots.messageid=cache.resourceid
WHERE spots.messageid = '%s'", Array($msgId));
} # default
} # switch
} # deleteSpot
/*
* Markeer een spot in de db moderated
*/
function markSpotModerated($msgId) {
$this->_conn->modify("UPDATE spots SET moderated = '%s' WHERE messageid = '%s'", Array($this->bool2dt(true), $msgId));
} # markSpotModerated
/*
* Markeer een comment in de db moderated
*/
function markCommentModerated($msgId) {
$this->_conn->modify("UPDATE commentsxover SET moderated = '%s' WHERE messageid = '%s'", Array($this->bool2dt(true), $msgId));
} # markCommentModerated
/*
* Verwijder oude spots uit de db
*/
function deleteSpotsRetention($retention) {
$retention = $retention * 24 * 60 * 60; // omzetten in seconden
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' :
case 'pdo_sqlite': {
$this->_conn->modify("DELETE FROM spots WHERE spots.stamp < " . (time() - $retention) );
$this->_conn->modify("DELETE FROM spotsfull WHERE spotsfull.messageid not in
(SELECT messageid FROM spots)") ;
$this->_conn->modify("DELETE FROM commentsfull WHERE messageid IN
(SELECT messageid FROM commentsxover WHERE commentsxover.nntpref not in
(SELECT messageid FROM spots))") ;
$this->_conn->modify("DELETE FROM commentsxover WHERE commentsxover.nntpref not in
(SELECT messageid FROM spots)") ;
$this->_conn->modify("DELETE FROM reportsxover WHERE reporsxover.nntpref not in
(SELECT messageid FROM spots)") ;
$this->_conn->modify("DELETE FROM spotstatelist WHERE spotstatelist.messageid not in
(SELECT messageid FROM spots)") ;
$this->_conn->modify("DELETE FROM reportsposted WHERE reportsposted.inreplyto not in
(SELECT messageid FROM spots)") ;
$this->_conn->modify("DELETE FROM cache WHERE (cache.cachetype = %d OR cache.cachetype = %d) AND cache.resourceid not in
(SELECT messageid FROM spots)", Array(SpotCache::SpotImage, SpotCache::SpotNzb)) ;
break;
} # pdo_sqlite
default : {
$this->_conn->modify("DELETE FROM spots, spotsfull, commentsxover, reportsxover, spotstatelist, reportsposted, cache USING spots
LEFT JOIN spotsfull ON spots.messageid=spotsfull.messageid
LEFT JOIN commentsxover ON spots.messageid=commentsxover.nntpref
LEFT JOIN reportsxover ON spots.messageid=reportsxover.nntpref
LEFT JOIN spotstatelist ON spots.messageid=spotstatelist.messageid
LEFT JOIN reportsposted ON spots.messageid=reportsposted.inreplyto
LEFT JOIN cache ON spots.messageid=cache.resourceid
WHERE spots.stamp < " . (time() - $retention) );
} # default
} # switch
} # deleteSpotsRetention
/*
* Voeg een reeks met spots toe aan de database
*/
function addSpots($spots, $fullSpots = array()) {
$this->beginTransaction();
# Databases can have a maximum length of statements, so we
# split the amount of spots in chunks of 100
if ($this->_dbsettings['engine'] == 'pdo_sqlite') {
$chunks = array_chunk($spots, 1);
} else {
$chunks = array_chunk($spots, 100);
} # else
foreach($chunks as $spots) {
$insertArray = array();
foreach($spots as $spot) {
# we checken hier handmatig of filesize wel numeriek is, dit is omdat printen met %d in sommige PHP
# versies een verkeerde afronding geeft bij >32bits getallen.
if (!is_numeric($spot['filesize'])) {
$spot['filesize'] = 0;
} # if
# Kap de verschillende strings af op een maximum van
# de datastructuur, de unique keys kappen we expres niet af
$spot['poster'] = substr($spot['poster'], 0, 127);
$spot['title'] = substr($spot['title'], 0, 127);
$spot['tag'] = substr($spot['tag'], 0, 127);
$spot['subcata'] = substr($spot['subcata'], 0, 63);
$spot['subcatb'] = substr($spot['subcatb'], 0, 63);
$spot['subcatc'] = substr($spot['subcatc'], 0, 63);
$spot['subcatd'] = substr($spot['subcatd'], 0, 63);
# Kap de verschillende strings af op een maximum van
# de datastructuur, de unique keys en de RSA keys en dergeijke
# kappen we expres niet af
$spot['spotterid'] = substr($spot['spotterid'], 0, 31);
$insertArray[] = vsprintf("('%s', '%s', '%s', '%s', %d, '%s', '%s', '%s', '%s', '%s', %d, %d, '%s', '%s')",
Array($this->safe($spot['messageid']),
$this->safe($spot['poster']),
$this->safe($spot['title']),
$this->safe($spot['tag']),
$this->safe((int) $spot['category']),
$this->safe($spot['subcata']),
$this->safe($spot['subcatb']),
$this->safe($spot['subcatc']),
$this->safe($spot['subcatd']),
$this->safe($spot['subcatz']),
(int) $this->safe($spot['stamp']),
(int) $this->safe(($spot['stamp'] * -1)),
$this->safe($spot['filesize']),
$this->safe($spot['spotterid']))); # Filesize mag niet naar int gecast worden, dan heb je 2GB limiet
} # foreach
# Actually insert the batch
if (!empty($insertArray)) {
$this->_conn->modify("INSERT INTO spots(messageid, poster, title, tag, category, subcata,
subcatb, subcatc, subcatd, subcatz, stamp, reversestamp, filesize, spotterid)
VALUES " . implode(',', $insertArray), array());
} # if
} # foreach
$this->commitTransaction();
if (!empty($fullSpots)) {
$this->addFullSpots($fullSpots);
} # if
} # addSpot()
/*
* Voeg enkel de full spot toe aan de database, niet gebruiken zonder dat er een entry in 'spots' staat
* want dan komt deze spot niet in het overzicht te staan.
*/
function addFullSpots($fullSpots) {
$this->beginTransaction();
# Databases can have a maximum length of statements, so we
# split the amount of spots in chunks of 100
if ($this->_dbsettings['engine'] == 'pdo_sqlite') {
$chunks = array_chunk($fullSpots, 1);
} else {
$chunks = array_chunk($fullSpots, 100);
} # else
foreach($chunks as $fullSpots) {
$insertArray = array();
# en voeg het aan de database toe
foreach($fullSpots as $fullSpot) {
$insertArray[] = vsprintf("('%s', '%s', '%s', '%s', '%s', '%s')",
Array($this->safe($fullSpot['messageid']),
$this->bool2dt($fullSpot['verified']),
$this->safe($fullSpot['user-signature']),
$this->safe(base64_encode(serialize($fullSpot['user-key']))),
$this->safe($fullSpot['xml-signature']),
$this->safe($fullSpot['fullxml'])));
} # foreach
# Actually insert the batch
$this->_conn->modify("INSERT INTO spotsfull(messageid, verified, usersignature, userkey, xmlsignature, fullxml)
VALUES " . implode(',', $insertArray), array());
} # foreach
$this->commitTransaction();
} # addFullSpot
function addToSpotStateList($list, $messageId, $ourUserId, $stamp='') {
SpotTiming::start(__FUNCTION__);
$verifiedList = $this->verifyListType($list);
if (empty($stamp)) { $stamp = time(); }
switch ($this->_dbsettings['engine']) {
case 'pdo_mysql' :
case 'mysql' : {
$this->_conn->modify("INSERT INTO spotstatelist (messageid, ouruserid, " . $verifiedList . ") VALUES ('%s', %d, %d) ON DUPLICATE KEY UPDATE " . $verifiedList . " = %d",
Array($messageId, (int) $ourUserId, $stamp, $stamp));
break;
} # mysql
default : {
$this->_conn->modify("UPDATE spotstatelist SET " . $verifiedList . " = %d WHERE messageid = '%s' AND ouruserid = %d", array($stamp, $messageId, $ourUserId));
if ($this->_conn->rows() == 0) {
$this->_conn->modify("INSERT INTO spotstatelist (messageid, ouruserid, " . $verifiedList . ") VALUES ('%s', %d, %d)",
Array($messageId, (int) $ourUserId, $stamp));
} # if
} # default
} # switch
SpotTiming::stop(__FUNCTION__, array($list, $messageId, $ourUserId, $stamp));
} # addToSpotStateList
function clearSpotStateList($list, $ourUserId) {
SpotTiming::start(__FUNCTION__);
$verifiedList = $this->verifyListType($list);
$this->_conn->modify("UPDATE spotstatelist SET " . $verifiedList . " = NULL WHERE ouruserid = %d", array($ourUserId));
SpotTiming::stop(__FUNCTION__, array($list, $ourUserId));
} # clearSpotStatelist
function cleanSpotStateList() {
$this->_conn->rawExec("DELETE FROM spotstatelist WHERE download IS NULL AND watch IS NULL AND seen IS NULL");
} # cleanSpotStateList
function removeFromSpotStateList($list, $messageid, $ourUserId) {
SpotTiming::start(__FUNCTION__);
$verifiedList = $this->verifyListType($list);
$this->_conn->modify("UPDATE spotstatelist SET " . $verifiedList . " = NULL WHERE messageid = '%s' AND ouruserid = %d LIMIT 1",
Array($messageid, (int) $ourUserId));
SpotTiming::stop(__FUNCTION__, array($list, $messageid, $ourUserId));
} # removeFromSpotStateList
function verifyListType($list) {
switch($list) {
case self::spotstate_Down : $verifiedList = 'download'; break;
case self::spotstate_Watch : $verifiedList = 'watch'; break;
case self::spotstate_Seen : $verifiedList = 'seen'; break;
default : throw new Exception("Invalid listtype given!");
} # switch
return $verifiedList;
} # verifyListType
/*
* Geeft de permissies terug van een bepaalde groep
*/
function getGroupPerms($groupId) {
return $this->_conn->arrayQuery("SELECT permissionid, objectid, deny FROM grouppermissions WHERE groupid = %d",
Array($groupId));
} # getgroupPerms
/*
* Geeft permissies terug welke user heeft, automatisch in het formaat zoals
* SpotSecurity dat heeft (maw - dat de rechtencheck een simpele 'isset' is om
* overhead te voorkomen
*/
function getPermissions($userId) {
$permList = array();
$tmpList = $this->_conn->arrayQuery('SELECT permissionid, objectid, deny FROM grouppermissions
WHERE groupid IN
(SELECT groupid FROM usergroups WHERE userid = %d ORDER BY prio)',
Array($userId));
foreach($tmpList as $perm) {
# Voeg dit permissionid toe aan de lijst met permissies
if (!isset($permList[$perm['permissionid']])) {
$permList[$perm['permissionid']] = array();
} # if
$permList[$perm['permissionid']][$perm['objectid']] = !(boolean) $perm['deny'];
} # foreach
return $permList;
} # getPermissions
/*
* Geeft alle gedefinieerde groepen terug
*/
function getGroupList($userId) {
if ($userId == null) {
return $this->_conn->arrayQuery("SELECT id,name,0 as \"ismember\" FROM securitygroups");
} else {
return $this->_conn->arrayQuery("SELECT sg.id,name,ug.userid IS NOT NULL as \"ismember\" FROM securitygroups sg LEFT JOIN usergroups ug ON (sg.id = ug.groupid) AND (ug.userid = %d)",
Array($userId));
} # if
} # getGroupList
/*
* Verwijdert een permissie uit een security group
*/
function removePermFromSecGroup($groupId, $perm) {
$this->_conn->modify("DELETE FROM grouppermissions WHERE (groupid = %d) AND (permissionid = %d) AND (objectid = '%s')",
Array($groupId, $perm['permissionid'], $perm['objectid']));
} # removePermFromSecGroup
/*
* Zet een permissie op deny in een security group
*/
function setDenyForPermFromSecGroup($groupId, $perm) {
$this->_conn->modify("UPDATE grouppermissions SET deny = '%s' WHERE (groupid = %d) AND (permissionid = %d) AND (objectid = '%s')",
Array($this->bool2dt($perm['deny']), $groupId, $perm['permissionid'], $perm['objectid']));
} # removePermFromSecGroup
/*
* Voegt een permissie aan een security group toe
*/
function addPermToSecGroup($groupId, $perm) {
$this->_conn->modify("INSERT INTO grouppermissions(groupid,permissionid,objectid) VALUES (%d, %d, '%s')",
Array($groupId, $perm['permissionid'], $perm['objectid']));
} # addPermToSecGroup
/*
* Geef een specifieke security group terug
*/
function getSecurityGroup($groupId) {
return $this->_conn->arrayQuery("SELECT id,name FROM securitygroups WHERE id = %d", Array($groupId));
} # getSecurityGroup
/*
* Geef een specifieke security group terug
*/
function setSecurityGroup($group) {
$this->_conn->modify("UPDATE securitygroups SET name = '%s' WHERE id = %d", Array($group['name'], $group['id']));
} # setSecurityGroup
/*
* Geef een specifieke security group terug
*/
function addSecurityGroup($group) {
$this->_conn->modify("INSERT INTO securitygroups(name) VALUES ('%s')", Array($group['name']));
} # addSecurityGroup
/*
* Geef een specifieke security group terug
*/
function removeSecurityGroup($group) {
$this->_conn->modify("DELETE FROM securitygroups WHERE id = %d", Array($group['id']));
} # removeSecurityGroup
/*
* Wijzigt group membership van een user
*/
function setUserGroupList($userId, $groupList) {
# We wissen eerst huidige group membership
$this->_conn->modify("DELETE FROM usergroups WHERE userid = %d", array($userId));
foreach($groupList as $groupInfo) {
$this->_conn->modify("INSERT INTO usergroups(userid,groupid,prio) VALUES(%d, %d, %d)",
Array($userId, $groupInfo['groupid'], $groupInfo['prio']));
} # foreach
} # setUserGroupList
/*
* Voegt een nieuwe notificatie toe
*/
function addNewNotification($userId, $objectId, $type, $title, $body) {
$this->_conn->modify("INSERT INTO notifications(userid,stamp,objectid,type,title,body,sent) VALUES(%d, %d, '%s', '%s', '%s', '%s', '%s')",
Array($userId, (int) time(), $objectId, $type, $title, $body, $this->bool2dt(false)));
} # addNewNotification
/*
* Haalt niet-verzonden notificaties op van een user
*/
function getUnsentNotifications($userId) {
return $this->_conn->arrayQuery("SELECT id,userid,objectid,type,title,body FROM notifications WHERE userid = %d AND NOT SENT;",
Array($userId));
} # getUnsentNotifications
/*
* Een notificatie updaten
*/
function updateNotification($msg) {
$this->_conn->modify("UPDATE notifications SET title = '%s', body = '%s', sent = '%s' WHERE id = %d",
Array($msg['title'], $msg['body'], $this->bool2dt($msg['sent']), $msg['id']));
} // updateNotification
/*
* Voegt een spotterid toe aan de blacklist
*/
function addSpotterToBlacklist($spotterId, $ourUserId, $origin) {
$this->_conn->modify("INSERT INTO spotteridblacklist(spotterid, origin, ouruserid) VALUES ('%s', '%s', %d)",
Array($spotterId, $origin, (int) $ourUserId));
} # addSpotterToBlackList
/*
* Removes a specific spotter from the blacklis
*/
function removeSpotterFromBlacklist($spotterId, $ourUserId) {
$this->_conn->modify("DELETE FROM spotteridblacklist WHERE ouruserid = %d AND spotterid = '%s'",
Array((int) $ourUserId, $spotterId));
} # addSpotterToBlackList
/*
* Geeft alle blacklisted spotterid's terug
*/
function getSpotterBlacklist($ourUserId) {
return $this->_conn->arrayQuery("SELECT spotterid, origin, ouruserid FROM spotteridblacklist WHERE ouruserid = %d",
Array((int) $ourUserId));
} # getSpotterBlacklist
/*
* Returns one specific blacklisted record for a given spotterid
*/
function getBlacklistForSpotterId($userId, $spotterId) {
$tmp = $this->_conn->arrayQuery("SELECT spotterid, origin, ouruserid FROM spotteridblacklist WHERE ouruserid = %d and spotterid = '%s'",
Array($userId, $spotterId));
if (!empty($tmp)) {
return $tmp[0];
} else {
return false;
} # else
} # getBlacklistForSpotterId
/*
* Geeft alle blacklisted spotterid's terug
*/
function isSpotterBlacklisted($spotterId, $ourUserId) {
$blacklistResult = $this->_conn->arrayQuery("SELECT spotterid FROM spotteridblacklist WHERE ((ouruserid = %d) OR (ouruserid = -1)) AND (spotterid = '%s')",
Array((int) $ourUserId, $spotterId));
return (!empty($blacklistResult));
} # getSpotterBlacklist
/*
* Verwijder een filter en de children toe (recursive)
*/
function deleteFilter($userId, $filterId, $filterType) {
$filterList = $this->getFilterList($userId, $filterType);
foreach($filterList as $filter) {
if ($filter['id'] == $filterId) {
foreach($filter['children'] as $child) {
$this->deleteFilter($userId, $child['id'], $filterType);
} # foreach
} # if
$this->_conn->modify("DELETE FROM filters WHERE userid = %d AND id = %d",
Array($userId, $filterId));
} # foreach
} # deleteFilter
/*
* Voegt een filter en de children toe (recursive)
*/
function addFilter($userId, $filter) {
$this->_conn->modify("INSERT INTO filters(userid, filtertype, title, icon, torder, tparent, tree, valuelist, sorton, sortorder)
VALUES(%d, '%s', '%s', '%s', %d, %d, '%s', '%s', '%s', '%s')",
Array((int) $userId,
$filter['filtertype'],
$filter['title'],
$filter['icon'],
(int) $filter['torder'],
(int) $filter['tparent'],
$filter['tree'],
implode('&', $filter['valuelist']),
$filter['sorton'],
$filter['sortorder']));
$parentId = $this->_conn->lastInsertId('filters');
foreach($filter['children'] as $tmpFilter) {
$tmpFilter['tparent'] = $parentId;
$this->addFilter($userId, $tmpFilter);
} # foreach
} # addFilter
/*
* Copieert de filterlijst van een user naar een andere user
*/
function copyFilterList($srcId, $dstId) {
$filterList = $this->getFilterList($srcId, '');
foreach($filterList as $filterItems) {
$this->addFilter($dstId, $filterItems);
} # foreach
} # copyFilterList
/*
* Verwijdert alle ingestelde filters voor een user
*/
function removeAllFilters($userId) {
$this->_conn->modify("DELETE FROM filters WHERE userid = %d", Array((int) $userId));
} # removeAllfilters
/*
* Get a specific filter
*/
function getFilter($userId, $filterId) {
/* Haal de lijst met filter values op */
$tmpResult = $this->_conn->arrayQuery("SELECT id,
userid,
filtertype,
title,
icon,
torder,
tparent,
tree,
valuelist,
sorton,
sortorder
FROM filters
WHERE userid = %d AND id = %d",
Array((int) $userId, (int) $filterId));
if (!empty($tmpResult)) {
return $tmpResult[0];
} else {
return false;
} # else
} # getFilter
/*
* Get a specific index filter
*/
function getUserIndexFilter($userId) {
/* Haal de lijst met filter values op */
$tmpResult = $this->_conn->arrayQuery("SELECT id,
userid,
filtertype,
title,
icon,
torder,
tparent,
tree,
valuelist,
sorton,
sortorder
FROM filters
WHERE userid = %d AND filtertype = 'index_filter'",
Array((int) $userId));
if (!empty($tmpResult)) {
return $tmpResult[0];
} else {
return false;
} # else
} # getUserIndexFilter
/*
* Get a specific filter
*/
function updateFilter($userId, $filter) {
/* Haal de lijst met filter values op */
$tmpResult = $this->_conn->modify("UPDATE filters
SET title = '%s',
icon = '%s',
torder = %d,
tparent = %d
WHERE userid = %d AND id = %d",
Array($filter['title'],
$filter['icon'],
(int) $filter['torder'],
(int) $filter['tparent'],
(int) $userId,
(int) $filter['id']));
} # updateFilter
/*
* Haalt de filterlijst op als een platte lijst
*/
function getPlainFilterList($userId, $filterType) {
/* willen we een specifiek soort filter hebben? */
if (empty($filterType)) {
$filterTypeFilter = '';
} else {
$filterTypeFilter = " AND filtertype = 'filter'";
} # else
/* Haal de lijst met filter values op */
return $this->_conn->arrayQuery("SELECT id,
userid,
filtertype,
title,
icon,
torder,
tparent,
tree,
valuelist,
sorton,
sortorder
FROM filters
WHERE userid = %d " . $filterTypeFilter . "
ORDER BY tparent,torder", /* was: id, tparent, torder */
Array($userId));
} # getPlainFilterList
/*
* Haalt de filter lijst op en formatteert die in een boom
*/
function getFilterList($userId, $filterType) {
$tmpResult = $this->getPlainFilterList($userId, $filterType);
$idMapping = array();
foreach($tmpResult as &$tmp) {
$idMapping[$tmp['id']] =& $tmp;
} # foreach
/* Hier zetten we het om naar een daadwerkelijke boom */
$tree = array();
foreach($tmpResult as &$filter) {
if (!isset($filter['children'])) {
$filter['children'] = array();
} # if
# de filter waardes zijn URL encoded opgeslagen
# en we gebruiken de & om individuele filterwaardes
# te onderscheiden
$filter['valuelist'] = explode('&', $filter['valuelist']);
if ($filter['tparent'] == 0) {
$tree[$filter['id']] =& $filter;
} else {
$idMapping[$filter['tparent']]['children'][] =& $filter;
} # else
} # foreach
return $tree;
} # getFilterList
/*
* Returns a list of all unique filter combinations
*/
function getUniqueFilterCombinations() {
return $this->_conn->arrayQuery("SELECT tree,valuelist FROM filters GROUP BY tree,valuelist ORDER BY tree,valuelist");
} # getUniqueFilterCombinations
/*
* Add a filter count for a specific SHA1 hash
* of a filter for this specific user
*/
function setCachedFilterCount($userId, $filterHashes) {
$maxSpotStamp = $this->getMaxMessageTime();
foreach($filterHashes as $filterHash => $filterCount) {
/* Remove any existing cached filtercount for this user */
$this->_conn->modify("DELETE FROM filtercounts WHERE (userid = %d) AND (filterhash = '%s')",
Array((int) $userId, $filterHash));
/* and insert our new filtercount hash */
$this->_conn->modify("INSERT INTO filtercounts(userid, filterhash, currentspotcount, lastvisitspotcount, lastupdate)
VALUES(%d, '%s', %d, %d, %d)",
Array((int) $userId, $filterHash, $filterCount['currentspotcount'], $filterCount['lastvisitspotcount'],
$maxSpotStamp ));
} # foreach
} # setCachedFilterCount
/*
* Add a filter count for a specific SHA1 hash
* of a filter for this specific user
*/
function getNewCountForFilters($userId) {
$filterHashes = array();
$tmp = $this->_conn->arrayQuery("SELECT f.filterhash AS filterhash,
f.currentspotcount AS currentspotcount,
f.lastvisitspotcount AS lastvisitspotcount,
f.lastupdate AS lastupdate,
t.currentspotcount - f.lastvisitspotcount AS newspotcount
FROM filtercounts f
INNER JOIN filtercounts t ON (t.filterhash = f.filterhash)
WHERE t.userid = -1
AND f.userid = %d",
Array((int) $userId) );
foreach($tmp as $cachedItem) {
$filterHashes[$cachedItem['filterhash']] = array('currentspotcount' => $cachedItem['currentspotcount'],
'lastvisitspotcount' => $cachedItem['lastvisitspotcount'],
'newspotcount' => $cachedItem['newspotcount'],
'lastupdate' => $cachedItem['lastupdate']);
} # foreach
return $filterHashes;
} # getNewCountForFilters
/*
* Makes sure all registered users have at least counts
* for all existing filters.
*/
function createFilterCountsForEveryone() {
$userIdList = $this->_conn->arrayQuery('SELECT id FROM users WHERE id <> -1');
foreach($userIdList as $user) {
$userId = $user['id'];
/* We can assume userid -1 (baseline) has all the filters which exist */
$filterList = $this->getPlainFilterList($userId, '');
$cachedList = $this->getCachedFilterCount($userId);
/* We add a dummy entry for 'all new spots' */
$filterList[] = array('id' => 9999, 'userid' => $userId, 'filtertype' => 'dummyfilter',
'title' => 'NewSpots', 'icon' => '', 'torder' => 0, 'tparent' => 0,
'tree' => '', 'valuelist' => 'New:0', 'sorton' => '', 'sortorder' => '');
foreach($filterList as $filter) {
$filterHash = sha1($filter['tree'] . '|' . urldecode($filter['valuelist']));
# Do we have a cache entry already for this filter?
if (!isset($cachedList[$filterHash])) {
/*
* Create the cached count filter
*/
$filter['currentspotcount'] = 0;
$filter['lastvisitspotcount'] = 0;
$this->setCachedFilterCount($userId, array($filterHash => $filter));
} # if
} # foreach
} # foreach
} # createFilterCountsForEveryone
/*
* Retrieves the filtercount for a specific userid
*/
function getCachedFilterCount($userId) {
$filterHashes = array();
$tmp = $this->_conn->arrayQuery("SELECT filterhash, currentspotcount, lastvisitspotcount, lastupdate FROM filtercounts WHERE userid = %d",
Array( (int) $userId) );
foreach($tmp as $cachedItem) {
$filterHashes[$cachedItem['filterhash']] = array('currentspotcount' => $cachedItem['currentspotcount'],
'lastvisitspotcount' => $cachedItem['lastvisitspotcount'],
'lastupdate' => $cachedItem['lastupdate']);
} # foreach
return $filterHashes;
} # getCachedFilterCount
/*
* Resets the unread count for a specific user
*/
function resetFilterCountForUser($userId) {
switch ($this->_dbsettings['engine']) {
case 'pdo_sqlite' : {
$filterList = $this->_conn->arrayQuery("SELECT currentspotcount, filterhash FROM filtercounts WHERE userid = -1", array());
foreach($filterList as $filter) {
$this->_conn->modify("UPDATE filtercounts
SET lastvisitspotcount = currentspotcount,
currentspotcount = %d
WHERE (filterhash = '%s')
AND (userid = %d)",
Array((int) $filter['currentspotcount'], $filter['filterhash'], (int) $userId));
} # foreach
break;
} # sqlite
case 'pdo_pgsql' : {
$this->_conn->modify("UPDATE filtercounts f
SET f.lastvisitspotcount = f.currentspotcount,
f.currentspotcount = t.currentspotcount
FROM filtercounts t
WHERE (f.filterhash = t.filterhash)
AND (t.userid = -1)
AND (f.userid = %d)",
Array((int) $userId) );
break;
} # pgsql
default : {
$this->_conn->modify("UPDATE filtercounts f, filtercounts t
SET f.lastvisitspotcount = f.currentspotcount,
f.currentspotcount = t.currentspotcount
WHERE (f.filterhash = t.filterhash)
AND (t.userid = -1)
AND (f.userid = %d)",
Array((int) $userId) );
} # default
} # switch
} # resetFilterCountForUser
/*
* Updates the last filtercounts for sessions which are active at the moment
*/
function updateCurrentFilterCounts() {
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' : {
/*
* Update the current filter counts if the session
* is still active
*/
$this->_conn->modify("UPDATE filtercounts f
SET f.currentspotcount = t.currentspotcount,
f.lastupdate = t.lastupdate
FROM filtercounts t
WHERE (f.filterhash = t.filterhash)
AND (t.userid = -1)
AND (f.userid IN (SELECT userid FROM sessions WHERE lasthit > f.lastupdate GROUP BY userid ))", array());
/*
* Sometimes retrieve removes some sports, make sure
* we do not get confusing results
*/
$this->_conn->modify("UPDATE filtercounts f
SET f.lastvisitspotcount = t.currentspotcount
FROM filtercounts t
WHERE (f.filterhash = t.filterhash)
AND (f.lastvisitspotcount > t.currentspotcount
AND (t.userid = -1))");
break;
} # pgsql
case 'pdo_sqlite' : {
/*
* Update the current filter counts if the session
* is still active
*/
$filterList = $this->_conn->arrayQuery("SELECT currentspotcount, lastupdate, filterhash FROM filtercounts WHERE userid = -1", array());
foreach($filterList as $filter) {
$this->_conn->modify("UPDATE filtercounts
SET currentspotcount = %d,
lastupdate = %d
WHERE (filterhash = '%s')
AND (userid IN (SELECT userid FROM sessions WHERE lasthit > f.lastupdate GROUP BY userid ))",
Array((int) $filter['currentspotcount'], (int) $filter['lastupdate'], $filter['filterhash'], (int) $userId));
} # foreach
break;
} # pdo_sqlite
default : {
/*
* We do this in two parts because MySQL seems to fall over
* when we use a subquery
*/
$sessionList = $this->_conn->arrayQuery("SELECT s.userid FROM sessions s
INNER JOIN filtercounts f ON (f.userid = s.userid)
WHERE lasthit > f.lastupdate
GROUP BY s.userid",
array());
# bereid de lijst voor met de queries in de where
$userIdList = '';
foreach($sessionList as $session) {
$userIdList .= (int) $this->_conn->safe($session['userid']) . ", ";
} # foreach
$userIdList = substr($userIdList, 0, -2);
/*
* Update the current filter counts if the session
* is still active
*/
if (!empty($userIdList)) {
$this->_conn->modify("UPDATE filtercounts f, filtercounts t
SET f.currentspotcount = t.currentspotcount,
f.lastupdate = t.lastupdate
WHERE (f.filterhash = t.filterhash)
AND (t.userid = -1)
AND (f.userid IN (" . $userIdList . "))");
} # if
/*
* Sometimes retrieve removes some sports, make sure
* we do not get confusing results
*/
$this->_conn->modify("UPDATE filtercounts f, filtercounts t
SET f.lastvisitspotcount = t.currentspotcount
WHERE (f.filterhash = t.filterhash)
AND (f.lastvisitspotcount > t.currentspotcount)
AND (t.userid = -1)");
} # default
} # switch
} # updateCurrentFilterCounts
/*
* Mark all filters as read
*/
function markFilterCountAsSeen($userId) {
switch ($this->_dbsettings['engine']) {
case 'pdo_sqlite' : {
$filterList = $this->_conn->arrayQuery("SELECT currentspotcount, lastupdate, filterhash FROM filtercounts WHERE userid = -1", array());
foreach($filterList as $filter) {
$this->_conn->modify("UPDATE filtercounts
SET lastvisitspotcount = %d,
currentspotcount = %d,
lastupdate = %d
WHERE (filterhash = '%s')
AND (userid = %d)",
Array((int) $filter['currentspotcount'],
(int) $filter['currentspotcount'],
(int) $filter['lastupdate'],
$filter['filterhash'],
(int) $userId));
} # foreach
break;
} # pdo_sqlite
case 'pdo_pgsql' : {
$this->_conn->modify("UPDATE filtercounts f, filtercounts t
SET f.lastvisitspotcount = t.currentspotcount,
f.currentspotcount = t.currentspotcount,
f.lastupdate = t.lastupdate
WHERE (f.filterhash = t.filterhash)
AND (t.userid = -1)
AND (f.userid = %d)",
Array( (int) $userId) );
break;
} # pgsql
default : {
$this->_conn->modify("UPDATE filtercounts f, filtercounts t
SET f.lastvisitspotcount = t.currentspotcount,
f.currentspotcount = t.currentspotcount,
f.lastupdate = t.lastupdate
WHERE (f.filterhash = t.filterhash)
AND (t.userid = -1)
AND (f.userid = %d)",
Array( (int) $userId) );
} # default
} # switch
} # markFilterCountAsSeen
/*
* Create an entry in the auditlog
*/
function addAuditEntry($userid, $perm, $objectid, $allowed, $ipaddr) {
return $this->_conn->modify("INSERT INTO permaudit(stamp, userid, permissionid, objectid, result, ipaddr)
VALUES(%d, %d, %d, '%s', '%s', '%s')",
Array(time(), (int) $userid, (int) $perm, $objectid, $this->bool2dt($allowed), $ipaddr));
} # addAuditEntry
function cleanCache($expireDays) {
return $this->_conn->modify("DELETE FROM cache WHERE (cachetype = %d OR cachetype = %d OR cachetype = %d) AND stamp < %d", Array(SpotCache::Web, SpotCache::Statistics, SpotCache::StatisticsData,(int) time()-$expireDays*24*60*60));
} # cleanCache
function isCached($resourceid, $cachetype) {
$tmpResult = $this->_conn->singleQuery("SELECT resourceid FROM cache WHERE resourceid = '%s' AND cachetype = '%s'", Array($resourceid, $cachetype));
return (!empty($tmpResult));
} # isCached
function getCache($resourceid, $cachetype) {
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' : {
$tmp = $this->_conn->arrayQuery("SELECT stamp, metadata, serialized, content FROM cache WHERE resourceid = '%s' AND cachetype = '%s'", array($resourceid, $cachetype));
if (!empty($tmp)) {
$tmp[0]['content'] = stream_get_contents($tmp[0]['content']);
} # if
break;
} # case 'pdo_pgsql'
case 'mysql' :
case 'pdo_mysql' : {
$tmp = $this->_conn->arrayQuery("SELECT stamp, metadata, serialized, UNCOMPRESS(content) AS content FROM cache WHERE resourceid = '%s' AND cachetype = '%s'", array($resourceid, $cachetype));
break;
} # mysql
default : {
$tmp = $this->_conn->arrayQuery("SELECT stamp, metadata, serialized, content FROM cache WHERE resourceid = '%s' AND cachetype = '%s'", array($resourceid, $cachetype));
} # default
} # switch
if (!empty($tmp)) {
if ($tmp[0]['serialized'] == 1) {
$tmp[0]['content'] = unserialize($tmp[0]['content']);
} # if
$tmp[0]['metadata'] = unserialize($tmp[0]['metadata']);
return $tmp[0];
} # if
return false;
} # getCache
function updateCacheStamp($resourceid, $cachetype) {
$this->_conn->exec("UPDATE cache SET stamp = %d WHERE resourceid = '%s' AND cachetype = '%s'", Array(time(), $resourceid, $cachetype));
} # updateCacheStamp
function saveCache($resourceid, $cachetype, $metadata, $content) {
if (is_array($content)) {
$serialize = true;
$content = serialize($content);
} else {
$serialize = false;
} # else
if ($metadata) {
$metadata = serialize($metadata);
} # if
if ($this->getMaxPacketsize() > 0 && (strlen($content)*1.15)+115 > $this->getMaxPacketSize()) {
return;
} # if
switch ($this->_dbsettings['engine']) {
case 'pdo_pgsql' : {
$this->_conn->exec("UPDATE cache SET stamp = %d, metadata = '%s', serialized = '%s', content = '%b' WHERE resourceid = '%s' AND cachetype = '%s'", Array(time(), $metadata, $this->bool2dt($serialize), $content, $resourceid, $cachetype));
if ($this->_conn->rows() == 0) {
$this->_conn->modify("INSERT INTO cache(resourceid,cachetype,stamp,metadata,serialized,content) VALUES ('%s', '%s', %d, '%s', '%s', '%b')", Array($resourceid, $cachetype, time(), $metadata, $this->bool2dt($serialize), $content));
} # if
break;
} # pgsql
case 'mysql' :
case 'pdo_mysql' : {
$this->_conn->exec("UPDATE cache SET stamp = %d, metadata = '%s', serialized = '%s', content = COMPRESS('%s') WHERE resourceid = '%s' AND cachetype = '%s'", Array(time(), $metadata, $this->bool2dt($serialize), $content, $resourceid, $cachetype));
if ($this->_conn->rows() == 0) {
$this->_conn->modify("INSERT INTO cache(resourceid,cachetype,stamp,metadata,serialized,content) VALUES ('%s', '%s', %d, '%s', '%s', COMPRESS('%s'))", Array($resourceid, $cachetype, time(), $metadata, $this->bool2dt($serialize), $content));
} # if
break;
} # mysql
default : {
$this->_conn->exec("UPDATE cache SET stamp = %d, metadata = '%s', serialized = '%s', content = '%s' WHERE resourceid = '%s' AND cachetype = '%s'", Array(time(), $metadata, $this->bool2dt($serialize), $content, $resourceid, $cachetype));
if ($this->_conn->rows() == 0) {
$this->_conn->modify("INSERT INTO cache(resourceid,cachetype,stamp,metadata,serialized,content) VALUES ('%s', '%s', %d, '%s', '%s', '%s')", Array($resourceid, $cachetype, time(), $metadata, $this->bool2dt($serialize), $content));
} # if
} # default
} # switch
} # saveCache
/*
* Updates a users' setting with an base64 encoded image
*/
function setUserAvatar($userId, $imageEncoded) {
$this->_conn->modify("UPDATE usersettings SET avatar = '%s' WHERE userid = %d", Array( $imageEncoded, (int) $userId));
} # setUserAvatar
function beginTransaction() {
$this->_conn->beginTransaction();
} # beginTransaction
function abortTransaction() {
$this->_conn->rollback();
} # abortTransaction
function commitTransaction() {
$this->_conn->commit();
} # commitTransaction
function safe($q) {
return $this->_conn->safe($q);
} # safe
/*
* Converts a boolean value to a string
* for usage by the database
*/
function bool2dt($b) {
return $this->_conn->bool2dt($b);
} # bool2dt
function removeOldBlackList($blacklistUrl) {
$this->_conn->modify("DELETE FROM spotteridblacklist WHERE (ouruserid = -1) AND (origin = 'external')");
$this->_conn->modify("DELETE FROM cache WHERE (resourceid = '%s') AND (cachetype = '%s')", Array(md5($blacklistUrl), SpotCache::Web));
} # removeOldBlackList
function updateExternalBlacklist($newblacklist) {
$updatelist = array();
$updskipped = 0;
$countnewblacklistspotterid = 0;
$countdelblacklistspotterid = 0;
/* Haal de oude blacklist op*/
$oldblacklist = $this->_conn->arrayQuery("SELECT spotterid
FROM spotteridblacklist
WHERE ouruserid = -1 AND origin = 'external'");
foreach ($oldblacklist as $obl) {
$updatelist[$obl['spotterid']] = 2; # 'oude' spotterid eerst op verwijderen zetten.
}
/* verwerk de nieuwe blacklist */
foreach ($newblacklist as $nbl) {
$nbl = trim($nbl); # Enters en eventuele spaties wegfilteren
if ((strlen($nbl) >= 3) && (strlen($nbl) <= 6)) { # de lengte van een spotterid is tussen 3 en 6 karakters groot (tot op heden)
if (empty($updatelist[$nbl])) {
$updatelist[$nbl] = 1; # nieuwe spoterids toevoegen
} elseif ($updatelist[$nbl] == 2) {
$updatelist[$nbl] = 3; # spotterid staat nog steeds op de blacklist, niet verwijderen.
} else {
$updskipped++; # dubbel spotterid in blacklist.txt.
}
} else {
$updskipped++; # er is iets mis met het spotterid (bijvoorbeeld een lege regel in blacklist.txt)
}
}
$updblacklist = array_keys($updatelist);
foreach ($updblacklist as $updl) {
if ($updatelist[$updl] == 1) {
# voeg nieuwe spotterid's toe aan de blacklist
$countnewblacklistspotterid++;
$this->_conn->modify("INSERT INTO spotteridblacklist (spotterid,ouruserid,origin) VALUES ('%s','-1','external')", Array($updl));
} elseif ($updatelist[$updl] == 2) {
# verwijder spotterid's die niet meer op de blacklist staan
$countdelblacklistspotterid++;
$this->_conn->modify("DELETE FROM spotteridblacklist WHERE (spotterid = '%s') AND (ouruserid = -1) AND (origin = 'external')", Array($updl));
}
}
return array('added' => $countnewblacklistspotterid,'removed' => $countdelblacklistspotterid,'skipped' => $updskipped);
} # updateExternalBlacklist
} # class db
debian/db-update/v20111210/lib/SpotSeclibToOpenSsl.php 0000644 0000000 0000000 00000012662 12267045571 017265 0 ustar _pubKeyCache[$rsaKey['modulo'] . $rsaKey['exponent']])) {
$openSslPubKey = $this->_pubKeyCache[$rsaKey['modulo'] . $rsaKey['exponent']];
$verified = openssl_verify($toCheck, $signature, $openSslPubKey);
} else {
# Initialize the public key to verify with
$pubKey['n'] = base64_decode($rsaKey['modulo']);
$pubKey['e'] = base64_decode($rsaKey['exponent']);
$openSslPubKey = openssl_get_publickey($this->seclibToOpenSsl($pubKey));
$verified = openssl_verify($toCheck, $signature, $openSslPubKey);
# Moeten we de resource cachen ipv vrijgeven?
if ($useCache) {
$this->_pubKeyCache[$rsaKey['modulo'] . $rsaKey['exponent']] = $openSslPubKey;
} else {
openssl_free_key($openSslPubKey);
} # else
} # else
return $verified;
} # verify
function _getOidElementLength($component) {
# Code copied from
# http://chaosinmotion.com/wiki/index.php?title=ASN.1_Library
if ($component < 0) return 10; // Full 64 bits takes 10*7 bits to encode
$l = 1;
for ($i = 1; $i < 9; ++$i) {
$l <<= 7;
if ($component < $l) break;
}
return $i;
}
function _encodeObjectId($vals) {
$return = array();
$return[] = 40 * $vals[0] + $vals[1];
$valCount = count($vals);
for($i = 2; $i < $valCount; $i++) {
# Code copied from
# http://chaosinmotion.com/wiki/index.php?title=ASN.1_Library
$v = $vals[$i];
$len = $this->_getOIDElementLength($v);
for ($j = $len-1; $j > 0; --$j) {
$m = 0x0080 | (0x007F & ($v >> ($j * 7)));
$return[] = (int) $m;
}
$return[] = (int)(0x007F & $v);
}
return $return;
} # _encodeObjectId
function seclibToOpenSsl($pubKey) {
/*
* Structuur van de OpenSSL publickey is als volgt:
*
* - Sequence
* +- Sequence
* ++- Object identifier die de RSA key weergeeft (1.2.840.113549.1.1.1)
* ++- NULL
* +- Bit String
* ++- Sequence
* +++- Integer
* +++- Integer
*
* Dit willen we nabootsen met deze encoding
*/
$publicExponent = $pubKey['e'];
$modulus = $pubKey['n'];
$components = array(
'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
);
/*
* First encoden we de keys in een bitstring
*/
$encodedKeys = pack('Ca*a*a*',
CRYPT_RSA_ASN1_SEQUENCE, # Sequence
$this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
$components['modulus'],
$components['publicExponent']
);
$encodedKeys = pack('Ca*Ca*',
0x03, # 0x03 means BIT STRING
$this->_encodeLength(strlen($encodedKeys) + 1), # add 1 voor de 0 unused bits
0,
$encodedKeys
);
/*
* Nu creeeren we de type header
*
* We kunnen de rsaIdentifier berekenen, maar omdat dat toch nooit verandert,
* zetten we de berekening klaar.
* Code om te berekenen:
* $rsaIdentifier = $this->_encodeObjectId(array(1,2,840,113549,1,1,1)); // Magic value of RSA
*
* $encryptionType = pack('Ca*',
* 0x06, # ASN.1 OBJECT IDENTIFIER
* $this->_encodeLength(count($rsaIdentifier))
* );
* $rsaIdentifierCount = count($rsaIdentifier);
* for($i = 0; $i < $rsaIdentifierCount; $i++) {
* $encryptionType .= chr($rsaIdentifier[$i]);
* } # foreach
*
*
* # de encryption type header wordt geappend met een ASN.1 NULL
* $encryptionType .= pack('CC',
* 0x05, # ASN.1 NULL
* 0
* );
*
* # en de encryptiontype pakken we in in een sequence
* $encryptionType = pack('Ca*a*',
* CRYPT_RSA_ASN1_SEQUENCE, # Sequence
* $this->_encodeLength(strlen($encryptionType)),
* $encryptionType
* );
*/
$encryptionType = "\x30\xd\x6\x9\x2a\x86\x48\x86\xf7\xd\x1\x1\x1\x5\x0";
# en ook dit alles pakken we in een sequence in
$endResult = pack('Ca*a*',
CRYPT_RSA_ASN1_SEQUENCE, # Sequence
$this->_encodeLength(15 + strlen($encodedKeys)), # 15 == strlen($encryptionType)
$encryptionType . $encodedKeys
);
return "-----BEGIN PUBLIC KEY-----\n" .
chunk_split(base64_encode($endResult), 64) .
"-----END PUBLIC KEY-----\n";
} # seclibToOpenSsl
/**
*
* From phpSeclib library
*
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information.
*
* @access private
* @param Integer $length
* @return String
*/
function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
} # SpotSeclibToOpenSsl
debian/db-update/v20111210/lib/dbstruct/ 0000755 0000000 0000000 00000000000 12267045571 014521 5 ustar debian/db-update/v20111210/lib/dbstruct/SpotStruct_mysql.php 0000644 0000000 0000000 00000026703 12267045571 020621 0 ustar _dbcon->rawExec("ANALYZE TABLE spotstatelist");
$this->_dbcon->rawExec("ANALYZE TABLE sessions");
$this->_dbcon->rawExec("ANALYZE TABLE users");
$this->_dbcon->rawExec("ANALYZE TABLE commentsfull");
$this->_dbcon->rawExec("ANALYZE TABLE spots");
$this->_dbcon->rawExec("ANALYZE TABLE spotsfull");
$this->_dbcon->rawExec("ANALYZE TABLE commentsxover");
} # analyze
/* converteert een "spotweb" datatype naar een mysql datatype */
function swDtToNative($colType) {
switch(strtoupper($colType)) {
case 'INTEGER' : $colType = 'int(11)'; break;
case 'UNSIGNED INTEGER' : $colType = 'int(10) unsigned'; break;
case 'BIGINTEGER' : $colType = 'bigint(20)'; break;
case 'UNSIGNED BIGINTEGER' : $colType = 'bigint(20) unsigned'; break;
case 'BOOLEAN' : $colType = 'tinyint(1)'; break;
case 'MEDIUMBLOB' : $colType = 'mediumblob'; break;
} # switch
return $colType;
} # swDtToNative
/* converteert een mysql datatype naar een "spotweb" datatype */
function nativeDtToSw($colInfo) {
switch(strtolower($colInfo)) {
case 'int(11)' : $colInfo = 'INTEGER'; break;
case 'int(10) unsigned' : $colInfo = 'UNSIGNED INTEGER'; break;
case 'bigint(20)' : $colInfo = 'BIGINTEGER'; break;
case 'bigint(20) unsigned' : $colInfo = 'UNSIGNED BIGINTEGER'; break;
case 'tinyint(1)' : $colInfo = 'BOOLEAN'; break;
case 'mediumblob' : $colInfo = 'MEDIUMBLOB'; break;
} # switch
return $colInfo;
} # nativeDtToSw
/* controleert of een index bestaat */
function indexExists($idxname, $tablename) {
$q = $this->_dbcon->arrayQuery("SHOW INDEXES FROM " . $tablename . " WHERE key_name = '%s'", Array($idxname));
return !empty($q);
} # indexExists
/* controleert of een column bestaat */
function columnExists($tablename, $colname) {
$q = $this->_dbcon->arrayQuery("SHOW COLUMNS FROM " . $tablename . " WHERE Field = '%s'", Array($colname));
return !empty($q);
} # columnExists
/* Add an index, kijkt eerst wel of deze index al bestaat */
function addIndex($idxname, $idxType, $tablename, $colList) {
if (!$this->indexExists($idxname, $tablename)) {
if ($idxType == "UNIQUE") {
$this->_dbcon->rawExec("ALTER IGNORE TABLE " . $tablename . " ADD " . $idxType . " INDEX " . $idxname . "(" . implode(",", $colList) . ");");
} else {
$this->_dbcon->rawExec("ALTER TABLE " . $tablename . " ADD " . $idxType . " INDEX " . $idxname . "(" . implode(",", $colList) . ");");
} # else
} # if
} # addIndex
/* controleert of een full text index bestaat */
function ftsExists($ftsname, $tablename, $colList) {
foreach($colList as $num => $col) {
$indexInfo = $this->getIndexInfo($ftsname . '_' . $num, $tablename);
if ((empty($indexInfo)) || (strtolower($indexInfo[0]['column_name']) != strtolower($col))) {
return false;
} # if
} # foreach
return true;
} # ftsExists
/* maakt een full text index aan */
function createFts($ftsname, $tablename, $colList) {
foreach($colList as $num => $col) {
$indexInfo = $this->getIndexInfo($ftsname . '_' . $num, $tablename);
if ((empty($indexInfo)) || (strtolower($indexInfo[0]['column_name']) != strtolower($col))) {
$this->dropIndex($ftsname . '_' . $num, $tablename);
$this->addIndex($ftsname . '_' . $num, 'FULLTEXT', $tablename, array($col));
} # if
} # foreach
} # createFts
/* dropt en fulltext index */
function dropFts($ftsname, $tablename, $colList) {
foreach($colList as $num => $col) {
$this->dropIndex($ftsname . '_' . $num, $tablename);
} # foreach
} # dropFts
/* geeft FTS info terug */
function getFtsInfo($ftsname, $tablename, $colList) {
$ftsList = array();
foreach($colList as $num => $col) {
$tmpIndex = $this->getIndexInfo($ftsname . '_' . $num, $tablename);
if (!empty($tmpIndex)) {
$ftsList[] = $tmpIndex[0];
} # if
} # foreach
return $ftsList;
} # getFtsInfo
/* dropt een index als deze bestaat */
function dropIndex($idxname, $tablename) {
# Check eerst of de tabel bestaat, anders kan
# indexExists mislukken en een fatal error geven
if (!$this->tableExists($tablename)) {
return ;
} # if
if ($this->indexExists($idxname, $tablename)) {
$this->_dbcon->rawExec("DROP INDEX " . $idxname . " ON " . $tablename);
} # if
} # dropIndex
/* voegt een column toe, kijkt wel eerst of deze nog niet bestaat */
function addColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation) {
if (!$this->columnExists($tablename, $colName)) {
# zet de DEFAULT waarde
if (strlen($colDefault) != 0) {
$colDefault = 'DEFAULT ' . $colDefault;
} # if
# converteer het kolom type naar het type dat wij gebruiken
$colType = $this->swDtToNative($colType);
# Zet de collation om naar iets dat we begrijpen
switch(strtolower($collation)) {
case 'utf8' : $colSetting = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci'; break;
case 'ascii' : $colSetting = 'CHARACTER SET ascii'; break;
case '' : $colSetting = ''; break;
default : throw new Exception("Invalid collation setting");
} # switch
# en zet de 'NOT NULL' om naar een string
switch($notNull) {
case true : $nullStr = 'NOT NULL'; break;
default : $nullStr = '';
} # switch
$this->_dbcon->rawExec("ALTER TABLE " . $tablename .
" ADD COLUMN(" . $colName . " " . $colType . " " . $colSetting . " " . $colDefault . " " . $nullStr . ")");
} # if
} # addColumn
/* wijzigt een column - controleert *niet* of deze voldoet aan het prototype */
function modifyColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation, $what) {
# zet de DEFAULT waarde
if (strlen($colDefault) != 0) {
$colDefault = 'DEFAULT ' . $colDefault;
} # if
# converteer het kolom type naar het type dat wij gebruiken
$colType = $this->swDtToNative($colType);
# Zet de collation om naar iets dat we begrijpen
switch(strtolower($collation)) {
case 'utf8' : $colSetting = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci'; break;
case 'ascii' : $colSetting = 'CHARACTER SET ascii'; break;
case '' : $colSetting = ''; break;
default : throw new Exception("Invalid collation setting");
} # switch
# en zet de 'NOT NULL' om naar een string
switch($notNull) {
case true : $nullStr = 'NOT NULL'; break;
default : $nullStr = '';
} # switch
$this->_dbcon->rawExec("ALTER TABLE " . $tablename .
" MODIFY COLUMN " . $colName . " " . $colType . " " . $colSetting . " " . $colDefault . " " . $nullStr);
} # modifyColumn
/* dropt een kolom (mits db dit ondersteunt) */
function dropColumn($colName, $tablename) {
if ($this->columnExists($tablename, $colName)) {
$this->_dbcon->rawExec("ALTER TABLE " . $tablename . " DROP COLUMN " . $colName);
} # if
} # dropColumn
/* controleert of een tabel bestaat */
function tableExists($tablename) {
$q = $this->_dbcon->arrayQuery("SHOW TABLES LIKE '" . $tablename . "'");
return !empty($q);
} # tableExists
/* ceeert een lege tabel met enkel een ID veld, collation kan UTF8 of ASCII zijn */
function createTable($tablename, $collation) {
if (!$this->tableExists($tablename)) {
switch(strtolower($collation)) {
case 'utf8' : $colSetting = 'CHARSET=utf8 COLLATE=utf8_unicode_ci'; break;
case 'ascii' : $colSetting = 'CHARSET=ascii'; break;
default : throw new Exception("Invalid collation setting");
} # switch
$this->_dbcon->rawExec("CREATE TABLE " . $tablename . " (id INTEGER PRIMARY KEY AUTO_INCREMENT) " . $colSetting);
} # if
} # createTable
/* drop een table */
function dropTable($tablename) {
if ($this->tableExists($tablename)) {
$this->_dbcon->rawExec("DROP TABLE " . $tablename);
} # if
} # dropTable
/* verandert een storage engine (concept dat enkel mysql kent :P ) */
function alterStorageEngine($tablename, $engine) {
$q = $this->_dbcon->singleQuery("SELECT ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = '" . $tablename . "'");
if (strtolower($q) != strtolower($engine)) {
$this->_dbcon->rawExec("ALTER TABLE " . $tablename . " ENGINE=" . $engine);
} # if
} # alterStorageEngine
/* rename een table */
function renameTable($tablename, $newTableName) {
$this->_dbcon->rawExec("RENAME TABLE " . $tablename . " TO " . $newTableName);
} # renameTable
/* dropped een foreign key constraint */
function dropForeignKey($tablename, $colname, $reftable, $refcolumn, $action) {
$q = $this->_dbcon->arrayQuery("SELECT CONSTRAINT_NAME FROM information_schema.key_column_usage
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = '" . $tablename . "'
AND COLUMN_NAME = '" . $colname . "'
AND REFERENCED_TABLE_NAME = '" . $reftable . "'
AND REFERENCED_COLUMN_NAME = '" . $refcolumn . "'");
if (!empty($q)) {
foreach($q as $res) {
$this->_dbcon->rawExec("ALTER TABLE " . $tablename . " DROP FOREIGN KEY " . $res['CONSTRAINT_NAME']);
} # foreach
} # if
} # dropForeignKey
/* creeert een foreign key constraint */
function addForeignKey($tablename, $colname, $reftable, $refcolumn, $action) {
$q = $this->_dbcon->arrayQuery("SELECT * FROM information_schema.key_column_usage
WHERE TABLE_SCHEMA = DATABASE()
AND TABLE_NAME = '" . $tablename . "'
AND COLUMN_NAME = '" . $colname . "'
AND REFERENCED_TABLE_NAME = '" . $reftable . "'
AND REFERENCED_COLUMN_NAME = '" . $refcolumn . "'");
if (empty($q)) {
$this->_dbcon->rawExec("ALTER TABLE " . $tablename . " ADD FOREIGN KEY (" . $colname . ")
REFERENCES " . $reftable . " (" . $refcolumn . ") " . $action);
} # if
} # addForeignKey
/* Geeft, in een afgesproken formaat, de index formatie terug */
function getColumnInfo($tablename, $colname) {
$q = $this->_dbcon->arrayQuery("SELECT COLUMN_NAME,
COLUMN_DEFAULT,
IS_NULLABLE,
COLUMN_TYPE,
CHARACTER_SET_NAME,
COLLATION_NAME
FROM information_schema.COLUMNS
WHERE TABLE_NAME = '" . $tablename . "'
AND COLUMN_NAME = '" . $colname . "'
AND TABLE_SCHEMA = DATABASE()");
if (!empty($q)) {
$q = $q[0];
$q['NOTNULL'] = ($q['IS_NULLABLE'] != 'YES');
# MySQL's boolean type is stiekem een tinyint, maar wij verwachten
# binnen spotweb een echte boolean. Dus we converteren dat stiekem
if (strtolower($q['COLUMN_TYPE']) == 'tinyint(1)') {
if (is_numeric($q['COLUMN_DEFAULT'])) {
if ($q['COLUMN_DEFAULT']) {
$q['COLUMN_DEFAULT'] = '1';
} else {
$q['COLUMN_DEFAULT'] = '0';
} # if
} # if
} # if
# converteer het default waarde naar iets anders
if ((strlen($q['COLUMN_DEFAULT']) == 0) && (is_string($q['COLUMN_DEFAULT']))) {
$q['COLUMN_DEFAULT'] = "''";
} # if
} # if
return $q;
} # getColumnInfo
/* Geeft, in een afgesproken formaat, de index informatie terug */
function getIndexInfo($idxname, $tablename) {
$q = $this->_dbcon->arrayQuery("SELECT
column_name,
non_unique,
lower(index_type) as index_type
FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE()
AND table_name = '" . $tablename . "'
AND index_name = '" . $idxname . "'
ORDER BY seq_in_index");
return $q;
} # getIndexInfo
} # class debian/db-update/v20111210/lib/dbstruct/SpotStruct_abs.php 0000644 0000000 0000000 00000113441 12267045571 020215 0 ustar _spotdb = $spotdb;
$this->_dbcon = $spotdb->getDbHandle();
} # __construct
/*
* optimaliseer/analyseer een aantal tables welke veel veranderen,
* deze functie wijzigt geen data!
*/
abstract function analyze();
/* converteert een "spotweb" datatype naar een mysql datatype */
abstract function swDtToNative($colType);
/* converteert een mysql datatype naar een "spotweb" datatype */
abstract function nativeDtToSw($colInfo);
/*
* Add an index, kijkt eerst wel of deze index al bestaat,
* $idxType kan danwel 'UNIQUE' danwel 'FULLTEXT' zijn
*/
abstract function addIndex($idxname, $idxType, $tablename, $colList);
/* dropt een index als deze bestaat */
abstract function dropIndex($idxname, $tablename);
/* voegt een column toe, kijkt wel eerst of deze nog niet bestaat */
abstract function addColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation);
/* wijzigt een column - controleert *niet* of deze voldoet aan het prototype */
abstract function modifyColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation, $what);
/* dropt een kolom (mits db dit ondersteunt) */
abstract function dropColumn($colName, $tablename);
/* controleert of een index bestaat */
abstract function indexExists($idxname, $tablename);
/* controleert of een kolom bestaat */
abstract function columnExists($tablename, $colname);
/* controleert of een tabel bestaat */
abstract function tableExists($tablename);
/* controleert of een full text index bestaat */
abstract function ftsExists($ftsname, $tablename, $colList);
/* maakt een full text index aan */
abstract function createFts($ftsname, $tablename, $colList);
/* dropt en fulltext index */
abstract function dropFts($ftsname, $tablename, $colList);
/* geeft FTS info terug */
abstract function getFtsInfo($ftsname, $tablename, $colList);
/* ceeert een lege tabel met enkel een ID veld, collation kan UTF8 of ASCII zijn */
abstract function createTable($tablename, $collation);
/* creeert een foreign key constraint */
abstract function addForeignKey($tablename, $colname, $reftable, $refcolumn, $action);
/* dropped een foreign key constraint */
abstract function dropForeignKey($tablename, $colname, $reftable, $refcolumn, $action);
/* verandert een storage engine (concept dat enkel mysql kent :P ) */
abstract function alterStorageEngine($tablename, $engine);
/* drop een table */
abstract function dropTable($tablename);
/* rename een table */
abstract function renameTable($tablename, $newTableName);
/* Geeft, in een afgesproken formaat, de index informatie terug */
abstract function getIndexInfo($idxname, $tablename);
/* Geeft, in een afgesproken formaat, de index formatie terug */
abstract function getColumnInfo($tablename, $colname);
/* controleert of de index structuur hetzelfde is als de gewenste, zo niet, maak hem opnieuw aan */
function validateIndex($idxname, $type, $tablename, $colList) {
echo "\tValidating index " . $idxname . PHP_EOL;
if (!$this->compareIndex($idxname, $type, $tablename, $colList)) {
# Drop de index
if ($this->indexExists($idxname, $tablename)) {
echo "\t\tDropping index " . $idxname . PHP_EOL;
$this->dropIndex($idxname, $tablename);
} # if
echo "\t\tAdding index " . $idxname . PHP_EOL;
# en creeer hem opnieuw
$this->addIndex($idxname, $type, $tablename, $colList);
} # if
} # validateIndex
/* controleert of de fulltext structuur hetzelfde is als de gewenste, zo niet, maak hem opnieuw aan */
function validateFts($ftsname, $tablename, $colList) {
echo "\tValidating FTS " . $ftsname . PHP_EOL;
if (!$this->compareFts($ftsname, $tablename, $colList)) {
# Drop de FTS
if ($this->ftsExists($ftsname, $tablename, $colList)) {
echo "\t\tDropping FTS " . $ftsname . PHP_EOL;
$this->dropFts($ftsname, $tablename, $colList);
} # if
echo "\t\tAdding FTS " . $ftsname . PHP_EOL;
# en creeer hem opnieuw
$this->createFts($ftsname, $tablename, $colList);
} # if
} # validateFts
/* controleert of de index structuur hetzelfde is als de gewenste, zo niet, maak hem opnieuw aan */
function validateColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation) {
echo "\tValidating " . $tablename . "(" . $colName . ")" . PHP_EOL;
$compResult = $this->compareColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation);
if ($compResult !== true) {
if ($this->columnExists($tablename, $colName)) {
echo "\t\tModifying column " . $colName . " (" . $compResult . ") on " . $tablename . PHP_EOL;
$this->modifyColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation, $compResult);
} else {
echo "\t\tAdding column " . $colName . "(" . $colType . ") to " . $tablename . PHP_EOL;
$this->addColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation);
} # else
} # if
} # validateColumn
/* vergelijkt een column met de gewenste structuur */
function compareColumn($colName, $tablename, $colType, $colDefault, $notNull, $collation) {
# Vraag nu de column informatie op
$q = $this->getColumnInfo($tablename, $colName);
# Als de column helemaal niet gevonden wordt..
if (empty($q)) {
return false;
} # if
# controleer het type
if (strtolower($q['COLUMN_TYPE']) != strtolower($this->swDtToNative($colType))) {
#var_dump($q);
#var_dump($colType);
#var_dump($this->swDtToNative($colType));
#die();
return 'type';
} # if
# controleer default
if (strtolower($q['COLUMN_DEFAULT']) != strtolower($colDefault)) {
return 'default';
} # if
# controleer NOT NULL setting
if (strtolower($q['NOTNULL']) != $notNull) {
return 'not null';
} # if
# controleer NOT NULL setting
if ((strtolower($q['CHARACTER_SET_NAME']) != $collation) && ($q['CHARACTER_SET_NAME'] != null)) {
return 'charset';
} # if
return true;
} # compareColumn
/* vergelijkt een index met de gewenste structuur */
function compareIndex($idxname, $type, $tablename, $colList) {
# Vraag nu de index informatie op
$q = $this->getIndexInfo($idxname, $tablename);
# Als het aantal kolommen niet gelijk is
if (count($q) != count($colList)) {
return false;
} # if
# we loopen vervolgens door elke index kolom heen, en vergelijken
# dan of ze in dezelfde volgorde staan en dezelfde eigenschappen hebben
for($i = 0; $i < count($colList); $i++) {
$same = true;
if ($colList[$i] != $q[$i]['column_name']) {
$same = false;
} # if
if ($same) {
switch(strtolower($type)) {
case 'fulltext' : $same = (strtolower($q[$i]['index_type']) == 'fulltext'); break;
case 'unique' : $same = ($q[$i]['non_unique'] == 0); break;
case '' : $same = (strtolower($q[$i]['index_type']) != 'fulltext') && ($q[$i]['non_unique'] == 1);
} # switch
} # if
if (!$same) {
#var_dump($q[$i]);
#var_dump($type);
#var_dump($colList);
#die();
return false;
} # if
} # for
return true;
} # compareIndex
/* vergelijkt een FTS met de gewenste structuur */
function compareFts($ftsname, $tablename, $colList) {
# Vraag nu de FTS informatie op
$q = $this->getFtsInfo($ftsname, $tablename, $colList);
# Als het aantal kolommen niet gelijk is
if (count($q) != count($colList)) {
return false;
} # if
# we loopen vervolgens door elke index kolom heen, en vergelijken
# dan of ze in dezelfde volgorde staan en dezelfde eigenschappen hebben
for($i = 0; $i < count($colList); $i++) {
if ($colList[$i + 1] != $q[$i]['column_name']) {
return false;
} # if
} # for
return true;
} # compareFts
function updateSchema() {
# drop eventueel FTS indexes op de spotsfull tabel
$this->dropIndex("idx_spotsfull_fts_1", "spotsfull");
$this->dropIndex("idx_spotsfull_fts_2", "spotsfull");
$this->dropIndex("idx_spotsfull_fts_3", "spotsfull");
$this->dropIndex("idx_spotsfull_2", "spotsfull"); # Index on userid
$this->dropIndex("idx_nntp_2", "nntp");
$this->dropIndex("idx_nntp_3", "nntp");
# relaties wissen
$this->dropForeignKey('spotsfull', 'messageid', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->dropForeignKey('spotstatelist', 'messageid', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->dropForeignKey('commentsposted', 'inreplyto', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->dropForeignKey('commentsposted', 'messageid', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->dropForeignKey('commentsxover', 'messageid', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->dropForeignKey('commentsfull', 'messageid', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->dropForeignKey('reportsposted', 'inreplyto', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->dropForeignKey('reportsposted', 'messageid', 'spots', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
##############################################################################################
# Opschonen data #############################################################################
##############################################################################################
if (($this instanceof SpotStruct_mysql) && (false)) {
echo "Cleaning up old data..." . PHP_EOL;
if ($this->tableExists('usersettings') && $this->tableExists('users')) {
$this->_dbcon->rawExec("DELETE usersettings FROM usersettings LEFT JOIN users ON usersettings.userid=users.id WHERE users.id IS NULL");
} # if
if ($this->tableExists('sessions') && $this->tableExists('users')) {
$this->_dbcon->rawExec("DELETE sessions FROM sessions LEFT JOIN users ON sessions.userid=users.id WHERE users.id IS NULL");
} # if
if ($this->tableExists('spotstatelist') && $this->tableExists('users')) {
$this->_dbcon->rawExec("DELETE spotstatelist FROM spotstatelist LEFT JOIN users ON spotstatelist.ouruserid=users.id WHERE users.id IS NULL");
} # if
if ($this->tableExists('usergroups') && $this->tableExists('users')) {
$this->_dbcon->rawExec("DELETE usergroups FROM usergroups LEFT JOIN users ON usergroups.userid=users.id WHERE users.id IS NULL");
} # if
if ($this->tableExists('usergroups') && $this->tableExists('securitygroups')) {
$this->_dbcon->rawExec("DELETE usergroups FROM usergroups LEFT JOIN securitygroups ON usergroups.groupid=securitygroups.id WHERE securitygroups.id IS NULL");
} # if
if ($this->tableExists('grouppermissions') && $this->tableExists('securitygroups')) {
$this->_dbcon->rawExec("DELETE grouppermissions FROM grouppermissions LEFT JOIN securitygroups ON grouppermissions.groupid=securitygroups.id WHERE securitygroups.id IS NULL");
} # if
if ($this->tableExists('commentsposted') && $this->tableExists('users')) {
$this->_dbcon->rawExec("DELETE commentsposted FROM commentsposted LEFT JOIN users ON commentsposted.ouruserid=users.id WHERE users.id IS NULL");
} # if
if ($this->tableExists('commentsposted') && $this->tableExists('spots')) {
$this->_dbcon->rawExec("DELETE commentsposted FROM commentsposted LEFT JOIN spots ON commentsposted.inreplyto=spots.messageid WHERE spots.messageid IS NULL");
} # if
if ($this->tableExists('commentsfull') && $this->tableExists('commentsxover')) {
$this->_dbcon->rawExec("DELETE commentsfull FROM commentsfull LEFT JOIN commentsxover ON commentsfull.messageid=commentsxover.messageid WHERE commentsxover.messageid IS NULL");
} # if
if ($this->tableExists('spotsfull') && $this->tableExists('spots')) {
$this->_dbcon->rawExec("DELETE spotsfull FROM spotsfull LEFT JOIN spots ON spotsfull.messageid=spots.messageid WHERE spots.messageid IS NULL");
} # if
if ($this->tableExists('spotstatelist') && $this->tableExists('spots')) {
$this->_dbcon->rawExec("DELETE spotstatelist FROM spotstatelist LEFT JOIN spots ON spotstatelist.messageid=spots.messageid WHERE spots.messageid IS NULL");
} # if
if ($this->tableExists('reportsposted') && $this->tableExists('users')) {
$this->_dbcon->rawExec("DELETE reportsposted FROM reportsposted LEFT JOIN users ON reportsposted.ouruserid=users.id WHERE users.id IS NULL");
} # if
if ($this->tableExists('reportsposted') && $this->tableExists('spots')) {
$this->_dbcon->rawExec("DELETE reportsposted FROM reportsposted LEFT JOIN spots ON reportsposted.inreplyto=spots.messageid WHERE spots.messageid IS NULL");
} # if
} # if
# ---- spots table ---- #
$this->createTable('spots', "utf8");
$this->validateColumn('messageid', 'spots', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('poster', 'spots', 'VARCHAR(128)', NULL, false, 'utf8');
$this->validateColumn('title', 'spots', 'VARCHAR(128)', NULL, false, 'utf8');
$this->validateColumn('tag', 'spots', 'VARCHAR(128)', NULL, false, 'utf8');
$this->validateColumn('category', 'spots', 'INTEGER', NULL, false, '');
$this->validateColumn('subcata', 'spots', 'VARCHAR(64)', NULL, false, 'ascii');
$this->validateColumn('subcatb', 'spots', 'VARCHAR(64)', NULL, false, 'ascii');
$this->validateColumn('subcatc', 'spots', 'VARCHAR(64)', NULL, false, 'ascii');
$this->validateColumn('subcatd', 'spots', 'VARCHAR(64)', NULL, false, 'ascii');
$this->validateColumn('subcatz', 'spots', 'VARCHAR(64)', NULL, false, 'ascii');
$this->validateColumn('stamp', 'spots', 'UNSIGNED INTEGER', NULL, false, '');
$this->validateColumn('reversestamp', 'spots', 'INTEGER', "0", false, '');
$this->validateColumn('filesize', 'spots', 'UNSIGNED BIGINTEGER', "0", true, '');
$this->validateColumn('moderated', 'spots', 'BOOLEAN', NULL, false, '');
$this->validateColumn('commentcount', 'spots', 'INTEGER', "0", false, '');
$this->validateColumn('spotrating', 'spots', 'INTEGER', "0", false, '');
$this->validateColumn('reportcount', 'spots', 'INTEGER', "0", false, '');
$this->validateColumn('spotterid', 'spots', 'VARCHAR(32)', NULL, false, 'ascii');
$this->alterStorageEngine("spots", "MyISAM");
# ---- spotsfull table ---- #
$this->createTable('spotsfull', "utf8");
$this->validateColumn('messageid', 'spotsfull', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('verified', 'spotsfull', 'BOOLEAN', NULL, false, '');
$this->validateColumn('usersignature', 'spotsfull', 'VARCHAR(255)', NULL, false, 'ascii');
$this->validateColumn('userkey', 'spotsfull', 'VARCHAR(512)', NULL, false, 'ascii');
$this->validateColumn('xmlsignature', 'spotsfull', 'VARCHAR(255)', NULL, false, 'ascii');
$this->validateColumn('fullxml', 'spotsfull', 'TEXT', NULL, false, 'utf8');
$this->alterStorageEngine("spotsfull", "InnoDB");
# ---- nntp table ---- #
$this->createTable('nntp', "utf8");
$this->validateColumn('server', 'nntp', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('maxarticleid', 'nntp', 'INTEGER', NULL, false, '');
$this->validateColumn('nowrunning', 'nntp', 'INTEGER', "0", false, '');
$this->validateColumn('lastrun', 'nntp', 'INTEGER', "0", false, '');
$this->validateColumn('serverdatelastrun', 'nntp', 'VARCHAR(14)', "00000000000000", false, 'ascii');
$this->alterStorageEngine("nntp", "InnoDB");
# ---- commentsxover table ---- #
$this->createTable('commentsxover', "ascii");
$this->validateColumn('messageid', 'commentsxover', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('nntpref', 'commentsxover', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('spotrating', 'commentsxover', 'INTEGER', "0", false, '');
$this->validateColumn('moderated', 'commentsxover', 'BOOLEAN', NULL, false, '');
$this->alterStorageEngine("commentsxover", "InnoDB");
# ---- reportsxover table ---- #
$this->createTable('reportsxover', "ascii");
$this->validateColumn('messageid', 'reportsxover', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('fromhdr', 'reportsxover', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('keyword', 'reportsxover', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('nntpref', 'reportsxover', 'VARCHAR(128)', "''", true, 'ascii');
$this->alterStorageEngine("reportsxover", "InnoDB");
# ---- spotstatelist table ---- #
$this->createTable('spotstatelist', "ascii");
$this->validateColumn('messageid', 'spotstatelist', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('ouruserid', 'spotstatelist', 'INTEGER', "0", false, '');
$this->validateColumn('download', 'spotstatelist', 'INTEGER', NULL, false, '');
$this->validateColumn('watch', 'spotstatelist', 'INTEGER', NULL, false, '');
$this->validateColumn('seen', 'spotstatelist', 'INTEGER', NULL, false, '');
$this->alterStorageEngine("spotstatelist", "InnoDB");
# ---- commentsfull table ---- #
$this->createTable('commentsfull', "ascii");
$this->validateColumn('messageid', 'commentsfull', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('fromhdr', 'commentsfull', 'VARCHAR(128)', NULL, false, 'utf8');
$this->validateColumn('stamp', 'commentsfull', 'INTEGER', NULL, false, '');
$this->validateColumn('usersignature', 'commentsfull', 'VARCHAR(255)', NULL, false, 'ascii');
$this->validateColumn('userkey', 'commentsfull', 'VARCHAR(512)', NULL, false, 'ascii');
$this->validateColumn('spotterid', 'commentsfull', 'VARCHAR(32)', NULL, false, 'ascii');
$this->validateColumn('hashcash', 'commentsfull', 'VARCHAR(255)', NULL, false, 'ascii');
$this->validateColumn('body', 'commentsfull', 'TEXT', NULL, false, 'utf8');
$this->validateColumn('verified', 'commentsfull', 'BOOLEAN', NULL, false, '');
$this->validateColumn('avatar', 'commentsfull', 'TEXT', NULL, false, 'ascii');
$this->alterStorageEngine("commentsfull", "InnoDB");
# ---- settings table ---- #
$this->createTable('settings', "ascii");
$this->validateColumn('name', 'settings', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('value', 'settings', 'TEXT', NULL, false, 'utf8');
$this->validateColumn('serialized', 'settings', 'boolean', NULL, false, '');
$this->alterStorageEngine("settings", "InnoDB");
# ---- commentsposted table ---- #
$this->createTable('commentsposted', "ascii");
$this->validateColumn('ouruserid', 'commentsposted', 'INTEGER', "0", true, '');
$this->validateColumn('messageid', 'commentsposted', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('inreplyto', 'commentsposted', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('randompart', 'commentsposted', 'VARCHAR(32)', "''", true, 'ascii');
$this->validateColumn('rating', 'commentsposted', 'INTEGER', 0, true, '');
$this->validateColumn('body', 'commentsposted', 'TEXT', NULL, false, 'utf8');
$this->validateColumn('stamp', 'commentsposted', 'INTEGER', "0", true, '');
$this->alterStorageEngine("commentsposted", "InnoDB");
# ---- spotsposted table ---- #
$this->createTable('spotsposted', "utf8");
$this->validateColumn('messageid', 'spotsposted', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('ouruserid', 'spotsposted', 'INTEGER', "0", true, '');
$this->validateColumn('stamp', 'spotsposted', 'UNSIGNED INTEGER', NULL, false, '');
$this->validateColumn('title', 'spotsposted', 'VARCHAR(128)', NULL, false, 'utf8');
$this->validateColumn('tag', 'spotsposted', 'VARCHAR(128)', NULL, false, 'utf8');
$this->validateColumn('category', 'spotsposted', 'INTEGER', NULL, false, '');
$this->validateColumn('subcats', 'spotsposted', 'VARCHAR(255)', NULL, false, 'ascii');
$this->validateColumn('filesize', 'spotsposted', 'UNSIGNED BIGINTEGER', "0", true, '');
$this->validateColumn('fullxml', 'spotsposted', 'TEXT', NULL, false, 'utf8');
$this->alterStorageEngine("spotsposted", "InnoDB");
# ---- reportsposted table ---- #
$this->createTable('reportsposted', "ascii");
$this->validateColumn('ouruserid', 'reportsposted', 'INTEGER', "0", true, '');
$this->validateColumn('messageid', 'reportsposted', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('inreplyto', 'reportsposted', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('randompart', 'reportsposted', 'VARCHAR(32)', "''", true, 'ascii');
$this->validateColumn('body', 'reportsposted', 'TEXT', NULL, false, 'utf8');
$this->validateColumn('stamp', 'reportsposted', 'INTEGER', "0", true, '');
$this->alterStorageEngine("reportsposted", "InnoDB");
# ---- usersettings table ---- #
$this->createTable('usersettings', "utf8");
$this->validateColumn('userid', 'usersettings', 'INTEGER', '0', true, '');
$this->validateColumn('privatekey', 'usersettings', "TEXT", NULL, false, 'ascii');
$this->validateColumn('publickey', 'usersettings', "TEXT", NULL, false, 'ascii');
$this->validateColumn('avatar', 'usersettings', "TEXT", NULL, false, 'ascii');
$this->validateColumn('otherprefs', 'usersettings', "TEXT", NULL, false, 'utf8');
$this->alterStorageEngine("usersettings", "InnoDB");
# ---- users table ---- #
$this->createTable('users', "utf8");
$this->validateColumn('username', 'users', "VARCHAR(128)", "''", true, 'utf8');
$this->validateColumn('firstname', 'users', "VARCHAR(128)", "''", true, 'utf8');
$this->validateColumn('passhash', 'users', "VARCHAR(40)", "''", true, 'ascii');
$this->validateColumn('lastname', 'users', "VARCHAR(128)", "''", true, 'utf8');
$this->validateColumn('mail', 'users', "VARCHAR(128)", "''", true, 'utf8');
$this->validateColumn('apikey', 'users', "VARCHAR(32)", "''", true, 'ascii');
$this->validateColumn('lastlogin', 'users', "INTEGER", "0", true, '');
$this->validateColumn('lastvisit', 'users', "INTEGER", "0", true, '');
$this->validateColumn('lastread', 'users', "INTEGER", "0", true, '');
$this->validateColumn('lastapiusage', 'users', "INTEGER", "0", true, '');
$this->validateColumn('deleted', 'users', "BOOLEAN", $this->_dbcon->bool2dt(false), true, '');
$this->alterStorageEngine("users", "InnoDB");
# ---- sessions ---- #
$this->createTable('sessions', "ascii");
$this->validateColumn('sessionid', 'sessions', 'VARCHAR(128)', NULL, false, 'ascii');
$this->validateColumn('userid', 'sessions', 'INTEGER', NULL, false, '');
$this->validateColumn('hitcount', 'sessions', 'INTEGER', NULL, false, '');
$this->validateColumn('lasthit', 'sessions', 'INTEGER', NULL, false, '');
$this->validateColumn('ipaddr', 'sessions', "VARCHAR(45)", "''", true, 'ascii');
$this->alterStorageEngine("sessions", "InnoDB");
# ---- securitygroups ----
$this->createTable('securitygroups', "ascii");
$this->validateColumn('name', 'securitygroups', 'VARCHAR(128)', NULL, false, 'ascii');
$this->alterStorageEngine("securitygroups", "InnoDB");
# ---- grouppermissions ----
$this->createTable('grouppermissions', "ascii");
$this->validateColumn('groupid', 'grouppermissions', 'INTEGER', "0", true, '');
$this->validateColumn('permissionid', 'grouppermissions', 'INTEGER', "0", true, '');
$this->validateColumn('objectid', 'grouppermissions', "VARCHAR(128)", "''", true, 'ascii');
$this->validateColumn('deny', 'grouppermissions', "BOOLEAN", $this->_dbcon->bool2dt(false), true, '');
$this->alterStorageEngine("grouppermissions", "InnoDB");
# ---- usergroups ----
$this->createTable('usergroups', "ascii");
$this->validateColumn('userid', 'usergroups', 'INTEGER', "0", true, '');
$this->validateColumn('groupid', 'usergroups', 'INTEGER', "0", true, '');
$this->validateColumn('prio', 'usergroups', 'INTEGER', '1', true, '');
$this->alterStorageEngine("usergroups", "InnoDB");
# ---- notifications ----
$this->createTable('notifications', "ascii");
$this->validateColumn('userid', 'notifications', 'INTEGER', "0", true, '');
$this->validateColumn('stamp', 'notifications', 'INTEGER', "0", true, '');
$this->validateColumn('objectid', 'notifications', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('type', 'notifications', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('title', 'notifications', 'VARCHAR(128)', "''", true, 'utf8');
$this->validateColumn('body', 'notifications', 'TEXT', NULL, false, 'utf8');
$this->validateColumn('sent', 'notifications', 'BOOLEAN', $this->_dbcon->bool2dt(false), true, '');
$this->alterStorageEngine("notifications", "InnoDB");
# ---- filters ----
$this->createTable('filters', "utf8");
$this->validateColumn('userid', 'filters', 'INTEGER', "0", true, '');
$this->validateColumn('filtertype', 'filters', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('title', 'filters', 'VARCHAR(128)', "''", true, 'utf8');
$this->validateColumn('icon', 'filters', 'VARCHAR(128)', "''", true, 'utf8');
$this->validateColumn('torder', 'filters', 'INTEGER', "0", true, '');
$this->validateColumn('tparent', 'filters', 'INTEGER', "0", true, '');
$this->validateColumn('tree', 'filters', 'TEXT', NULL, false, 'ascii');
$this->validateColumn('valuelist', 'filters', 'TEXT', NULL, false, 'utf8');
$this->validateColumn('sorton', 'filters', 'VARCHAR(128)', NULL, false, 'ascii');
$this->validateColumn('sortorder', 'filters', 'VARCHAR(128)', NULL, false, 'ascii');
$this->alterStorageEngine("filters", "InnoDB");
# ---- filtercounts ----
$this->createTable('filtercounts', "utf8");
$this->validateColumn('userid', 'filtercounts', 'INTEGER', "0", true, '');
$this->validateColumn('filterhash', 'filtercounts', 'VARCHAR(40)', "''", true, 'ascii');
$this->validateColumn('currentspotcount', 'filtercounts', 'INTEGER', "0", true, '');
$this->validateColumn('lastvisitspotcount', 'filtercounts', 'INTEGER', "0", true, '');
$this->validateColumn('lastupdate', 'filtercounts', 'INTEGER', "0", true, '');
$this->alterStorageEngine("filtercounts", "InnoDB");
# ---- spotteridblacklist table ---- #
$this->createTable('spotteridblacklist', "utf8");
$this->validateColumn('spotterid', 'spotteridblacklist', 'VARCHAR(32)', NULL, false, 'ascii');
$this->validateColumn('ouruserid', 'spotteridblacklist', 'INTEGER', "0", true, '');
$this->validateColumn('origin', 'spotteridblacklist', 'VARCHAR(255)', NULL, false, 'ascii');
$this->alterStorageEngine("spotteridblacklist", "InnoDB");
# oude cache droppen, converteren gaat te vaak fout
if (($this->_spotdb->getSchemaVer() < 0.50) && ($this->tableExists('cache'))) {
$this->dropTable('cache');
} # if
if (($this->_spotdb->getSchemaVer() < 0.51) && ($this->tableExists('cache')) && (!$this->tableExists('cachetmp')) && ($this instanceof SpotStruct_mysql)) {
$this->renameTable('cache', 'cachetmp');
} # if
# ---- cache table ---- #
$this->createTable('cache', "ascii");
$this->validateColumn('resourceid', 'cache', 'VARCHAR(128)', "''", true, 'ascii');
$this->validateColumn('cachetype', 'cache', 'INTEGER', "0", true, '');
$this->validateColumn('stamp', 'cache', 'INTEGER', "0", true, '');
$this->validateColumn('metadata', 'cache', 'TEXT', NULL, false, 'ascii');
$this->validateColumn('serialized', 'cache', 'BOOLEAN', NULL, false, '');
$this->validateColumn('content', 'cache', 'MEDIUMBLOB', NULL, false, '');
$this->alterStorageEngine("cache", "InnoDB");
# ---- permaudit table ---- #
$this->createTable('permaudit', "ascii");
$this->validateColumn('stamp', 'permaudit', 'INTEGER', "0", true, '');
$this->validateColumn('userid', 'permaudit', 'INTEGER', "0", true, '');
$this->validateColumn('permissionid', 'permaudit', 'INTEGER', "0", true, '');
$this->validateColumn('objectid', 'permaudit', "VARCHAR(128)", "''", true, 'ascii');
$this->validateColumn('result', 'permaudit', "BOOLEAN", $this->_dbcon->bool2dt(true), true, '');
$this->validateColumn('ipaddr', 'permaudit', "VARCHAR(45)", "''", true, 'ascii');
$this->alterStorageEngine("permaudit", "InnoDB");
##############################################################################################
### deprecation van oude Spotweb versies #####################################################
##############################################################################################
if ($this->_spotdb->getSchemaVer() > 0.00 && ($this->_spotdb->getSchemaVer() < 0.34)) {
if ($this->_spotdb->getSchemaVer() > 0.00 && ($this->_spotdb->getSchemaVer() < 0.30)) {
throw new Exception("Je huidige Spotweb database installatie is te oud om in een keer te upgraden naar deze versie." . PHP_EOL .
"Download een eerdere versie van spotweb (https://github.com/spotweb/spotweb/zipball/da6ba29071c49ae88823cccfefc39375b37e9bee), " . PHP_EOL .
"draai daarmee upgrade-db.php en als die succesvol is, start dan nogmaals de upgrade via deze versie.");
} # if
# Tabellen terug samenvoegen in een MyISAM tabel
if (($this->_spotdb->getSchemaVer() < 0.34) && ($this->tableExists('spottexts'))) {
throw new Exception("Je huidige Spotweb database installatie is te oud om in een keer te upgraden naar deze versie." . PHP_EOL .
"Download een eerdere versie van spotweb (https://github.com/spotweb/spotweb/zipball/48bc94a63f94959f9fe6b2372b312e35a4d09997), " . PHP_EOL .
"draai daarmee upgrade-db.php en als die succesvol is, start dan nogmaals de upgrade via deze versie.");
} # if
} # if
/*
* Convert the information from 'spotsfull' to 'spots' table
*/
if (($this->_spotdb->getSchemaVer() < 0.48) && ($this->_spotdb->getSchemaVer() > 0.00)) {
echo PHP_EOL . PHP_EOL;
echo 'Converting your spotsfull data to another format' . PHP_EOL;
echo 'Please note - if you had spotsfull enabled, this can take a long time' . PHP_EOL;
echo PHP_EOL . PHP_EOL;
# Empty the blacklist table because the userid column is renamed to spotterid
$tmp = $this->_dbcon->rawExec("TRUNCATE spotteridblacklist");
# Update the spotterid field with the userid field
$this->_dbcon->rawExec("UPDATE commentsfull SET spotterid = userid");
# MySQL specifieke syntax to update the spots
if ($this instanceof SpotStruct_mysql) {
$this->_dbcon->rawExec("UPDATE spots s, spotsfull f SET s.spotterid = f.userid WHERE (s.messageid = f.messageid)");
} # if
# PostgreSQL (?) specifieke syntax
if ($this instanceof SpotStruct_pgsql) {
$this->_dbcon->rawExec("UPDATE spots s SET spotterid = spotsfull.userid FROM spotsfull WHERE (s.messageid = spotsfull.messageid)");
} # if
} # if
# cache omzetten naar nieuw systeem
if (($this->_spotdb->getSchemaVer() < 0.51) && ($this->tableExists('cachetmp'))) {
$cachetmpCount = $this->_dbcon->singleQuery("SELECT COUNT(1) FROM cachetmp;");
if ($cachetmpCount > 7500) {
$dbname = $this->_dbcon->singleQuery("SELECT DATABASE();");
echo PHP_EOL;
echo "Converting the cache is not necessary to continue working with SpotWeb. If you don't want" . PHP_EOL;
echo "to wait for this conversion, please enter the following command in MySQL or phpMyAdmin:" . PHP_EOL;
echo "\tDROP TABLE " . $dbname . ".cachetmp;" . PHP_EOL . PHP_EOL;
echo "If you like to convert the cache, enter:" . PHP_EOL;
echo "\tINSERT INTO " . $dbname . ".cache SELECT resourceid, cachetype, stamp, metadata, serialized, COMPRESS(content) FROM " . $dbname . ".cachetmp;" . PHP_EOL;
echo "\tDROP TABLE " . $dbname . ".cachetmp;" . PHP_EOL . PHP_EOL;
echo "After this operation you must run upgrade-db.php again." . PHP_EOL . PHP_EOL;
die();
} # if
} # if
# En creeer de diverse indexen
# ---- Indexen op spots -----
$this->validateIndex("idx_spots_1", "UNIQUE", "spots", array("messageid"));
$this->validateIndex("idx_spots_2", "", "spots", array("stamp"));
$this->validateIndex("idx_spots_3", "", "spots", array("reversestamp"));
$this->validateIndex("idx_spots_4", "", "spots", array("category", "subcata", "subcatb", "subcatc", "subcatd", "subcatz"));
$this->validateIndex("idx_spots_5", "", "spots", array("spotterid"));
$this->validateFts("idx_fts_spots", "spots",
array(1 => "poster",
2 => 'title',
3 => 'tag'));
# ---- Indexen op nntp ----
$this->validateIndex("idx_nntp_1", "UNIQUE", "nntp", array("server"));
# ---- Indexen op spotsfull ----
$this->validateIndex("idx_spotsfull_1", "UNIQUE", "spotsfull", array("messageid"));
# ---- Indexen op commentsfull ----
$this->validateIndex("idx_commentsfull_1", "UNIQUE", "commentsfull", array("messageid"));
# ---- Indexen op commentsxover ----
$this->validateIndex("idx_commentsxover_1", "UNIQUE", "commentsxover", array("messageid"));
$this->validateIndex("idx_commentsxover_2", "", "commentsxover", array("nntpref"));
# ---- Indexen op reportsxover ----
$this->validateIndex("idx_reportsxover_1", "UNIQUE", "reportsxover", array("messageid"));
$this->validateIndex("idx_reportsxover_2", "", "reportsxover", array("nntpref"));
# ---- Indexen op reportsposted ----
$this->validateIndex("idx_reportsposted_1", "UNIQUE", "reportsposted", array("messageid"));
$this->validateIndex("idx_reportsposted_2", "UNIQUE", "reportsposted", array("inreplyto", "ouruserid"));
$this->validateIndex("idx_reportspostedrel_1", "", "reportsposted", array("ouruserid"));
# ---- Indexen op commentsposted ----
$this->validateIndex("idx_commentsposted_1", "UNIQUE", "commentsposted", array("messageid"));
$this->validateIndex("idx_commentspostedrel_1", "", "commentsposted", array("ouruserid"));
# ---- Indexen op spotsposted ----
$this->validateIndex("idx_spotsposted_1", "UNIQUE", "spotsposted", array("messageid"));
$this->validateIndex("idx_spotspostedrel_1", "", "spotsposted", array("ouruserid"));
# ---- Indexen op settings ----
$this->validateIndex("idx_settings_1", "UNIQUE", "settings", array("name"));
# ---- Indexen op usersettings ----
$this->validateIndex("idx_usersettings_1", "UNIQUE", "usersettings", array("userid"));
# ---- Indexen op users ----
$this->validateIndex("idx_users_1", "UNIQUE", "users", array("username"));
$this->validateIndex("idx_users_2", "UNIQUE", "users", array("mail"));
$this->validateIndex("idx_users_3", "", "users", array("deleted"));
$this->validateIndex("idx_users_4", "UNIQUE", "users", array("apikey"));
# ---- Indexen op sessions
$this->validateIndex("idx_sessions_1", "UNIQUE", "sessions", array("sessionid"));
$this->validateIndex("idx_sessions_2", "", "sessions", array("lasthit"));
$this->validateIndex("idx_sessions_3", "", "sessions", array("sessionid", "userid"));
$this->validateIndex("idx_sessionsrel_1", "", "sessions", array("userid"));
# ---- Indexen op spotstatelist ----
$this->validateIndex("idx_spotstatelist_1", "UNIQUE", "spotstatelist", array("messageid", "ouruserid"));
$this->validateIndex("idx_spotstatelistrel_1", "", "spotstatelist", array("ouruserid"));
# ---- Indexen op securitygroups ----
$this->validateIndex("idx_securitygroups_1", "UNIQUE", "securitygroups", array("name"));
# ---- Indexen op grouppermissions ----
$this->validateIndex("idx_grouppermissions_1", "UNIQUE", "grouppermissions", array("groupid", "permissionid", "objectid"));
# ---- Indexen op usergroups ----
$this->validateIndex("idx_usergroups_1", "UNIQUE", "usergroups", array("userid", "groupid"));
$this->validateIndex("idx_usergroupsrel_1", "", "usergroups", array("groupid"));
# ---- Indexen op notifications ----
$this->validateIndex("idx_notifications_1", "", "notifications", array("userid"));
$this->validateIndex("idx_notifications_2", "", "notifications", array("sent"));
# ---- Indexen op filters ----
$this->validateIndex("idx_filters_1", "", "filters", array("userid", "filtertype", 'tparent', 'torder'));
# ---- Indexen op filtercounts ----
$this->validateIndex("idx_filtercounts_1", "UNIQUE", "filtercounts", array("userid", "filterhash"));
# ---- Indexen op spotteridblacklist ----
$this->validateIndex("idx_spotteridblacklist_1", "UNIQUE", "spotteridblacklist", array("spotterid", "ouruserid"));
# ---- Indexen op cache ----
$this->validateIndex("idx_cache_1", "UNIQUE", "cache", array("resourceid", "cachetype"));
$this->validateIndex("idx_cache_2", "", "cache", array("cachetype", "stamp"));
# leg foreign keys aan
$this->addForeignKey('usersettings', 'userid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('sessions', 'userid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('spotstatelist', 'ouruserid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('usergroups', 'userid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('usergroups', 'groupid', 'securitygroups', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('grouppermissions', 'groupid', 'securitygroups', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('commentsfull', 'messageid', 'commentsxover', 'messageid', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('notifications', 'userid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('commentsposted', 'ouruserid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('reportsposted', 'ouruserid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('filters', 'userid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
$this->addForeignKey('spotsposted', 'ouruserid', 'users', 'id', 'ON DELETE CASCADE ON UPDATE CASCADE');
##############################################################################################
# Hier droppen we kolommen ###################################################################
##############################################################################################
$this->dropColumn('filesize', 'spotsfull');
$this->dropColumn('userid', 'spotsfull');
$this->dropColumn('userid', 'spotteridblacklist');
$this->dropColumn('userid', 'commentsfull');
##############################################################################################
# Hier droppen we tabellen ###################################################################
##############################################################################################
$this->dropTable('webcache');
$this->dropTable('cachetmp');
# voeg het database schema versie nummer toe
$this->_spotdb->updateSetting('schemaversion', SPOTDB_SCHEMA_VERSION);
} # updateSchema
} # class
debian/db-update/v20111210/lib/SpotUpgrader.php 0000644 0000000 0000000 00000003507 12267045571 016024 0 ustar _db = new SpotDb($dbSettings);
$this->_db->connect();
$this->_dbEngine = $dbSettings['engine'];
} # ctor
/*
* Upgrade de settings
*/
function settings($settings) {
include "settings.php";
# Creer het settings object
$settings = SpotSettings::singleton($this->_db, $settings);
$spotSettingsUpgrader = new SpotSettingsUpgrader($this->_db, $settings);
$spotSettingsUpgrader->update();
} # settings
/*
* Upgrade de users
*/
function users() {
include "settings.php";
# Creer het settings object
$settings = SpotSettings::singleton($this->_db, $settings);
$spotUserUpgrader = new SpotUserUpgrader($this->_db, $settings);
$spotUserUpgrader->update();
} # users
/*
* Creeert en upgrade de database
*/
function database() {
# Instantieeer een struct object
switch($this->_dbEngine) {
case 'mysql' :
case 'pdo_mysql' : $dbStruct = new SpotStruct_mysql($this->_db); break;
case 'pdo_pgsql' : $dbStruct = new SpotStruct_pgsql($this->_db); break;
case 'pdo_sqlite' : $dbStruct = new SpotStruct_sqlite($this->_db); break;
default : throw new Exception("Unknown database engine");
} # switch
$dbStruct->updateSchema();
} # database
/*
* Optimaliseert de database
*/
function analyze() {
# Instantieeer een struct object
switch($this->_dbEngine) {
case 'mysql' :
case 'pdo_mysql' : $dbStruct = new SpotStruct_mysql($this->_db); break;
case 'pdo_pgsql' : $dbStruct = new SpotStruct_pgsql($this->_db); break;
case 'pdo_sqlite' : $dbStruct = new SpotStruct_sqlite($this->_db); break;
default : throw new Exception("Unknown database engine");
} # switch
$dbStruct->analyze();
} # analyze
} # SpotUpgrader
debian/db-update/v20111210/lib/SpotUserSystem.php 0000644 0000000 0000000 00000112536 12267045571 016401 0 ustar _db = $db;
$this->_settings = $settings;
} # ctor
/*
* Generates an unique id, mostly used for sessions
*/
function generateUniqueId() {
$sessionId = '';
for($i = 0; $i < 10; $i++) {
$sessionId .= base_convert(mt_rand(), 10, 36);
} # for
return $sessionId;
} # generateUniqueId
/*
* Create a new session for the userid
*/
private function createNewSession($userid) {
# If this is an actual user, we need to have the user record
$tmpUser = $this->getUser($userid);
/*
* If this is an anonymous user, or if the user has never
* logged in before, the last visit time is always the
* session creation time
*/
if (($userid == SPOTWEB_ANONYMOUS_USERID) || ($tmpUser['lastlogin'] == 0)) {
$tmpUser['lastvisit'] = time();
# Mark everything as read for anonymous users
$this->_db->markFilterCountAsSeen($userid);
} else {
$tmpUser['lastvisit'] = $tmpUser['lastlogin'];
} # if
# Create a new session record
$session = array('sessionid' => $this->generateUniqueId(),
'userid' => $userid,
'hitcount' => 1,
'lasthit' => time(),
'ipaddr' => $this->determineUsersIpAddress()
);
$this->_db->addSession($session);
return array('user' => $tmpUser,
'session' => $session);
} # createNewSession
/*
* Update the users cookie
*/
function updateCookie($userSession) {
SetCookie("spotsession",
$userSession['session']['sessionid'] . '.' . $userSession['user']['userid'],
time()+60*60*24*30,
'', # path: The default value is the current directory that the cookie is being set in.
$this->_settings->get('cookie_host'),
false, # Indicates if the cookie should only be transmitted over a secure HTTPS connection from the client.
true); # Only available to the HTTP protocol. This means that the cookie won't be accessible by scripting languages, such as JavaScript.
} # updateCookie
/*
* Removes a session from the database.
*/
function removeSession($sessionId) {
$this->_db->deleteSession($sessionId);
} # removeSession
/*
* Removes all users' sessions from the database
*/
function removeAllUserSessions($userId) {
$this->_db->deleteAllUserSessions($userId);
} # removeAllUserSessions
/*
* Checks whether the user already has a session in its cookie. If it
* has, we use the existing session, else we create a new one for the
* anonymous user.
*/
function useOrStartSession() {
$userSession = false;
if (isset($_COOKIE['spotsession'])) {
$userSession = $this->validSession($_COOKIE['spotsession']);
} # if
if ($userSession === false) {
/*
* If we don't have a session by now, let's create a new
* anonymous session.
*
* UserID is our default anonymous user, but this can be
* overriden by the usersystem
*/
$userSession = $this->createNewSession( $this->_settings->get('nonauthenticated_userid') );
} # if
# Initialize the security system
$spotSec = new SpotSecurity($this->_db, $this->_settings, $userSession['user'], $userSession['session']['ipaddr']);
$userSession['security'] = $spotSec;
/*
* And always update the cookie even if one already exists,
* this prevents the cookie from expiring all of a sudden
*/
$this->updateCookie($userSession);
return $userSession;
} # useOrStartSession
/*
* Password to hash
*/
function passToHash($password) {
return sha1(strrev(substr($this->_settings->get('pass_salt'), 1, 3)) . $password . $this->_settings->get('pass_salt'));
} # passToHash
/*
* Tries to authenticate the user with the given credentials.
* Returns an user record when authed, or false if the
* authentication fails
*/
function login($user, $password) {
# Sals the password with the unique salt given in the database
$password = $this->passToHash($password);
# authenticate the user
$userId = $this->_db->authUser($user, $password);
if ($userId !== false) {
/*
* If the user is logged in, create a session.
*
* Order of actions is import here, because
* in a new session the lastvisit time is always
* set to the lastlogon time, therefore we first
* want the session to be created and after that
* we can update the last logon time
*/
$userSession = $this->createNewSession($userId);
$this->updateCookie($userSession);
# now update the user record with the last logon time
$userSession['user']['lastlogin'] = time();
$this->_db->setUser($userSession['user']);
# Initialize the security system
$userSession['security'] = new SpotSecurity($this->_db, $this->_settings, $userSession['user'], $userSession['session']['ipaddr']);
return $userSession;
} else {
return false;
} # else
} # login
function verifyApi($apikey) {
# try to authenticate the user
$userId = $this->_db->authUser(false, $apikey);
if ($userId !== false && $userId > SPOTWEB_ADMIN_USERID && $apikey != '') {
/*
* In a normal logon, we need to have a session.
* For API logons, we do not want a session because
* that would bloat the session table.
*
* We therefore manually retrieve the user record
*/
$userRecord['user'] = $this->getUser($userId);
# and use the userrecord to update the lastapiusage time
$userRecord['user']['lastapiusage'] = time();
$this->_db->setUser($userRecord['user']);
# Initialize the security system
$userRecord['security'] = new SpotSecurity($this->_db, $this->_settings, $userRecord['user'], $this->determineUsersIpAddress() );
return $userRecord;
} else {
return false;
} # else
} # verifyApi
/*
* Reset the seenstamp timestamp
*/
function resetReadStamp($user) {
$user['lastvisit'] = time();
$user['lastread'] = $this->_db->getMaxMessageTime();
$this->_db->setUser($user);
# Mark everything as read for this user
$this->_db->markFilterCountAsSeen($user['userid']);
return $user;
} # resetReadStamp
/*
* Checks whether an given session is valid. If the session
* is valid, this function returns an userrecord
*/
function validSession($sessionCookie) {
$sessionParts = explode(".", $sessionCookie);
if (count($sessionParts) != 2) {
return false;
} # if
# Check whether the session is to be found in the database
$sessionValid = $this->_db->getSession($sessionParts[0], $sessionParts[1]);
if ($sessionValid === false) {
return false;
} # if
# The session is valid, let's update the hit counter and retrieve the user
$this->_db->hitSession($sessionParts[0]);
$userRecord = $this->getUser($sessionValid['userid']);
/*
* If the user could not be found, the session wasn't valid after all
*/
if ($userRecord === false) {
return false;
} # if
/*
* Now determine whether we need to update the lastvisit timestamp.
*
* If the *lasthit* is older than 15 minutes, we update the *lastvisit*
* timestamp to the *lasthit* time.
*
* Basically this makes sure the 'lastvisit' time is only reset when
* the user wasn't active on Spotweb for 15 minutes. This ensures us
* the unread count for the user doesn't get unset all of a sudden
* during a browsing session.
*/
if ($sessionValid['lasthit'] < (time() - 900)) {
$userRecord['lastvisit'] = $sessionValid['lasthit'];
/*
* Update the last read time to the last spot we find in the
* database. Theoreticall this still contains an race condtion
* because the spots could be updated by now.
*
* We ignore this for now to not cause any performance issues
*/
if ($userRecord['prefs']['auto_markasread']) {
# Retrieve the last update stamp from the filters
$filterHashes = $this->_db->getCachedFilterCount($userRecord['userid']);
/*
* Set the lastread stamp to the last time the spotcount was updated
* in the filtercounts
*/
if (!empty($filterHashes)) {
$filterKeys = array_keys($filterHashes);
$userRecord['lastread'] = $filterHashes[$filterKeys[0]]['lastupdate'];
} else {
$userRecord['lastread'] = $this->_db->getMaxMessageTime();
} # else
# Mark older spots as read for this user
$this->_db->resetFilterCountForUser($userRecord['userid']);
} # if
$this->_db->setUser($userRecord);
} # if
return array('user' => $userRecord,
'session' => $sessionValid);
} # validSession
/*
* Validates a username
*/
function validUsername($user) {
$invalidNames = array('god', 'mod', 'modje', 'spot', 'spotje', 'spotmod',
'admin', 'drazix', 'moderator', 'superuser', 'supervisor',
'spotnet', 'spotnetmod', 'administrator', 'spotweb',
'root', 'anonymous', 'spotlite');
$validUsername = !in_array(strtolower($user), $invalidNames);
if ($validUsername) {
$validUsername = strlen($user) >= 3;
} # if
return $validUsername;
} # validUsername
/*
* Adds a user to the database
*/
function addUser($user) {
if (!$this->validUsername($user['username'])) {
throw new Exception("Invalid username");
} # if
# Convert the password to an passhash
$user['passhash'] = $this->passToHash($user['newpassword1']);
# Create an API key
$user['apikey'] = md5($this->generateUniqueId());
# and actually add the user to the database
$tmpUser = $this->_db->addUser($user);
$this->_db->setUserRsaKeys($tmpUser['userid'], $user['publickey'], $user['privatekey']);
/*
* Now copy the preferences from the anonymous user to this
* new user
*/
$anonUser = $this->_db->getUser(SPOTWEB_ANONYMOUS_USERID);
$tmpUser = array_merge($anonUser, $tmpUser);
$tmpUser['prefs']['newspotdefault_tag'] = $user['username'];
$this->_db->setUser($tmpUser);
# and add the user to the default set of groups as configured
$this->_db->setUserGroupList($tmpUser['userid'], $this->_settings->get('newuser_grouplist'));
# now copy the users' filters to the new user
$this->_db->copyFilterList(SPOTWEB_ANONYMOUS_USERID, $tmpUser['userid']);
} # addUser()
/*
* Update a user's group membership
*/
function setUserGroupList($user, $groupList) {
$this->_db->setUserGroupList($user['userid'], $groupList);
} # setUserGroupList
/*
* Update a user's password
*/
function setUserPassword($user) {
# Convert the password to an passhash
$user['passhash'] = $this->passToHash($user['newpassword1']);
$this->_db->setUserPassword($user);
} # setUserPassword
/*
* Update a user's API key
*/
function resetUserApi($user) {
$user['apikey'] = md5($this->generateUniqueId());
$this->_db->setUser($user);
return $user;
} # setUserApi
/*
* Cleanup of user preferences
*/
function cleanseUserPreferences($prefs, $tpl) {
/*
* Make sure the user didn't try to submit preferences
* we do not support in Spotweb
*/
foreach(array_diff_key($prefs, $tpl) as $keys => $values) {
unset($prefs[$keys]);
} # foreach
return $prefs;
} # cleanseUserPreferences
/*
* Validate user preferences
*/
function validateUserPreferences($prefs, $currentPrefs) {
$errorList = array();
# Define several arrays with valid settings
$validDateFormats = array('human', '%a, %d-%b-%Y (%H:%M)', '%d-%m-%Y (%H:%M)');
$validTemplates = array('we1rdo');
$validDefaultSorts = array('', 'stamp');
$validLanguages = array_keys($this->_settings->get('system_languages'));
# Check per page setting
$prefs['perpage'] = (int) $prefs['perpage'];
if (($prefs['perpage'] < 2) || ($prefs['perpage'] > 250)) {
$errorList[] = _('Invalid preference value (perpage)');
} # if
# Controleer basis settings
if (in_array($prefs['date_formatting'], $validDateFormats) === false) {
$errorList[] = _('Invalid user preference value (date_formatting)');
} # if
if (in_array($prefs['template'], $validTemplates) === false) {
$errorList[] = _('Invalid user preference value (template)');
} # if
if (in_array($prefs['user_language'], $validLanguages) === false) {
$errorList[] = _('Invalid user preference value (language)');
} # if
if (in_array($prefs['defaultsortfield'], $validDefaultSorts) === false) {
$errorList[] = _('Invalid user preference value (defaultsortfield)');
} # if
# When nzbhandling settings are not entered at all, we default to disable
if (!isset($prefs['nzbhandling'])) {
$prefs['nzbhandling'] = array('action' => 'disable',
'prepare_action' => 'merge');
} # if
# when an sabnzbd host is entered, it has to be a valid URL
if ( ($prefs['nzbhandling']['action'] == 'client-sabnzbd') || ($prefs['nzbhandling']['action'] == 'push-sabnzbd') ) {
$tmpHost = parse_url($prefs['nzbhandling']['sabnzbd']['url']);
if ( ($tmpHost === false) | (!isset($tmpHost['scheme'])) || (($tmpHost['scheme'] != 'http') && ($tmpHost['scheme'] != 'https')) ) {
$errorList[] = _('sabnzbd host is not a valid URL');
} # if
# SABnzbd URL should always end with a s slash
if(substr($prefs['nzbhandling']['sabnzbd']['url'], -1) !== '/') {
$prefs['nzbhandling']['sabnzbd']['url'] .= '/';
} # if
} # if
/*
* Convert other settings to booleans so we always have a valid result.
* We need to do this because not all browsers post checkboxes in a form in
* the same way.
*/
$prefs['count_newspots'] = (isset($prefs['count_newspots'])) ? true : false;
$prefs['keep_seenlist'] = (isset($prefs['keep_seenlist'])) ? true : false;
$prefs['auto_markasread'] = (isset($prefs['auto_markasread'])) ? true : false;
$prefs['keep_downloadlist'] = (isset($prefs['keep_downloadlist'])) ? true : false;
$prefs['keep_watchlist'] = (isset($prefs['keep_watchlist'])) ? true : false;
$prefs['show_filesize'] = (isset($prefs['show_filesize'])) ? true : false;
$prefs['show_reportcount'] = (isset($prefs['show_reportcount'])) ? true : false;
$prefs['show_nzbbutton'] = (isset($prefs['show_nzbbutton'])) ? true : false;
$prefs['show_multinzb'] = (isset($prefs['show_multinzb'])) ? true : false;
$prefs['show_avatars'] = (isset($prefs['show_avatars'])) ? true : false;
$notifProviders = Notifications_Factory::getActiveServices();
foreach ($notifProviders as $notifProvider) {
$prefs['notifications'][$notifProvider]['enabled'] = (isset($prefs['notifications'][$notifProvider]['enabled'])) ? true : false;
$prefs['notifications'][$notifProvider]['events']['watchlist_handled'] = (isset($prefs['notifications'][$notifProvider]['events']['watchlist_handled'])) ? true : false;
$prefs['notifications'][$notifProvider]['events']['nzb_handled'] = (isset($prefs['notifications'][$notifProvider]['events']['nzb_handled'])) ? true : false;
$prefs['notifications'][$notifProvider]['events']['retriever_finished'] = (isset($prefs['notifications'][$notifProvider]['events']['retriever_finished'])) ? true : false;
$prefs['notifications'][$notifProvider]['events']['report_posted'] = (isset($prefs['notifications'][$notifProvider]['events']['report_posted'])) ? true : false;
$prefs['notifications'][$notifProvider]['events']['spot_posted'] = (isset($prefs['notifications'][$notifProvider]['events']['spot_posted'])) ? true : false;
$prefs['notifications'][$notifProvider]['events']['user_added'] = (isset($prefs['notifications'][$notifProvider]['events']['user_added'])) ? true : false;
}
# Twitter tokens are never posted by the form, but they shouldn't be tossed out
$prefs['notifications']['twitter']['screen_name'] = $currentPrefs['notifications']['twitter']['screen_name'];
$prefs['notifications']['twitter']['access_token'] = $currentPrefs['notifications']['twitter']['access_token'];
$prefs['notifications']['twitter']['access_token_secret'] = $currentPrefs['notifications']['twitter']['access_token_secret'];
$prefs['notifications']['twitter']['request_token'] = $currentPrefs['notifications']['twitter']['request_token'];
$prefs['notifications']['twitter']['request_token_secret'] = $currentPrefs['notifications']['twitter']['request_token_secret'];
# We don't want to save megabyts of CSS, so put a limit to the size
if (strlen($prefs['customcss'] > 1024 * 10)) {
$errorList[] = _('Custom CSS is too large');
} # if
# We don't want to save megabytes of default newspot body, so limit it
if (strlen($prefs['newspotdefault_tag'] > 90)) {
$errorList[] = _('Default value for a spots\' tag is too long');
} # if
if (strlen($prefs['newspotdefault_body'] > 9000)) {
$errorList[] = _('Default value for a spots\' body is too long');
} # if
# When a 'runcommand' or 'save' action is chosen, 'local_dir' is a mandatry setting
if (($prefs['nzbhandling']['action'] == 'save') || ($prefs['nzbhandling']['action'] == 'runcommand')) {
if (empty($prefs['nzbhandling']['local_dir'])) {
$errorList[] = _('When NZB handling is either "save" or "runcommand" the directory must be entered');
} # if
} # if
# When a 'runcommand' action is chosen, 'command' is a mandatry setting
if ($prefs['nzbhandling']['action'] == 'runcommand') {
if (empty($prefs['nzbhandling']['command'])) {
$errorList[] = _('When NZB handling is "runcommand" a command must be entered');
} # if
} # if
# For the 'growl' notification provider, a host is mandatory
if ($prefs['notifications']['growl']['enabled']) {
if (empty($prefs['notifications']['growl']['host'])) {
$errorList[] = _('Growl notifications require a growl host to be entered');
} # if
} # if
# 'Notify My Android' requires an API key
if ($prefs['notifications']['nma']['enabled']) {
if (empty($prefs['notifications']['nma']['api'])) {
$errorList[] = _('"Notify My Android" notifications require an API key');
} # if
} # if
# 'Notifo' requires both a username and apikey
if ($prefs['notifications']['notifo']['enabled']) {
if (empty($prefs['notifications']['notifo']['username'])) {
$errorList[] = _('"Notifo" notifications require an username to be entered');
} # if
if (empty($prefs['notifications']['notifo']['api'])) {
$errorList[] = _('"Notifo" notifications require an api key to be entered');
} # if
} # if
# 'Prowl' requires an API key
if ($prefs['notifications']['prowl']['enabled']) {
if (empty($prefs['notifications']['prowl']['apikey'])) {
$errorList[] = _('"Prowl" notifications require an API key to be entered');
} # if
} # if
# To use Twitter, an twitter account should be defined
if ($prefs['notifications']['twitter']['enabled']) {
if (empty($prefs['notifications']['twitter']['access_token']) || empty($prefs['notifications']['twitter']['access_token_secret'])) {
$errorList[] = _('To use twitter you need to enter and validate a twitter account');
} # if
} # if
return array($errorList, $prefs);
} # validateUserPreferences
/*
* Validate the user record. Might be used for both adding and changing
*/
function validateUserRecord($user, $isEdit) {
$errorList = array();
# Make sure the username is valid
if (!$isEdit) {
if (!$this->validUsername($user['username'])) {
$errorList[] = _('Invalid username chosen');
} # if
} # if
# Check a firstname is entered
if (strlen($user['firstname']) < 2) {
$errorList[] = _('Not a valid firstname');
} # if
# Check a lastname is entered
if (strlen($user['lastname']) < 2) {
$errorList[] = _('Not a valid lastname');
} # if
# Make sure a psasword is entered
if (strlen($user['newpassword1'] > 0)) {
if (strlen($user['newpassword1']) < 5){
$errorList[] = _('Entered password is too short');
} # if
} # if
# and make sure the passwords match
if ($user['newpassword1'] != $user['newpassword2']) {
$errorList[] = _('Passwords do not match');
} # if
# disallow a user from editting the ANONYMOUS user
if ($user['userid'] == SPOTWEB_ANONYMOUS_USERID) {
$errorList[] = _('You cannot edit the anonymous account');
} # if
# check the mailaddress
if (!filter_var($user['mail'], FILTER_VALIDATE_EMAIL)) {
$errorList[] = _('Not a valid email address');
} # if
# and make sure the mailaddress is unique among all users
$emailExistResult = $this->_db->userEmailExists($user['mail']);
if (($emailExistResult !== $user['userid']) && ($emailExistResult !== false)) {
$errorList[] = _('Mailaddress is alread in use');
} # if
return $errorList;
} # validateUserRecord
/*
* Set the users' public and private keys
*/
function setUserRsaKeys($user, $privateKey, $publicKey) {
$this->_db->setUserRsaKeys($user['userid'], $privateKey, $publicKey);
} # setUserRsaKeys
/*
* Validate a group record
*/
function validateSecGroup($group) {
$errorList = array();
# Remove any lingering spaces
$group['name'] = trim($group['name']);
# Ensure a gorupname is given and it is not too short
if (strlen($group['name']) < 3) {
$errorList[] = _('Invalid groupname');
} # if
/*
* Now list all security groups to make sure the groupname
* is unique.
*
* This is not the most efficient way to do stuff, but we
* do not expect dozens of security groups so this is acceptable
*/
$secGroupList = $this->_db->getGroupList(null);
foreach($secGroupList as $secGroup) {
if ($secGroup['name'] == $group['name']) {
if ($secGroup['id'] != $group['id']) {
$errorList[] = _('Name is already in use');
} # if
} # if
} # foreach
return array($errorList, $group);
} # validateSecGroup
/*
* Removes a permission from a securitygroup
*/
function removePermFromSecGroup($groupId, $perm) {
$this->_db->removePermFromSecGroup($groupId, $perm);
} # removePermFromSecGroup
/*
* Sets a speific permission in a group to either allow or deny
*/
function setDenyForPermFromSecGroup($groupId, $perm) {
$this->_db->setDenyForPermFromSecGroup($groupId, $perm);
} # setDenyForPermFromSecGroup
/*
* Adds a permission to an security group
*/
function addPermToSecGroup($groupId, $perm) {
$errorList = array();
# Remove any superfluous spaces
$perm['objectid'] = trim($perm['objectid']);
/*
* Make sure this specific permission is unique in the group
*
* We do not check the deny here, because we do not want
* groups with both a deny and an allow setting as the results
* would be undefined
*/
$groupPerms = $this->_db->getGroupPerms($groupId);
foreach($groupPerms as $groupPerm) {
if (($groupPerm['permissionid'] == $perm['permissionid']) &&
($groupPerm['objectid'] == $perm['objectid'])) {
# Duplicate permission
$errorList[] = _('Permission already exists in this group');
} # if
} # foreach
# Add the permission to the group
if (empty($errorList)) {
$this->_db->addPermToSecGroup($groupId, $perm);
} # if
return $errorList;
} # addPermToSecGroup
/*
* Update a group record
*/
function setSecGroup($group) {
$this->_db->setSecurityGroup($group);
} # setSecGroup
/*
* Add an security group
*/
function addSecGroup($group) {
$this->_db->addSecurityGroup($group);
} # addSecGroup
/*
* Retrieve a group record
*/
function getSecGroup($groupId) {
$tmpGroup = $this->_db->getSecurityGroup($groupId);
if (!empty($tmpGroup)) {
return $tmpGroup[0];
} else {
return false;
} # else
} # getSecGroup
/*
* Removes a group record
*/
function removeSecGroup($group) {
$this->_db->removeSecurityGroup($group);
} # removeSecGroup
/*
* Retrieves an user record
*/
function getUser($userid) {
$tmpUser = $this->_db->getUser($userid);
return $tmpUser;
} # getUser()
/*
* Retrieves an unformatted filterlist
*/
function getPlainFilterList($userId, $filterType) {
return $this->_db->getPlainFilterList($userId, $filterType);
} # get PlainFilterList
/*
* Retrieves a list of filters (in an hierarchical list)
*/
function getFilterList($userId, $filterType) {
return $this->_db->getFilterList($userId, $filterType);
} # getFilterList
/*
* Retrieves one specific filter
*/
function getFilter($userId, $filterId) {
return $this->_db->getFilter($userId, $filterId);
} # getFilter
/*
* Changes the filter values.
*
* For now only the following values might be changed:
*
* * Title
* * Order
* * Parent
*/
function changeFilter($userId, $filterForm) {
return $this->_db->updateFilter($userId, $filterForm);
} # changeFilter
/*
* Validates a filter
*/
function validateFilter($filter) {
$errorList = array();
# Remove any spaces
$filter['title'] = trim(utf8_decode($filter['title']), " \t\n\r\0\x0B");
$filter['title'] = trim(utf8_decode($filter['title']), " \t\n\r\0\x0B");
# Make sure a filter name is valid
if (strlen($filter['title']) < 3) {
$errorList[] = _('Invalid filter name');
} # if
return array($filter, $errorList);
} # validateFilter
/*
* Adds a filter to a user
*/
function addFilter($userId, $filter) {
$errorList = array();
list($filter, $errorList) = $this->validateFilter($filter);
# No errors found? add it to the datbase
if (empty($errorList)) {
$this->_db->addFilter($userId, $filter);
} # if
return $errorList;
} # addFilter
/*
* Retrieves the users' index filter
*/
function getIndexFilter($userId) {
/*
* The users' index filter is usually retrieved two or
* thee times for the index page, make sure we don't approach
* the database that many times
*/
$userIndexFilter = $this->_db->getUserIndexFilter($userId);
if ($userIndexFilter === false) {
return array('tree' => '');
} else {
return $userIndexFilter;
} # else
} # getIndexFilter
/*
* Add user's index filter
*/
function setIndexFilter($userId, $filter) {
# There can only be one
$this->removeIndexFilter($userId);
# and actually add the index filter
$filter['filtertype'] = 'index_filter';
$this->_db->addFilter($userId, $filter);
} # addIndexFilter
/*
* Remove an index filter
*/
function removeIndexFilter($userId) {
$tmpFilter = $this->_db->getUserIndexFilter($userId);
if (!empty($tmpFilter)) {
$this->_db->deleteFilter($userId, $tmpFilter['id'], 'index_filter');
} # if
} # removeIndexFilter
/*
* Removes a userfilter
*/
function removeFilter($userId, $filterId) {
$this->_db->deleteFilter($userId, $filterId, 'filter');
} # removeFilter
/*
* Removes all existing filters for a user, and reset its
* filerlist to the one for the 'ANONYMOUS' account
*/
function resetFilterList($userId) {
# Remove all filters
$this->_db->removeAllFilters($userId);
# and copy them back from the userlist
$this->_db->copyFilterList(SPOTWEB_ANONYMOUS_USERID, $userId);
} # resetFilterList
/*
* Set the filterlist as specified
*/
function setFilterList($userId, $filterList) {
# remove all existing filters
$this->_db->removeAllFilters($userId);
# and add the filters from the list
foreach($filterList as $filter) {
$this->_db->addFilter($userId, $filter);
} # foreach
} # setFilterList
/*
* Copy the filters from a specific user to be the
* default filters
*/
function setFiltersAsDefault($userId) {
# Remove all filters for the Anonymous user
$this->_db->removeAllFilters(SPOTWEB_ANONYMOUS_USERID);
# and copy them from the specified user to anonymous
$this->_db->copyFilterList($userId, SPOTWEB_ANONYMOUS_USERID);
} # setFiltersAsDefault
/*
* Update a user record (does not change the password)
*/
function setUser($user) {
/*
* We always assume the password is not set using
* this function, hence the password is never updated
* by setUser()
*/
$this->_db->setUser($user);
} # setUser()
/*
* Removes an user record
*/
function removeUser($userid) {
$this->_db->deleteUser($userid);
} # removeUser()
/*
* Retrieves an RSA key from the users' record.
*/
function getUserPrivateRsaKey($userId) {
return $this->_db->getUserPrivateRsaKey($userId);
} # getUserPrivateRsaKey
/*
* Converts a list of filters to an XML record which should
* be interchangeable
*/
public function filtersToXml($filterList) {
$spotsOverview = new SpotsOverview($this->_db, $this->_settings);
# create the XML document
$doc = new DOMDocument('1.0', 'utf-8');
$doc->formatOutput = true;
$mainElm = $doc->createElement('spotwebfilter');
$mainElm->appendChild($doc->createElement('version', '1.0'));
$mainElm->appendChild($doc->createElement('generator', 'SpotWeb v' . SPOTWEB_VERSION));
$doc->appendChild($mainElm);
$filterListElm = $doc->createElement('filters');
foreach($filterList as $filter) {
$filterElm = $doc->createElement('filter');
$filterElm->appendChild($doc->createElement('id', $filter['id']));
$filterElm->appendChild($doc->createElement('title', $filter['title']));
$filterElm->appendChild($doc->createElement('icon', $filter['icon']));
$filterElm->appendChild($doc->createElement('parent', $filter['tparent']));
$filterElm->appendChild($doc->createElement('order', $filter['torder']));
/*
* Now add the tree. We get the list of filters as a tree, but we
* want to keep the XML as clean as possible so we try to compress it.
*
* First we have to extract the tree to a list of selections, strongnots
* and excludes
*/
$dynaList = explode(',', $filter['tree']);
list($categoryList, $strongNotList) = $spotsOverview->prepareCategorySelection($dynaList);
$treeList = explode(',', $spotsOverview->compressCategorySelection($categoryList, $strongNotList));
$tree = $doc->createElement('tree');
foreach($treeList as $treeItem) {
if (!empty($treeItem)) {
# determine what type of element this is
$treeType = 'include';
if ($treeItem[0] == '~') {
$treeType = 'strongnot';
$treeItem = substr($treeItem, 1);
} elseif ($treeItem[1] == '!') {
$treeType = 'exclude';
$treeItem = substr($treeItem, 1);
} # else
# and create the XML item
$treeElm = $doc->createElement('item', $treeItem);
$treeElm->setAttribute('type', $treeType);
if (!empty($treeItem)) {
$tree->appendChild($treeElm);
} # if
} # if
} # treeItems
$filterElm->appendChild($tree);
/*
* Prepareer the filtervalue list to make it usable for the XML
*/
$tmpFilterValues = explode('&', $filter['valuelist']);
$filterValueList = array();
foreach($tmpFilterValues as $filterValue) {
$tmpFilter = explode(':', urldecode($filterValue));
# and create the actual filter
if (count($tmpFilter) >= 3) {
$filterValueList[] = Array('fieldname' => $tmpFilter[0],
'operator' => $tmpFilter[1],
'value' => join(":", array_slice($tmpFilter, 2)));
} # if
} # foreach
/*
* Now add the filter items (text searches etc)
*/
if (!empty($filterValueList)) {
$valuesElm = $doc->createElement('values');
foreach($filterValueList as $filterValue) {
# Create the value XML item
$itemElm = $doc->createElement('item');
$itemElm->appendChild($doc->createElement('fieldname', $filterValue['fieldname']));
$itemElm->appendChild($doc->createElement('operator', $filterValue['operator']));
$itemElm->appendChild($doc->createElement('value', $filterValue['value']));
$valuesElm->appendChild($itemElm);
} # foreach
$filterElm->appendChild($valuesElm);
} # if
/*
* Add the sorting items
*/
if (!empty($filter['sorton'])) {
$sortElm = $doc->createElement('sort');
$itemElm = $doc->createElement('item');
$itemElm->appendChild($doc->createElement('fieldname', $filter['sorton']));
$itemElm->appendChild($doc->createElement('direction', $filter['sortorder']));
$sortElm->appendChild($itemElm);
$filterElm->appendChild($sortElm);
} # if
$filterListElm->appendChild($filterElm);
} # foreach
$mainElm->appendChild($filterListElm);
return $doc->saveXML();
} # filtersToXml
/*
* Translates an XML string back to a list of filters
*/
public function xmlToFilters($xmlStr) {
$filterList = array();
$idMapping = array();
$spotsOverview = new SpotsOverview($this->_db, $this->_settings);
/*
* Parse the XML file
*/
$xml = @(new SimpleXMLElement($xmlStr));
# We can only parse version 1.0 of the filters
if ( (string) $xml->version != '1.0') {
return $filterList;
} # if
# and try to process all of the filters
foreach($xml->xpath('/spotwebfilter/filters/filter') as $filterItem) {
$filter['id'] = (string) $filterItem->id;
$filter['title'] = (string) $filterItem->title;
$filter['icon'] = (string) $filterItem->icon;
$filter['tparent'] = (string) $filterItem->parent;
$filter['torder'] = (string) $filterItem->order;
$filter['filtertype'] = 'filter';
$filter['sorton'] = '';
$filter['sortorder'] = '';
$filter['tree'] = '';
$filter['children'] = array();
/*
* start with the tree items
*/
$treeStr = "";
foreach($filterItem->xpath('tree/item') as $treeItem) {
$treeType = (string) $treeItem->attributes()->type;
if ($treeType == 'exclude') {
$treeStr .= ',!' . $treeItem[0];
} elseif ($treeType == 'strongnot') {
$treeStr .= ',~' . $treeItem[0];
} elseif ($treeType == 'include') {
$treeStr .= ',' . $treeItem[0];
} # if
} # foreach
if (strlen($treeStr) > 1) {
$treeStr = substr($treeStr, 1);
} # if
$filter['tree'] = $treeStr;
/*
* now parse the values (textsearches etc)
*/
$filterValues = array();
foreach($filterItem->xpath('values/item') as $valueItem) {
$value = array();
$filterValues[] = urlencode(
(string) $valueItem->fieldname .
':' .
(string) $valueItem->operator .
':' .
(string) $valueItem->value
);
} # foreach
$filter['valuelist'] = $filterValues;
/*
* Sorting elements are optional
*/
if ($filterItem->sort) {
$filter['sorton'] = (string) $filterItem->sort->item->fieldname;
$filter['sortorder'] = (string) $filterItem->sort->item->direction;
} # if
$filterList[$filter['id']] = $filter;
} # foreach
/*
* Now create a tree out of it. We cannot do this the same way
* as in SpotDb because we cannot create references to the XPATH
* function
*/
foreach($filterList as $idx => &$filter) {
if ($filter['tparent'] != 0) {
$filterList[$filter['tparent']]['children'][] =& $filter;
unset($filterList[$filter['id']]);
} # if
} # for
return $filterList;
} # xmlToFilters
/*
* Changes the avatar of this user
*/
function changeAvatar($userId, $imageFile) {
$errorList = array();
/*
* Don't allow images larger than 4000 bytes
*/
if (strlen($imageFile) > 4000) {
$errorList[] = _('An avatar image has a maximum of 4000 bytes');
} # if
/*
* Make sure the image can be read, and stuff
*/
$spotImage = new SpotImage($this->_db);
if ($spotImage->getImageInfoFromString($imageFile) === false) {
$errorList[] = _('Invalid avatar image was supplied');
} # if
if (empty($errorList)) {
/*
* We store the images base64 encoded
*/
$imageFile = base64_encode($imageFile);
/*
* and update the database
*/
$this->_db->setUserAvatar($userId, $imageFile);
} # if
return $errorList;
} # changeAvatar
/*
* Blacklist a specific spotter
*/
function addSpotterToBlacklist($ourUserId, $spotterId, $origin) {
$this->_db->addSpotterToBlacklist($spotterId, $ourUserId, $origin);
} # addSpotterToBlacklist
/*
* Removes a specific spotter from the blacklis
*/
function removeSpotterFromBlacklist($ourUserId, $spotterId) {
$this->_db->removeSpotterFromBlacklist($spotterId, $ourUserId);
} # removeSpotterFromBlacklist
/*
* Returns if an spotter is blacklisted
*/
function isSpotterBlacklisted($ourUserId, $spotterId) {
return $this->_db->isSpotterBlacklisted($spotterId, $ourUserId);
} # isSpotterBlacklisted
/*
* Returns the users' remote IP address
*/
function determineUsersIpAddress() {
/*
* We now compare the X-Fowarded-For header and it's not clear if this
* is the right thing to do.
*/
foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) {
if (array_key_exists($key, $_SERVER) === true) {
foreach (explode(',', $_SERVER[$key]) as $ip) {
if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
$remote_addr = $ip;
} # if
} # foreach
} # if
} # foreach
if (isset($remote_addr)) {
return $remote_addr;
} else {
return "N/A";
} # if
} # determineUsersIpAddress
} # class SpotUserSystem
debian/db-update/v20111210/lib/SpotSettings.php 0000644 0000000 0000000 00000021254 12267045571 016052 0 ustar getAllSettings();
# en merge de settings met degene die we door krijgen
self::$_settings = array_merge(self::$_dbSettings, self::$_phpSettings);
# Override NNTP header/comments settings, als er geen aparte NNTP header/comments server is opgegeven, gebruik die van
# de NZB server
if ((empty(self::$_settings['nntp_hdr']['host'])) && (!empty(self::$_settings['nntp_nzb']))) {
self::$_settings['nntp_hdr'] = self::$_settings['nntp_nzb'];
} # if
# Hetzelfde voor de NNTP upload server
if ((empty(self::$_settings['nntp_post']['host'])) && (!empty(self::$_settings['nntp_nzb']))) {
self::$_settings['nntp_post'] = self::$_settings['nntp_nzb'];
} # if
} # if
return self::$_instance;
} # singleton
/*
* Geeft de waarde van de setting terug
*/
function get($name) {
return self::$_settings[$name];
} # get
/*
* Unset een bepaalde waarde
*/
function remove($name) {
unset(self::$_settings[$name]);
$this->_db->removeSetting($name);
} # remove
/*
* Geeft terug of een bepaalde setting uit de database
* komt of uit de settings.php file. De settings-file
* heeft altijd prioriteit
*/
function getOrigin($name) {
if (isset(self::$_phpSettings[$name])) {
return "php";
} else {
return "db";
} # if
} # getOrigin
/*
* Set de waarde van de setting, maakt hem ook
* meteen persistent dus mee oppassen
*/
function set($name, $value) {
# Als de setting uit PHP komt, dan mag die niet geupdate worden
# hier omdat we dan niet meer weten wat er gebeurt.
if (isset(self::$_phpSettings[$name])) {
throw new InvalidSettingsUpdateException("InvalidSettingUpdat Exception for '" . $name . '"');
} # if
# Update onze eigen settings array zodat we meteen up-to-date zijn
self::$_settings[$name] = $value;
$this->_db->updateSetting($name, $value);
} # set
/*
* Validate settings
*/
function validateSettings($settings) {
$errorList = array();
# Define arrays with valid settings
$validNntpEnc = array(false, 'ssl', 'tls');
$validModerationAction = array('disable', 'act', 'markspot');
# Get the given value for NNTP encryption
$settings['nntp_nzb']['enc'] = (isset($settings['nntp_nzb']['enc']['switch'])) ? $settings['nntp_nzb']['enc']['select'] : false;
$settings['nntp_hdr']['enc'] = (isset($settings['nntp_hdr']['enc']['switch'])) ? $settings['nntp_hdr']['enc']['select'] : false;
$settings['nntp_post']['enc'] = (isset($settings['nntp_post']['enc']['switch'])) ? $settings['nntp_post']['enc']['select'] : false;
# Trim human-entered text fields
$settings['nntp_nzb']['host'] = trim($settings['nntp_nzb']['host']);
$settings['nntp_hdr']['host'] = trim($settings['nntp_hdr']['host']);
$settings['nntp_post']['host'] = trim($settings['nntp_post']['host']);
# Verify settings with the previous declared arrays
if (in_array($settings['nntp_nzb']['enc'], $validNntpEnc) === false || in_array($settings['nntp_hdr']['enc'], $validNntpEnc) === false || in_array($settings['nntp_post']['enc'], $validNntpEnc) === false) {
$errorList[] = _('Invalid encryption setting');
} # if
if (in_array($settings['spot_moderation'], $validModerationAction) === false) {
$errorList[] = _('Invalid spot moderation setting');
} # if
# Verify settings
$settings['cookie_expires'] = (int) $settings['cookie_expires'];
if ($settings['cookie_expires'] < 0) {
$errorList[] = _('Invalid cookie_expires setting');
} # if
$settings['retention'] = (int) $settings['retention'];
if ($settings['retention'] < 0) {
$errorList[] = _('Invalid retention setting');
} # if
if (($settings['retrieve_newer_than'] = strtotime($settings['retrieve_newer_than'])) === false || $settings['retrieve_newer_than'] > time()) {
$errorList[] = _('Invalid retrieve_newer_than setting');
} elseif ($settings['retrieve_newer_than'] < 1230789600) {
$settings['retrieve_newer_than'] = 1230789600;
} # elseif
$settings['retrieve_increment'] = (int) $settings['retrieve_increment'];
if ($settings['retrieve_increment'] < 1) {
$errorList[] = _('Invalid retrieve_increment setting');
} # if
# check the mailaddress
if (!filter_var($settings['systemfrommail'], FILTER_VALIDATE_EMAIL)) {
$errorList[] = _('Not a valid email address');
} # if
# converteer overige settings naar boolean zodat we gewoon al weten wat er uitkomt
$settings['deny_robots'] = (isset($settings['deny_robots'])) ? true : false;
$settings['sendwelcomemail'] = (isset($settings['sendwelcomemail'])) ? true : false;
$settings['nntp_nzb']['buggy'] = (isset($settings['nntp_nzb']['buggy'])) ? true : false;
$settings['nntp_hdr']['buggy'] = (isset($settings['nntp_hdr']['buggy'])) ? true : false;
$settings['nntp_post']['buggy'] = (isset($settings['nntp_post']['buggy'])) ? true : false;
$settings['retrieve_full'] = (isset($settings['retrieve_full'])) ? true : false;
$settings['prefetch_image'] = (isset($settings['prefetch_image'])) ? true : false;
$settings['prefetch_nzb'] = (isset($settings['prefetch_nzb'])) ? true : false;
$settings['retrieve_comments'] = (isset($settings['retrieve_comments'])) ? true : false;
$settings['retrieve_full_comments'] = (isset($settings['retrieve_full_comments'])) ? true : false;
$settings['retrieve_reports'] = (isset($settings['retrieve_reports'])) ? true : false;
$settings['enable_timing'] = (isset($settings['enable_timing'])) ? true : false;
$settings['enable_stacktrace'] = (isset($settings['enable_stacktrace'])) ? true : false;
$settings['prepare_statistics'] = (isset($settings['prepare_statistics'])) ? true : false;
$settings['external_blacklist'] = (isset($settings['external_blacklist'])) ? true : false;
# Default server settings if they won't be used
if (!isset($settings['nntp_hdr']['use'])) {
$settings['nntp_hdr'] = array('host' => '',
'user' => '',
'pass' => '',
'enc' => false,
'port' => 119,
'buggy' => false);
} # if
if (!isset($settings['nntp_post']['use'])) {
$settings['nntp_post'] = array('host' => '',
'user' => '',
'pass' => '',
'enc' => false,
'port' => 119,
'buggy' => false);
} # if
unset($settings['nntp_hdr']['use'], $settings['nntp_post']['use']);
return array($errorList, $settings);
} # validateSettings
function setSettings($settings) {
# If we disable the external blacklist, clear all entries
if ($settings['external_blacklist'] == false && $this->get('external_blacklist') == true) {
$this->_db->removeOldBlackList($this->get('blacklist_url'));
} # if
# clear some stuff we don't need to store
unset($settings['xsrfid'], $settings['http_referer'], $settings['buttonpressed']);
# Store settings
foreach ($settings as $key => $value) {
# and write these updated settings to the database
$this->set($key, $value);
} # foreach
} # setSettings
/*
* Is onze database versie nog wel geldig?
*/
function schemaValid() {
# SPOTDB_SCHEMA_VERSION is gedefinieerd bovenin SpotDb
return ($this->get('schemaversion') == SPOTDB_SCHEMA_VERSION);
} # schemaValid
/*
* Zijn onze settings versie nog wel geldig?
*/
function settingsValid() {
# SPOTWEB_SETTINGS_VERSION is gedefinieerd bovenin dit bestand
return ($this->get('settingsversion') == SPOTWEB_SETTINGS_VERSION);
} # settingsValid
/*
* Bestaat de opgegeven setting ?
*/
function exists($name) {
return isset(self::$_settings[$name]);
} # isSet
/*
* Private constructor, moet altijd via singleton gaan
*/
private function __construct($db) {
$this->_db = $db;
} # ctor
} # class SpotSettings
debian/db-update/v20111210/lib/exceptions/ 0000755 0000000 0000000 00000000000 12267045571 015050 5 ustar debian/db-update/v20111210/lib/exceptions/InvalidSettingsUpdateException.php 0000644 0000000 0000000 00000000112 12267045571 023704 0 ustar message}' in {$this->file}({$this->line})\n"
. "{$this->getTraceAsString()}";
}
}
?> debian/db-update/v20111210/lib/exceptions/InvalidOwnSettingsSettingException.php 0000644 0000000 0000000 00000000116 12267045571 024567 0 ustar rawExec($this->prepareSql($s, $p));
} # exec()
/*
* INSERT or UPDATE statement, doesn't return anything. Exception
* thrown if a error occurs
*/
abstract function modify($s, $p = array());
/*
* Constructs a query part to match textfields. Abstracted so we can use
* a database specific FTS engine if one is provided by the DBMS
*/
function createTextQuery($searchFields) {
# Initialize some basic variables so our return statements are simple
$filterValueSql = array();
foreach($searchFields as $searchItem) {
$searchValue = trim($searchItem['value']);
$field = $searchItem['fieldname'];
$filterValueSql[] = " (" . $searchItem['fieldname'] . " LIKE '%" . $this->safe($searchValue) . "%') ";
} # foreach
return array('filterValueSql' => $filterValueSql,
'additionalTables' => array(),
'additionalFields' => array(),
'sortFields' => array());
} # createTextQuery
} # dbeng_abs
debian/db-update/v20111210/lib/dbeng/dbeng_pdo_mysql.php 0000644 0000000 0000000 00000002452 12267045571 017630 0 ustar _db_host = $host;
$this->_db_user = $user;
$this->_db_pass = $pass;
$this->_db_db = $db;
}
function connect() {
if (!$this->_conn instanceof PDO) {
if ($this->_db_host[0] === '/') {
$this->_db_conn = "unix_socket=" . $this->_db_host;
} else {
$this->_db_conn = "host=" . $this->_db_host . ";port=3306";
}
try {
$this->_conn = new PDO('mysql:' . $this->_db_conn . ';dbname=' . $this->_db_db, $this->_db_user, $this->_db_pass, array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
} catch (PDOException $e) {
throw new DatabaseConnectionException($e->getMessage(), -1);
}
$this->_conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} # if
} # connect()
/*
* Returns a database specific representation of a boolean value
*/
function bool2dt($b) {
if ($b) {
return '1';
} # if
return '0';
} # bool2dt
function safe($s) {
$search=array("\\","\0","\n","\r","\x1a","'",'"');
$replace=array("\\\\","\\0","\\n","\\r","\Z","\'",'\"');
return str_replace($search, $replace, $s);
} # safe
} # class
debian/db-update/v20111210/lib/dbeng/dbeng_pdo.php 0000644 0000000 0000000 00000011064 12267045571 016402 0 ustar _conn->prepare($s);
} # if
$pattern = '/(\'?\%[dsb]\'?)/';
$matches = array();
preg_match_all($pattern, $s, $matches);
$s = preg_replace($pattern, '?', $s);
$stmt = $this->_conn->prepare($s);
$idx=1;
$totalCount = count($p);
foreach ($matches[1] as $m) {
if ($idx > ($totalCount+1)) {
break;
} # if
if (is_null($p[$idx-1])) {
$stmt->bindValue($idx, null, PDO::PARAM_NULL);
} else {
switch ($m) {
case '%d': {
# We convet explicitly to strval because PDO changes a zero to an ''
$stmt->bindParam($idx, strval($p[$idx-1]), PDO::PARAM_INT);
break;
}
case "'%b'": {
$stmt->bindParam($idx, $p[$idx-1], PDO::PARAM_LOB);
break;
}
default: {
$stmt->bindParam($idx, $p[$idx-1], PDO::PARAM_STR);
}
}
}
$idx++;
}
if (!$stmt instanceof PDOStatement) {
throw new Exception(print_r($stmt, true));
}
return $stmt;
}
public function rawExec($s) {
SpotTiming::start(__FUNCTION__);
try {
$stmt = $this->_conn->query($s);
} catch(PDOException $x) {
throw new SqlErrorException( $x->errorInfo[0] . ': ' . $x->errorInfo[2], -1);
} # catch
SpotTiming::stop(__FUNCTION__,array($s));
return $stmt;
}
/*
* Returns a database specific representation of a boolean value
*/
function bool2dt($b) {
if ($b) {
return '1';
} # if
return '0';
} # bool2dt
/**
* Execute the query and saves the rowcount in a property for later retrieval
*
* @param string $s
* @param array $p
* @return PDOStatement
*/
public function exec($s, $p = array()) {
SpotTiming::start(__FUNCTION__);
try {
$stmt = $this->prepareSql($s, $p);
$stmt->execute();
} catch(PDOException $x) {
throw new SqlErrorException( $x->errorInfo[0] . ': ' . $x->errorInfo[2], -1);
} # catch
$this->_rows_changed = $stmt->rowCount();
SpotTiming::stop(__FUNCTION__, array($s, $p));
return $stmt;
}
/*
* INSERT or UPDATE statement, doesn't return anything. Exception
* thrown if a error occurs
*/
function modify($s, $p = array()) {
SpotTiming::start(__FUNCTION__);
$res = $this->exec($s, $p);
$res->closeCursor();
unset($res);
SpotTiming::stop(__FUNCTION__, array($s,$p));
} # modify
/*
* Begins an transaction
*/
function beginTransaction() {
$this->_conn->beginTransaction();
} # beginTransaction
/*
* Commits an transaction
*/
function commit() {
$this->_conn->commit();
} # commit
/*
* Rolls back an transaction
*/
function rollback() {
$this->_conn->rollback();
} # rollback
function rows() {
return $this->_rows_changed;
} # rows()
function lastInsertId($tableName) {
return $this->_conn->lastInsertId($tableName . "_id_seq");
} # lastInsertId
/**
* Executes the query with $params as parameters. All parameters are
* parsed through sthe safe() function to prevent SQL injection.
*
* Returns a single associative array when query succeeds, returns
* an exception when the query fails.
*
* @param array $s
* @param array $p
* @return array
*/
function singleQuery($s, $p = array()) {
SpotTiming::start(__FUNCTION__);
$stmt = $this->exec($s, $p);
$row = $stmt->fetch();
$stmt->closeCursor();
unset($stmt);
SpotTiming::stop(__FUNCTION__, array($s,$p));
return $row[0];
} # singleQuery
/**
* Executes the query with $params as parameters. All parameters are
* parsed through sthe safe() function to prevent SQL injection.
*
*
* Returns an array of associative arrays when query succeeds, returns
* an exception when the query fails.
*
* @param string $s
* @param array $p
* @return array
*/
function arrayQuery($s, $p = array()) {
SpotTiming::start(__FUNCTION__);
$stmt = $this->exec($s, $p);
$tmpArray = $stmt->fetchAll();
$stmt->closeCursor();
unset($stmt);
SpotTiming::stop(__FUNCTION__, array($s,$p));
return $tmpArray;
} # arrayQuery
} # class
debian/db-update/v20111210/lib/SpotSettingsUpgrader.php 0000644 0000000 0000000 00000011761 12267045571 017546 0 ustar _db = $db;
$this->_settings = $settings;
} # ctor
function update() {
# Zorg dat de diverse versienummers altijd in de db staan zodat
# we er mee kunnen vergelijken
$this->setIfNot("settingsversion", "0.00");
$this->setIfNot("securityversion", "0.00");
if ($this->_settings->get('settingsversion') < 0.15) {
$this->remove('system_languages');
} # if
$this->createServerKeys($this->_settings->get('openssl_cnf_path'));
$this->createPasswordSalt();
$this->setupNewsgroups();
$this->createRsaKeys();
$this->createXsrfSecret();
$this->remove('sabnzbdurltpl');
$this->remove('sabnzbdurl');
$this->remove('recompress_nzb');
$this->remove('available_languages');
$this->remove('featureversion');
$this->remove('max_newcount');
$this->setIfNot('cookie_expires', 30);
$this->setIfNot('sendwelcomemail', true);
$this->setIfNot('twitter_consumer_key', 'LRJCpeHASigYtWEmxoNPA');
$this->setIfNot('twitter_consumer_secret', 'QvwZglJNpzAnoVDt40uUyu5dRDlVFVs4ddxfEkYp7A'); // This secret can be shared
$this->setIfNot('boxcar_api_key', 'pOQM9O2AnEWL0RjSoHln');
$this->setIfNot('boxcar_api_secret', '7CwTFfX7KeAKfjM1DJjg5s9qcHm4cwmLkxQgW9fe'); // This secret can be shared
$this->setIfNot('auditlevel', 0); // No auditing
$this->setIfNot('system_languages', array('nl_NL' => 'Nederlands', 'en_US' => 'English'));
$this->setIfNot('retention', 0);
$this->setIfNot('deny_robots', true);
$this->setIfNot('nntp_nzb', array('host' => '', 'user' => '', 'pass' => '', 'enc' => false, 'port' => 119, 'buggy' => false));
$this->setIfNot('nntp_hdr', array('host' => '', 'user' => '', 'pass' => '', 'enc' => false, 'port' => 119, 'buggy' => false));
$this->setIfNot('nntp_post', array('host' => '', 'user' => '', 'pass' => '', 'enc' => false, 'port' => 119, 'buggy' => false));
$this->setIfNot('retrieve_newer_than', 0);
$this->setIfNot('retrieve_full', true);
$this->setIfNot('prefetch_image', false);
$this->setIfNot('prefetch_nzb', false);
$this->setIfNot('retrieve_comments', true);
$this->setIfNot('retrieve_full_comments', false);
$this->setIfNot('retrieve_reports', true);
$this->setIfNot('retrieve_increment', 1000);
$this->setIfNot('spot_moderation', 'act');
$this->setIfNot('prepare_statistics', true);
$this->setIfNot('external_blacklist', true);
$this->setIfNot('blacklist_url', 'http://jij.haatmij.nl/spotnet/blacklist.txt');
$this->setIfNot('enable_timing', false);
$this->setIfNot('enable_stacktrace', true);
$this->setIfNot('systemfrommail', 'spotweb@example.com');
$this->updateSettingsVersion();
} # update()
/*
* Set een setting alleen als hij nog niet bestaat
*/
function setIfNot($name, $value) {
if ($this->_settings->exists($name)) {
return ;
} # if
$this->_settings->set($name,$value);
} # setIfNot
/*
* Verwijder een setting
*/
function remove($name) {
$this->_settings->remove($name);
} # remove
/*
* Update de huidige versie van de settings
*/
function updateSettingsVersion() {
$this->_settings->set('settingsversion', SPOTWEB_SETTINGS_VERSION);
} # updateSettingsVersion
/*
* Creeer de server private en public keys
*/
function createServerKeys($openSslCnfPath) {
$spotSigning = new SpotSigning();
$x = $spotSigning->createPrivateKey($openSslCnfPath);
$this->setIfNot('publickey', $x['public']);
$this->setIfNot('privatekey', $x['private']);
} # createServerKeys
/*
* Creeer de RSA keys
*/
function createRsaKeys() {
#
# RSA keys
# Worden gebruikt om te valideren of spots geldig zijn, hoef je normaal niet aan te komen
#
$rsaKeys = array();
$rsaKeys[2] = array('modulo' => 'ys8WSlqonQMWT8ubG0tAA2Q07P36E+CJmb875wSR1XH7IFhEi0CCwlUzNqBFhC+P',
'exponent' => 'AQAB');
$rsaKeys[3] = array('modulo' => 'uiyChPV23eguLAJNttC/o0nAsxXgdjtvUvidV2JL+hjNzc4Tc/PPo2JdYvsqUsat',
'exponent' => 'AQAB');
$rsaKeys[4] = array('modulo' => '1k6RNDVD6yBYWR6kHmwzmSud7JkNV4SMigBrs+jFgOK5Ldzwl17mKXJhl+su/GR9',
'exponent' => 'AQAB');
$this->setIfNot('rsa_keys', $rsaKeys);
} # createRsaKeys
/*
* Create an xsrf secret
*/
function createXsrfSecret() {
$userSystem = new SpotUserSystem($this->_db, $this->_settings);
$secret = substr($userSystem->generateUniqueId(), 0, 8);
$this->setIfNot('xsrfsecret', $secret);
} # createXsrfSecret
/*
* Creer de servers' password salt
*/
function createPasswordSalt() {
$userSystem = new SpotUserSystem($this->_db, $this->_settings);
$salt = $userSystem->generateUniqueId() . $userSystem->generateUniqueId();
$this->setIfNot('pass_salt', $salt);
} # createPasswordSalt
/*
* Definieer de standaard SpotNet groepen
*/
function setupNewsgroups() {
$this->setIfNot('hdr_group', 'free.pt');
$this->setIfNot('nzb_group', 'alt.binaries.ftd');
$this->setIfNot('comment_group', 'free.usenet');
$this->setIfNot('report_group', 'free.willey');
} # setupNewsgroups()
} # SpotSettingsUpgrader
debian/db-update/v20111210/lib/SpotSigning.php 0000644 0000000 0000000 00000020555 12267045571 015653 0 ustar _nativeVerify = new SpotSeclibToOpenSsl();
} # if
} # ctor
private function checkRsaSignature($toCheck, $signature, $rsaKey, $useCache) {
# de signature is base64 encoded, eerst decoden
$signature = base64_decode($signature);
# Controleer of we de native OpenSSL libraries moeten
# gebruiken om RSA signatures te controleren
if (CRYPT_RSA_MODE != CRYPT_RSA_MODE_OPENSSL) {
# Initialize the public key to verify with
$pubKey['n'] = new Math_BigInteger(base64_decode($rsaKey['modulo']), 256);
$pubKey['e'] = new Math_BigInteger(base64_decode($rsaKey['exponent']), 256);
# and verify the signature
$rsa = new Crypt_RSA();
$rsa->loadKey($pubKey, CRYPT_RSA_PUBLIC_FORMAT_RAW);
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
# Supress notice if the signature was invalid
$saveErrorReporting = error_reporting(E_ERROR);
$tmpSave = $rsa->verify($toCheck, $signature);
error_reporting($saveErrorReporting);
} else {
$tmpSave = $this->_nativeVerify->verify($rsaKey, $toCheck, $signature, $useCache);
} # else
return $tmpSave;
} # checkRsaSignature
/*
* Creeert een private en public key paar
*/
public function createPrivateKey($sslCnfPath) {
$rsa = new Crypt_RSA();
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
# We hebben deze code geconfigureerd uit Crypt/RSA.php omdat
# we anders de configuratie parameter niet mee kunnen geven aan
# openssl_pkey_new()
if (CRYPT_RSA_MODE != CRYPT_RSA_MODE_OPENSSL) {
# We krijgen de keys base encoded terug
$keyPair = $rsa->createKey();
return array('public' => $keyPair['publickey'],
'private' => $keyPair['privatekey']);
} else {
$opensslPrivKey = openssl_pkey_new(array('private_key_bits' => 1024, 'config' => $sslCnfPath));
openssl_pkey_export($opensslPrivKey, $privateKey, null, array('config' => $sslCnfPath));
$publicKey = openssl_pkey_get_details($opensslPrivKey);
$publicKey = $publicKey['key'];
openssl_free_key($opensslPrivKey);
return array('public' => $publicKey,
'private' => $privateKey);
} # else
} # createPrivateKey
/*
* RSA signed een bericht, en geeft alle componenten terug
* die nodig zijn om dit te valideren, dus:
*
* - base64 encoded signature (signature)
* - Public key (publickey)
* - Het bericht dat gesigned is (message)
*/
public function signMessage($privatekey, $message) {
/**
* Test code:
*
* $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
* extract($rsa->createKey());
* $spotSigning = new SpotSigning();
* $x = $spotSigning->signMessage($privatekey, 'testmessage');
* var_dump($x);
* var_dump($spotSigning->checkRsaSignature('testmessage', $x['signature'], $x['publickey'], false));
*
*/
if (empty($privatekey)) {
throw new Exception("Given privatekey is invalid, please correct (eg: run upgrade-db.php when testinstall.php is without errors)");
} # if
$rsa = new Crypt_RSA();
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$rsa->loadKey($privatekey);
# extract de public key
$signature = $rsa->sign($message);
$publickey = $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
return array('signature' => base64_encode($signature),
'publickey' => array('modulo' => base64_encode($publickey['n']->toBytes()), 'exponent' => base64_encode($publickey['e']->toBytes())),
'message' => $message);
} # signMessage
/*
* Returns a public key
*/
function getPublicKey($privateKey) {
$rsa = new Crypt_RSA();
$rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1);
$rsa->loadKey($privateKey);
# extract de public key
$publicKey = $rsa->getPublicKey(CRYPT_RSA_PUBLIC_FORMAT_RAW);
return array('publickey' => array('modulo' => base64_encode($publicKey['n']->toBytes()), 'exponent' => base64_encode($publicKey['e']->toBytes())));
} # getPublicKey
/*
* Converteer een voor ons bruikbare publickey, naar een publickey
* formaat gebruikt door de SpotNet native client
*/
public function pubkeyToXml($pubkey) {
return "" . $pubkey['modulo'] . ' ' . $pubkey['exponent'] . ' ';
} # pubkeyToXml
/*
* Helper functie om een spot header (resultaat uit een xover of getHeader()) te verifieeren
*/
public function verifySpotHeader($spot, $signature, $rsaKeys) {
# This is the string to verify
$toCheck = $spot['title'] . substr($spot['header'], 0, strlen($spot['header']) - strlen($spot['headersign']) - 1) . $spot['poster'];
# Check the RSA signature on the spot
return $this->checkRsaSignature($toCheck, $signature, $rsaKeys[$spot['keyid']], true);
} # verifySpotHeader()
/*
* Helper functie om een fullspot te verifieeren
*/
public function verifyFullSpot($spot) {
if ((empty($spot['user-signature'])) || (empty($spot['user-key']))) {
return false;
} # if
$verified = $this->checkRsaSignature('<' . $spot['messageid'] . '>', $spot['user-signature'], $spot['user-key'], false);
if ((!$verified) && (!empty($spot['xml-signature']))) {
$verified = $this->checkRsaSignature($spot['xml-signature'], $spot['user-signature'], $spot['user-key'], false);
} # if
return $verified;
} # verifyFullSpot()
/*
* Helper functie om een comment header te verifieeren
*/
public function verifyComment($comment) {
$verified = false;
if ((!empty($comment['user-signature'])) && (!empty($comment['user-key']))) {
$verified = $this->checkRsaSignature('<' . $comment['messageid'] . '>', $comment['user-signature'], $comment['user-key'], false);
if (!$verified) {
$verified = $this->checkRsaSignature('<' . $comment['messageid'] . '>' .
implode("\r\n", $comment['body']) . "\r\n" .
$comment['fromhdr'],
$comment['user-signature'],
$comment['user-key'],
false);
} # if
} # if
# als een spot qua RSA signature al klopt, kunnen we ook nog controleren op de users'
# hash, deze zou eigenlijk ook moeten kloppen.
# Deze hash is puur gemaakt om rekenkracht te vereisen aan de kant van de poster om
# eventuele floods te voorkomen, de hash is dus ook op zich door iedereen te creeeren.
#
if ($verified) {
# $userSignedHash = sha1('<' . $comment['messageid'] . '>', false);
# $verified = (substr($userSignedHash, 0, 4) == '0000');
} # if
return $verified;
} # verifyComment()
/*
* Bereken een SHA1 hash van het bericht en doe dit net zo lang tot de eerste bytes
* bestaan uit 0000.
*
* Normaal gebruik je hiervoor de JS variant.
*/
function makeExpensiveHash($prefix, $suffix) {
$runCount = 0;
$hash = $prefix . $suffix;
while(substr($hash, 0, 4) !== '0000') {
if ($runCount > 400000) {
throw new Exception("Unable to calculate SHA1 hash: " . $runCount);
} # if
$runCount++;
$uniquePart = $this->makeRandomStr(15);
$hash = sha1($prefix . $uniquePart . $suffix, false);
} # while
return $prefix . $uniquePart . $suffix;
} # makeExpensiveHash
/*
* Creeert een random strng van A-Za-z,0-9 van $len length
*/
function makeRandomStr($len) {
$possibleChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890';
$unique = '';
for($i = 0; $i < $len; $i++) {
$unique .= $possibleChars[mt_rand(0, strlen($possibleChars) - 1)];
} # for
return $unique;
} # makeRandomStr
/*
* 'Bereken' de userid aan de hand van z'n publickey
*/
public function calculateSpotterId($userKey) {
$userSignCrc = crc32(base64_decode($userKey));
$userIdTmp = chr($userSignCrc & 0xFF) .
chr(($userSignCrc >> 8) & 0xFF ).
chr(($userSignCrc >> 16) & 0xFF) .
chr(($userSignCrc >> 24) & 0xFF);
return str_replace(array('/', '+', '='), '', base64_encode($userIdTmp));
} # calculateSpotterId
} # class SpotSigning
debian/db-update/v20111210/dbsettings.inc.php 0000644 0000000 0000000 00000000466 12267045571 015556 0 ustar
* setKey('abcdefg');
*
* echo base64_encode($hash->hash('abcdefg'));
* ?>
*
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_Hash
* @author Jim Wigginton
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: Hash.php,v 1.6 2009/11/23 23:37:07 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**#@+
* @access private
* @see Crypt_Hash::Crypt_Hash()
*/
/**
* Toggles the internal implementation
*/
define('CRYPT_HASH_MODE_INTERNAL', 1);
/**
* Toggles the mhash() implementation, which has been deprecated on PHP 5.3.0+.
*/
define('CRYPT_HASH_MODE_MHASH', 2);
/**
* Toggles the hash() implementation, which works on PHP 5.1.2+.
*/
define('CRYPT_HASH_MODE_HASH', 3);
/**#@-*/
/**
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
*
* @author Jim Wigginton
* @version 0.1.0
* @access public
* @package Crypt_Hash
*/
class Crypt_Hash {
/**
* Byte-length of compression blocks / key (Internal HMAC)
*
* @see Crypt_Hash::setAlgorithm()
* @var Integer
* @access private
*/
var $b;
/**
* Byte-length of hash output (Internal HMAC)
*
* @see Crypt_Hash::setHash()
* @var Integer
* @access private
*/
var $l = false;
/**
* Hash Algorithm
*
* @see Crypt_Hash::setHash()
* @var String
* @access private
*/
var $hash;
/**
* Key
*
* @see Crypt_Hash::setKey()
* @var String
* @access private
*/
var $key = '';
/**
* Outer XOR (Internal HMAC)
*
* @see Crypt_Hash::setKey()
* @var String
* @access private
*/
var $opad;
/**
* Inner XOR (Internal HMAC)
*
* @see Crypt_Hash::setKey()
* @var String
* @access private
*/
var $ipad;
/**
* Default Constructor.
*
* @param optional String $hash
* @return Crypt_Hash
* @access public
*/
function Crypt_Hash($hash = 'sha1')
{
if ( !defined('CRYPT_HASH_MODE') ) {
switch (true) {
case extension_loaded('hash'):
define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_HASH);
break;
case extension_loaded('mhash'):
define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_MHASH);
break;
default:
define('CRYPT_HASH_MODE', CRYPT_HASH_MODE_INTERNAL);
}
}
$this->setHash($hash);
}
/**
* Sets the key for HMACs
*
* Keys can be of any length.
*
* @access public
* @param String $key
*/
function setKey($key)
{
$this->key = $key;
}
/**
* Sets the hash function.
*
* @access public
* @param String $hash
*/
function setHash($hash)
{
switch ($hash) {
case 'md5-96':
case 'sha1-96':
$this->l = 12; // 96 / 8 = 12
break;
case 'md2':
case 'md5':
$this->l = 16;
break;
case 'sha1':
$this->l = 20;
break;
case 'sha256':
$this->l = 32;
break;
case 'sha384':
$this->l = 48;
break;
case 'sha512':
$this->l = 64;
}
switch ($hash) {
case 'md2':
$mode = CRYPT_HASH_MODE_INTERNAL;
break;
case 'sha384':
case 'sha512':
$mode = CRYPT_HASH_MODE == CRYPT_HASH_MODE_MHASH ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
break;
default:
$mode = CRYPT_HASH_MODE;
}
switch ( $mode ) {
case CRYPT_HASH_MODE_MHASH:
switch ($hash) {
case 'md5':
case 'md5-96':
$this->hash = MHASH_MD5;
break;
case 'sha256':
$this->hash = MHASH_SHA256;
break;
case 'sha1':
case 'sha1-96':
default:
$this->hash = MHASH_SHA1;
}
return;
case CRYPT_HASH_MODE_HASH:
switch ($hash) {
case 'md5':
case 'md5-96':
$this->hash = 'md5';
return;
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = $hash;
return;
case 'sha1':
case 'sha1-96':
default:
$this->hash = 'sha1';
}
return;
}
switch ($hash) {
case 'md2':
$this->b = 16;
$this->hash = array($this, '_md2');
break;
case 'md5':
case 'md5-96':
$this->b = 64;
$this->hash = array($this, '_md5');
break;
case 'sha256':
$this->b = 64;
$this->hash = array($this, '_sha256');
break;
case 'sha384':
case 'sha512':
$this->b = 128;
$this->hash = array($this, '_sha512');
break;
case 'sha1':
case 'sha1-96':
default:
$this->b = 64;
$this->hash = array($this, '_sha1');
}
$this->ipad = str_repeat(chr(0x36), $this->b);
$this->opad = str_repeat(chr(0x5C), $this->b);
}
/**
* Compute the HMAC.
*
* @access public
* @param String $text
* @return String
*/
function hash($text)
{
$mode = is_array($this->hash) ? CRYPT_HASH_MODE_INTERNAL : CRYPT_HASH_MODE;
if (!empty($this->key)) {
switch ( $mode ) {
case CRYPT_HASH_MODE_MHASH:
$output = mhash($this->hash, $text, $this->key);
break;
case CRYPT_HASH_MODE_HASH:
$output = hash_hmac($this->hash, $text, $this->key, true);
break;
case CRYPT_HASH_MODE_INTERNAL:
/* "Applications that use keys longer than B bytes will first hash the key using H and then use the
resultant L byte string as the actual key to HMAC."
-- http://tools.ietf.org/html/rfc2104#section-2 */
$key = strlen($this->key) > $this->b ? call_user_func($this->$hash, $this->key) : $this->key;
$key = str_pad($key, $this->b, chr(0)); // step 1
$temp = $this->ipad ^ $key; // step 2
$temp .= $text; // step 3
$temp = call_user_func($this->hash, $temp); // step 4
$output = $this->opad ^ $key; // step 5
$output.= $temp; // step 6
$output = call_user_func($this->hash, $output); // step 7
}
} else {
switch ( $mode ) {
case CRYPT_HASH_MODE_MHASH:
$output = mhash($this->hash, $text);
break;
case CRYPT_HASH_MODE_HASH:
$output = hash($this->hash, $text, true);
break;
case CRYPT_HASH_MODE_INTERNAL:
$output = call_user_func($this->hash, $text);
}
}
return substr($output, 0, $this->l);
}
/**
* Returns the hash length (in bytes)
*
* @access private
* @return Integer
*/
function getLength()
{
return $this->l;
}
/**
* Wrapper for MD5
*
* @access private
* @param String $text
*/
function _md5($m)
{
return pack('H*', md5($m));
}
/**
* Wrapper for SHA1
*
* @access private
* @param String $text
*/
function _sha1($m)
{
return pack('H*', sha1($m));
}
/**
* Pure-PHP implementation of MD2
*
* See {@link http://tools.ietf.org/html/rfc1319 RFC1319}.
*
* @access private
* @param String $text
*/
function _md2($m)
{
static $s = array(
41, 46, 67, 201, 162, 216, 124, 1, 61, 54, 84, 161, 236, 240, 6,
19, 98, 167, 5, 243, 192, 199, 115, 140, 152, 147, 43, 217, 188,
76, 130, 202, 30, 155, 87, 60, 253, 212, 224, 22, 103, 66, 111, 24,
138, 23, 229, 18, 190, 78, 196, 214, 218, 158, 222, 73, 160, 251,
245, 142, 187, 47, 238, 122, 169, 104, 121, 145, 21, 178, 7, 63,
148, 194, 16, 137, 11, 34, 95, 33, 128, 127, 93, 154, 90, 144, 50,
39, 53, 62, 204, 231, 191, 247, 151, 3, 255, 25, 48, 179, 72, 165,
181, 209, 215, 94, 146, 42, 172, 86, 170, 198, 79, 184, 56, 210,
150, 164, 125, 182, 118, 252, 107, 226, 156, 116, 4, 241, 69, 157,
112, 89, 100, 113, 135, 32, 134, 91, 207, 101, 230, 45, 168, 2, 27,
96, 37, 173, 174, 176, 185, 246, 28, 70, 97, 105, 52, 64, 126, 15,
85, 71, 163, 35, 221, 81, 175, 58, 195, 92, 249, 206, 186, 197,
234, 38, 44, 83, 13, 110, 133, 40, 132, 9, 211, 223, 205, 244, 65,
129, 77, 82, 106, 220, 55, 200, 108, 193, 171, 250, 36, 225, 123,
8, 12, 189, 177, 74, 120, 136, 149, 139, 227, 99, 232, 109, 233,
203, 213, 254, 59, 0, 29, 57, 242, 239, 183, 14, 102, 88, 208, 228,
166, 119, 114, 248, 235, 117, 75, 10, 49, 68, 80, 180, 143, 237,
31, 26, 219, 153, 141, 51, 159, 17, 131, 20
);
// Step 1. Append Padding Bytes
$pad = 16 - (strlen($m) & 0xF);
$m.= str_repeat(chr($pad), $pad);
$length = strlen($m);
// Step 2. Append Checksum
$c = str_repeat(chr(0), 16);
$l = chr(0);
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
$c[$j] = chr($s[ord($m[$i + $j] ^ $l)]);
$l = $c[$j];
}
}
$m.= $c;
$length+= 16;
// Step 3. Initialize MD Buffer
$x = str_repeat(chr(0), 48);
// Step 4. Process Message in 16-Byte Blocks
for ($i = 0; $i < $length; $i+= 16) {
for ($j = 0; $j < 16; $j++) {
$x[$j + 16] = $m[$i + $j];
$x[$j + 32] = $x[$j + 16] ^ $x[$j];
}
$t = chr(0);
for ($j = 0; $j < 18; $j++) {
for ($k = 0; $k < 48; $k++) {
$x[$k] = $t = $x[$k] ^ chr($s[ord($t)]);
//$t = $x[$k] = $x[$k] ^ chr($s[ord($t)]);
}
$t = chr(ord($t) + $j);
}
}
// Step 5. Output
return substr($x, 0, 16);
}
/**
* Pure-PHP implementation of SHA256
*
* See {@link http://en.wikipedia.org/wiki/SHA_hash_functions#SHA-256_.28a_SHA-2_variant.29_pseudocode SHA-256 (a SHA-2 variant) pseudocode - Wikipedia}.
*
* @access private
* @param String $text
*/
function _sha256($m)
{
if (extension_loaded('suhosin')) {
return pack('H*', sha256($m));
}
// Initialize variables
$hash = array(
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
);
// Initialize table of round constants
// (first 32 bits of the fractional parts of the cube roots of the first 64 primes 2..311)
static $k = array(
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
);
// Pre-processing
$length = strlen($m);
// to round to nearest 56 mod 64, we'll add 64 - (length + (64 - 56)) % 64
$m.= str_repeat(chr(0), 64 - (($length + 8) & 0x3F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N2', 0, $length << 3);
// Process the message in successive 512-bit chunks
$chunks = str_split($m, 64);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
extract(unpack('Ntemp', $this->_string_shift($chunk, 4)));
$w[] = $temp;
}
// Extend the sixteen 32-bit words into sixty-four 32-bit words
for ($i = 16; $i < 64; $i++) {
$s0 = $this->_rightRotate($w[$i - 15], 7) ^
$this->_rightRotate($w[$i - 15], 18) ^
$this->_rightShift( $w[$i - 15], 3);
$s1 = $this->_rightRotate($w[$i - 2], 17) ^
$this->_rightRotate($w[$i - 2], 19) ^
$this->_rightShift( $w[$i - 2], 10);
$w[$i] = $this->_add($w[$i - 16], $s0, $w[$i - 7], $s1);
}
// Initialize hash value for this chunk
list($a, $b, $c, $d, $e, $f, $g, $h) = $hash;
// Main loop
for ($i = 0; $i < 64; $i++) {
$s0 = $this->_rightRotate($a, 2) ^
$this->_rightRotate($a, 13) ^
$this->_rightRotate($a, 22);
$maj = ($a & $b) ^
($a & $c) ^
($b & $c);
$t2 = $this->_add($s0, $maj);
$s1 = $this->_rightRotate($e, 6) ^
$this->_rightRotate($e, 11) ^
$this->_rightRotate($e, 25);
$ch = ($e & $f) ^
($this->_not($e) & $g);
$t1 = $this->_add($h, $s1, $ch, $k[$i], $w[$i]);
$h = $g;
$g = $f;
$f = $e;
$e = $this->_add($d, $t1);
$d = $c;
$c = $b;
$b = $a;
$a = $this->_add($t1, $t2);
}
// Add this chunk's hash to result so far
$hash = array(
$this->_add($hash[0], $a),
$this->_add($hash[1], $b),
$this->_add($hash[2], $c),
$this->_add($hash[3], $d),
$this->_add($hash[4], $e),
$this->_add($hash[5], $f),
$this->_add($hash[6], $g),
$this->_add($hash[7], $h)
);
}
// Produce the final hash value (big-endian)
return pack('N8', $hash[0], $hash[1], $hash[2], $hash[3], $hash[4], $hash[5], $hash[6], $hash[7]);
}
/**
* Pure-PHP implementation of SHA384 and SHA512
*
* @access private
* @param String $text
*/
function _sha512($m)
{
if (!class_exists('Math_BigInteger')) {
require_once('Math/BigInteger.php');
}
static $init384, $init512, $k;
if (!isset($k)) {
// Initialize variables
$init384 = array( // initial values for SHA384
'cbbb9d5dc1059ed8', '629a292a367cd507', '9159015a3070dd17', '152fecd8f70e5939',
'67332667ffc00b31', '8eb44a8768581511', 'db0c2e0d64f98fa7', '47b5481dbefa4fa4'
);
$init512 = array( // initial values for SHA512
'6a09e667f3bcc908', 'bb67ae8584caa73b', '3c6ef372fe94f82b', 'a54ff53a5f1d36f1',
'510e527fade682d1', '9b05688c2b3e6c1f', '1f83d9abfb41bd6b', '5be0cd19137e2179'
);
for ($i = 0; $i < 8; $i++) {
$init384[$i] = new Math_BigInteger($init384[$i], 16);
$init384[$i]->setPrecision(64);
$init512[$i] = new Math_BigInteger($init512[$i], 16);
$init512[$i]->setPrecision(64);
}
// Initialize table of round constants
// (first 64 bits of the fractional parts of the cube roots of the first 80 primes 2..409)
$k = array(
'428a2f98d728ae22', '7137449123ef65cd', 'b5c0fbcfec4d3b2f', 'e9b5dba58189dbbc',
'3956c25bf348b538', '59f111f1b605d019', '923f82a4af194f9b', 'ab1c5ed5da6d8118',
'd807aa98a3030242', '12835b0145706fbe', '243185be4ee4b28c', '550c7dc3d5ffb4e2',
'72be5d74f27b896f', '80deb1fe3b1696b1', '9bdc06a725c71235', 'c19bf174cf692694',
'e49b69c19ef14ad2', 'efbe4786384f25e3', '0fc19dc68b8cd5b5', '240ca1cc77ac9c65',
'2de92c6f592b0275', '4a7484aa6ea6e483', '5cb0a9dcbd41fbd4', '76f988da831153b5',
'983e5152ee66dfab', 'a831c66d2db43210', 'b00327c898fb213f', 'bf597fc7beef0ee4',
'c6e00bf33da88fc2', 'd5a79147930aa725', '06ca6351e003826f', '142929670a0e6e70',
'27b70a8546d22ffc', '2e1b21385c26c926', '4d2c6dfc5ac42aed', '53380d139d95b3df',
'650a73548baf63de', '766a0abb3c77b2a8', '81c2c92e47edaee6', '92722c851482353b',
'a2bfe8a14cf10364', 'a81a664bbc423001', 'c24b8b70d0f89791', 'c76c51a30654be30',
'd192e819d6ef5218', 'd69906245565a910', 'f40e35855771202a', '106aa07032bbd1b8',
'19a4c116b8d2d0c8', '1e376c085141ab53', '2748774cdf8eeb99', '34b0bcb5e19b48a8',
'391c0cb3c5c95a63', '4ed8aa4ae3418acb', '5b9cca4f7763e373', '682e6ff3d6b2b8a3',
'748f82ee5defb2fc', '78a5636f43172f60', '84c87814a1f0ab72', '8cc702081a6439ec',
'90befffa23631e28', 'a4506cebde82bde9', 'bef9a3f7b2c67915', 'c67178f2e372532b',
'ca273eceea26619c', 'd186b8c721c0c207', 'eada7dd6cde0eb1e', 'f57d4f7fee6ed178',
'06f067aa72176fba', '0a637dc5a2c898a6', '113f9804bef90dae', '1b710b35131c471b',
'28db77f523047d84', '32caab7b40c72493', '3c9ebe0a15c9bebc', '431d67c49c100d4c',
'4cc5d4becb3e42b6', '597f299cfc657e2a', '5fcb6fab3ad6faec', '6c44198c4a475817'
);
for ($i = 0; $i < 80; $i++) {
$k[$i] = new Math_BigInteger($k[$i], 16);
}
}
$hash = $this->l == 48 ? $init384 : $init512;
// Pre-processing
$length = strlen($m);
// to round to nearest 112 mod 128, we'll add 128 - (length + (128 - 112)) % 128
$m.= str_repeat(chr(0), 128 - (($length + 16) & 0x7F));
$m[$length] = chr(0x80);
// we don't support hashing strings 512MB long
$m.= pack('N4', 0, 0, 0, $length << 3);
// Process the message in successive 1024-bit chunks
$chunks = str_split($m, 128);
foreach ($chunks as $chunk) {
$w = array();
for ($i = 0; $i < 16; $i++) {
$temp = new Math_BigInteger($this->_string_shift($chunk, 8), 256);
$temp->setPrecision(64);
$w[] = $temp;
}
// Extend the sixteen 32-bit words into eighty 32-bit words
for ($i = 16; $i < 80; $i++) {
$temp = array(
$w[$i - 15]->bitwise_rightRotate(1),
$w[$i - 15]->bitwise_rightRotate(8),
$w[$i - 15]->bitwise_rightShift(7)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$w[$i - 2]->bitwise_rightRotate(19),
$w[$i - 2]->bitwise_rightRotate(61),
$w[$i - 2]->bitwise_rightShift(6)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$w[$i] = $w[$i - 16]->copy();
$w[$i] = $w[$i]->add($s0);
$w[$i] = $w[$i]->add($w[$i - 7]);
$w[$i] = $w[$i]->add($s1);
}
// Initialize hash value for this chunk
$a = $hash[0]->copy();
$b = $hash[1]->copy();
$c = $hash[2]->copy();
$d = $hash[3]->copy();
$e = $hash[4]->copy();
$f = $hash[5]->copy();
$g = $hash[6]->copy();
$h = $hash[7]->copy();
// Main loop
for ($i = 0; $i < 80; $i++) {
$temp = array(
$a->bitwise_rightRotate(28),
$a->bitwise_rightRotate(34),
$a->bitwise_rightRotate(39)
);
$s0 = $temp[0]->bitwise_xor($temp[1]);
$s0 = $s0->bitwise_xor($temp[2]);
$temp = array(
$a->bitwise_and($b),
$a->bitwise_and($c),
$b->bitwise_and($c)
);
$maj = $temp[0]->bitwise_xor($temp[1]);
$maj = $maj->bitwise_xor($temp[2]);
$t2 = $s0->add($maj);
$temp = array(
$e->bitwise_rightRotate(14),
$e->bitwise_rightRotate(18),
$e->bitwise_rightRotate(41)
);
$s1 = $temp[0]->bitwise_xor($temp[1]);
$s1 = $s1->bitwise_xor($temp[2]);
$temp = array(
$e->bitwise_and($f),
$g->bitwise_and($e->bitwise_not())
);
$ch = $temp[0]->bitwise_xor($temp[1]);
$t1 = $h->add($s1);
$t1 = $t1->add($ch);
$t1 = $t1->add($k[$i]);
$t1 = $t1->add($w[$i]);
$h = $g->copy();
$g = $f->copy();
$f = $e->copy();
$e = $d->add($t1);
$d = $c->copy();
$c = $b->copy();
$b = $a->copy();
$a = $t1->add($t2);
}
// Add this chunk's hash to result so far
$hash = array(
$hash[0]->add($a),
$hash[1]->add($b),
$hash[2]->add($c),
$hash[3]->add($d),
$hash[4]->add($e),
$hash[5]->add($f),
$hash[6]->add($g),
$hash[7]->add($h)
);
}
// Produce the final hash value (big-endian)
// (Crypt_Hash::hash() trims the output for hashes but not for HMACs. as such, we trim the output here)
$temp = $hash[0]->toBytes() . $hash[1]->toBytes() . $hash[2]->toBytes() . $hash[3]->toBytes() .
$hash[4]->toBytes() . $hash[5]->toBytes();
if ($this->l != 48) {
$temp.= $hash[6]->toBytes() . $hash[7]->toBytes();
}
return $temp;
}
/**
* Right Rotate
*
* @access private
* @param Integer $int
* @param Integer $amt
* @see _sha256()
* @return Integer
*/
function _rightRotate($int, $amt)
{
$invamt = 32 - $amt;
$mask = (1 << $invamt) - 1;
return (($int << $invamt) & 0xFFFFFFFF) | (($int >> $amt) & $mask);
}
/**
* Right Shift
*
* @access private
* @param Integer $int
* @param Integer $amt
* @see _sha256()
* @return Integer
*/
function _rightShift($int, $amt)
{
$mask = (1 << (32 - $amt)) - 1;
return ($int >> $amt) & $mask;
}
/**
* Not
*
* @access private
* @param Integer $int
* @see _sha256()
* @return Integer
*/
function _not($int)
{
return ~$int & 0xFFFFFFFF;
}
/**
* Add
*
* _sha256() adds multiple unsigned 32-bit integers. Since PHP doesn't support unsigned integers and since the
* possibility of overflow exists, care has to be taken. Math_BigInteger() could be used but this should be faster.
*
* @param String $string
* @param optional Integer $index
* @return String
* @see _sha256()
* @access private
*/
function _add()
{
static $mod;
if (!isset($mod)) {
$mod = pow(2, 32);
}
$result = 0;
$arguments = func_get_args();
foreach ($arguments as $argument) {
$result+= $argument < 0 ? ($argument & 0x7FFFFFFF) + 0x80000000 : $argument;
}
return fmod($result, $mod);
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param String $string
* @param optional Integer $index
* @return String
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
} debian/db-update/v20111210/Crypt/RSA.php 0000644 0000000 0000000 00000214222 12267045571 014363 0 ustar
* createKey());
*
* $plaintext = 'terrafrost';
*
* $rsa->loadKey($privatekey);
* $ciphertext = $rsa->encrypt($plaintext);
*
* $rsa->loadKey($publickey);
* echo $rsa->decrypt($ciphertext);
* ?>
*
*
* Here's an example of how to create signatures and verify signatures with this library:
*
* createKey());
*
* $plaintext = 'terrafrost';
*
* $rsa->loadKey($privatekey);
* $signature = $rsa->sign($plaintext);
*
* $rsa->loadKey($publickey);
* echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
* ?>
*
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_RSA
* @author Jim Wigginton
* @copyright MMIX Jim Wigginton
* @license http://www.opensource.org/licenses/mit-license.html MIT License
* @version $Id: RSA.php,v 1.19 2010/09/12 21:58:54 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**
* Include Math_BigInteger
*/
require_once('Math/BigInteger.php');
/**
* Include Crypt_Random
*/
require_once('Crypt/Random.php');
/**
* Include Crypt_Hash
*/
require_once('Crypt/Hash.php');
/**#@+
* @access public
* @see Crypt_RSA::encrypt()
* @see Crypt_RSA::decrypt()
*/
/**
* Use {@link http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding Optimal Asymmetric Encryption Padding}
* (OAEP) for encryption / decryption.
*
* Uses sha1 by default.
*
* @see Crypt_RSA::setHash()
* @see Crypt_RSA::setMGFHash()
*/
define('CRYPT_RSA_ENCRYPTION_OAEP', 1);
/**
* Use PKCS#1 padding.
*
* Although CRYPT_RSA_ENCRYPTION_OAEP offers more security, including PKCS#1 padding is necessary for purposes of backwards
* compatability with protocols (like SSH-1) written before OAEP's introduction.
*/
define('CRYPT_RSA_ENCRYPTION_PKCS1', 2);
/**#@-*/
/**#@+
* @access public
* @see Crypt_RSA::sign()
* @see Crypt_RSA::verify()
* @see Crypt_RSA::setHash()
*/
/**
* Use the Probabilistic Signature Scheme for signing
*
* Uses sha1 by default.
*
* @see Crypt_RSA::setSaltLength()
* @see Crypt_RSA::setMGFHash()
*/
define('CRYPT_RSA_SIGNATURE_PSS', 1);
/**
* Use the PKCS#1 scheme by default.
*
* Although CRYPT_RSA_SIGNATURE_PSS offers more security, including PKCS#1 signing is necessary for purposes of backwards
* compatability with protocols (like SSH-2) written before PSS's introduction.
*/
define('CRYPT_RSA_SIGNATURE_PKCS1', 2);
/**#@-*/
/**#@+
* @access private
* @see Crypt_RSA::createKey()
*/
/**
* ASN1 Integer
*/
define('CRYPT_RSA_ASN1_INTEGER', 2);
/**
* ASN1 Sequence (with the constucted bit set)
*/
define('CRYPT_RSA_ASN1_SEQUENCE', 48);
/**#@-*/
/**#@+
* @access private
* @see Crypt_RSA::Crypt_RSA()
*/
/**
* To use the pure-PHP implementation
*/
define('CRYPT_RSA_MODE_INTERNAL', 1);
/**
* To use the OpenSSL library
*
* (if enabled; otherwise, the internal implementation will be used)
*/
define('CRYPT_RSA_MODE_OPENSSL', 2);
/**#@-*/
/**#@+
* @access public
* @see Crypt_RSA::createKey()
* @see Crypt_RSA::setPrivateKeyFormat()
*/
/**
* PKCS#1 formatted private key
*
* Used by OpenSSH
*/
define('CRYPT_RSA_PRIVATE_FORMAT_PKCS1', 0);
/**#@-*/
/**#@+
* @access public
* @see Crypt_RSA::createKey()
* @see Crypt_RSA::setPublicKeyFormat()
*/
/**
* Raw public key
*
* An array containing two Math_BigInteger objects.
*
* The exponent can be indexed with any of the following:
*
* 0, e, exponent, publicExponent
*
* The modulus can be indexed with any of the following:
*
* 1, n, modulo, modulus
*/
define('CRYPT_RSA_PUBLIC_FORMAT_RAW', 1);
/**
* PKCS#1 formatted public key
*/
define('CRYPT_RSA_PUBLIC_FORMAT_PKCS1', 2);
/**
* OpenSSH formatted public key
*
* Place in $HOME/.ssh/authorized_keys
*/
define('CRYPT_RSA_PUBLIC_FORMAT_OPENSSH', 3);
/**#@-*/
/**
* Pure-PHP PKCS#1 compliant implementation of RSA.
*
* @author Jim Wigginton
* @version 0.1.0
* @access public
* @package Crypt_RSA
*/
class Crypt_RSA {
/**
* Precomputed Zero
*
* @var Array
* @access private
*/
var $zero;
/**
* Precomputed One
*
* @var Array
* @access private
*/
var $one;
/**
* Private Key Format
*
* @var Integer
* @access private
*/
var $privateKeyFormat = CRYPT_RSA_PRIVATE_FORMAT_PKCS1;
/**
* Public Key Format
*
* @var Integer
* @access public
*/
var $publicKeyFormat = CRYPT_RSA_PUBLIC_FORMAT_PKCS1;
/**
* Modulus (ie. n)
*
* @var Math_BigInteger
* @access private
*/
var $modulus;
/**
* Modulus length
*
* @var Math_BigInteger
* @access private
*/
var $k;
/**
* Exponent (ie. e or d)
*
* @var Math_BigInteger
* @access private
*/
var $exponent;
/**
* Primes for Chinese Remainder Theorem (ie. p and q)
*
* @var Array
* @access private
*/
var $primes;
/**
* Exponents for Chinese Remainder Theorem (ie. dP and dQ)
*
* @var Array
* @access private
*/
var $exponents;
/**
* Coefficients for Chinese Remainder Theorem (ie. qInv)
*
* @var Array
* @access private
*/
var $coefficients;
/**
* Hash name
*
* @var String
* @access private
*/
var $hashName;
/**
* Hash function
*
* @var Crypt_Hash
* @access private
*/
var $hash;
/**
* Length of hash function output
*
* @var Integer
* @access private
*/
var $hLen;
/**
* Length of salt
*
* @var Integer
* @access private
*/
var $sLen;
/**
* Hash function for the Mask Generation Function
*
* @var Crypt_Hash
* @access private
*/
var $mgfHash;
/**
* Length of MGF hash function output
*
* @var Integer
* @access private
*/
var $mgfHLen;
/**
* Encryption mode
*
* @var Integer
* @access private
*/
var $encryptionMode = CRYPT_RSA_ENCRYPTION_OAEP;
/**
* Signature mode
*
* @var Integer
* @access private
*/
var $signatureMode = CRYPT_RSA_SIGNATURE_PSS;
/**
* Public Exponent
*
* @var Mixed
* @access private
*/
var $publicExponent = false;
/**
* Password
*
* @var String
* @access private
*/
var $password = '';
/**
* The constructor
*
* If you want to make use of the openssl extension, you'll need to set the mode manually, yourself. The reason
* Crypt_RSA doesn't do it is because OpenSSL doesn't fail gracefully. openssl_pkey_new(), in particular, requires
* openssl.cnf be present somewhere and, unfortunately, the only real way to find out is too late.
*
* @return Crypt_RSA
* @access public
*/
function Crypt_RSA()
{
if ( !defined('CRYPT_RSA_MODE') ) {
switch (true) {
//case extension_loaded('openssl') && version_compare(PHP_VERSION, '4.2.0', '>='):
// define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_OPENSSL);
// break;
default:
define('CRYPT_RSA_MODE', CRYPT_RSA_MODE_INTERNAL);
}
}
$this->zero = new Math_BigInteger();
$this->one = new Math_BigInteger(1);
$this->hash = new Crypt_Hash('sha1');
$this->hLen = $this->hash->getLength();
$this->hashName = 'sha1';
$this->mgfHash = new Crypt_Hash('sha1');
$this->mgfHLen = $this->mgfHash->getLength();
}
/**
* Create public / private key pair
*
* Returns an array with the following three elements:
* - 'privatekey': The private key.
* - 'publickey': The public key.
* - 'partialkey': A partially computed key (if the execution time exceeded $timeout).
* Will need to be passed back to Crypt_RSA::createKey() as the third parameter for further processing.
*
* @access public
* @param optional Integer $bits
* @param optional Integer $timeout
* @param optional Math_BigInteger $p
*/
function createKey($bits = 1024, $timeout = false, $partial = array())
{
if ( CRYPT_RSA_MODE == CRYPT_RSA_MODE_OPENSSL ) {
$rsa = openssl_pkey_new(array('private_key_bits' => $bits));
openssl_pkey_export($rsa, $privatekey);
$publickey = openssl_pkey_get_details($rsa);
$publickey = $publickey['key'];
if ($this->privateKeyFormat != CRYPT_RSA_PRIVATE_FORMAT_PKCS1) {
$privatekey = call_user_func_array(array($this, '_convertPrivateKey'), array_values($this->_parseKey($privatekey, CRYPT_RSA_PRIVATE_FORMAT_PKCS1)));
$publickey = call_user_func_array(array($this, '_convertPublicKey'), array_values($this->_parseKey($publickey, CRYPT_RSA_PUBLIC_FORMAT_PKCS1)));
}
return array(
'privatekey' => $privatekey,
'publickey' => $publickey,
'partialkey' => false
);
}
static $e;
if (!isset($e)) {
if (!defined('CRYPT_RSA_EXPONENT')) {
// http://en.wikipedia.org/wiki/65537_%28number%29
define('CRYPT_RSA_EXPONENT', '65537');
}
if (!defined('CRYPT_RSA_COMMENT')) {
define('CRYPT_RSA_COMMENT', 'phpseclib-generated-key');
}
// per , this number ought not result in primes smaller
// than 256 bits.
if (!defined('CRYPT_RSA_SMALLEST_PRIME')) {
define('CRYPT_RSA_SMALLEST_PRIME', 4096);
}
$e = new Math_BigInteger(CRYPT_RSA_EXPONENT);
}
extract($this->_generateMinMax($bits));
$absoluteMin = $min;
$temp = $bits >> 1;
if ($temp > CRYPT_RSA_SMALLEST_PRIME) {
$num_primes = floor($bits / CRYPT_RSA_SMALLEST_PRIME);
$temp = CRYPT_RSA_SMALLEST_PRIME;
} else {
$num_primes = 2;
}
extract($this->_generateMinMax($temp + $bits % $temp));
$finalMax = $max;
extract($this->_generateMinMax($temp));
$generator = new Math_BigInteger();
$generator->setRandomGenerator('crypt_random');
$n = $this->one->copy();
if (!empty($partial)) {
extract(unserialize($partial));
} else {
$exponents = $coefficients = $primes = array();
$lcm = array(
'top' => $this->one->copy(),
'bottom' => false
);
}
$start = time();
$i0 = count($primes) + 1;
do {
for ($i = $i0; $i <= $num_primes; $i++) {
if ($timeout !== false) {
$timeout-= time() - $start;
$start = time();
if ($timeout <= 0) {
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
))
);
}
}
if ($i == $num_primes) {
list($min, $temp) = $absoluteMin->divide($n);
if (!$temp->equals($this->zero)) {
$min = $min->add($this->one); // ie. ceil()
}
$primes[$i] = $generator->randomPrime($min, $finalMax, $timeout);
} else {
$primes[$i] = $generator->randomPrime($min, $max, $timeout);
}
if ($primes[$i] === false) { // if we've reached the timeout
if (count($primes) > 1) {
$partialkey = '';
} else {
array_pop($primes);
$partialkey = serialize(array(
'primes' => $primes,
'coefficients' => $coefficients,
'lcm' => $lcm,
'exponents' => $exponents
));
}
return array(
'privatekey' => '',
'publickey' => '',
'partialkey' => $partialkey
);
}
// the first coefficient is calculated differently from the rest
// ie. instead of being $primes[1]->modInverse($primes[2]), it's $primes[2]->modInverse($primes[1])
if ($i > 2) {
$coefficients[$i] = $n->modInverse($primes[$i]);
}
$n = $n->multiply($primes[$i]);
$temp = $primes[$i]->subtract($this->one);
// textbook RSA implementations use Euler's totient function instead of the least common multiple.
// see http://en.wikipedia.org/wiki/Euler%27s_totient_function
$lcm['top'] = $lcm['top']->multiply($temp);
$lcm['bottom'] = $lcm['bottom'] === false ? $temp : $lcm['bottom']->gcd($temp);
$exponents[$i] = $e->modInverse($temp);
}
list($lcm) = $lcm['top']->divide($lcm['bottom']);
$gcd = $lcm->gcd($e);
$i0 = 1;
} while (!$gcd->equals($this->one));
$d = $e->modInverse($lcm);
$coefficients[2] = $primes[2]->modInverse($primes[1]);
// from :
// RSAPrivateKey ::= SEQUENCE {
// version Version,
// modulus INTEGER, -- n
// publicExponent INTEGER, -- e
// privateExponent INTEGER, -- d
// prime1 INTEGER, -- p
// prime2 INTEGER, -- q
// exponent1 INTEGER, -- d mod (p-1)
// exponent2 INTEGER, -- d mod (q-1)
// coefficient INTEGER, -- (inverse of q) mod p
// otherPrimeInfos OtherPrimeInfos OPTIONAL
// }
return array(
'privatekey' => $this->_convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients),
'publickey' => $this->_convertPublicKey($n, $e),
'partialkey' => false
);
}
/**
* Convert a private key to the appropriate format.
*
* @access private
* @see setPrivateKeyFormat()
* @param String $RSAPrivateKey
* @return String
*/
function _convertPrivateKey($n, $e, $d, $primes, $exponents, $coefficients)
{
$num_primes = count($primes);
$raw = array(
'version' => $num_primes == 2 ? chr(0) : chr(1), // two-prime vs. multi
'modulus' => $n->toBytes(true),
'publicExponent' => $e->toBytes(true),
'privateExponent' => $d->toBytes(true),
'prime1' => $primes[1]->toBytes(true),
'prime2' => $primes[2]->toBytes(true),
'exponent1' => $exponents[1]->toBytes(true),
'exponent2' => $exponents[2]->toBytes(true),
'coefficient' => $coefficients[2]->toBytes(true)
);
// if the format in question does not support multi-prime rsa and multi-prime rsa was used,
// call _convertPublicKey() instead.
switch ($this->privateKeyFormat) {
default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
$components = array();
foreach ($raw as $name => $value) {
$components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
}
$RSAPrivateKey = implode('', $components);
if ($num_primes > 2) {
$OtherPrimeInfos = '';
for ($i = 3; $i <= $num_primes; $i++) {
// OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
//
// OtherPrimeInfo ::= SEQUENCE {
// prime INTEGER, -- ri
// exponent INTEGER, -- di
// coefficient INTEGER -- ti
// }
$OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
$OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
$OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
}
$RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
}
$RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
if (!empty($this->password)) {
$iv = $this->_random(8);
$symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php');
}
$des = new Crypt_TripleDES();
$des->setKey($symkey);
$des->setIV($iv);
$iv = strtoupper(bin2hex($iv));
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
"Proc-Type: 4,ENCRYPTED\r\n" .
"DEK-Info: DES-EDE3-CBC,$iv\r\n" .
"\r\n" .
chunk_split(base64_encode($des->encrypt($RSAPrivateKey))) .
'-----END RSA PRIVATE KEY-----';
} else {
$RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
chunk_split(base64_encode($RSAPrivateKey)) .
'-----END RSA PRIVATE KEY-----';
}
return $RSAPrivateKey;
}
}
/**
* Convert a public key to the appropriate format
*
* @access private
* @see setPublicKeyFormat()
* @param String $RSAPrivateKey
* @return String
*/
function _convertPublicKey($n, $e)
{
$modulus = $n->toBytes(true);
$publicExponent = $e->toBytes(true);
switch ($this->publicKeyFormat) {
case CRYPT_RSA_PUBLIC_FORMAT_RAW:
return array('e' => $e->copy(), 'n' => $n->copy());
case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
// from :
// string "ssh-rsa"
// mpint e
// mpint n
$RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
$RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . CRYPT_RSA_COMMENT;
return $RSAPublicKey;
default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1
// from :
// RSAPublicKey ::= SEQUENCE {
// modulus INTEGER, -- n
// publicExponent INTEGER -- e
// }
$components = array(
'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
);
$RSAPublicKey = pack('Ca*a*a*',
CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
$components['modulus'], $components['publicExponent']
);
$RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
chunk_split(base64_encode($RSAPublicKey)) .
'-----END PUBLIC KEY-----';
return $RSAPublicKey;
}
}
/**
* Break a public or private key down into its constituant components
*
* @access private
* @see _convertPublicKey()
* @see _convertPrivateKey()
* @param String $key
* @param Integer $type
* @return Array
*/
function _parseKey($key, $type)
{
switch ($type) {
case CRYPT_RSA_PUBLIC_FORMAT_RAW:
if (!is_array($key)) {
return false;
}
$components = array();
switch (true) {
case isset($key['e']):
$components['publicExponent'] = $key['e']->copy();
break;
case isset($key['exponent']):
$components['publicExponent'] = $key['exponent']->copy();
break;
case isset($key['publicExponent']):
$components['publicExponent'] = $key['publicExponent']->copy();
break;
case isset($key[0]):
$components['publicExponent'] = $key[0]->copy();
}
switch (true) {
case isset($key['n']):
$components['modulus'] = $key['n']->copy();
break;
case isset($key['modulo']):
$components['modulus'] = $key['modulo']->copy();
break;
case isset($key['modulus']):
$components['modulus'] = $key['modulus']->copy();
break;
case isset($key[1]):
$components['modulus'] = $key[1]->copy();
}
return $components;
case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
/* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
"outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
http://tools.ietf.org/html/rfc1421#section-4.6.1.1
http://tools.ietf.org/html/rfc1421#section-4.6.1.3
DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
implementation are part of the standard, as well.
* OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
$iv = pack('H*', trim($matches[2]));
$symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
$symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
$ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-#s', '', $key);
$ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
if ($ciphertext === false) {
$ciphertext = $key;
}
switch ($matches[1]) {
case 'AES-128-CBC':
if (!class_exists('Crypt_AES')) {
require_once('Crypt/AES.php');
}
$symkey = substr($symkey, 0, 16);
break;
case 'DES-EDE3-CFB':
if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php');
}
$crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
break;
case 'DES-EDE3-CBC':
if (!class_exists('Crypt_TripleDES')) {
require_once('Crypt/TripleDES.php');
}
$crypto = new Crypt_TripleDES();
break;
case 'DES-CBC':
if (!class_exists('Crypt_DES')) {
require_once('Crypt/DES.php');
}
$crypto = new Crypt_DES();
break;
default:
return false;
}
$crypto->setKey($symkey);
$crypto->setIV($iv);
$decoded = $crypto->decrypt($ciphertext);
} else {
$decoded = preg_replace('#-.+-|[\r\n]#', '', $key);
$decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
}
if ($decoded !== false) {
$key = $decoded;
}
$components = array();
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
/* intended for keys for which OpenSSL's asn1parse returns the following:
0:d=0 hl=4 l= 290 cons: SEQUENCE
4:d=1 hl=2 l= 13 cons: SEQUENCE
6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
17:d=2 hl=2 l= 0 prim: NULL
19:d=1 hl=4 l= 271 prim: BIT STRING */
$this->_string_shift($key, $this->_decodeLength($key));
$this->_string_shift($key); // skip over the BIT STRING tag
$this->_decodeLength($key); // skip over the BIT STRING length
// "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
// unused bits in teh final subsequent octet. The number shall be in the range zero to seven."
// -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
$this->_string_shift($key);
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
if ($this->_decodeLength($key) != strlen($key)) {
return false;
}
$tag = ord($this->_string_shift($key));
}
if ($tag != CRYPT_RSA_ASN1_INTEGER) {
return false;
}
/* We peek a little forwards, if an sequence follows the integer field,
there is some additional padding we need to strip */
if (ord(substr($key, 2, 1)) == CRYPT_RSA_ASN1_SEQUENCE) {
/*
intended for keys for which openssl's asn1parse returns the following:
0:d=0 hl=4 l= 631 cons: SEQUENCE
4:d=1 hl=2 l= 1 prim: INTEGER :00
7:d=1 hl=2 l= 13 cons: SEQUENCE
9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
20:d=2 hl=2 l= 0 prim: NULL
22:d=1 hl=4 l= 609 prim: OCTET STRING [HEX DUMP]
*/
$tag = ord($this->_string_shift($key, $this->_decodeLength($key)));
/* Read the sequence with the object in it */
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
/* skip over the rsaEncryption object and over the trailing NULL */
$encLength = $this->_decodeLength($key);
$this->_string_shift($key, $encLength);
if (ord($this->_string_shift($key)) != 4) { /* skip over the OCTET STRING tag */
return false;
} # if
$this->_decodeLength($key); // skip over the OCTET STRING length
/* Inside this package we will find another ASN1 sequence and length,
because at this time in the parser, it is expected to be beyond this,
skip it */
$this->_string_shift($key); // skip over the sequence tag
$this->_decodeLength($key); // skip over the sequence length
$tag = ord($this->_string_shift($key));
} # if
$length = $this->_decodeLength($key);
$temp = $this->_string_shift($key, $length);
if (strlen($temp) != 1 || ord($temp) > 2) {
$components['modulus'] = new Math_BigInteger($temp, -256);
$this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
$length = $this->_decodeLength($key);
$components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
return $components;
}
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
return false;
}
$length = $this->_decodeLength($key);
$components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), -256));
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), -256));
if (!empty($key)) {
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
while (!empty($key)) {
if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
return false;
}
$this->_decodeLength($key);
$key = substr($key, 1);
$length = $this->_decodeLength($key);
$components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
$this->_string_shift($key);
$length = $this->_decodeLength($key);
$components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), -256);
}
}
return $components;
case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
$key = base64_decode(preg_replace('#^ssh-rsa | .+$#', '', $key));
if ($key === false) {
return false;
}
$cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
extract(unpack('Nlength', $this->_string_shift($key, 4)));
$modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
if ($cleanup && strlen($key)) {
extract(unpack('Nlength', $this->_string_shift($key, 4)));
return array(
'modulus' => new Math_BigInteger($this->_string_shift($key, $length), -256),
'publicExponent' => $modulus
);
} else {
return array(
'modulus' => $modulus,
'publicExponent' => $publicExponent
);
}
}
}
/**
* Loads a public or private key
*
* Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
*
* @access public
* @param String $key
* @param Integer $type optional
*/
function loadKey($key, $type = CRYPT_RSA_PRIVATE_FORMAT_PKCS1)
{
$components = $this->_parseKey($key, $type);
if ($components === false) {
return false;
}
$this->modulus = $components['modulus'];
$this->k = strlen($this->modulus->toBytes());
$this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
if (isset($components['primes'])) {
$this->primes = $components['primes'];
$this->exponents = $components['exponents'];
$this->coefficients = $components['coefficients'];
$this->publicExponent = $components['publicExponent'];
} else {
$this->primes = array();
$this->exponents = array();
$this->coefficients = array();
$this->publicExponent = false;
}
return true;
}
/**
* Sets the password
*
* Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
* Or rather, pass in $password such that empty($password) is true.
*
* @see createKey()
* @see loadKey()
* @access public
* @param String $password
*/
function setPassword($password)
{
$this->password = $password;
}
/**
* Defines the public key
*
* Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
* used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
* message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
* and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
* exponent this won't work unless you manually add the public exponent.
*
* Do note that when a new key is loaded the index will be cleared.
*
* Returns true on success, false on failure
*
* @see getPublicKey()
* @access public
* @param String $key
* @param Integer $type optional
* @return Boolean
*/
function setPublicKey($key, $type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
{
$components = $this->_parseKey($key, $type);
if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
user_error('Trying to load a public key? Use loadKey() instead. It\'s called loadKey() and not loadPrivateKey() for a reason.', E_USER_NOTICE);
return false;
}
$this->publicExponent = $components['publicExponent'];
return true;
}
/**
* Returns the public key
*
* The public key is only returned under two circumstances - if the private key had the public key embedded within it
* or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
* function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
*
* @see getPublicKey()
* @access public
* @param String $key
* @param Integer $type optional
*/
function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
{
if (empty($this->modulus) || empty($this->publicExponent)) {
return false;
}
$oldFormat = $this->publicKeyFormat;
$this->publicKeyFormat = $type;
$temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
$this->publicKeyFormat = $oldFormat;
return $temp;
}
/**
* Generates the smallest and largest numbers requiring $bits bits
*
* @access private
* @param Integer $bits
* @return Array
*/
function _generateMinMax($bits)
{
$bytes = $bits >> 3;
$min = str_repeat(chr(0), $bytes);
$max = str_repeat(chr(0xFF), $bytes);
$msb = $bits & 7;
if ($msb) {
$min = chr(1 << ($msb - 1)) . $min;
$max = chr((1 << $msb) - 1) . $max;
} else {
$min[0] = chr(0x80);
}
return array(
'min' => new Math_BigInteger($min, 256),
'max' => new Math_BigInteger($max, 256)
);
}
/**
* DER-decode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information.
*
* @access private
* @param String $string
* @return Integer
*/
function _decodeLength(&$string)
{
$length = ord($this->_string_shift($string));
if ( $length & 0x80 ) { // definite length, long form
$length&= 0x7F;
$temp = $this->_string_shift($string, $length);
list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
}
return $length;
}
/**
* DER-encode the length
*
* DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
* {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 § 8.1.3} for more information.
*
* @access private
* @param Integer $length
* @return String
*/
function _encodeLength($length)
{
if ($length <= 0x7F) {
return chr($length);
}
$temp = ltrim(pack('N', $length), chr(0));
return pack('Ca*', 0x80 | strlen($temp), $temp);
}
/**
* String Shift
*
* Inspired by array_shift
*
* @param String $string
* @param optional Integer $index
* @return String
* @access private
*/
function _string_shift(&$string, $index = 1)
{
$substr = substr($string, 0, $index);
$string = substr($string, $index);
return $substr;
}
/**
* Determines the private key format
*
* @see createKey()
* @access public
* @param Integer $format
*/
function setPrivateKeyFormat($format)
{
$this->privateKeyFormat = $format;
}
/**
* Determines the public key format
*
* @see createKey()
* @access public
* @param Integer $format
*/
function setPublicKeyFormat($format)
{
$this->publicKeyFormat = $format;
}
/**
* Determines which hashing function should be used
*
* Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
* decryption. If $hash isn't supported, sha1 is used.
*
* @access public
* @param String $hash
*/
function setHash($hash)
{
// Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->hash = new Crypt_Hash($hash);
$this->hashName = $hash;
break;
default:
$this->hash = new Crypt_Hash('sha1');
$this->hashName = 'sha1';
}
$this->hLen = $this->hash->getLength();
}
/**
* Determines which hashing function should be used for the mask generation function
*
* The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
* best if Hash and MGFHash are set to the same thing this is not a requirement.
*
* @access public
* @param String $hash
*/
function setMGFHash($hash)
{
// Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
switch ($hash) {
case 'md2':
case 'md5':
case 'sha1':
case 'sha256':
case 'sha384':
case 'sha512':
$this->mgfHash = new Crypt_Hash($hash);
break;
default:
$this->mgfHash = new Crypt_Hash('sha1');
}
$this->mgfHLen = $this->mgfHash->getLength();
}
/**
* Determines the salt length
*
* To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
*
* Typical salt lengths in octets are hLen (the length of the output
* of the hash function Hash) and 0.
*
* @access public
* @param Integer $format
*/
function setSaltLength($sLen)
{
$this->sLen = $sLen;
}
/**
* Generates a random string x bytes long
*
* @access public
* @param Integer $bytes
* @param optional Integer $nonzero
* @return String
*/
function _random($bytes, $nonzero = false)
{
$temp = '';
if ($nonzero) {
for ($i = 0; $i < $bytes; $i++) {
$temp.= chr(crypt_random(1, 255));
}
} else {
$ints = ($bytes + 1) >> 2;
for ($i = 0; $i < $ints; $i++) {
$temp.= pack('N', crypt_random());
}
$temp = substr($temp, 0, $bytes);
}
return $temp;
}
/**
* Integer-to-Octet-String primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
*
* @access private
* @param Math_BigInteger $x
* @param Integer $xLen
* @return String
*/
function _i2osp($x, $xLen)
{
$x = $x->toBytes();
if (strlen($x) > $xLen) {
user_error('Integer too large', E_USER_NOTICE);
return false;
}
return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
}
/**
* Octet-String-to-Integer primitive
*
* See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
*
* @access private
* @param String $x
* @return Math_BigInteger
*/
function _os2ip($x)
{
return new Math_BigInteger($x, 256);
}
/**
* Exponentiate with or without Chinese Remainder Theorem
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
*
* @access private
* @param Math_BigInteger $x
* @return Math_BigInteger
*/
function _exponentiate($x)
{
if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
return $x->modPow($this->exponent, $this->modulus);
}
$num_primes = count($this->primes);
if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
$m_i = array(
1 => $x->modPow($this->exponents[1], $this->primes[1]),
2 => $x->modPow($this->exponents[2], $this->primes[2])
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
} else {
$smallest = $this->primes[1];
for ($i = 2; $i <= $num_primes; $i++) {
if ($smallest->compare($this->primes[$i]) > 0) {
$smallest = $this->primes[$i];
}
}
$one = new Math_BigInteger(1);
$one->setRandomGenerator('crypt_random');
$r = $one->random($one, $smallest->subtract($one));
$m_i = array(
1 => $this->_blind($x, $r, 1),
2 => $this->_blind($x, $r, 2)
);
$h = $m_i[1]->subtract($m_i[2]);
$h = $h->multiply($this->coefficients[2]);
list(, $h) = $h->divide($this->primes[1]);
$m = $m_i[2]->add($h->multiply($this->primes[2]));
$r = $this->primes[1];
for ($i = 3; $i <= $num_primes; $i++) {
$m_i = $this->_blind($x, $r, $i);
$r = $r->multiply($this->primes[$i - 1]);
$h = $m_i->subtract($m);
$h = $h->multiply($this->coefficients[$i]);
list(, $h) = $h->divide($this->primes[$i]);
$m = $m->add($r->multiply($h));
}
}
return $m;
}
/**
* Performs RSA Blinding
*
* Protects against timing attacks by employing RSA Blinding.
* Returns $x->modPow($this->exponents[$i], $this->primes[$i])
*
* @access private
* @param Math_BigInteger $x
* @param Math_BigInteger $r
* @param Integer $i
* @return Math_BigInteger
*/
function _blind($x, $r, $i)
{
$x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
$x = $x->modPow($this->exponents[$i], $this->primes[$i]);
$r = $r->modInverse($this->primes[$i]);
$x = $x->multiply($r);
list(, $x) = $x->divide($this->primes[$i]);
return $x;
}
/**
* RSAEP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
*
* @access private
* @param Math_BigInteger $m
* @return Math_BigInteger
*/
function _rsaep($m)
{
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
user_error('Message representative out of range', E_USER_NOTICE);
return false;
}
return $this->_exponentiate($m);
}
/**
* RSADP
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
*
* @access private
* @param Math_BigInteger $c
* @return Math_BigInteger
*/
function _rsadp($c)
{
if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
user_error('Ciphertext representative out of range', E_USER_NOTICE);
return false;
}
return $this->_exponentiate($c);
}
/**
* RSASP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
*
* @access private
* @param Math_BigInteger $m
* @return Math_BigInteger
*/
function _rsasp1($m)
{
if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
user_error('Message representative out of range', E_USER_NOTICE);
return false;
}
return $this->_exponentiate($m);
}
/**
* RSAVP1
*
* See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
*
* @access private
* @param Math_BigInteger $s
* @return Math_BigInteger
*/
function _rsavp1($s)
{
if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
user_error('Signature representative out of range', E_USER_NOTICE);
return false;
}
return $this->_exponentiate($s);
}
/**
* MGF1
*
* See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
*
* @access private
* @param String $mgfSeed
* @param Integer $mgfLen
* @return String
*/
function _mgf1($mgfSeed, $maskLen)
{
// if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
$t = '';
$count = ceil($maskLen / $this->mgfHLen);
for ($i = 0; $i < $count; $i++) {
$c = pack('N', $i);
$t.= $this->mgfHash->hash($mgfSeed . $c);
}
return substr($t, 0, $maskLen);
}
/**
* RSAES-OAEP-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
* {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
*
* @access private
* @param String $m
* @param String $l
* @return String
*/
function _rsaes_oaep_encrypt($m, $l = '')
{
$mLen = strlen($m);
// Length checking
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if ($mLen > $this->k - 2 * $this->hLen - 2) {
user_error('Message too long', E_USER_NOTICE);
return false;
}
// EME-OAEP encoding
$lHash = $this->hash->hash($l);
$ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
$db = $lHash . $ps . chr(1) . $m;
$seed = $this->_random($this->hLen);
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$maskedSeed = $seed ^ $seedMask;
$em = chr(0) . $maskedSeed . $maskedDB;
// RSA encryption
$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-OAEP-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
* messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
*
* Note. Care must be taken to ensure that an opponent cannot
* distinguish the different error conditions in Step 3.g, whether by
* error message or timing, or, more generally, learn partial
* information about the encoded message EM. Otherwise an opponent may
* be able to obtain useful information about the decryption of the
* ciphertext C, leading to a chosen-ciphertext attack such as the one
* observed by Manger [36].
*
* As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
*
* Both the encryption and the decryption operations of RSAES-OAEP take
* the value of a label L as input. In this version of PKCS #1, L is
* the empty string; other uses of the label are outside the scope of
* this document.
*
* @access private
* @param String $c
* @param String $l
* @return String
*/
function _rsaes_oaep_decrypt($c, $l = '')
{
// Length checking
// if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
user_error('Decryption error', E_USER_NOTICE);
return false;
}
// RSA decryption
$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
user_error('Decryption error', E_USER_NOTICE);
return false;
}
$em = $this->_i2osp($m, $this->k);
// EME-OAEP decoding
$lHash = $this->hash->hash($l);
$y = ord($em[0]);
$maskedSeed = substr($em, 1, $this->hLen);
$maskedDB = substr($em, $this->hLen + 1);
$seedMask = $this->_mgf1($maskedDB, $this->hLen);
$seed = $maskedSeed ^ $seedMask;
$dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$lHash2 = substr($db, 0, $this->hLen);
$m = substr($db, $this->hLen);
if ($lHash != $lHash2) {
user_error('Decryption error', E_USER_NOTICE);
return false;
}
$m = ltrim($m, chr(0));
if (ord($m[0]) != 1) {
user_error('Decryption error', E_USER_NOTICE);
return false;
}
// Output the message M
return substr($m, 1);
}
/**
* RSAES-PKCS1-V1_5-ENCRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
*
* @access private
* @param String $m
* @return String
*/
function _rsaes_pkcs1_v1_5_encrypt($m)
{
$mLen = strlen($m);
// Length checking
if ($mLen > $this->k - 11) {
user_error('Message too long', E_USER_NOTICE);
return false;
}
// EME-PKCS1-v1_5 encoding
$ps = $this->_random($this->k - $mLen - 3, true);
$em = chr(0) . chr(2) . $ps . chr(0) . $m;
// RSA encryption
$m = $this->_os2ip($em);
$c = $this->_rsaep($m);
$c = $this->_i2osp($c, $this->k);
// Output the ciphertext C
return $c;
}
/**
* RSAES-PKCS1-V1_5-DECRYPT
*
* See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
*
* For compatability purposes, this function departs slightly from the description given in RFC3447.
* The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
* private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
* public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
* to be 2 regardless of which key is used. for compatability purposes, we'll just check to make sure the
* second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
*
* As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
* with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
* not private key encrypted ciphertext's.
*
* @access private
* @param String $c
* @return String
*/
function _rsaes_pkcs1_v1_5_decrypt($c)
{
// Length checking
if (strlen($c) != $this->k) { // or if k < 11
user_error('Decryption error', E_USER_NOTICE);
return false;
}
// RSA decryption
$c = $this->_os2ip($c);
$m = $this->_rsadp($c);
if ($m === false) {
user_error('Decryption error', E_USER_NOTICE);
return false;
}
$em = $this->_i2osp($m, $this->k);
// EME-PKCS1-v1_5 decoding
if (ord($em[0]) != 0 || ord($em[1]) > 2) {
user_error('Decryption error', E_USER_NOTICE);
return false;
}
$ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
$m = substr($em, strlen($ps) + 3);
if (strlen($ps) < 8) {
user_error('Decryption error', E_USER_NOTICE);
return false;
}
// Output M
return $m;
}
/**
* EMSA-PSS-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
*
* @access private
* @param String $m
* @param Integer $emBits
*/
function _emsa_pss_encode($m, $emBits)
{
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
$sLen = $this->sLen == false ? $this->hLen : $this->sLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
user_error('Encoding error', E_USER_NOTICE);
return false;
}
$salt = $this->_random($sLen);
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h = $this->hash->hash($m2);
$ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
$db = $ps . chr(1) . $salt;
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$maskedDB = $db ^ $dbMask;
$maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
$em = $maskedDB . $h . chr(0xBC);
return $em;
}
/**
* EMSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
*
* @access private
* @param String $m
* @param String $em
* @param Integer $emBits
* @return String
*/
function _emsa_pss_verify($m, $em, $emBits)
{
// if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
// be output.
$emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
$sLen = $this->sLen == false ? $this->hLen : $this->sLen;
$mHash = $this->hash->hash($m);
if ($emLen < $this->hLen + $sLen + 2) {
return false;
}
if ($em[strlen($em) - 1] != chr(0xBC)) {
return false;
}
$maskedDB = substr($em, 0, $em - $this->hLen - 1);
$h = substr($em, $em - $this->hLen - 1, $this->hLen);
$temp = chr(0xFF << ($emBits & 7));
if ((~$maskedDB[0] & $temp) != $temp) {
return false;
}
$dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
$db = $maskedDB ^ $dbMask;
$db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
$temp = $emLen - $this->hLen - $sLen - 2;
if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
return false;
}
$salt = substr($db, $temp + 1); // should be $sLen long
$m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
$h2 = $this->hash->hash($m2);
return $h == $h2;
}
/**
* RSASSA-PSS-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
*
* @access private
* @param String $m
* @return String
*/
function _rsassa_pss_sign($m)
{
// EMSA-PSS encoding
$em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
// RSA signature
$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PSS-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
*
* @access private
* @param String $m
* @param String $s
* @return String
*/
function _rsassa_pss_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
user_error('Invalid signature', E_USER_NOTICE);
return false;
}
// RSA verification
$modBits = 8 * $this->k;
$s2 = $this->_os2ip($s);
$m2 = $this->_rsavp1($s2);
if ($m2 === false) {
user_error('Invalid signature', E_USER_NOTICE);
return false;
}
$em = $this->_i2osp($m2, $modBits >> 3);
if ($em === false) {
user_error('Invalid signature', E_USER_NOTICE);
return false;
}
// EMSA-PSS verification
return $this->_emsa_pss_verify($m, $em, $modBits - 1);
}
/**
* EMSA-PKCS1-V1_5-ENCODE
*
* See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
*
* @access private
* @param String $m
* @param Integer $emLen
* @return String
*/
function _emsa_pkcs1_v1_5_encode($m, $emLen)
{
$h = $this->hash->hash($m);
if ($h === false) {
return false;
}
// see http://tools.ietf.org/html/rfc3447#page-43
switch ($this->hashName) {
case 'md2':
$t = pack('H*', '3020300c06082a864886f70d020205000410');
break;
case 'md5':
$t = pack('H*', '3020300c06082a864886f70d020505000410');
break;
case 'sha1':
$t = pack('H*', '3021300906052b0e03021a05000414');
break;
case 'sha256':
$t = pack('H*', '3031300d060960864801650304020105000420');
break;
case 'sha384':
$t = pack('H*', '3041300d060960864801650304020205000430');
break;
case 'sha512':
$t = pack('H*', '3051300d060960864801650304020305000440');
}
$t.= $h;
$tLen = strlen($t);
if ($emLen < $tLen + 11) {
user_error('Intended encoded message length too short', E_USER_NOTICE);
return false;
}
$ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
$em = "\0\1$ps\0$t";
return $em;
}
/**
* RSASSA-PKCS1-V1_5-SIGN
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
*
* @access private
* @param String $m
* @return String
*/
function _rsassa_pkcs1_v1_5_sign($m)
{
// EMSA-PKCS1-v1_5 encoding
$em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
if ($em === false) {
user_error('RSA modulus too short', E_USER_NOTICE);
return false;
}
// RSA signature
$m = $this->_os2ip($em);
$s = $this->_rsasp1($m);
$s = $this->_i2osp($s, $this->k);
// Output the signature S
return $s;
}
/**
* RSASSA-PKCS1-V1_5-VERIFY
*
* See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
*
* @access private
* @param String $m
* @return String
*/
function _rsassa_pkcs1_v1_5_verify($m, $s)
{
// Length checking
if (strlen($s) != $this->k) {
user_error('Invalid signature', E_USER_NOTICE);
return false;
}
// RSA verification
$s = $this->_os2ip($s);
$m2 = $this->_rsavp1($s);
if ($m2 === false) {
user_error('Invalid signature', E_USER_NOTICE);
return false;
}
$em = $this->_i2osp($m2, $this->k);
if ($em === false) {
user_error('Invalid signature', E_USER_NOTICE);
return false;
}
// EMSA-PKCS1-v1_5 encoding
$em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
if ($em2 === false) {
user_error('RSA modulus too short', E_USER_NOTICE);
return false;
}
// Compare
return $em === $em2;
}
/**
* Set Encryption Mode
*
* Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
*
* @access public
* @param Integer $mode
*/
function setEncryptionMode($mode)
{
$this->encryptionMode = $mode;
}
/**
* Set Signature Mode
*
* Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
*
* @access public
* @param Integer $mode
*/
function setSignatureMode($mode)
{
$this->signatureMode = $mode;
}
/**
* Encryption
*
* Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
* If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
* be concatenated together.
*
* @see decrypt()
* @access public
* @param String $plaintext
* @return String
*/
function encrypt($plaintext)
{
switch ($this->encryptionMode) {
case CRYPT_RSA_ENCRYPTION_PKCS1:
$length = $this->k - 11;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
}
return $ciphertext;
//case CRYPT_RSA_ENCRYPTION_OAEP:
default:
$length = $this->k - 2 * $this->hLen - 2;
if ($length <= 0) {
return false;
}
$plaintext = str_split($plaintext, $length);
$ciphertext = '';
foreach ($plaintext as $m) {
$ciphertext.= $this->_rsaes_oaep_encrypt($m);
}
return $ciphertext;
}
}
/**
* Decryption
*
* @see encrypt()
* @access public
* @param String $plaintext
* @return String
*/
function decrypt($ciphertext)
{
if ($this->k <= 0) {
return false;
}
$ciphertext = str_split($ciphertext, $this->k);
$plaintext = '';
switch ($this->encryptionMode) {
case CRYPT_RSA_ENCRYPTION_PKCS1:
$decrypt = '_rsaes_pkcs1_v1_5_decrypt';
break;
//case CRYPT_RSA_ENCRYPTION_OAEP:
default:
$decrypt = '_rsaes_oaep_decrypt';
}
foreach ($ciphertext as $c) {
$temp = $this->$decrypt($c);
if ($temp === false) {
return false;
}
$plaintext.= $temp;
}
return $plaintext;
}
/**
* Create a signature
*
* @see verify()
* @access public
* @param String $message
* @return String
*/
function sign($message)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case CRYPT_RSA_SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_sign($message);
//case CRYPT_RSA_SIGNATURE_PSS:
default:
return $this->_rsassa_pss_sign($message);
}
}
/**
* Verifies a signature
*
* @see sign()
* @access public
* @param String $message
* @param String $signature
* @return Boolean
*/
function verify($message, $signature)
{
if (empty($this->modulus) || empty($this->exponent)) {
return false;
}
switch ($this->signatureMode) {
case CRYPT_RSA_SIGNATURE_PKCS1:
return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
//case CRYPT_RSA_SIGNATURE_PSS:
default:
return $this->_rsassa_pss_verify($message, $signature);
}
}
} debian/db-update/v20111210/Crypt/Random.php 0000644 0000000 0000000 00000011165 12267045571 015157 0 ustar
*
*
*
* LICENSE: This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
* @category Crypt
* @package Crypt_Random
* @author Jim Wigginton
* @copyright MMVII Jim Wigginton
* @license http://www.gnu.org/licenses/lgpl.txt
* @version $Id: Random.php,v 1.9 2010/04/24 06:40:48 terrafrost Exp $
* @link http://phpseclib.sourceforge.net
*/
/**
* Generate a random value.
*
* On 32-bit machines, the largest distance that can exist between $min and $max is 2**31.
* If $min and $max are farther apart than that then the last ($max - range) numbers.
*
* Depending on how this is being used, it may be worth while to write a replacement. For example,
* a PHP-based web app that stores its data in an SQL database can collect more entropy than this function
* can.
*
* @param optional Integer $min
* @param optional Integer $max
* @return Integer
* @access public
*/
function crypt_random($min = 0, $max = 0x7FFFFFFF)
{
if ($min == $max) {
return $min;
}
// see http://en.wikipedia.org/wiki//dev/random
// if open_basedir is enabled file_exists() will ouput an "open_basedir restriction in effect" warning,
// so we suppress it.
if (@file_exists('/dev/urandom')) {
static $fp;
if (!$fp) {
$fp = fopen('/dev/urandom', 'rb');
}
extract(unpack('Nrandom', fread($fp, 4)));
// say $min = 0 and $max = 3. if we didn't do abs() then we could have stuff like this:
// -4 % 3 + 0 = -1, even though -1 < $min
return abs($random) % ($max - $min) + $min;
}
/* Prior to PHP 4.2.0, mt_srand() had to be called before mt_rand() could be called.
Prior to PHP 5.2.6, mt_rand()'s automatic seeding was subpar, as elaborated here:
http://www.suspekt.org/2008/08/17/mt_srand-and-not-so-random-numbers/
The seeding routine is pretty much ripped from PHP's own internal GENERATE_SEED() macro:
http://svn.php.net/viewvc/php/php-src/branches/PHP_5_3_2/ext/standard/php_rand.h?view=markup */
if (version_compare(PHP_VERSION, '5.2.5', '<=')) {
static $seeded;
if (!isset($seeded)) {
$seeded = true;
mt_srand(fmod(time() * getmypid(), 0x7FFFFFFF) ^ fmod(1000000 * lcg_value(), 0x7FFFFFFF));
}
}
static $crypto;
// The CSPRNG's Yarrow and Fortuna periodically reseed. This function can be reseeded by hitting F5
// in the browser and reloading the page.
if (!isset($crypto)) {
$key = $iv = '';
for ($i = 0; $i < 8; $i++) {
$key.= pack('n', mt_rand(0, 0xFFFF));
$iv .= pack('n', mt_rand(0, 0xFFFF));
}
switch (true) {
case class_exists('Crypt_AES'):
$crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
break;
case class_exists('Crypt_TripleDES'):
$crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
break;
case class_exists('Crypt_DES'):
$crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
break;
case class_exists('Crypt_RC4'):
$crypto = new Crypt_RC4();
break;
default:
extract(unpack('Nrandom', pack('H*', sha1(mt_rand(0, 0x7FFFFFFF)))));
return abs($random) % ($max - $min) + $min;
}
$crypto->setKey($key);
$crypto->setIV($iv);
$crypto->enableContinuousBuffer();
}
extract(unpack('Nrandom', $crypto->encrypt("\0\0\0\0")));
return abs($random) % ($max - $min) + $min;
}
?> debian/db-update/v20111210.sh 0000755 0000000 0000000 00000000110 12267045571 012450 0 ustar #!/bin/sh
cd /usr/share/spotweb/db-update/v20111210
php upgrade-db.php
debian/dbsettings.inc.php 0000644 0000000 0000000 00000000461 12267045571 012627 0 ustar ' +
_d.title.replace('\'', '\\\'') + '