Merge develop into 20170321_-_frio-fbbrowser
Conflicts: view/theme/frio/js/filebrowser.js view/theme/frio/js/theme.js view/theme/frio/templates/filebrowser.tpl view/theme/frio/templates/jot.tpl
This commit is contained in:
commit
ee293f2ce2
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -51,3 +51,6 @@ nbproject
|
||||||
|
|
||||||
#ignore things from transifex-client
|
#ignore things from transifex-client
|
||||||
venv/
|
venv/
|
||||||
|
|
||||||
|
#ignore git projects in vendor
|
||||||
|
vendor/**/.git
|
||||||
|
|
|
@ -37,7 +37,7 @@ local .htaccess file
|
||||||
- PHP *command line* access with register_argc_argv set to true in the
|
- PHP *command line* access with register_argc_argv set to true in the
|
||||||
php.ini file [or see 'poormancron' in section 8]
|
php.ini file [or see 'poormancron' in section 8]
|
||||||
|
|
||||||
- curl, gd (with at least jpeg support), mysql, mbstring, mcrypt, and openssl extensions
|
- curl, gd (with at least jpeg support), mysql, mbstring and openssl extensions
|
||||||
|
|
||||||
- some form of email server or email gateway such that PHP mail() works
|
- some form of email server or email gateway such that PHP mail() works
|
||||||
|
|
||||||
|
|
4
boot.php
4
boot.php
|
@ -17,7 +17,7 @@
|
||||||
* easily as email does today.
|
* easily as email does today.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once('include/autoloader.php');
|
require_once(__DIR__ . DIRECTORY_SEPARATOR. 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php');
|
||||||
|
|
||||||
use \Friendica\Core\Config;
|
use \Friendica\Core\Config;
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ require_once('include/datetime.php');
|
||||||
require_once('include/pgettext.php');
|
require_once('include/pgettext.php');
|
||||||
require_once('include/nav.php');
|
require_once('include/nav.php');
|
||||||
require_once('include/cache.php');
|
require_once('include/cache.php');
|
||||||
require_once('library/Mobile_Detect/Mobile_Detect.php');
|
|
||||||
require_once('include/features.php');
|
require_once('include/features.php');
|
||||||
require_once('include/identity.php');
|
require_once('include/identity.php');
|
||||||
require_once('update.php');
|
require_once('update.php');
|
||||||
|
@ -648,7 +647,6 @@ class App {
|
||||||
set_include_path(
|
set_include_path(
|
||||||
'include' . PATH_SEPARATOR
|
'include' . PATH_SEPARATOR
|
||||||
. 'library' . PATH_SEPARATOR
|
. 'library' . PATH_SEPARATOR
|
||||||
. 'library/phpsec' . PATH_SEPARATOR
|
|
||||||
. 'library/langdet' . PATH_SEPARATOR
|
. 'library/langdet' . PATH_SEPARATOR
|
||||||
. '.' );
|
. '.' );
|
||||||
|
|
||||||
|
|
34
composer.json
Normal file
34
composer.json
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"name": "friendica/friendica",
|
||||||
|
"description": "A decentralized social network part of The Federation",
|
||||||
|
"type": "project",
|
||||||
|
"keywords": [
|
||||||
|
"social network",
|
||||||
|
"dfrn",
|
||||||
|
"ostatus",
|
||||||
|
"diaspora"
|
||||||
|
],
|
||||||
|
"licence": "GNU-Affero",
|
||||||
|
"support": {
|
||||||
|
"issues": "https://github.com/friendica/friendica/issues"
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"ezyang/htmlpurifier": "~4.7.0",
|
||||||
|
"mobiledetect/mobiledetectlib": "2.8.*"
|
||||||
|
},
|
||||||
|
"autoload": {
|
||||||
|
"psr-4": {
|
||||||
|
"Friendica\\": "src/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"config": {
|
||||||
|
"autoloader-suffix": "Friendica",
|
||||||
|
"optimize-autoloader": true,
|
||||||
|
"preferred-install": "dist"
|
||||||
|
},
|
||||||
|
"archive": {
|
||||||
|
"exclude": [
|
||||||
|
"log", "cache", "/photo", "/proxy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
114
composer.lock
generated
Normal file
114
composer.lock
generated
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
{
|
||||||
|
"_readme": [
|
||||||
|
"This file locks the dependencies of your project to a known state",
|
||||||
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||||
|
"This file is @generated automatically"
|
||||||
|
],
|
||||||
|
"content-hash": "49b00f1ed3192e5173bd5577a3b91ba2",
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"name": "ezyang/htmlpurifier",
|
||||||
|
"version": "v4.7.0",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/ezyang/htmlpurifier.git",
|
||||||
|
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/ezyang/htmlpurifier/zipball/ae1828d955112356f7677c465f94f7deb7d27a40",
|
||||||
|
"reference": "ae1828d955112356f7677c465f94f7deb7d27a40",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.2"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"psr-0": {
|
||||||
|
"HTMLPurifier": "library/"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"library/HTMLPurifier.composer.php"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"LGPL"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Edward Z. Yang",
|
||||||
|
"email": "admin@htmlpurifier.org",
|
||||||
|
"homepage": "http://ezyang.com"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Standards compliant HTML filter written in PHP",
|
||||||
|
"homepage": "http://htmlpurifier.org/",
|
||||||
|
"keywords": [
|
||||||
|
"html"
|
||||||
|
],
|
||||||
|
"time": "2015-08-05T01:03:42+00:00"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "mobiledetect/mobiledetectlib",
|
||||||
|
"version": "2.8.25",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/serbanghita/Mobile-Detect.git",
|
||||||
|
"reference": "f0896b5c7274d1450023b0b376240be902c3251c"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/f0896b5c7274d1450023b0b376240be902c3251c",
|
||||||
|
"reference": "f0896b5c7274d1450023b0b376240be902c3251c",
|
||||||
|
"shasum": ""
|
||||||
|
},
|
||||||
|
"require": {
|
||||||
|
"php": ">=5.0.0"
|
||||||
|
},
|
||||||
|
"require-dev": {
|
||||||
|
"phpunit/phpunit": "*"
|
||||||
|
},
|
||||||
|
"type": "library",
|
||||||
|
"autoload": {
|
||||||
|
"classmap": [
|
||||||
|
"Mobile_Detect.php"
|
||||||
|
],
|
||||||
|
"psr-0": {
|
||||||
|
"Detection": "namespaced/"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"notification-url": "https://packagist.org/downloads/",
|
||||||
|
"license": [
|
||||||
|
"MIT"
|
||||||
|
],
|
||||||
|
"authors": [
|
||||||
|
{
|
||||||
|
"name": "Serban Ghita",
|
||||||
|
"email": "serbanghita@gmail.com",
|
||||||
|
"homepage": "http://mobiledetect.net",
|
||||||
|
"role": "Developer"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.",
|
||||||
|
"homepage": "https://github.com/serbanghita/Mobile-Detect",
|
||||||
|
"keywords": [
|
||||||
|
"detect mobile devices",
|
||||||
|
"mobile",
|
||||||
|
"mobile detect",
|
||||||
|
"mobile detector",
|
||||||
|
"php mobile detect"
|
||||||
|
],
|
||||||
|
"time": "2017-03-29T13:59:30+00:00"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"packages-dev": [],
|
||||||
|
"aliases": [],
|
||||||
|
"minimum-stability": "stable",
|
||||||
|
"stability-flags": [],
|
||||||
|
"prefer-stable": false,
|
||||||
|
"prefer-lowest": false,
|
||||||
|
"platform": [],
|
||||||
|
"platform-dev": []
|
||||||
|
}
|
98
doc/Composer.md
Normal file
98
doc/Composer.md
Normal file
|
@ -0,0 +1,98 @@
|
||||||
|
Using Composer
|
||||||
|
==============
|
||||||
|
|
||||||
|
* [Home](help)
|
||||||
|
* [Developer Intro](help/Developers-Intro)
|
||||||
|
|
||||||
|
Friendica uses [Composer](https://getcomposer.org) to manage dependencies libraries and the class autoloader both for libraries and namespaced Friendica classes.
|
||||||
|
|
||||||
|
It's a command-line tool that downloads required libraries into the `vendor` folder and makes any namespaced class in `src` available through the whole application through `boot.php`.
|
||||||
|
|
||||||
|
* [Class autoloading](help/autoloader)
|
||||||
|
|
||||||
|
## How to use Composer
|
||||||
|
|
||||||
|
If you don't have Composer installed on your system, Friendica ships with a copy of it at `util/composer.phar`.
|
||||||
|
For the purpose of this help, all examples will use this path to run Composer commands, however feel free to replace them with your own way of calling Composer.
|
||||||
|
Composer requires PHP CLI and the following examples assume it's available system-wide.
|
||||||
|
|
||||||
|
### Installing/Updating Friendica
|
||||||
|
|
||||||
|
#### From Archive
|
||||||
|
|
||||||
|
If you just unpacked a Friendica release archive, you don't have to use Commposer at all, all the required libraries are already bundled in the archive.
|
||||||
|
|
||||||
|
#### Installing with Git
|
||||||
|
|
||||||
|
If you prefer to use `git`, you will have to run Composer to fetch the required libraries and build the autoloader before you can run Friendica.
|
||||||
|
Here are the typical commands you will have to run to do so:
|
||||||
|
|
||||||
|
````
|
||||||
|
~> git clone https://github.com/friendica/friendica.git friendica
|
||||||
|
~/friendica> cd friendica
|
||||||
|
~/friendica> util/composer.phar install
|
||||||
|
````
|
||||||
|
|
||||||
|
That's it! Composer will take care of fetching all the required libraries in the `vendor` folder and build the autoloader to make those libraries available to Friendica.
|
||||||
|
|
||||||
|
#### Updating with Git
|
||||||
|
|
||||||
|
Updating Friendica to the current stable or the latest develop version is easy with Git, just remember to run Composer after every branch pull.
|
||||||
|
|
||||||
|
````
|
||||||
|
~> cd friendica
|
||||||
|
~/friendica> git pull
|
||||||
|
~/friendica> util/composer.phar install
|
||||||
|
````
|
||||||
|
|
||||||
|
And that's it. If any library used by Friendica has been upgraded, Composer will fetch the version currently used by Friendica and refresh the autoloader to ensure the best performances.
|
||||||
|
|
||||||
|
### Developing Friendica
|
||||||
|
|
||||||
|
First of all, thanks for contributing to Friendica!
|
||||||
|
Composer is meant to be used by developers to maintain third-party libraries required by Friendica.
|
||||||
|
If you don't need to use any third-party library, then you don't need to use Composer beyond what is above to install/update Friendica.
|
||||||
|
|
||||||
|
#### Adding a third-party library to Friendica
|
||||||
|
|
||||||
|
Does your shiny new [Plugin](help/Plugins) need to rely on a third-party library not required by Friendica yet?
|
||||||
|
First of all, this library should be available on [Packagist](https://packagist.org) so that Composer knows how to fetch it directly just by mentioning its name in `composer.json`.
|
||||||
|
|
||||||
|
This file is the configuration of Friendica for Composer. It lists details about the Friendica project, but also a list of required dependencies and their target version.
|
||||||
|
Here's a simplified version of the one we currently use on Friendica:
|
||||||
|
|
||||||
|
````json
|
||||||
|
{
|
||||||
|
"name": "friendica/friendica",
|
||||||
|
"description": "A decentralized social network part of The Federation",
|
||||||
|
"type": "project",
|
||||||
|
...
|
||||||
|
"require": {
|
||||||
|
"ezyang/htmlpurifier": "~4.7.0",
|
||||||
|
"mobiledetect/mobiledetectlib": "2.8.*"
|
||||||
|
},
|
||||||
|
...
|
||||||
|
}
|
||||||
|
````
|
||||||
|
|
||||||
|
The important part is under the `require` key, this is a list of all the libraries Friendica may need to run.
|
||||||
|
As you can see, at the moment we only require two, HTMLPurifier and MobileDetect.
|
||||||
|
Each library has a different target version, and [per Composer documentation about version constraints](https://getcomposer.org/doc/articles/versions.md#writing-basic-version-constraints), this means that:
|
||||||
|
|
||||||
|
* We will update HTMLPurifier up to version 4.8.0 excluded
|
||||||
|
* We will update MobileDetect up to version 2.9.0 excluded
|
||||||
|
|
||||||
|
There are other operators you can use to allow Composer to update the package up to the next major version excluded.
|
||||||
|
Or you can specify the exact version of the library if you code requires it, and Composer will never update it although it isn't recommended.
|
||||||
|
|
||||||
|
To add a library, just add its Packagist identifier to the `require` list and set a target version string.
|
||||||
|
|
||||||
|
Then you should run `util/composer.phar update` to add it to your local `vendor` folder and update the `composer.lock` file that specifies the current versions of the dependencies.
|
||||||
|
|
||||||
|
#### Updating an existing dependency
|
||||||
|
|
||||||
|
If a package needs to be updated, whether to the next minor version or to the next major version provided you changed the adequate code in Friendica, simply edit `composer.json` to update the target version string of the relevant library.
|
||||||
|
|
||||||
|
Then you should run `util/composer.phar update` to update it in your local `vendor` folder and update the `composer.lock` file that specifies the current versions of the dependencies.
|
||||||
|
|
||||||
|
Please note that you should commit both `composer.json` and `composer.lock` with your work every time you make a change to the former.
|
|
@ -6,8 +6,7 @@ Where to get started to help improve Friendica?
|
||||||
Do you want to help us improve Friendica?
|
Do you want to help us improve Friendica?
|
||||||
Here we have compiled some hints on how to get started and some tasks to help you choose.
|
Here we have compiled some hints on how to get started and some tasks to help you choose.
|
||||||
A project like Friendica is the sum of many different contributions.
|
A project like Friendica is the sum of many different contributions.
|
||||||
**Very different skills are required to make good software.
|
**Very different skills are required to make good software, not all of them involve coding!**
|
||||||
Some of them involve coding, others do not.**
|
|
||||||
We are looking for helpers in all areas, whether you write text or code, whether you spread the word to convince people or design new icons.
|
We are looking for helpers in all areas, whether you write text or code, whether you spread the word to convince people or design new icons.
|
||||||
Whether you feel like an expert or like a newbie - join us with your ideas!
|
Whether you feel like an expert or like a newbie - join us with your ideas!
|
||||||
|
|
||||||
|
@ -47,6 +46,14 @@ We can't promise we have the right skills in the group but we'll try.
|
||||||
Programming
|
Programming
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### Composer
|
||||||
|
|
||||||
|
Friendica uses [Composer](https://getcomposer.org) to manage dependencies libraries and the class autoloader both for libraries and namespaced Friendica classes.
|
||||||
|
|
||||||
|
It's a command-line tool that downloads required libraries into the `vendor` folder and makes any namespaced class in `src` available through the whole application through `boot.php`.
|
||||||
|
|
||||||
|
* [Using Composer](help/Composer)
|
||||||
|
|
||||||
###Coding standards
|
###Coding standards
|
||||||
|
|
||||||
For the sake of consistency between contribution and general code readability, Friendica follows the widespread [PSR-2 coding standards](http://www.php-fig.org/psr/psr-2/) to the exception of a few rules.
|
For the sake of consistency between contribution and general code readability, Friendica follows the widespread [PSR-2 coding standards](http://www.php-fig.org/psr/psr-2/) to the exception of a few rules.
|
||||||
|
@ -120,6 +127,7 @@ Ask us to find out whom to talk to about their experiences.
|
||||||
Do not worry about cross-posting.
|
Do not worry about cross-posting.
|
||||||
|
|
||||||
###Client software
|
###Client software
|
||||||
|
|
||||||
As Friendica is using a [Twitter/GNU Social compatible API](help/api) any of the clients for those platforms should work with Friendica as well.
|
As Friendica is using a [Twitter/GNU Social compatible API](help/api) any of the clients for those platforms should work with Friendica as well.
|
||||||
Furthermore there are several client projects, especially for use with Friendica.
|
Furthermore there are several client projects, especially for use with Friendica.
|
||||||
If you are interested in improving those clients, please contact the developers of the clients directly.
|
If you are interested in improving those clients, please contact the developers of the clients directly.
|
||||||
|
|
|
@ -47,6 +47,7 @@ Friendica Documentation and Resources
|
||||||
* [Protocol Documentation](help/Protocol)
|
* [Protocol Documentation](help/Protocol)
|
||||||
* [Database schema documantation](help/database)
|
* [Database schema documantation](help/database)
|
||||||
* [Class Autoloading](help/autoloader)
|
* [Class Autoloading](help/autoloader)
|
||||||
|
* [Using Composer](help/Composer)
|
||||||
* [Code - Reference(Doxygen generated - sets cookies)](doc/html/)
|
* [Code - Reference(Doxygen generated - sets cookies)](doc/html/)
|
||||||
* [Twitter/GNU Social API Functions](help/api)
|
* [Twitter/GNU Social API Functions](help/api)
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,6 @@ Requirements
|
||||||
* PHP *command line* access with register_argc_argv set to true in the php.ini file
|
* PHP *command line* access with register_argc_argv set to true in the php.ini file
|
||||||
* curl, gd, mysql, hash and openssl extensions
|
* curl, gd, mysql, hash and openssl extensions
|
||||||
* some form of email server or email gateway such that PHP mail() works
|
* some form of email server or email gateway such that PHP mail() works
|
||||||
* mcrypt (optional; used for server-to-server message encryption)
|
|
||||||
* Mysql 5.5.3+ or an equivalant alternative for MySQL (MariaDB, Percona Server etc.)
|
* Mysql 5.5.3+ or an equivalant alternative for MySQL (MariaDB, Percona Server etc.)
|
||||||
* the ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks (Windows) (Note: other options are presented in Section 7 of this document.)
|
* the ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks (Windows) (Note: other options are presented in Section 7 of this document.)
|
||||||
* Installation into a top-level domain or sub-domain (without a directory/path component in the URL) is preferred. Directory paths will not be as convenient to use and have not been thoroughly tested.
|
* Installation into a top-level domain or sub-domain (without a directory/path component in the URL) is preferred. Directory paths will not be as convenient to use and have not been thoroughly tested.
|
||||||
|
|
|
@ -152,13 +152,6 @@ Value is in seconds.
|
||||||
Default is 60 seconds.
|
Default is 60 seconds.
|
||||||
Set to 0 for unlimited (not recommended).
|
Set to 0 for unlimited (not recommended).
|
||||||
|
|
||||||
#### UTF-8 Regular Expressions
|
|
||||||
|
|
||||||
During registrations, full names are checked using UTF-8 regular expressions.
|
|
||||||
This requires PHP to have been compiled with a special setting to allow UTF-8 expressions.
|
|
||||||
If you are completely unable to register accounts, set no_utf to true.
|
|
||||||
The default is set to false (meaning UTF8 regular expressions are supported and working).
|
|
||||||
|
|
||||||
#### Verify SSL Certitificates
|
#### Verify SSL Certitificates
|
||||||
|
|
||||||
By default Friendica allows SSL communication between websites that have "self-signed" SSL certificates.
|
By default Friendica allows SSL communication between websites that have "self-signed" SSL certificates.
|
||||||
|
|
|
@ -1,35 +1,30 @@
|
||||||
Autoloader
|
Autoloader with Composer
|
||||||
==========
|
==========
|
||||||
|
|
||||||
* [Home](help)
|
* [Home](help)
|
||||||
|
* [Developer Intro](help/Developers-Intro)
|
||||||
|
|
||||||
There is some initial support to class autoloading in Friendica core.
|
Friendica uses [Composer](https://getcomposer.org) to manage dependencies libraries and the class autoloader both for libraries and namespaced Friendica classes.
|
||||||
|
|
||||||
The autoloader code is in `include/autoloader.php`.
|
It's a command-line tool that downloads required libraries into the `vendor` folder and makes any namespaced class in `src` available through the whole application through `boot.php`.
|
||||||
It's derived from composer autoloader code.
|
|
||||||
|
|
||||||
Namespaces and Classes are mapped to folders and files in `library/`,
|
* [Using Composer](help/Composer)
|
||||||
and the map must be updated by hand, because we don't use composer yet.
|
|
||||||
The mapping is defined by files in `include/autoloader/` folder.
|
|
||||||
|
|
||||||
Currently, only HTMLPurifier library is loaded using autoloader.
|
## A quick introduction to class autoloading
|
||||||
|
|
||||||
|
The autoloader dynamically includes the file defining a class when it is first referenced, either by instantiating an object or simply making sure that it is available, without the need to explicitly use "require_once".
|
||||||
|
|
||||||
## A quick introdution to class autoloading
|
Once it is set up you don't have to directly use it, you can directly use any class that is covered by the autoloader (currently `vendor` and `src`)
|
||||||
|
|
||||||
The autoloader it's a way for php to automagically include the file that define a class when the class is first used, without the need to use "require_once" every time.
|
Under the hood, Composer registers a callback with [`spl_autoload_register()`](http://php.net/manual/en/function.spl-autoload-register.php) that receives a class name as an argument and includes the corresponding class definition file.
|
||||||
|
For more info about PHP autoloading, please refer to the [official PHP documentation](http://php.net/manual/en/language.oop5.autoload.php).
|
||||||
|
|
||||||
Once is setup you don't have to use it in any way. You need a class? you use the class.
|
### Example
|
||||||
|
|
||||||
At his basic is a function passed to the "spl_autoload_register()" function, which receive as argument the class name the script want and is it job to include the correct php file where that class is defined.
|
Let's say you have a PHP file in `src/` that define a very useful class:
|
||||||
The best source for documentation is [php site](http://php.net/manual/en/language.oop5.autoload.php).
|
|
||||||
|
|
||||||
One example, based on fictional friendica code.
|
```php
|
||||||
|
// src/ItemsManager.php
|
||||||
Let's say you have a php file in "include/" that define a very useful class:
|
|
||||||
|
|
||||||
```
|
|
||||||
file: include/ItemsManager.php
|
|
||||||
<?php
|
<?php
|
||||||
namespace \Friendica;
|
namespace \Friendica;
|
||||||
|
|
||||||
|
@ -39,33 +34,16 @@ Let's say you have a php file in "include/" that define a very useful class:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The class "ItemsManager" has been declared in "Friendica" namespace.
|
The class `ItemsManager` has been declared in the `Friendica` namespace.
|
||||||
Namespaces are useful to keep things separated and avoid names clash (could be that a library you want to use defines a class named "ItemsManager", but as long as is in another namespace, you don't have any problem)
|
Namespaces are useful to keep classes separated and avoid names conflicts (could be that a library you want to use also defines a class named `ItemsManager`, but as long as it is in another namespace, you don't have any problem)
|
||||||
|
|
||||||
If we were using composer, we had configured it with path where to find the classes of "Friendica" namespace, and then the composer script will generate the autoloader machinery for us.
|
Let's say now that you need to load some items in a view, maybe in a fictional `mod/network.php`.
|
||||||
As we don't use composer, we need check that the autoloader knows the Friendica namespace.
|
In order for the Composer autoloader to work, it must first be included. In Friendica this is already done at the top of `boot.php`, with `require_once('vendor/autoload.php');`.
|
||||||
So in "include/autoloader/autoload_psr4.php" there should be something like
|
|
||||||
|
|
||||||
```
|
|
||||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
return array(
|
|
||||||
"Friendica" => array($baseDir."/include");
|
|
||||||
);
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
That tells the autoloader code to look for files that defines classes in "Friendica" namespace under "include/" folder. (And btw, that's why the file has the same name as the class it defines.)
|
|
||||||
|
|
||||||
*note*: The structure of files in "include/autoloader/" has been copied from the code generated by composer, to ease the work of enable autoloader for external libraries under "library/"
|
|
||||||
|
|
||||||
Let's say now that you need to load some items in a view, maybe in a fictional "mod/network.php".
|
|
||||||
Somewere at the start of the scripts, the autoloader was initialized. In Friendica is done at the top of "boot.php", with "require_once('include/autoloader.php');".
|
|
||||||
|
|
||||||
The code will be something like:
|
The code will be something like:
|
||||||
|
|
||||||
```
|
```php
|
||||||
file: mod/network.php
|
// mod/network.php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
function network_content(App $a) {
|
function network_content(App $a) {
|
||||||
|
@ -77,13 +55,13 @@ The code will be something like:
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
That's a quite simple example, but look: no "require()"!
|
That's a quite simple example, but look: no `require()`!
|
||||||
You need to use a class, you use the class and you don't need to do anything more.
|
If you need to use a class, you can simply use it and you don't need to do anything else.
|
||||||
|
|
||||||
Going further: now we have a bunch of "*Manager" classes that cause some code duplication, let's define a BaseManager class, where to move all code in common between all managers:
|
Going further: now we have a bunch of `*Manager` classes that cause some code duplication, let's define a `BaseManager` class, where we move all common code between all managers:
|
||||||
|
|
||||||
```
|
```php
|
||||||
file: include/BaseManager.php
|
// src/BaseManager.php
|
||||||
<?php
|
<?php
|
||||||
namespace \Friendica;
|
namespace \Friendica;
|
||||||
|
|
||||||
|
@ -94,8 +72,8 @@ Going further: now we have a bunch of "*Manager" classes that cause some code du
|
||||||
|
|
||||||
and then let's change the ItemsManager class to use this code
|
and then let's change the ItemsManager class to use this code
|
||||||
|
|
||||||
```
|
```php
|
||||||
file: include/ItemsManager.php
|
// src/ItemsManager.php
|
||||||
<?php
|
<?php
|
||||||
namespace \Friendica;
|
namespace \Friendica;
|
||||||
|
|
||||||
|
@ -105,21 +83,21 @@ and then let's change the ItemsManager class to use this code
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The autoloader don't mind what you need the class for. You need a class, you get the class.
|
Even though we didn't explicitly include the `src/BaseManager.php` file, the autoloader will when this class is first defined, because it is referenced as a parent class.
|
||||||
It works with the "BaseManager" example here, it works when we need to call static methods on a class:
|
It works with the "BaseManager" example here and it works when we need to call static methods:
|
||||||
|
|
||||||
```
|
```php
|
||||||
file: include/dfrn.php
|
// src/Dfrn.php
|
||||||
<?php
|
<?php
|
||||||
namespace \Friendica;
|
namespace \Friendica;
|
||||||
|
|
||||||
class dfrn {
|
class Dfrn {
|
||||||
public static function mail($item, $owner) { ... }
|
public static function mail($item, $owner) { ... }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
```
|
```php
|
||||||
file: mod/mail.php
|
// mod/mail.php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
mail_post($a){
|
mail_post($a){
|
||||||
|
@ -131,8 +109,8 @@ It works with the "BaseManager" example here, it works when we need to call stat
|
||||||
|
|
||||||
If your code is in same namespace as the class you need, you don't need to prepend it:
|
If your code is in same namespace as the class you need, you don't need to prepend it:
|
||||||
|
|
||||||
```
|
```php
|
||||||
file: include/delivery.php
|
// include/delivery.php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace \Friendica;
|
namespace \Friendica;
|
||||||
|
@ -142,24 +120,25 @@ If your code is in same namespace as the class you need, you don't need to prepe
|
||||||
|
|
||||||
[...]
|
[...]
|
||||||
switch($contact['network']) {
|
switch($contact['network']) {
|
||||||
|
|
||||||
case NETWORK_DFRN:
|
case NETWORK_DFRN:
|
||||||
if ($mail) {
|
if ($mail) {
|
||||||
$item['body'] = ...
|
$item['body'] = ...
|
||||||
$atom = dfrn::mail($item, $owner);
|
$atom = Dfrn::mail($item, $owner);
|
||||||
} elseif ($fsuggest) {
|
} elseif ($fsuggest) {
|
||||||
$atom = dfrn::fsuggest($item, $owner);
|
$atom = Dfrn::fsuggest($item, $owner);
|
||||||
q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
|
q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1", intval($item['id']));
|
||||||
} elseif ($relocate)
|
} elseif ($relocate)
|
||||||
$atom = dfrn::relocate($owner, $uid);
|
$atom = Dfrn::relocate($owner, $uid);
|
||||||
[...]
|
[...]
|
||||||
```
|
```
|
||||||
|
|
||||||
This is real "include/delivery.php" unchanged, but as the code is declared to be in "Friendica" namespace, you don't need to write it when you need to use the "dfrn" class.
|
This is the current code of `include/delivery.php`, and since the code is declared to be in the "Friendica" namespace, you don't need to write it when you need to use the "Dfrn" class.
|
||||||
But if you want to use classes from another library, you need to use the full namespace, e.g.
|
But if you want to use classes from another library, you need to use the full namespace, e.g.
|
||||||
|
|
||||||
```
|
```php
|
||||||
|
// src/Diaspora.php
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
namespace \Friendica;
|
namespace \Friendica;
|
||||||
|
|
||||||
class Diaspora {
|
class Diaspora {
|
||||||
|
@ -169,9 +148,10 @@ But if you want to use classes from another library, you need to use the full na
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
if you use that class in many places of the code and you don't want to write the full path to the class everytime, you can use the "use" php keyword
|
if you use that class in many places of the code and you don't want to write the full path to the class every time, you can use the "use" PHP keyword
|
||||||
|
|
||||||
```
|
```php
|
||||||
|
// src/Diaspora.php
|
||||||
<?php
|
<?php
|
||||||
namespace \Friendica;
|
namespace \Friendica;
|
||||||
|
|
||||||
|
@ -185,25 +165,28 @@ if you use that class in many places of the code and you don't want to write the
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.
|
Note that namespaces are like paths in filesystem, separated by "\", with the first "\" being the global scope.
|
||||||
You can go more deep if you want to, like:
|
You can go deeper if you want to, like:
|
||||||
|
|
||||||
```
|
```
|
||||||
|
// src/Network/Dfrn.php
|
||||||
<?php
|
<?php
|
||||||
namespace \Friendica\Network;
|
namespace \Friendica\Network;
|
||||||
|
|
||||||
class DFRN {
|
class Dfrn {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Please note that the location of the file defining the class must be placed in the appropriate sub-folders of `src` if the namespace isn't plain `\Friendica`.
|
||||||
|
|
||||||
or
|
or
|
||||||
|
|
||||||
```
|
```
|
||||||
|
// src/Dba/Mysql
|
||||||
<?php
|
<?php
|
||||||
namespace \Friendica\DBA;
|
namespace \Friendica\Dba;
|
||||||
|
|
||||||
class MySQL {
|
class Mysql {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
So you can think of namespaces as folders in a unix filesystem, with global scope as the root ("\").
|
So you can think of namespaces as folders in a Unix file system, with global scope as the root ("\").
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,8 @@ Friendica - Dokumentation und Ressourcen
|
||||||
* [Smarty 3 Templates](help/smarty3-templates)
|
* [Smarty 3 Templates](help/smarty3-templates)
|
||||||
* [Protokoll Dokumentation](help/Protocol) (EN)
|
* [Protokoll Dokumentation](help/Protocol) (EN)
|
||||||
* [Datenbank-Schema](help/database)
|
* [Datenbank-Schema](help/database)
|
||||||
|
* [Class Autoloading](help/autoloader) (EN)
|
||||||
|
* [Using Composer](help/Composer) (EN)
|
||||||
* [Code-Referenz (mit doxygen generiert - setzt Cookies)](doc/html/)
|
* [Code-Referenz (mit doxygen generiert - setzt Cookies)](doc/html/)
|
||||||
* [Twitter/GNU Social API Functions](help/api) (EN)
|
* [Twitter/GNU Social API Functions](help/api) (EN)
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ Wir planen, diese Einschränkung in einer zukünftigen Version zu beheben.
|
||||||
- PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
|
- PHP *Kommandozeilen*-Zugang mit register_argc_argv auf "true" gesetzt in der php.ini-Datei
|
||||||
- curl, gd, mysql und openssl-Erweiterung
|
- curl, gd, mysql und openssl-Erweiterung
|
||||||
- etwas in der Art eines Email-Servers oder eines Gateways wie PHP mail()
|
- etwas in der Art eines Email-Servers oder eines Gateways wie PHP mail()
|
||||||
- mcrypt (optional; wird für die Server-zu-Server Nachrichtenentschlüsselung benötigt)
|
|
||||||
- Mysql 5.x
|
- Mysql 5.x
|
||||||
- die Möglichkeit, wiederkehrende Aufgaben mit cron (Linux/Mac) oder "Scheduled Tasks" einzustellen (Windows) [Beachte: andere Optionen sind in Abschnitt 7 dieser Dokumentation zu finden]
|
- die Möglichkeit, wiederkehrende Aufgaben mit cron (Linux/Mac) oder "Scheduled Tasks" einzustellen (Windows) [Beachte: andere Optionen sind in Abschnitt 7 dieser Dokumentation zu finden]
|
||||||
- Installation in einer Top-Level-Domain oder Subdomain (ohne eine Verzeichnis/Pfad-Komponente in der URL) wird bevorzugt. Verzeichnispfade sind für diesen Zweck nicht so günstig und wurden auch nicht ausführlich getestet.
|
- Installation in einer Top-Level-Domain oder Subdomain (ohne eine Verzeichnis/Pfad-Komponente in der URL) wird bevorzugt. Verzeichnispfade sind für diesen Zweck nicht so günstig und wurden auch nicht ausführlich getestet.
|
||||||
|
@ -37,7 +36,7 @@ Wir planen, diese Einschränkung in einer zukünftigen Version zu beheben.
|
||||||
1.1. APT-Pakete
|
1.1. APT-Pakete
|
||||||
- Apache: sudo apt-get install apache2
|
- Apache: sudo apt-get install apache2
|
||||||
- PHP5: sudo apt-get install php5
|
- PHP5: sudo apt-get install php5
|
||||||
- PHP5-Zusätzliche Pakete: sudo apt-get install php5-curl php5-gd php5-mysql php5-mcrypt
|
- PHP5-Zusätzliche Pakete: sudo apt-get install php5-curl php5-gd php5-mysql
|
||||||
- MySQL: sudo apt-get install mysql-server
|
- MySQL: sudo apt-get install mysql-server
|
||||||
|
|
||||||
2. Entpacke die Friendica-Daten in das Quellverzeichnis (root) des Dokumentenbereichs deines Webservers.
|
2. Entpacke die Friendica-Daten in das Quellverzeichnis (root) des Dokumentenbereichs deines Webservers.
|
||||||
|
|
|
@ -65,9 +65,8 @@ $a->config['php_path'] = 'php';
|
||||||
$a->config['system']['huburl'] = '[internal]';
|
$a->config['system']['huburl'] = '[internal]';
|
||||||
|
|
||||||
// Server-to-server private message encryption (RINO) is allowed by default.
|
// Server-to-server private message encryption (RINO) is allowed by default.
|
||||||
// Encryption will only be provided if this setting is set to a non zero
|
// Encryption will only be provided if this setting is set to a non zero value
|
||||||
// value and the PHP mcrypt extension is installed on both systems
|
// set to 0 to disable, 2 to enable, 1 is deprecated
|
||||||
// set to 0 to disable, 2 to enable, 1 is deprecated but wont need mcrypt
|
|
||||||
|
|
||||||
$a->config['system']['rino_encrypt'] = 2;
|
$a->config['system']['rino_encrypt'] = 2;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,8 @@ require_once('include/network.php');
|
||||||
*/
|
*/
|
||||||
class Probe {
|
class Probe {
|
||||||
|
|
||||||
|
private static $baseurl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Rearrange the array so that it always has the same order
|
* @brief Rearrange the array so that it always has the same order
|
||||||
*
|
*
|
||||||
|
@ -54,6 +56,9 @@ class Probe {
|
||||||
*/
|
*/
|
||||||
private function xrd($host) {
|
private function xrd($host) {
|
||||||
|
|
||||||
|
// Reset the static variable
|
||||||
|
self::$baseurl = '';
|
||||||
|
|
||||||
$ssl_url = "https://".$host."/.well-known/host-meta";
|
$ssl_url = "https://".$host."/.well-known/host-meta";
|
||||||
$url = "http://".$host."/.well-known/host-meta";
|
$url = "http://".$host."/.well-known/host-meta";
|
||||||
|
|
||||||
|
@ -102,6 +107,9 @@ class Probe {
|
||||||
elseif ($attributes["rel"] == "lrdd")
|
elseif ($attributes["rel"] == "lrdd")
|
||||||
$xrd_data["lrdd"] = $attributes["template"];
|
$xrd_data["lrdd"] = $attributes["template"];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self::$baseurl = "http://".$host;
|
||||||
|
|
||||||
return $xrd_data;
|
return $xrd_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,6 +177,8 @@ class Probe {
|
||||||
|
|
||||||
$path_parts = explode("/", trim($parts["path"], "/"));
|
$path_parts = explode("/", trim($parts["path"], "/"));
|
||||||
|
|
||||||
|
$nick = array_pop($path_parts);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$lrdd = self::xrd($host);
|
$lrdd = self::xrd($host);
|
||||||
$host .= "/".array_shift($path_parts);
|
$host .= "/".array_shift($path_parts);
|
||||||
|
@ -192,6 +202,19 @@ class Probe {
|
||||||
$path = str_replace('{uri}', urlencode("acct:".$uri), $link);
|
$path = str_replace('{uri}', urlencode("acct:".$uri), $link);
|
||||||
$webfinger = self::webfinger($path);
|
$webfinger = self::webfinger($path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Special treatment for Mastodon
|
||||||
|
// Problem is that Mastodon uses an URL format like http://domain.tld/@nick
|
||||||
|
// But the webfinger for this format fails.
|
||||||
|
if (!$webfinger AND isset($nick)) {
|
||||||
|
// Mastodon uses a "@" as prefix for usernames in their url format
|
||||||
|
$nick = ltrim($nick, '@');
|
||||||
|
|
||||||
|
$addr = $nick."@".$host;
|
||||||
|
|
||||||
|
$path = str_replace('{uri}', urlencode("acct:".$addr), $link);
|
||||||
|
$webfinger = self::webfinger($path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_array($webfinger["links"]))
|
if (!is_array($webfinger["links"]))
|
||||||
|
@ -258,8 +281,13 @@ class Probe {
|
||||||
$data['nick'] = trim(substr($data['nick'], 0, strpos($data['nick'], ' ')));
|
$data['nick'] = trim(substr($data['nick'], 0, strpos($data['nick'], ' ')));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isset($data["network"]))
|
if (self::$baseurl != "") {
|
||||||
|
$data["baseurl"] = self::$baseurl;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isset($data["network"])) {
|
||||||
$data["network"] = NETWORK_PHANTOM;
|
$data["network"] = NETWORK_PHANTOM;
|
||||||
|
}
|
||||||
|
|
||||||
$data = self::rearrange_data($data);
|
$data = self::rearrange_data($data);
|
||||||
|
|
||||||
|
@ -286,6 +314,7 @@ class Probe {
|
||||||
dbesc(normalise_link($data['url']))
|
dbesc(normalise_link($data['url']))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,7 +330,34 @@ class Probe {
|
||||||
* @return array uri data
|
* @return array uri data
|
||||||
*/
|
*/
|
||||||
private function detect($uri, $network, $uid) {
|
private function detect($uri, $network, $uid) {
|
||||||
if (strstr($uri, '@')) {
|
$parts = parse_url($uri);
|
||||||
|
|
||||||
|
if (isset($parts["scheme"]) AND isset($parts["host"]) AND isset($parts["path"])) {
|
||||||
|
|
||||||
|
/// @todo: Ports?
|
||||||
|
$host = $parts["host"];
|
||||||
|
|
||||||
|
if ($host == 'twitter.com') {
|
||||||
|
return array("network" => NETWORK_TWITTER);
|
||||||
|
}
|
||||||
|
$lrdd = self::xrd($host);
|
||||||
|
|
||||||
|
$path_parts = explode("/", trim($parts["path"], "/"));
|
||||||
|
|
||||||
|
while (!$lrdd AND (sizeof($path_parts) > 1)) {
|
||||||
|
$host .= "/".array_shift($path_parts);
|
||||||
|
$lrdd = self::xrd($host);
|
||||||
|
}
|
||||||
|
if (!$lrdd) {
|
||||||
|
return self::feed($uri);
|
||||||
|
}
|
||||||
|
$nick = array_pop($path_parts);
|
||||||
|
|
||||||
|
// Mastodon uses a "@" as prefix for usernames in their url format
|
||||||
|
$nick = ltrim($nick, '@');
|
||||||
|
|
||||||
|
$addr = $nick."@".$host;
|
||||||
|
} elseif (strstr($uri, '@')) {
|
||||||
// If the URI starts with "mailto:" then jump directly to the mail detection
|
// If the URI starts with "mailto:" then jump directly to the mail detection
|
||||||
if (strpos($url,'mailto:') !== false) {
|
if (strpos($url,'mailto:') !== false) {
|
||||||
$uri = str_replace('mailto:', '', $url);
|
$uri = str_replace('mailto:', '', $url);
|
||||||
|
@ -317,42 +373,19 @@ class Probe {
|
||||||
$host = substr($uri,strpos($uri, '@') + 1);
|
$host = substr($uri,strpos($uri, '@') + 1);
|
||||||
$nick = substr($uri,0, strpos($uri, '@'));
|
$nick = substr($uri,0, strpos($uri, '@'));
|
||||||
|
|
||||||
if (strpos($uri, '@twitter.com'))
|
if (strpos($uri, '@twitter.com')) {
|
||||||
return array("network" => NETWORK_TWITTER);
|
return array("network" => NETWORK_TWITTER);
|
||||||
|
}
|
||||||
$lrdd = self::xrd($host);
|
$lrdd = self::xrd($host);
|
||||||
|
|
||||||
if (!$lrdd)
|
if (!$lrdd) {
|
||||||
return self::mail($uri, $uid);
|
return self::mail($uri, $uid);
|
||||||
|
}
|
||||||
$addr = $uri;
|
$addr = $uri;
|
||||||
} else {
|
} else {
|
||||||
$parts = parse_url($uri);
|
|
||||||
if (!isset($parts["scheme"]) OR
|
|
||||||
!isset($parts["host"]) OR
|
|
||||||
!isset($parts["path"]))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/// @todo: Ports?
|
|
||||||
$host = $parts["host"];
|
|
||||||
|
|
||||||
if ($host == 'twitter.com')
|
|
||||||
return array("network" => NETWORK_TWITTER);
|
|
||||||
|
|
||||||
$lrdd = self::xrd($host);
|
|
||||||
|
|
||||||
$path_parts = explode("/", trim($parts["path"], "/"));
|
|
||||||
|
|
||||||
while (!$lrdd AND (sizeof($path_parts) > 1)) {
|
|
||||||
$host .= "/".array_shift($path_parts);
|
|
||||||
$lrdd = self::xrd($host);
|
|
||||||
}
|
}
|
||||||
if (!$lrdd)
|
|
||||||
return self::feed($uri);
|
|
||||||
|
|
||||||
$nick = array_pop($path_parts);
|
|
||||||
$addr = $nick."@".$host;
|
|
||||||
}
|
|
||||||
$webfinger = false;
|
$webfinger = false;
|
||||||
|
|
||||||
/// @todo Do we need the prefix "acct:" or "acct://"?
|
/// @todo Do we need the prefix "acct:" or "acct://"?
|
||||||
|
@ -855,33 +888,36 @@ class Probe {
|
||||||
* @return array OStatus data
|
* @return array OStatus data
|
||||||
*/
|
*/
|
||||||
private function ostatus($webfinger) {
|
private function ostatus($webfinger) {
|
||||||
|
|
||||||
$data = array();
|
$data = array();
|
||||||
if (is_array($webfinger["aliases"]))
|
if (is_array($webfinger["aliases"])) {
|
||||||
foreach($webfinger["aliases"] AS $alias)
|
foreach ($webfinger["aliases"] AS $alias) {
|
||||||
if (strstr($alias, "@"))
|
if (strstr($alias, "@")) {
|
||||||
$data["addr"] = str_replace('acct:', '', $alias);
|
$data["addr"] = str_replace('acct:', '', $alias);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (is_string($webfinger["subject"]) AND strstr($webfinger["subject"], "@"))
|
if (is_string($webfinger["subject"]) AND strstr($webfinger["subject"], "@")) {
|
||||||
$data["addr"] = str_replace('acct:', '', $webfinger["subject"]);
|
$data["addr"] = str_replace('acct:', '', $webfinger["subject"]);
|
||||||
|
}
|
||||||
$pubkey = "";
|
$pubkey = "";
|
||||||
foreach ($webfinger["links"] AS $link) {
|
foreach ($webfinger["links"] AS $link) {
|
||||||
if (($link["rel"] == "http://webfinger.net/rel/profile-page") AND
|
if (($link["rel"] == "http://webfinger.net/rel/profile-page") AND
|
||||||
($link["type"] == "text/html") AND ($link["href"] != ""))
|
($link["type"] == "text/html") AND ($link["href"] != "")) {
|
||||||
$data["url"] = $link["href"];
|
$data["url"] = $link["href"];
|
||||||
elseif (($link["rel"] == "salmon") AND ($link["href"] != ""))
|
} elseif (($link["rel"] == "salmon") AND ($link["href"] != "")) {
|
||||||
$data["notify"] = $link["href"];
|
$data["notify"] = $link["href"];
|
||||||
elseif (($link["rel"] == NAMESPACE_FEED) AND ($link["href"] != ""))
|
} elseif (($link["rel"] == NAMESPACE_FEED) AND ($link["href"] != "")) {
|
||||||
$data["poll"] = $link["href"];
|
$data["poll"] = $link["href"];
|
||||||
elseif (($link["rel"] == "magic-public-key") AND ($link["href"] != "")) {
|
} elseif (($link["rel"] == "magic-public-key") AND ($link["href"] != "")) {
|
||||||
$pubkey = $link["href"];
|
$pubkey = $link["href"];
|
||||||
|
|
||||||
if (substr($pubkey, 0, 5) === 'data:') {
|
if (substr($pubkey, 0, 5) === 'data:') {
|
||||||
if (strstr($pubkey, ','))
|
if (strstr($pubkey, ',')) {
|
||||||
$pubkey = substr($pubkey, strpos($pubkey, ',') + 1);
|
$pubkey = substr($pubkey, strpos($pubkey, ',') + 1);
|
||||||
else
|
} else {
|
||||||
$pubkey = substr($pubkey, 5);
|
$pubkey = substr($pubkey, 5);
|
||||||
|
}
|
||||||
} elseif (normalise_link($pubkey) == 'http://') {
|
} elseif (normalise_link($pubkey) == 'http://') {
|
||||||
$ret = z_fetch_url($pubkey);
|
$ret = z_fetch_url($pubkey);
|
||||||
if ($ret['errno'] == CURLE_OPERATION_TIMEDOUT) {
|
if ($ret['errno'] == CURLE_OPERATION_TIMEDOUT) {
|
||||||
|
@ -897,16 +933,15 @@ class Probe {
|
||||||
$e = base64url_decode($key[2]);
|
$e = base64url_decode($key[2]);
|
||||||
$data["pubkey"] = metopem($m,$e);
|
$data["pubkey"] = metopem($m,$e);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($data["notify"]) AND isset($data["pubkey"]) AND
|
if (isset($data["notify"]) AND isset($data["pubkey"]) AND
|
||||||
isset($data["poll"]) AND isset($data["url"])) {
|
isset($data["poll"]) AND isset($data["url"])) {
|
||||||
$data["network"] = NETWORK_OSTATUS;
|
$data["network"] = NETWORK_OSTATUS;
|
||||||
} else
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
// Fetch all additional data from the feed
|
// Fetch all additional data from the feed
|
||||||
$ret = z_fetch_url($data["poll"]);
|
$ret = z_fetch_url($data["poll"]);
|
||||||
if ($ret['errno'] == CURLE_OPERATION_TIMEDOUT) {
|
if ($ret['errno'] == CURLE_OPERATION_TIMEDOUT) {
|
||||||
|
@ -914,32 +949,32 @@ class Probe {
|
||||||
}
|
}
|
||||||
$feed = $ret['body'];
|
$feed = $ret['body'];
|
||||||
$feed_data = feed_import($feed,$dummy1,$dummy2, $dummy3, true);
|
$feed_data = feed_import($feed,$dummy1,$dummy2, $dummy3, true);
|
||||||
if (!$feed_data)
|
if (!$feed_data) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
if ($feed_data["header"]["author-name"] != "")
|
if ($feed_data["header"]["author-name"] != "") {
|
||||||
$data["name"] = $feed_data["header"]["author-name"];
|
$data["name"] = $feed_data["header"]["author-name"];
|
||||||
|
}
|
||||||
if ($feed_data["header"]["author-nick"] != "")
|
if ($feed_data["header"]["author-nick"] != "") {
|
||||||
$data["nick"] = $feed_data["header"]["author-nick"];
|
$data["nick"] = $feed_data["header"]["author-nick"];
|
||||||
|
}
|
||||||
if ($feed_data["header"]["author-avatar"] != "")
|
if ($feed_data["header"]["author-avatar"] != "") {
|
||||||
$data["photo"] = $feed_data["header"]["author-avatar"];
|
$data["photo"] = ostatus::fix_avatar($feed_data["header"]["author-avatar"], $data["url"]);
|
||||||
|
}
|
||||||
if ($feed_data["header"]["author-id"] != "")
|
if ($feed_data["header"]["author-id"] != "") {
|
||||||
$data["alias"] = $feed_data["header"]["author-id"];
|
$data["alias"] = $feed_data["header"]["author-id"];
|
||||||
|
}
|
||||||
if ($feed_data["header"]["author-location"] != "")
|
if ($feed_data["header"]["author-location"] != "") {
|
||||||
$data["location"] = $feed_data["header"]["author-location"];
|
$data["location"] = $feed_data["header"]["author-location"];
|
||||||
|
}
|
||||||
if ($feed_data["header"]["author-about"] != "")
|
if ($feed_data["header"]["author-about"] != "") {
|
||||||
$data["about"] = $feed_data["header"]["author-about"];
|
$data["about"] = $feed_data["header"]["author-about"];
|
||||||
|
}
|
||||||
// OStatus has serious issues when the the url doesn't fit (ssl vs. non ssl)
|
// OStatus has serious issues when the the url doesn't fit (ssl vs. non ssl)
|
||||||
// So we take the value that we just fetched, although the other one worked as well
|
// So we take the value that we just fetched, although the other one worked as well
|
||||||
if ($feed_data["header"]["author-link"] != "")
|
if ($feed_data["header"]["author-link"] != "") {
|
||||||
$data["url"] = $feed_data["header"]["author-link"];
|
$data["url"] = $feed_data["header"]["author-link"];
|
||||||
|
}
|
||||||
/// @todo Fetch location and "about" from the feed as well
|
/// @todo Fetch location and "about" from the feed as well
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -352,6 +352,7 @@ use \Friendica\Core\Config;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
logger('API call not implemented: '.$a->query_string);
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
} catch (HTTPException $e) {
|
} catch (HTTPException $e) {
|
||||||
header("HTTP/1.1 {$e->httpcode} {$e->httpdesc}");
|
header("HTTP/1.1 {$e->httpcode} {$e->httpdesc}");
|
||||||
|
@ -2720,6 +2721,7 @@ use \Friendica\Core\Config;
|
||||||
return api_format_data('config', $type, array('config' => $config));
|
return api_format_data('config', $type, array('config' => $config));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
api_register_func('api/gnusocial/config','api_statusnet_config',false);
|
||||||
api_register_func('api/statusnet/config','api_statusnet_config',false);
|
api_register_func('api/statusnet/config','api_statusnet_config',false);
|
||||||
|
|
||||||
function api_statusnet_version($type) {
|
function api_statusnet_version($type) {
|
||||||
|
@ -2728,6 +2730,7 @@ use \Friendica\Core\Config;
|
||||||
|
|
||||||
return api_format_data('version', $type, array('version' => $fake_statusnet_version));
|
return api_format_data('version', $type, array('version' => $fake_statusnet_version));
|
||||||
}
|
}
|
||||||
|
api_register_func('api/gnusocial/version','api_statusnet_version',false);
|
||||||
api_register_func('api/statusnet/version','api_statusnet_version',false);
|
api_register_func('api/statusnet/version','api_statusnet_version',false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -3971,11 +3974,10 @@ use \Friendica\Core\Config;
|
||||||
// error message if specified gid is not in database
|
// error message if specified gid is not in database
|
||||||
if (!dbm::is_result($r))
|
if (!dbm::is_result($r))
|
||||||
throw new BadRequestException("profile_id not available");
|
throw new BadRequestException("profile_id not available");
|
||||||
}
|
} else {
|
||||||
else
|
|
||||||
$r = q("SELECT * FROM `profile` WHERE `uid` = %d",
|
$r = q("SELECT * FROM `profile` WHERE `uid` = %d",
|
||||||
intval(api_user()));
|
intval(api_user()));
|
||||||
|
}
|
||||||
// loop through all returned profiles and retrieve data and users
|
// loop through all returned profiles and retrieve data and users
|
||||||
$k = 0;
|
$k = 0;
|
||||||
foreach ($r as $rr) {
|
foreach ($r as $rr) {
|
||||||
|
@ -4002,9 +4004,11 @@ use \Friendica\Core\Config;
|
||||||
}
|
}
|
||||||
|
|
||||||
// return settings, authenticated user and profiles data
|
// return settings, authenticated user and profiles data
|
||||||
|
$self = q("SELECT `nurl` FROM `contact` WHERE `uid`= %d AND `self` LIMIT 1", intval(api_user()));
|
||||||
|
|
||||||
$result = array('multi_profiles' => $multi_profiles ? true : false,
|
$result = array('multi_profiles' => $multi_profiles ? true : false,
|
||||||
'global_dir' => $directory,
|
'global_dir' => $directory,
|
||||||
'friendica_owner' => api_get_user($a, intval(api_user())),
|
'friendica_owner' => api_get_user($a, $self[0]['nurl']),
|
||||||
'profiles' => $profiles);
|
'profiles' => $profiles);
|
||||||
return api_format_data("friendica_profiles", $type, array('$result' => $result));
|
return api_format_data("friendica_profiles", $type, array('$result' => $result));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* @file include/autoloader.php
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief composer-derived autoloader init
|
|
||||||
**/
|
|
||||||
class FriendicaAutoloaderInit
|
|
||||||
{
|
|
||||||
private static $loader;
|
|
||||||
|
|
||||||
public static function loadClassLoader($class)
|
|
||||||
{
|
|
||||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
|
||||||
require __DIR__ . '/autoloader/ClassLoader.php';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getLoader()
|
|
||||||
{
|
|
||||||
if (null !== self::$loader) {
|
|
||||||
return self::$loader;
|
|
||||||
}
|
|
||||||
|
|
||||||
spl_autoload_register(array('FriendicaAutoloaderInit', 'loadClassLoader'), true, true);
|
|
||||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
|
||||||
spl_autoload_unregister(array('FriendicaAutoloaderInit', 'loadClassLoader'));
|
|
||||||
|
|
||||||
// library
|
|
||||||
$map = require __DIR__ . '/autoloader/autoload_namespaces.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->set($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$map = require __DIR__ . '/autoloader/autoload_psr4.php';
|
|
||||||
foreach ($map as $namespace => $path) {
|
|
||||||
$loader->setPsr4($namespace, $path);
|
|
||||||
}
|
|
||||||
|
|
||||||
$classMap = require __DIR__ . '/autoloader/autoload_classmap.php';
|
|
||||||
if ($classMap) {
|
|
||||||
$loader->addClassMap($classMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
$loader->register(true);
|
|
||||||
|
|
||||||
$includeFiles = require __DIR__ . '/autoloader/autoload_files.php';
|
|
||||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
|
||||||
friendicaRequire($fileIdentifier, $file);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return $loader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function friendicaRequire($fileIdentifier, $file)
|
|
||||||
{
|
|
||||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
|
||||||
require $file;
|
|
||||||
|
|
||||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return FriendicaAutoloaderInit::getLoader();
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// autoload_classmap.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
);
|
|
|
@ -1,10 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
// autoload_psr4.php @generated by Composer
|
|
||||||
|
|
||||||
$vendorDir = dirname(dirname(dirname(__FILE__)))."/library";
|
|
||||||
$baseDir = dirname($vendorDir);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'Friendica\\' => array($baseDir . '/include'),
|
|
||||||
);
|
|
|
@ -59,15 +59,6 @@ function diaspora2bb($s) {
|
||||||
|
|
||||||
$s = str_replace('#', '#', $s);
|
$s = str_replace('#', '#', $s);
|
||||||
|
|
||||||
$search = array(" \n", "\n ");
|
|
||||||
$replace = array("\n", "\n");
|
|
||||||
do {
|
|
||||||
$oldtext = $s;
|
|
||||||
$s = str_replace($search, $replace, $s);
|
|
||||||
} while ($oldtext != $s);
|
|
||||||
|
|
||||||
$s = str_replace("\n\n", '<br>', $s);
|
|
||||||
|
|
||||||
$s = html2bbcode($s);
|
$s = html2bbcode($s);
|
||||||
|
|
||||||
// protect the recycle symbol from turning into a tag, but without unescaping angles and naked ampersands
|
// protect the recycle symbol from turning into a tag, but without unescaping angles and naked ampersands
|
||||||
|
|
|
@ -88,7 +88,7 @@ function network_to_name($s, $profile = "") {
|
||||||
NETWORK_PUMPIO => t('pump.io'),
|
NETWORK_PUMPIO => t('pump.io'),
|
||||||
NETWORK_TWITTER => t('Twitter'),
|
NETWORK_TWITTER => t('Twitter'),
|
||||||
NETWORK_DIASPORA2 => t('Diaspora Connector'),
|
NETWORK_DIASPORA2 => t('Diaspora Connector'),
|
||||||
NETWORK_STATUSNET => t('GNU Social'),
|
NETWORK_STATUSNET => t('GNU Social Connector'),
|
||||||
NETWORK_PNUT => t('pnut'),
|
NETWORK_PNUT => t('pnut'),
|
||||||
NETWORK_APPNET => t('App.net')
|
NETWORK_APPNET => t('App.net')
|
||||||
);
|
);
|
||||||
|
@ -100,16 +100,15 @@ function network_to_name($s, $profile = "") {
|
||||||
|
|
||||||
$networkname = str_replace($search, $replace, $s);
|
$networkname = str_replace($search, $replace, $s);
|
||||||
|
|
||||||
if (($s == NETWORK_DIASPORA) AND ($profile != "") AND Diaspora::is_redmatrix($profile)) {
|
if ((in_array($s, array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS))) AND ($profile != "")) {
|
||||||
$networkname = t("Hubzilla/Redmatrix");
|
|
||||||
|
|
||||||
$r = q("SELECT `gserver`.`platform` FROM `gcontact`
|
$r = q("SELECT `gserver`.`platform` FROM `gcontact`
|
||||||
INNER JOIN `gserver` ON `gserver`.`nurl` = `gcontact`.`server_url`
|
INNER JOIN `gserver` ON `gserver`.`nurl` = `gcontact`.`server_url`
|
||||||
WHERE `gcontact`.`nurl` = '%s' AND `platform` != ''",
|
WHERE `gcontact`.`nurl` = '%s' AND `platform` != ''",
|
||||||
dbesc(normalise_link($profile)));
|
dbesc(normalise_link($profile)));
|
||||||
if ($r)
|
if (dbm::is_result($r)) {
|
||||||
$networkname = $r[0]["platform"];
|
$networkname = $r[0]["platform"];
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $networkname;
|
return $networkname;
|
||||||
}
|
}
|
||||||
|
|
261
include/cron.php
261
include/cron.php
|
@ -1,27 +1,23 @@
|
||||||
<?php
|
<?php
|
||||||
use \Friendica\Core\Config;
|
use \Friendica\Core\Config;
|
||||||
|
|
||||||
require_once('include/photos.php');
|
|
||||||
require_once('include/user.php');
|
|
||||||
|
|
||||||
function cron_run(&$argv, &$argc){
|
function cron_run(&$argv, &$argc){
|
||||||
global $a;
|
global $a;
|
||||||
|
|
||||||
require_once('include/session.php');
|
|
||||||
require_once('include/datetime.php');
|
require_once('include/datetime.php');
|
||||||
require_once('include/items.php');
|
|
||||||
require_once('include/Contact.php');
|
// Poll contacts with specific parameters
|
||||||
require_once('include/email.php');
|
if ($argc > 1) {
|
||||||
require_once('include/socgraph.php');
|
cron_poll_contacts($argc, $argv);
|
||||||
require_once('mod/nodeinfo.php');
|
return;
|
||||||
require_once('include/post_update.php');
|
}
|
||||||
|
|
||||||
$last = get_config('system','last_cron');
|
$last = get_config('system','last_cron');
|
||||||
|
|
||||||
$poll_interval = intval(get_config('system','cron_interval'));
|
$poll_interval = intval(get_config('system','cron_interval'));
|
||||||
if(! $poll_interval)
|
if (! $poll_interval) {
|
||||||
$poll_interval = 10;
|
$poll_interval = 10;
|
||||||
|
}
|
||||||
if ($last) {
|
if ($last) {
|
||||||
$next = $last + ($poll_interval * 60);
|
$next = $last + ($poll_interval * 60);
|
||||||
if($next > time()) {
|
if($next > time()) {
|
||||||
|
@ -33,19 +29,16 @@ function cron_run(&$argv, &$argc){
|
||||||
logger('cron: start');
|
logger('cron: start');
|
||||||
|
|
||||||
// run queue delivery process in the background
|
// run queue delivery process in the background
|
||||||
|
|
||||||
proc_run(PRIORITY_NEGLIGIBLE, "include/queue.php");
|
proc_run(PRIORITY_NEGLIGIBLE, "include/queue.php");
|
||||||
|
|
||||||
// run the process to discover global contacts in the background
|
// run the process to discover global contacts in the background
|
||||||
|
|
||||||
proc_run(PRIORITY_LOW, "include/discover_poco.php");
|
proc_run(PRIORITY_LOW, "include/discover_poco.php");
|
||||||
|
|
||||||
// run the process to update locally stored global contacts in the background
|
// run the process to update locally stored global contacts in the background
|
||||||
|
|
||||||
proc_run(PRIORITY_LOW, "include/discover_poco.php", "checkcontact");
|
proc_run(PRIORITY_LOW, "include/discover_poco.php", "checkcontact");
|
||||||
|
|
||||||
// Expire and remove user entries
|
// Expire and remove user entries
|
||||||
cron_expire_and_remove_users();
|
proc_run(PRIORITY_MEDIUM, "include/cronjobs.php", "expire_and_remove_users");
|
||||||
|
|
||||||
// Check OStatus conversations
|
// Check OStatus conversations
|
||||||
proc_run(PRIORITY_MEDIUM, "include/cronjobs.php", "ostatus_mentions");
|
proc_run(PRIORITY_MEDIUM, "include/cronjobs.php", "ostatus_mentions");
|
||||||
|
@ -59,14 +52,22 @@ function cron_run(&$argv, &$argc){
|
||||||
// update nodeinfo data
|
// update nodeinfo data
|
||||||
proc_run(PRIORITY_LOW, "include/cronjobs.php", "nodeinfo");
|
proc_run(PRIORITY_LOW, "include/cronjobs.php", "nodeinfo");
|
||||||
|
|
||||||
// once daily run birthday_updates and then expire in background
|
// Clear cache entries
|
||||||
|
proc_run(PRIORITY_LOW, "include/cronjobs.php", "clear_cache");
|
||||||
|
|
||||||
|
// Repair missing Diaspora values in contacts
|
||||||
|
proc_run(PRIORITY_LOW, "include/cronjobs.php", "repair_diaspora");
|
||||||
|
|
||||||
|
// Repair entries in the database
|
||||||
|
proc_run(PRIORITY_LOW, "include/cronjobs.php", "repair_database");
|
||||||
|
|
||||||
|
// once daily run birthday_updates and then expire in background
|
||||||
$d1 = get_config('system','last_expire_day');
|
$d1 = get_config('system','last_expire_day');
|
||||||
$d2 = intval(datetime_convert('UTC','UTC','now','d'));
|
$d2 = intval(datetime_convert('UTC','UTC','now','d'));
|
||||||
|
|
||||||
if($d2 != intval($d1)) {
|
if($d2 != intval($d1)) {
|
||||||
|
|
||||||
update_contact_birthdays();
|
proc_run(PRIORITY_LOW, "include/cronjobs.php", "update_contact_birthdays");
|
||||||
|
|
||||||
proc_run(PRIORITY_LOW, "include/discover_poco.php", "update_server");
|
proc_run(PRIORITY_LOW, "include/discover_poco.php", "update_server");
|
||||||
|
|
||||||
|
@ -78,18 +79,9 @@ function cron_run(&$argv, &$argc){
|
||||||
|
|
||||||
proc_run(PRIORITY_MEDIUM, 'include/dbclean.php');
|
proc_run(PRIORITY_MEDIUM, 'include/dbclean.php');
|
||||||
|
|
||||||
cron_update_photo_albums();
|
proc_run(PRIORITY_LOW, "include/cronjobs.php", "update_photo_albums");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear cache entries
|
|
||||||
cron_clear_cache($a);
|
|
||||||
|
|
||||||
// Repair missing Diaspora values in contacts
|
|
||||||
cron_repair_diaspora($a);
|
|
||||||
|
|
||||||
// Repair entries in the database
|
|
||||||
cron_repair_database();
|
|
||||||
|
|
||||||
// Poll contacts
|
// Poll contacts
|
||||||
cron_poll_contacts($argc, $argv);
|
cron_poll_contacts($argc, $argv);
|
||||||
|
|
||||||
|
@ -100,39 +92,6 @@ function cron_run(&$argv, &$argc){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Update the cached values for the number of photo albums per user
|
|
||||||
*/
|
|
||||||
function cron_update_photo_albums() {
|
|
||||||
$r = q("SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`");
|
|
||||||
if (!dbm::is_result($r)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($r AS $user) {
|
|
||||||
photo_albums($user['uid'], true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Expire and remove user entries
|
|
||||||
*/
|
|
||||||
function cron_expire_and_remove_users() {
|
|
||||||
// expire any expired accounts
|
|
||||||
q("UPDATE user SET `account_expired` = 1 where `account_expired` = 0
|
|
||||||
AND `account_expires_on` > '%s'
|
|
||||||
AND `account_expires_on` < UTC_TIMESTAMP()", dbesc(NULL_DATE));
|
|
||||||
|
|
||||||
// delete user and contact records for recently removed accounts
|
|
||||||
$r = q("SELECT * FROM `user` WHERE `account_removed` AND `account_expires_on` < UTC_TIMESTAMP() - INTERVAL 3 DAY");
|
|
||||||
if ($r) {
|
|
||||||
foreach($r as $user) {
|
|
||||||
q("DELETE FROM `contact` WHERE `uid` = %d", intval($user['uid']));
|
|
||||||
q("DELETE FROM `user` WHERE `uid` = %d", intval($user['uid']));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Poll contacts for unreceived messages
|
* @brief Poll contacts for unreceived messages
|
||||||
*
|
*
|
||||||
|
@ -145,15 +104,16 @@ function cron_poll_contacts($argc, $argv) {
|
||||||
$force = false;
|
$force = false;
|
||||||
$restart = false;
|
$restart = false;
|
||||||
|
|
||||||
if (($argc > 1) && ($argv[1] == 'force'))
|
if (($argc > 1) && ($argv[1] == 'force')) {
|
||||||
$force = true;
|
$force = true;
|
||||||
|
}
|
||||||
if (($argc > 1) && ($argv[1] == 'restart')) {
|
if (($argc > 1) && ($argv[1] == 'restart')) {
|
||||||
$restart = true;
|
$restart = true;
|
||||||
$generation = intval($argv[2]);
|
$generation = intval($argv[2]);
|
||||||
if (!$generation)
|
if (!$generation) {
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (($argc > 1) && intval($argv[1])) {
|
if (($argc > 1) && intval($argv[1])) {
|
||||||
$manual_id = intval($argv[1]);
|
$manual_id = intval($argv[1]);
|
||||||
|
@ -171,9 +131,9 @@ function cron_poll_contacts($argc, $argv) {
|
||||||
// we are unable to match those posts with a Diaspora GUID and prevent duplicates.
|
// we are unable to match those posts with a Diaspora GUID and prevent duplicates.
|
||||||
|
|
||||||
$abandon_days = intval(get_config('system','account_abandon_days'));
|
$abandon_days = intval(get_config('system','account_abandon_days'));
|
||||||
if($abandon_days < 1)
|
if ($abandon_days < 1) {
|
||||||
$abandon_days = 0;
|
$abandon_days = 0;
|
||||||
|
}
|
||||||
$abandon_sql = (($abandon_days)
|
$abandon_sql = (($abandon_days)
|
||||||
? sprintf(" AND `user`.`login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY ", intval($abandon_days))
|
? sprintf(" AND `user`.`login_date` > UTC_TIMESTAMP() - INTERVAL %d DAY ", intval($abandon_days))
|
||||||
: ''
|
: ''
|
||||||
|
@ -244,185 +204,44 @@ function cron_poll_contacts($argc, $argv) {
|
||||||
|
|
||||||
switch ($contact['priority']) {
|
switch ($contact['priority']) {
|
||||||
case 5:
|
case 5:
|
||||||
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 month"))
|
if (datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 month")) {
|
||||||
$update = true;
|
$update = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 week"))
|
if (datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 week")) {
|
||||||
$update = true;
|
$update = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day"))
|
if (datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day")) {
|
||||||
$update = true;
|
$update = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 12 hour"))
|
if (datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 12 hour")) {
|
||||||
$update = true;
|
$update = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
default:
|
default:
|
||||||
if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 hour"))
|
if (datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 hour")) {
|
||||||
$update = true;
|
$update = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!$update)
|
if (!$update) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
logger("Polling ".$contact["network"]." ".$contact["id"]." ".$contact["nick"]." ".$contact["name"]);
|
logger("Polling ".$contact["network"]." ".$contact["id"]." ".$contact["nick"]." ".$contact["name"]);
|
||||||
|
|
||||||
if (($contact['network'] == NETWORK_FEED) AND ($contact['priority'] <= 3)) {
|
if (($contact['network'] == NETWORK_FEED) AND ($contact['priority'] <= 3)) {
|
||||||
proc_run(PRIORITY_MEDIUM, 'include/onepoll.php', $contact['id']);
|
proc_run(PRIORITY_MEDIUM, 'include/onepoll.php', intval($contact['id']));
|
||||||
} else {
|
} else {
|
||||||
proc_run(PRIORITY_LOW, 'include/onepoll.php', $contact['id']);
|
proc_run(PRIORITY_LOW, 'include/onepoll.php', intval($contact['id']));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Clear cache entries
|
|
||||||
*
|
|
||||||
* @param App $a
|
|
||||||
*/
|
|
||||||
function cron_clear_cache(App $a) {
|
|
||||||
|
|
||||||
$last = get_config('system','cache_last_cleared');
|
|
||||||
|
|
||||||
if($last) {
|
|
||||||
$next = $last + (3600); // Once per hour
|
|
||||||
$clear_cache = ($next <= time());
|
|
||||||
} else
|
|
||||||
$clear_cache = true;
|
|
||||||
|
|
||||||
if (!$clear_cache)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// clear old cache
|
|
||||||
Cache::clear();
|
|
||||||
|
|
||||||
// clear old item cache files
|
|
||||||
clear_cache();
|
|
||||||
|
|
||||||
// clear cache for photos
|
|
||||||
clear_cache($a->get_basepath(), $a->get_basepath()."/photo");
|
|
||||||
|
|
||||||
// clear smarty cache
|
|
||||||
clear_cache($a->get_basepath()."/view/smarty3/compiled", $a->get_basepath()."/view/smarty3/compiled");
|
|
||||||
|
|
||||||
// clear cache for image proxy
|
|
||||||
if (!get_config("system", "proxy_disabled")) {
|
|
||||||
clear_cache($a->get_basepath(), $a->get_basepath()."/proxy");
|
|
||||||
|
|
||||||
$cachetime = get_config('system','proxy_cache_time');
|
|
||||||
if (!$cachetime) $cachetime = PROXY_DEFAULT_TIME;
|
|
||||||
|
|
||||||
q('DELETE FROM `photo` WHERE `uid` = 0 AND `resource-id` LIKE "pic:%%" AND `created` < NOW() - INTERVAL %d SECOND', $cachetime);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete the cached OEmbed entries that are older than one year
|
|
||||||
q("DELETE FROM `oembed` WHERE `created` < NOW() - INTERVAL 3 MONTH");
|
|
||||||
|
|
||||||
// Delete the cached "parse_url" entries that are older than one year
|
|
||||||
q("DELETE FROM `parsed_url` WHERE `created` < NOW() - INTERVAL 3 MONTH");
|
|
||||||
|
|
||||||
// Maximum table size in megabyte
|
|
||||||
$max_tablesize = intval(get_config('system','optimize_max_tablesize')) * 1000000;
|
|
||||||
if ($max_tablesize == 0)
|
|
||||||
$max_tablesize = 100 * 1000000; // Default are 100 MB
|
|
||||||
|
|
||||||
if ($max_tablesize > 0) {
|
|
||||||
// Minimum fragmentation level in percent
|
|
||||||
$fragmentation_level = intval(get_config('system','optimize_fragmentation')) / 100;
|
|
||||||
if ($fragmentation_level == 0)
|
|
||||||
$fragmentation_level = 0.3; // Default value is 30%
|
|
||||||
|
|
||||||
// Optimize some tables that need to be optimized
|
|
||||||
$r = q("SHOW TABLE STATUS");
|
|
||||||
foreach($r as $table) {
|
|
||||||
|
|
||||||
// Don't optimize tables that are too large
|
|
||||||
if ($table["Data_length"] > $max_tablesize)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Don't optimize empty tables
|
|
||||||
if ($table["Data_length"] == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Calculate fragmentation
|
|
||||||
$fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]);
|
|
||||||
|
|
||||||
logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG);
|
|
||||||
|
|
||||||
// Don't optimize tables that needn't to be optimized
|
|
||||||
if ($fragmentation < $fragmentation_level)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// So optimize it
|
|
||||||
logger("Optimize Table ".$table["Name"], LOGGER_DEBUG);
|
|
||||||
q("OPTIMIZE TABLE `%s`", dbesc($table["Name"]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_config('system','cache_last_cleared', time());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Repair missing values in Diaspora contacts
|
|
||||||
*
|
|
||||||
* @param App $a
|
|
||||||
*/
|
|
||||||
function cron_repair_diaspora(App $a) {
|
|
||||||
$r = q("SELECT `id`, `url` FROM `contact`
|
|
||||||
WHERE `network` = '%s' AND (`batch` = '' OR `notify` = '' OR `poll` = '' OR pubkey = '')
|
|
||||||
ORDER BY RAND() LIMIT 50", dbesc(NETWORK_DIASPORA));
|
|
||||||
if (dbm::is_result($r)) {
|
|
||||||
foreach ($r AS $contact) {
|
|
||||||
if (poco_reachable($contact["url"])) {
|
|
||||||
$data = probe_url($contact["url"]);
|
|
||||||
if ($data["network"] == NETWORK_DIASPORA) {
|
|
||||||
logger("Repair contact ".$contact["id"]." ".$contact["url"], LOGGER_DEBUG);
|
|
||||||
q("UPDATE `contact` SET `batch` = '%s', `notify` = '%s', `poll` = '%s', pubkey = '%s' WHERE `id` = %d",
|
|
||||||
dbesc($data["batch"]), dbesc($data["notify"]), dbesc($data["poll"]), dbesc($data["pubkey"]),
|
|
||||||
intval($contact["id"]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Do some repairs in database entries
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function cron_repair_database() {
|
|
||||||
|
|
||||||
// Sometimes there seem to be issues where the "self" contact vanishes.
|
|
||||||
// We haven't found the origin of the problem by now.
|
|
||||||
$r = q("SELECT `uid` FROM `user` WHERE NOT EXISTS (SELECT `uid` FROM `contact` WHERE `contact`.`uid` = `user`.`uid` AND `contact`.`self`)");
|
|
||||||
if (dbm::is_result($r)) {
|
|
||||||
foreach ($r AS $user) {
|
|
||||||
logger('Create missing self contact for user '.$user['uid']);
|
|
||||||
user_create_self_contact($user['uid']);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the parent if it wasn't set. (Shouldn't happen - but does sometimes)
|
|
||||||
// This call is very "cheap" so we can do it at any time without a problem
|
|
||||||
q("UPDATE `item` INNER JOIN `item` AS `parent` ON `parent`.`uri` = `item`.`parent-uri` AND `parent`.`uid` = `item`.`uid` SET `item`.`parent` = `parent`.`id` WHERE `item`.`parent` = 0");
|
|
||||||
|
|
||||||
// There was an issue where the nick vanishes from the contact table
|
|
||||||
q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''");
|
|
||||||
|
|
||||||
// Update the global contacts for local users
|
|
||||||
$r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`");
|
|
||||||
if (dbm::is_result($r))
|
|
||||||
foreach ($r AS $user)
|
|
||||||
update_gcontact_for_user($user["uid"]);
|
|
||||||
|
|
||||||
/// @todo
|
|
||||||
/// - remove thread entries without item
|
|
||||||
/// - remove sign entries without item
|
|
||||||
/// - remove children when parent got lost
|
|
||||||
/// - set contact-id in item when not present
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,10 +8,17 @@ function cronjobs_run(&$argv, &$argc){
|
||||||
require_once('include/ostatus.php');
|
require_once('include/ostatus.php');
|
||||||
require_once('include/post_update.php');
|
require_once('include/post_update.php');
|
||||||
require_once('mod/nodeinfo.php');
|
require_once('mod/nodeinfo.php');
|
||||||
|
require_once('include/photos.php');
|
||||||
|
require_once('include/user.php');
|
||||||
|
require_once('include/socgraph.php');
|
||||||
|
require_once('include/Probe.php');
|
||||||
|
|
||||||
// No parameter set? So return
|
// No parameter set? So return
|
||||||
if ($argc <= 1)
|
if ($argc <= 1) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger("Starting cronjob ".$argv[1], LOGGER_DEBUG);
|
||||||
|
|
||||||
// Check OStatus conversations
|
// Check OStatus conversations
|
||||||
// Check only conversations with mentions (for a longer time)
|
// Check only conversations with mentions (for a longer time)
|
||||||
|
@ -39,5 +46,244 @@ function cronjobs_run(&$argv, &$argc){
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Expire and remove user entries
|
||||||
|
if ($argv[1] == 'expire_and_remove_users') {
|
||||||
|
cron_expire_and_remove_users();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($argv[1] == 'update_contact_birthdays') {
|
||||||
|
update_contact_birthdays();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($argv[1] == 'update_photo_albums') {
|
||||||
|
cron_update_photo_albums();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear cache entries
|
||||||
|
if ($argv[1] == 'clear_cache') {
|
||||||
|
cron_clear_cache($a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repair missing Diaspora values in contacts
|
||||||
|
if ($argv[1] == 'repair_diaspora') {
|
||||||
|
cron_repair_diaspora($a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Repair entries in the database
|
||||||
|
if ($argv[1] == 'repair_database') {
|
||||||
|
cron_repair_database();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger("Xronjob ".$argv[1]." is unknown.", LOGGER_DEBUG);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the cached values for the number of photo albums per user
|
||||||
|
*/
|
||||||
|
function cron_update_photo_albums() {
|
||||||
|
$r = q("SELECT `uid` FROM `user` WHERE NOT `account_expired` AND NOT `account_removed`");
|
||||||
|
if (!dbm::is_result($r)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($r AS $user) {
|
||||||
|
photo_albums($user['uid'], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Expire and remove user entries
|
||||||
|
*/
|
||||||
|
function cron_expire_and_remove_users() {
|
||||||
|
// expire any expired accounts
|
||||||
|
q("UPDATE user SET `account_expired` = 1 where `account_expired` = 0
|
||||||
|
AND `account_expires_on` > '%s'
|
||||||
|
AND `account_expires_on` < UTC_TIMESTAMP()", dbesc(NULL_DATE));
|
||||||
|
|
||||||
|
// delete user and contact records for recently removed accounts
|
||||||
|
$r = q("SELECT * FROM `user` WHERE `account_removed` AND `account_expires_on` < UTC_TIMESTAMP() - INTERVAL 3 DAY");
|
||||||
|
if (dbm::is_result($r)) {
|
||||||
|
foreach ($r as $user) {
|
||||||
|
q("DELETE FROM `contact` WHERE `uid` = %d", intval($user['uid']));
|
||||||
|
q("DELETE FROM `user` WHERE `uid` = %d", intval($user['uid']));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Clear cache entries
|
||||||
|
*
|
||||||
|
* @param App $a
|
||||||
|
*/
|
||||||
|
function cron_clear_cache(App $a) {
|
||||||
|
|
||||||
|
$last = get_config('system','cache_last_cleared');
|
||||||
|
|
||||||
|
if ($last) {
|
||||||
|
$next = $last + (3600); // Once per hour
|
||||||
|
$clear_cache = ($next <= time());
|
||||||
|
} else {
|
||||||
|
$clear_cache = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$clear_cache) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// clear old cache
|
||||||
|
Cache::clear();
|
||||||
|
|
||||||
|
// clear old item cache files
|
||||||
|
clear_cache();
|
||||||
|
|
||||||
|
// clear cache for photos
|
||||||
|
clear_cache($a->get_basepath(), $a->get_basepath()."/photo");
|
||||||
|
|
||||||
|
// clear smarty cache
|
||||||
|
clear_cache($a->get_basepath()."/view/smarty3/compiled", $a->get_basepath()."/view/smarty3/compiled");
|
||||||
|
|
||||||
|
// clear cache for image proxy
|
||||||
|
if (!get_config("system", "proxy_disabled")) {
|
||||||
|
clear_cache($a->get_basepath(), $a->get_basepath()."/proxy");
|
||||||
|
|
||||||
|
$cachetime = get_config('system','proxy_cache_time');
|
||||||
|
if (!$cachetime) {
|
||||||
|
$cachetime = PROXY_DEFAULT_TIME;
|
||||||
|
}
|
||||||
|
q('DELETE FROM `photo` WHERE `uid` = 0 AND `resource-id` LIKE "pic:%%" AND `created` < NOW() - INTERVAL %d SECOND', $cachetime);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete the cached OEmbed entries that are older than one year
|
||||||
|
q("DELETE FROM `oembed` WHERE `created` < NOW() - INTERVAL 3 MONTH");
|
||||||
|
|
||||||
|
// Delete the cached "parse_url" entries that are older than one year
|
||||||
|
q("DELETE FROM `parsed_url` WHERE `created` < NOW() - INTERVAL 3 MONTH");
|
||||||
|
|
||||||
|
// Maximum table size in megabyte
|
||||||
|
$max_tablesize = intval(get_config('system','optimize_max_tablesize')) * 1000000;
|
||||||
|
if ($max_tablesize == 0) {
|
||||||
|
$max_tablesize = 100 * 1000000; // Default are 100 MB
|
||||||
|
}
|
||||||
|
if ($max_tablesize > 0) {
|
||||||
|
// Minimum fragmentation level in percent
|
||||||
|
$fragmentation_level = intval(get_config('system','optimize_fragmentation')) / 100;
|
||||||
|
if ($fragmentation_level == 0) {
|
||||||
|
$fragmentation_level = 0.3; // Default value is 30%
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimize some tables that need to be optimized
|
||||||
|
$r = q("SHOW TABLE STATUS");
|
||||||
|
foreach ($r as $table) {
|
||||||
|
|
||||||
|
// Don't optimize tables that are too large
|
||||||
|
if ($table["Data_length"] > $max_tablesize) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't optimize empty tables
|
||||||
|
if ($table["Data_length"] == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate fragmentation
|
||||||
|
$fragmentation = $table["Data_free"] / ($table["Data_length"] + $table["Index_length"]);
|
||||||
|
|
||||||
|
logger("Table ".$table["Name"]." - Fragmentation level: ".round($fragmentation * 100, 2), LOGGER_DEBUG);
|
||||||
|
|
||||||
|
// Don't optimize tables that needn't to be optimized
|
||||||
|
if ($fragmentation < $fragmentation_level) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// So optimize it
|
||||||
|
logger("Optimize Table ".$table["Name"], LOGGER_DEBUG);
|
||||||
|
q("OPTIMIZE TABLE `%s`", dbesc($table["Name"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
set_config('system','cache_last_cleared', time());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Repair missing values in Diaspora contacts
|
||||||
|
*
|
||||||
|
* @param App $a
|
||||||
|
*/
|
||||||
|
function cron_repair_diaspora(App $a) {
|
||||||
|
|
||||||
|
$starttime = time();
|
||||||
|
|
||||||
|
$r = q("SELECT `id`, `url` FROM `contact`
|
||||||
|
WHERE `network` = '%s' AND (`batch` = '' OR `notify` = '' OR `poll` = '' OR pubkey = '')
|
||||||
|
ORDER BY RAND() LIMIT 50", dbesc(NETWORK_DIASPORA));
|
||||||
|
if (!dbm::is_result($r)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($r AS $contact) {
|
||||||
|
// Quit the loop after 3 minutes
|
||||||
|
if (time() > ($starttime + 180)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!poco_reachable($contact["url"])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = Probe::uri($contact["url"]);
|
||||||
|
if ($data["network"] != NETWORK_DIASPORA) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger("Repair contact ".$contact["id"]." ".$contact["url"], LOGGER_DEBUG);
|
||||||
|
q("UPDATE `contact` SET `batch` = '%s', `notify` = '%s', `poll` = '%s', pubkey = '%s' WHERE `id` = %d",
|
||||||
|
dbesc($data["batch"]), dbesc($data["notify"]), dbesc($data["poll"]), dbesc($data["pubkey"]),
|
||||||
|
intval($contact["id"]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Do some repairs in database entries
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function cron_repair_database() {
|
||||||
|
|
||||||
|
// Sometimes there seem to be issues where the "self" contact vanishes.
|
||||||
|
// We haven't found the origin of the problem by now.
|
||||||
|
$r = q("SELECT `uid` FROM `user` WHERE NOT EXISTS (SELECT `uid` FROM `contact` WHERE `contact`.`uid` = `user`.`uid` AND `contact`.`self`)");
|
||||||
|
if (dbm::is_result($r)) {
|
||||||
|
foreach ($r AS $user) {
|
||||||
|
logger('Create missing self contact for user '.$user['uid']);
|
||||||
|
user_create_self_contact($user['uid']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the parent if it wasn't set. (Shouldn't happen - but does sometimes)
|
||||||
|
// This call is very "cheap" so we can do it at any time without a problem
|
||||||
|
q("UPDATE `item` INNER JOIN `item` AS `parent` ON `parent`.`uri` = `item`.`parent-uri` AND `parent`.`uid` = `item`.`uid` SET `item`.`parent` = `parent`.`id` WHERE `item`.`parent` = 0");
|
||||||
|
|
||||||
|
// There was an issue where the nick vanishes from the contact table
|
||||||
|
q("UPDATE `contact` INNER JOIN `user` ON `contact`.`uid` = `user`.`uid` SET `nick` = `nickname` WHERE `self` AND `nick`=''");
|
||||||
|
|
||||||
|
// Update the global contacts for local users
|
||||||
|
$r = q("SELECT `uid` FROM `user` WHERE `verified` AND NOT `blocked` AND NOT `account_removed` AND NOT `account_expired`");
|
||||||
|
if (dbm::is_result($r)) {
|
||||||
|
foreach ($r AS $user) {
|
||||||
|
update_gcontact_for_user($user["uid"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @todo
|
||||||
|
/// - remove thread entries without item
|
||||||
|
/// - remove sign entries without item
|
||||||
|
/// - remove children when parent got lost
|
||||||
|
/// - set contact-id in item when not present
|
||||||
|
}
|
||||||
|
|
|
@ -1,60 +1,20 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once('library/ASNValue.class.php');
|
require_once 'library/ASNValue.class.php';
|
||||||
require_once('library/asn1.php');
|
require_once 'library/asn1.php';
|
||||||
|
|
||||||
// supported algorithms are 'sha256', 'sha1'
|
// supported algorithms are 'sha256', 'sha1'
|
||||||
|
|
||||||
function rsa_sign($data, $key, $alg = 'sha256') {
|
function rsa_sign($data, $key, $alg = 'sha256') {
|
||||||
|
|
||||||
$sig = '';
|
|
||||||
if (version_compare(PHP_VERSION, '5.3.0', '>=') || $alg === 'sha1') {
|
|
||||||
openssl_sign($data, $sig, $key, (($alg == 'sha1') ? OPENSSL_ALGO_SHA1 : $alg));
|
openssl_sign($data, $sig, $key, (($alg == 'sha1') ? OPENSSL_ALGO_SHA1 : $alg));
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(strlen($key) < 1024 || extension_loaded('gmp')) {
|
|
||||||
require_once('library/phpsec/Crypt/RSA.php');
|
|
||||||
$rsa = new CRYPT_RSA();
|
|
||||||
$rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1;
|
|
||||||
$rsa->setHash($alg);
|
|
||||||
$rsa->loadKey($key);
|
|
||||||
$sig = $rsa->sign($data);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
logger('rsa_sign: insecure algorithm used. Please upgrade PHP to 5.3');
|
|
||||||
openssl_private_encrypt(hex2bin('3031300d060960864801650304020105000420') . hash('sha256',$data,true), $sig, $key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $sig;
|
return $sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
function rsa_verify($data, $sig, $key, $alg = 'sha256') {
|
function rsa_verify($data, $sig, $key, $alg = 'sha256') {
|
||||||
|
return openssl_verify($data, $sig, $key, (($alg == 'sha1') ? OPENSSL_ALGO_SHA1 : $alg));
|
||||||
if (version_compare(PHP_VERSION, '5.3.0', '>=') || $alg === 'sha1') {
|
|
||||||
$verify = openssl_verify($data,$sig,$key,(($alg == 'sha1') ? OPENSSL_ALGO_SHA1 : $alg));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if(strlen($key) <= 300 || extension_loaded('gmp')) {
|
|
||||||
require_once('library/phpsec/Crypt/RSA.php');
|
|
||||||
$rsa = new CRYPT_RSA();
|
|
||||||
$rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1;
|
|
||||||
$rsa->setHash($alg);
|
|
||||||
$rsa->loadKey($key);
|
|
||||||
$verify = $rsa->verify($data,$sig);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// fallback sha256 verify for PHP < 5.3 and large key lengths
|
|
||||||
$rawsig = '';
|
|
||||||
openssl_public_decrypt($sig,$rawsig,$key);
|
|
||||||
$verify = (($rawsig && substr($rawsig,-32) === hash('sha256',$data,true)) ? true : false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $verify;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function DerToPem($Der, $Private = false) {
|
||||||
function DerToPem($Der, $Private=false)
|
|
||||||
{
|
|
||||||
//Encode:
|
//Encode:
|
||||||
$Der = base64_encode($Der);
|
$Der = base64_encode($Der);
|
||||||
//Split lines:
|
//Split lines:
|
||||||
|
@ -70,8 +30,7 @@ function DerToPem($Der, $Private=false)
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DerToRsa($Der)
|
function DerToRsa($Der) {
|
||||||
{
|
|
||||||
//Encode:
|
//Encode:
|
||||||
$Der = base64_encode($Der);
|
$Der = base64_encode($Der);
|
||||||
//Split lines:
|
//Split lines:
|
||||||
|
@ -87,7 +46,6 @@ function DerToRsa($Der)
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function pkcs8_encode($Modulus, $PublicExponent) {
|
function pkcs8_encode($Modulus, $PublicExponent) {
|
||||||
//Encode key sequence
|
//Encode key sequence
|
||||||
$modulus = new ASNValue(ASNValue::TAG_INTEGER);
|
$modulus = new ASNValue(ASNValue::TAG_INTEGER);
|
||||||
|
@ -111,7 +69,6 @@ function pkcs8_encode($Modulus,$PublicExponent) {
|
||||||
return $PublicDER;
|
return $PublicDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function pkcs1_encode($Modulus, $PublicExponent) {
|
function pkcs1_encode($Modulus, $PublicExponent) {
|
||||||
//Encode key sequence
|
//Encode key sequence
|
||||||
$modulus = new ASNValue(ASNValue::TAG_INTEGER);
|
$modulus = new ASNValue(ASNValue::TAG_INTEGER);
|
||||||
|
@ -126,14 +83,12 @@ function pkcs1_encode($Modulus,$PublicExponent) {
|
||||||
return $bitStringValue;
|
return $bitStringValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function metopem($m, $e) {
|
function metopem($m, $e) {
|
||||||
$der = pkcs8_encode($m, $e);
|
$der = pkcs8_encode($m, $e);
|
||||||
$key = DerToPem($der, false);
|
$key = DerToPem($der, false);
|
||||||
return $key;
|
return $key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function pubrsatome($key,&$m,&$e) {
|
function pubrsatome($key,&$m,&$e) {
|
||||||
require_once('library/asn1.php');
|
require_once('library/asn1.php');
|
||||||
require_once('include/salmon.php');
|
require_once('include/salmon.php');
|
||||||
|
@ -152,12 +107,12 @@ function pubrsatome($key,&$m,&$e) {
|
||||||
|
|
||||||
function rsatopem($key) {
|
function rsatopem($key) {
|
||||||
pubrsatome($key, $m, $e);
|
pubrsatome($key, $m, $e);
|
||||||
return(metopem($m,$e));
|
return metopem($m, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pemtorsa($key) {
|
function pemtorsa($key) {
|
||||||
pemtome($key, $m, $e);
|
pemtome($key, $m, $e);
|
||||||
return(metorsa($m,$e));
|
return metorsa($m, $e);
|
||||||
}
|
}
|
||||||
|
|
||||||
function pemtome($key, &$m, &$e) {
|
function pemtome($key, &$m, &$e) {
|
||||||
|
@ -184,52 +139,7 @@ function salmon_key($pubkey) {
|
||||||
return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true) ;
|
return 'RSA' . '.' . base64url_encode($m, true) . '.' . base64url_encode($e, true) ;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if(! function_exists('aes_decrypt')) {
|
|
||||||
// DEPRECATED IN 3.4.1
|
|
||||||
function aes_decrypt($val,$ky)
|
|
||||||
{
|
|
||||||
$key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
|
||||||
for($a=0;$a<strlen($ky);$a++)
|
|
||||||
$key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a]));
|
|
||||||
$mode = MCRYPT_MODE_ECB;
|
|
||||||
$enc = MCRYPT_RIJNDAEL_128;
|
|
||||||
$dec = @mcrypt_decrypt($enc, $key, $val, $mode, @mcrypt_create_iv( @mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM ) );
|
|
||||||
return rtrim($dec,(( ord(substr($dec,strlen($dec)-1,1))>=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null));
|
|
||||||
}}
|
|
||||||
|
|
||||||
|
|
||||||
if(! function_exists('aes_encrypt')) {
|
|
||||||
// DEPRECATED IN 3.4.1
|
|
||||||
function aes_encrypt($val,$ky)
|
|
||||||
{
|
|
||||||
$key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
|
|
||||||
for($a=0;$a<strlen($ky);$a++)
|
|
||||||
$key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a]));
|
|
||||||
$mode=MCRYPT_MODE_ECB;
|
|
||||||
$enc=MCRYPT_RIJNDAEL_128;
|
|
||||||
$val=str_pad($val, (16*(floor(strlen($val) / 16)+(strlen($val) % 16==0?2:1))), chr(16-(strlen($val) % 16)));
|
|
||||||
return mcrypt_encrypt($enc, $key, $val, $mode, mcrypt_create_iv( mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM));
|
|
||||||
}}
|
|
||||||
|
|
||||||
function pkcs5_pad ($text, $blocksize)
|
|
||||||
{
|
|
||||||
$pad = $blocksize - (strlen($text) % $blocksize);
|
|
||||||
return $text . str_repeat(chr($pad), $pad);
|
|
||||||
}
|
|
||||||
|
|
||||||
function pkcs5_unpad($text)
|
|
||||||
{
|
|
||||||
$pad = ord($text{strlen($text)-1});
|
|
||||||
if ($pad > strlen($text)) return false;
|
|
||||||
if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false;
|
|
||||||
return substr($text, 0, -1 * $pad);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function new_keypair($bits) {
|
function new_keypair($bits) {
|
||||||
|
|
||||||
$openssl_options = array(
|
$openssl_options = array(
|
||||||
'digest_alg' => 'sha1',
|
'digest_alg' => 'sha1',
|
||||||
'private_key_bits' => $bits,
|
'private_key_bits' => $bits,
|
||||||
|
@ -237,9 +147,9 @@ function new_keypair($bits) {
|
||||||
);
|
);
|
||||||
|
|
||||||
$conf = get_config('system', 'openssl_conf_file');
|
$conf = get_config('system', 'openssl_conf_file');
|
||||||
if($conf)
|
if ($conf) {
|
||||||
$openssl_options['config'] = $conf;
|
$openssl_options['config'] = $conf;
|
||||||
|
}
|
||||||
$result = openssl_pkey_new($openssl_options);
|
$result = openssl_pkey_new($openssl_options);
|
||||||
|
|
||||||
if (empty($result)) {
|
if (empty($result)) {
|
||||||
|
@ -248,7 +158,6 @@ function new_keypair($bits) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get private key
|
// Get private key
|
||||||
|
|
||||||
$response = array('prvkey' => '', 'pubkey' => '');
|
$response = array('prvkey' => '', 'pubkey' => '');
|
||||||
|
|
||||||
openssl_pkey_export($result, $response['prvkey']);
|
openssl_pkey_export($result, $response['prvkey']);
|
||||||
|
@ -258,6 +167,4 @@ function new_keypair($bits) {
|
||||||
$response['pubkey'] = $pkey["key"];
|
$response['pubkey'] = $pkey["key"];
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -864,6 +864,30 @@ class dfrn {
|
||||||
return $entry;
|
return $entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief encrypts data via AES
|
||||||
|
*
|
||||||
|
* @param string $data The data that is to be encrypted
|
||||||
|
* @param string $key The AES key
|
||||||
|
*
|
||||||
|
* @return string encrypted data
|
||||||
|
*/
|
||||||
|
private static function aes_encrypt($data, $key) {
|
||||||
|
return openssl_encrypt($data, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief decrypts data via AES
|
||||||
|
*
|
||||||
|
* @param string $encrypted The encrypted data
|
||||||
|
* @param string $key The AES key
|
||||||
|
*
|
||||||
|
* @return string decrypted data
|
||||||
|
*/
|
||||||
|
public static function aes_decrypt($encrypted, $key) {
|
||||||
|
return openssl_decrypt($encrypted, 'aes-128-ecb', $key, OPENSSL_RAW_DATA);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Delivers the atom content to the contacts
|
* @brief Delivers the atom content to the contacts
|
||||||
*
|
*
|
||||||
|
@ -888,8 +912,6 @@ class dfrn {
|
||||||
|
|
||||||
$rino = get_config('system','rino_encrypt');
|
$rino = get_config('system','rino_encrypt');
|
||||||
$rino = intval($rino);
|
$rino = intval($rino);
|
||||||
// use RINO1 if mcrypt isn't installed and RINO2 was selected
|
|
||||||
if ($rino==2 and !function_exists('mcrypt_create_iv')) $rino=1;
|
|
||||||
|
|
||||||
logger("Local rino version: ". $rino, LOGGER_DEBUG);
|
logger("Local rino version: ". $rino, LOGGER_DEBUG);
|
||||||
|
|
||||||
|
@ -1015,8 +1037,8 @@ class dfrn {
|
||||||
switch($rino_remote_version) {
|
switch($rino_remote_version) {
|
||||||
case 1:
|
case 1:
|
||||||
// Deprecated rino version!
|
// Deprecated rino version!
|
||||||
$key = substr(random_string(),0,16);
|
$key = openssl_random_pseudo_bytes(16);
|
||||||
$data = aes_encrypt($postvars['data'],$key);
|
$data = self::aes_encrypt($postvars['data'], $key);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
// RINO 2 based on php-encryption
|
// RINO 2 based on php-encryption
|
||||||
|
@ -1352,7 +1374,9 @@ class dfrn {
|
||||||
$poco["photo"] = $author["avatar"];
|
$poco["photo"] = $author["avatar"];
|
||||||
$poco["hide"] = $hide;
|
$poco["hide"] = $hide;
|
||||||
$poco["contact-type"] = $contact["contact-type"];
|
$poco["contact-type"] = $contact["contact-type"];
|
||||||
update_gcontact($poco);
|
$gcid = update_gcontact($poco);
|
||||||
|
|
||||||
|
link_gcontact($gcid, $importer["uid"], $contact["id"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return($author);
|
return($author);
|
||||||
|
|
|
@ -10,17 +10,17 @@
|
||||||
|
|
||||||
use \Friendica\Core\Config;
|
use \Friendica\Core\Config;
|
||||||
|
|
||||||
require_once("include/items.php");
|
require_once 'include/items.php';
|
||||||
require_once("include/bb2diaspora.php");
|
require_once 'include/bb2diaspora.php';
|
||||||
require_once("include/Scrape.php");
|
require_once 'include/Scrape.php';
|
||||||
require_once("include/Contact.php");
|
require_once 'include/Contact.php';
|
||||||
require_once("include/Photo.php");
|
require_once 'include/Photo.php';
|
||||||
require_once("include/socgraph.php");
|
require_once 'include/socgraph.php';
|
||||||
require_once("include/group.php");
|
require_once 'include/group.php';
|
||||||
require_once("include/xml.php");
|
require_once 'include/xml.php';
|
||||||
require_once("include/datetime.php");
|
require_once 'include/datetime.php';
|
||||||
require_once("include/queue_fn.php");
|
require_once 'include/queue_fn.php';
|
||||||
require_once("include/cache.php");
|
require_once 'include/cache.php';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief This class contain functions to create and send Diaspora XML files
|
* @brief This class contain functions to create and send Diaspora XML files
|
||||||
|
@ -160,6 +160,32 @@ class Diaspora {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief encrypts data via AES
|
||||||
|
*
|
||||||
|
* @param string $key The AES key
|
||||||
|
* @param string $iv The IV (is used for CBC encoding)
|
||||||
|
* @param string $data The data that is to be encrypted
|
||||||
|
*
|
||||||
|
* @return string encrypted data
|
||||||
|
*/
|
||||||
|
private static function aes_encrypt($key, $iv, $data) {
|
||||||
|
return openssl_encrypt($data, 'aes-256-cbc', str_pad($key, 32, "\0"), OPENSSL_RAW_DATA, str_pad($iv, 16, "\0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief decrypts data via AES
|
||||||
|
*
|
||||||
|
* @param string $key The AES key
|
||||||
|
* @param string $iv The IV (is used for CBC encoding)
|
||||||
|
* @param string $encrypted The encrypted data
|
||||||
|
*
|
||||||
|
* @return string decrypted data
|
||||||
|
*/
|
||||||
|
private static function aes_decrypt($key, $iv, $encrypted) {
|
||||||
|
return openssl_decrypt($encrypted,'aes-256-cbc', str_pad($key, 32, "\0"), OPENSSL_RAW_DATA,str_pad($iv, 16, "\0"));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief: Decodes incoming Diaspora message
|
* @brief: Decodes incoming Diaspora message
|
||||||
*
|
*
|
||||||
|
@ -199,10 +225,7 @@ class Diaspora {
|
||||||
$outer_iv = base64_decode($j_outer_key_bundle->iv);
|
$outer_iv = base64_decode($j_outer_key_bundle->iv);
|
||||||
$outer_key = base64_decode($j_outer_key_bundle->key);
|
$outer_key = base64_decode($j_outer_key_bundle->key);
|
||||||
|
|
||||||
$decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv);
|
$decrypted = self::aes_decrypt($outer_key, $outer_iv, $ciphertext);
|
||||||
|
|
||||||
|
|
||||||
$decrypted = pkcs5_unpad($decrypted);
|
|
||||||
|
|
||||||
logger('decrypted: '.$decrypted, LOGGER_DEBUG);
|
logger('decrypted: '.$decrypted, LOGGER_DEBUG);
|
||||||
$idom = parse_xml_string($decrypted,false);
|
$idom = parse_xml_string($decrypted,false);
|
||||||
|
@ -261,8 +284,7 @@ class Diaspora {
|
||||||
// Decode the encrypted blob
|
// Decode the encrypted blob
|
||||||
|
|
||||||
$inner_encrypted = base64_decode($data);
|
$inner_encrypted = base64_decode($data);
|
||||||
$inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv);
|
$inner_decrypted = self::aes_decrypt($inner_aes_key, $inner_iv, $inner_encrypted);
|
||||||
$inner_decrypted = pkcs5_unpad($inner_decrypted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$author_link) {
|
if (!$author_link) {
|
||||||
|
@ -1848,18 +1870,15 @@ class Diaspora {
|
||||||
intval($importer["uid"])
|
intval($importer["uid"])
|
||||||
);
|
);
|
||||||
|
|
||||||
if ($searchable) {
|
|
||||||
poco_check($contact["url"], $name, NETWORK_DIASPORA, $image_url, $about, $location, $gender, $keywords, "",
|
|
||||||
datetime_convert(), 2, $contact["id"], $importer["uid"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$gcontact = array("url" => $contact["url"], "network" => NETWORK_DIASPORA, "generation" => 2,
|
$gcontact = array("url" => $contact["url"], "network" => NETWORK_DIASPORA, "generation" => 2,
|
||||||
"photo" => $image_url, "name" => $name, "location" => $location,
|
"photo" => $image_url, "name" => $name, "location" => $location,
|
||||||
"about" => $about, "birthday" => $birthday, "gender" => $gender,
|
"about" => $about, "birthday" => $birthday, "gender" => $gender,
|
||||||
"addr" => $author, "nick" => $nick, "keywords" => $keywords,
|
"addr" => $author, "nick" => $nick, "keywords" => $keywords,
|
||||||
"hide" => !$searchable, "nsfw" => $nsfw);
|
"hide" => !$searchable, "nsfw" => $nsfw);
|
||||||
|
|
||||||
update_gcontact($gcontact);
|
$gcid = update_gcontact($gcontact);
|
||||||
|
|
||||||
|
link_gcontact($gcid, $importer["uid"], $contact["id"]);
|
||||||
|
|
||||||
logger("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], LOGGER_DEBUG);
|
logger("Profile of contact ".$contact["id"]." stored for user ".$importer["uid"], LOGGER_DEBUG);
|
||||||
|
|
||||||
|
@ -2621,20 +2640,19 @@ class Diaspora {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$inner_aes_key = random_string(32);
|
$inner_aes_key = openssl_random_pseudo_bytes(32);
|
||||||
$b_inner_aes_key = base64_encode($inner_aes_key);
|
$b_inner_aes_key = base64_encode($inner_aes_key);
|
||||||
$inner_iv = random_string(16);
|
$inner_iv = openssl_random_pseudo_bytes(16);
|
||||||
$b_inner_iv = base64_encode($inner_iv);
|
$b_inner_iv = base64_encode($inner_iv);
|
||||||
|
|
||||||
$outer_aes_key = random_string(32);
|
$outer_aes_key = openssl_random_pseudo_bytes(32);
|
||||||
$b_outer_aes_key = base64_encode($outer_aes_key);
|
$b_outer_aes_key = base64_encode($outer_aes_key);
|
||||||
$outer_iv = random_string(16);
|
$outer_iv = openssl_random_pseudo_bytes(16);
|
||||||
$b_outer_iv = base64_encode($outer_iv);
|
$b_outer_iv = base64_encode($outer_iv);
|
||||||
|
|
||||||
$handle = self::my_handle($user);
|
$handle = self::my_handle($user);
|
||||||
|
|
||||||
$padded_data = pkcs5_pad($msg,16);
|
$inner_encrypted = self::aes_encrypt($inner_aes_key, $inner_iv, $msg);
|
||||||
$inner_encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $padded_data, MCRYPT_MODE_CBC, $inner_iv);
|
|
||||||
|
|
||||||
$b64_data = base64_encode($inner_encrypted);
|
$b64_data = base64_encode($inner_encrypted);
|
||||||
|
|
||||||
|
@ -2656,9 +2674,8 @@ class Diaspora {
|
||||||
"author_id" => $handle));
|
"author_id" => $handle));
|
||||||
|
|
||||||
$decrypted_header = xml::from_array($xmldata, $xml, true);
|
$decrypted_header = xml::from_array($xmldata, $xml, true);
|
||||||
$decrypted_header = pkcs5_pad($decrypted_header,16);
|
|
||||||
|
|
||||||
$ciphertext = mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $outer_aes_key, $decrypted_header, MCRYPT_MODE_CBC, $outer_iv);
|
$ciphertext = self::aes_encrypt($outer_aes_key, $outer_iv, $decrypted_header);
|
||||||
|
|
||||||
$outer_json = json_encode(array("iv" => $b_outer_iv, "key" => $b_outer_aes_key));
|
$outer_json = json_encode(array("iv" => $b_outer_iv, "key" => $b_outer_aes_key));
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,9 @@ function discover_poco_run(&$argv, &$argc) {
|
||||||
- suggestions: Discover other servers for their contacts.
|
- suggestions: Discover other servers for their contacts.
|
||||||
- server <poco url>: Searches for the poco server list. "poco url" is base64 encoded.
|
- server <poco url>: Searches for the poco server list. "poco url" is base64 encoded.
|
||||||
- update_server: Frequently check the first 250 servers for vitality.
|
- update_server: Frequently check the first 250 servers for vitality.
|
||||||
|
- update_server_directory: Discover the given server id for their contacts
|
||||||
|
- poco_load: Load POCO data from a given POCO address
|
||||||
|
- check_profile: Update remote profile data
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (($argc > 2) && ($argv[1] == "dirsearch")) {
|
if (($argc > 2) && ($argv[1] == "dirsearch")) {
|
||||||
|
@ -27,6 +30,12 @@ function discover_poco_run(&$argv, &$argc) {
|
||||||
$mode = 4;
|
$mode = 4;
|
||||||
} elseif (($argc == 2) && ($argv[1] == "update_server")) {
|
} elseif (($argc == 2) && ($argv[1] == "update_server")) {
|
||||||
$mode = 5;
|
$mode = 5;
|
||||||
|
} elseif (($argc == 3) && ($argv[1] == "update_server_directory")) {
|
||||||
|
$mode = 6;
|
||||||
|
} elseif (($argc > 5) && ($argv[1] == "poco_load")) {
|
||||||
|
$mode = 7;
|
||||||
|
} elseif (($argc == 3) && ($argv[1] == "check_profile")) {
|
||||||
|
$mode = 8;
|
||||||
} elseif ($argc == 1) {
|
} elseif ($argc == 1) {
|
||||||
$search = "";
|
$search = "";
|
||||||
$mode = 0;
|
$mode = 0;
|
||||||
|
@ -36,7 +45,21 @@ function discover_poco_run(&$argv, &$argc) {
|
||||||
|
|
||||||
logger('start '.$search);
|
logger('start '.$search);
|
||||||
|
|
||||||
if ($mode == 5) {
|
if ($mode == 8) {
|
||||||
|
$profile_url = base64_decode($argv[2]);
|
||||||
|
if ($profile_url != "") {
|
||||||
|
poco_last_updated($profile_url, true);
|
||||||
|
}
|
||||||
|
} elseif ($mode == 7) {
|
||||||
|
if ($argc == 6) {
|
||||||
|
$url = base64_decode($argv[5]);
|
||||||
|
} else {
|
||||||
|
$url = '';
|
||||||
|
}
|
||||||
|
poco_load_worker(intval($argv[2]), intval($argv[3]), intval($argv[4]), $url);
|
||||||
|
} elseif ($mode == 6) {
|
||||||
|
poco_discover_single_server(intval($argv[2]));
|
||||||
|
} elseif ($mode == 5) {
|
||||||
update_server();
|
update_server();
|
||||||
} elseif ($mode == 4) {
|
} elseif ($mode == 4) {
|
||||||
$server_url = base64_decode($argv[2]);
|
$server_url = base64_decode($argv[2]);
|
||||||
|
@ -106,7 +129,9 @@ function update_server() {
|
||||||
function discover_users() {
|
function discover_users() {
|
||||||
logger("Discover users", LOGGER_DEBUG);
|
logger("Discover users", LOGGER_DEBUG);
|
||||||
|
|
||||||
$users = q("SELECT `url`, `created`, `updated`, `last_failure`, `last_contact`, `server_url` FROM `gcontact`
|
$starttime = time();
|
||||||
|
|
||||||
|
$users = q("SELECT `url`, `created`, `updated`, `last_failure`, `last_contact`, `server_url`, `network` FROM `gcontact`
|
||||||
WHERE `last_contact` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND
|
WHERE `last_contact` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND
|
||||||
`last_failure` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND
|
`last_failure` < UTC_TIMESTAMP - INTERVAL 1 MONTH AND
|
||||||
`network` IN ('%s', '%s', '%s', '%s', '') ORDER BY rand()",
|
`network` IN ('%s', '%s', '%s', '%s', '') ORDER BY rand()",
|
||||||
|
@ -140,14 +165,19 @@ function discover_users() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($user["server_url"] != "") {
|
|
||||||
$server_url = $user["server_url"];
|
|
||||||
} else {
|
|
||||||
$server_url = poco_detect_server($user["url"]);
|
$server_url = poco_detect_server($user["url"]);
|
||||||
|
$force_update = false;
|
||||||
|
|
||||||
|
if ($user["server_url"] != "") {
|
||||||
|
|
||||||
|
$force_update = (normalise_link($user["server_url"]) != normalise_link($server_url));
|
||||||
|
|
||||||
|
$server_url = $user["server_url"];
|
||||||
}
|
}
|
||||||
if (($server_url == "") OR poco_check_server($server_url, $gcontacts[0]["network"])) {
|
|
||||||
logger('Check user '.$user["url"]);
|
if ((($server_url == "") AND ($user["network"] == NETWORK_FEED)) OR $force_update OR poco_check_server($server_url, $user["network"])) {
|
||||||
poco_last_updated($user["url"], true);
|
logger('Check profile '.$user["url"]);
|
||||||
|
proc_run(PRIORITY_LOW, "include/discover_poco.php", "check_profile", base64_encode($user["url"]));
|
||||||
|
|
||||||
if (++$checked > 100) {
|
if (++$checked > 100) {
|
||||||
return;
|
return;
|
||||||
|
@ -156,6 +186,11 @@ function discover_users() {
|
||||||
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
|
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
|
||||||
dbesc(datetime_convert()), dbesc(normalise_link($user["url"])));
|
dbesc(datetime_convert()), dbesc(normalise_link($user["url"])));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quit the loop after 3 minutes
|
||||||
|
if (time() > ($starttime + 180)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,7 +237,14 @@ function discover_directory($search) {
|
||||||
if ($data["network"] == NETWORK_DFRN) {
|
if ($data["network"] == NETWORK_DFRN) {
|
||||||
logger("Profile ".$jj->url." is reachable (".$search.")", LOGGER_DEBUG);
|
logger("Profile ".$jj->url." is reachable (".$search.")", LOGGER_DEBUG);
|
||||||
logger("Add profile ".$jj->url." to local directory (".$search.")", LOGGER_DEBUG);
|
logger("Add profile ".$jj->url." to local directory (".$search.")", LOGGER_DEBUG);
|
||||||
poco_check($data["url"], $data["name"], $data["network"], $data["photo"], "", "", "", $jj->tags, $data["addr"], "", 0);
|
|
||||||
|
if ($jj->tags != "") {
|
||||||
|
$data["keywords"] = $jj->tags;
|
||||||
|
}
|
||||||
|
|
||||||
|
$data["server_url"] = $data["baseurl"];
|
||||||
|
|
||||||
|
update_gcontact($data);
|
||||||
} else {
|
} else {
|
||||||
logger("Profile ".$jj->url." is not responding or no Friendica contact - but network ".$data["network"], LOGGER_DEBUG);
|
logger("Profile ".$jj->url." is not responding or no Friendica contact - but network ".$data["network"], LOGGER_DEBUG);
|
||||||
}
|
}
|
||||||
|
|
|
@ -79,16 +79,25 @@ function node2bbcodesub(&$doc, $oldnode, $attributes, $startbb, $endbb)
|
||||||
return($replace);
|
return($replace);
|
||||||
}
|
}
|
||||||
|
|
||||||
function _replace_code_cb($m){
|
|
||||||
return "<code>".str_replace("\n","<br>\n",$m[1]). "</code>";
|
|
||||||
}
|
|
||||||
|
|
||||||
function html2bbcode($message)
|
function html2bbcode($message)
|
||||||
{
|
{
|
||||||
|
|
||||||
$message = str_replace("\r", "", $message);
|
$message = str_replace("\r", "", $message);
|
||||||
|
|
||||||
$message = preg_replace_callback("|<pre><code>([^<]*)</code></pre>|ism", "_replace_code_cb", $message);
|
// Removing code blocks before the whitespace removal processing below
|
||||||
|
$codeblocks = [];
|
||||||
|
$message = preg_replace_callback('#<pre><code(?: class="([^"]*)")?>(.*)</code></pre>#iUs',
|
||||||
|
function ($matches) use (&$codeblocks) {
|
||||||
|
$return = '[codeblock-' . count($codeblocks) . ']';
|
||||||
|
|
||||||
|
$prefix = '[code]';
|
||||||
|
if ($matches[1] != '') {
|
||||||
|
$prefix = '[code=' . $matches[1] . ']';
|
||||||
|
}
|
||||||
|
$codeblocks[] = $prefix . $matches[2] . '[/code]';
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
, $message);
|
||||||
|
|
||||||
$message = str_replace(array(
|
$message = str_replace(array(
|
||||||
"<li><p>",
|
"<li><p>",
|
||||||
|
@ -232,7 +241,6 @@ function html2bbcode($message)
|
||||||
node2bbcode($doc, 'audio', array('src'=>'/(.+)/'), '[audio]$1', '[/audio]');
|
node2bbcode($doc, 'audio', array('src'=>'/(.+)/'), '[audio]$1', '[/audio]');
|
||||||
node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]');
|
node2bbcode($doc, 'iframe', array('src'=>'/(.+)/'), '[iframe]$1', '[/iframe]');
|
||||||
|
|
||||||
node2bbcode($doc, 'code', array(), '[code]', '[/code]');
|
|
||||||
node2bbcode($doc, 'key', array(), '[code]', '[/code]');
|
node2bbcode($doc, 'key', array(), '[code]', '[/code]');
|
||||||
|
|
||||||
$message = $doc->saveHTML();
|
$message = $doc->saveHTML();
|
||||||
|
@ -302,6 +310,19 @@ function html2bbcode($message)
|
||||||
// Handling Yahoo style of mails
|
// Handling Yahoo style of mails
|
||||||
$message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
|
$message = str_replace('[hr][b]From:[/b]', '[quote][b]From:[/b]', $message);
|
||||||
|
|
||||||
return(trim($message));
|
// Restore code blocks
|
||||||
|
$message = preg_replace_callback('#\[codeblock-([0-9]+)\]#iU',
|
||||||
|
function ($matches) use ($codeblocks) {
|
||||||
|
$return = '';
|
||||||
|
if (isset($codeblocks[intval($matches[1])])) {
|
||||||
|
$return = $codeblocks[$matches[1]];
|
||||||
|
}
|
||||||
|
return $return;
|
||||||
|
}
|
||||||
|
, $message);
|
||||||
|
|
||||||
|
$message = trim($message);
|
||||||
|
|
||||||
|
return $message;
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
|
|
|
@ -74,17 +74,6 @@ function oembed_fetch_url($embedurl, $no_rich_type = false){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($txt==false || $txt=="") {
|
|
||||||
$embedly = Config::get("system", "embedly");
|
|
||||||
if ($embedly != "") {
|
|
||||||
// try embedly service
|
|
||||||
$ourl = "https://api.embed.ly/1/oembed?key=".$embedly."&url=".urlencode($embedurl);
|
|
||||||
$txt = fetch_url($ourl);
|
|
||||||
|
|
||||||
logger("oembed_fetch_url: ".$txt, LOGGER_DEBUG);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$txt = trim($txt);
|
$txt = trim($txt);
|
||||||
|
|
||||||
if ($txt[0] != "{") {
|
if ($txt[0] != "{") {
|
||||||
|
|
|
@ -27,6 +27,42 @@ class ostatus {
|
||||||
const OSTATUS_DEFAULT_POLL_TIMEFRAME = 1440; // given in minutes
|
const OSTATUS_DEFAULT_POLL_TIMEFRAME = 1440; // given in minutes
|
||||||
const OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS = 14400; // given in minutes
|
const OSTATUS_DEFAULT_POLL_TIMEFRAME_MENTIONS = 14400; // given in minutes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Mix two paths together to possibly fix missing parts
|
||||||
|
*
|
||||||
|
* @param string $avatar Path to the avatar
|
||||||
|
* @param string $base Another path that is hopefully complete
|
||||||
|
*
|
||||||
|
* @return string fixed avatar path
|
||||||
|
*/
|
||||||
|
public static function fix_avatar($avatar, $base) {
|
||||||
|
$base_parts = parse_url($base);
|
||||||
|
|
||||||
|
// Remove all parts that could create a problem
|
||||||
|
unset($base_parts['path']);
|
||||||
|
unset($base_parts['query']);
|
||||||
|
unset($base_parts['fragment']);
|
||||||
|
|
||||||
|
$avatar_parts = parse_url($avatar);
|
||||||
|
|
||||||
|
// Now we mix them
|
||||||
|
$parts = array_merge($base_parts, $avatar_parts);
|
||||||
|
|
||||||
|
// And put them together again
|
||||||
|
$scheme = isset($parts['scheme']) ? $parts['scheme'] . '://' : '';
|
||||||
|
$host = isset($parts['host']) ? $parts['host'] : '';
|
||||||
|
$port = isset($parts['port']) ? ':' . $parts['port'] : '';
|
||||||
|
$path = isset($parts['path']) ? $parts['path'] : '';
|
||||||
|
$query = isset($parts['query']) ? '?' . $parts['query'] : '';
|
||||||
|
$fragment = isset($parts['fragment']) ? '#' . $parts['fragment'] : '';
|
||||||
|
|
||||||
|
$fixed = $scheme.$host.$port.$path.$query.$fragment;
|
||||||
|
|
||||||
|
logger('Base: '.$base.' - Avatar: '.$avatar.' - Fixed: '.$fixed, LOGGER_DATA);
|
||||||
|
|
||||||
|
return $fixed;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Fetches author data
|
* @brief Fetches author data
|
||||||
*
|
*
|
||||||
|
@ -77,7 +113,7 @@ class ostatus {
|
||||||
}
|
}
|
||||||
if (count($avatarlist) > 0) {
|
if (count($avatarlist) > 0) {
|
||||||
krsort($avatarlist);
|
krsort($avatarlist);
|
||||||
$author["author-avatar"] = current($avatarlist);
|
$author["author-avatar"] = self::fix_avatar(current($avatarlist), $author["author-link"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue;
|
$displayname = $xpath->evaluate('atom:author/poco:displayName/text()', $context)->item(0)->nodeValue;
|
||||||
|
@ -132,9 +168,6 @@ class ostatus {
|
||||||
dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["alias"]),
|
dbesc($contact["name"]), dbesc($contact["nick"]), dbesc($contact["alias"]),
|
||||||
dbesc($contact["about"]), dbesc($contact["location"]),
|
dbesc($contact["about"]), dbesc($contact["location"]),
|
||||||
dbesc(datetime_convert()), intval($contact["id"]));
|
dbesc(datetime_convert()), intval($contact["id"]));
|
||||||
|
|
||||||
poco_check($contact["url"], $contact["name"], $contact["network"], $author["author-avatar"], $contact["about"], $contact["location"],
|
|
||||||
"", "", "", datetime_convert(), 2, $contact["id"], $contact["uid"]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($author["author-avatar"]) AND ($author["author-avatar"] != $r[0]['avatar'])) {
|
if (isset($author["author-avatar"]) AND ($author["author-avatar"] != $r[0]['avatar'])) {
|
||||||
|
@ -163,7 +196,9 @@ class ostatus {
|
||||||
$contact["generation"] = 2;
|
$contact["generation"] = 2;
|
||||||
$contact["hide"] = false; // OStatus contacts are never hidden
|
$contact["hide"] = false; // OStatus contacts are never hidden
|
||||||
$contact["photo"] = $author["author-avatar"];
|
$contact["photo"] = $author["author-avatar"];
|
||||||
update_gcontact($contact);
|
$gcid = update_gcontact($contact);
|
||||||
|
|
||||||
|
link_gcontact($gcid, $contact["uid"], $contact["id"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return($author);
|
return($author);
|
||||||
|
@ -501,12 +536,16 @@ class ostatus {
|
||||||
$item["author-name"] = $orig_author["author-name"];
|
$item["author-name"] = $orig_author["author-name"];
|
||||||
$item["author-link"] = $orig_author["author-link"];
|
$item["author-link"] = $orig_author["author-link"];
|
||||||
$item["author-avatar"] = $orig_author["author-avatar"];
|
$item["author-avatar"] = $orig_author["author-avatar"];
|
||||||
|
|
||||||
$item["body"] = add_page_info_to_body(html2bbcode($orig_body));
|
$item["body"] = add_page_info_to_body(html2bbcode($orig_body));
|
||||||
$item["created"] = $orig_created;
|
$item["created"] = $orig_created;
|
||||||
$item["edited"] = $orig_edited;
|
$item["edited"] = $orig_edited;
|
||||||
|
|
||||||
$item["uri"] = $orig_uri;
|
$item["uri"] = $orig_uri;
|
||||||
|
|
||||||
|
if (!isset($item["plink"])) {
|
||||||
$item["plink"] = $orig_link;
|
$item["plink"] = $orig_link;
|
||||||
|
}
|
||||||
|
|
||||||
$item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue;
|
$item["verb"] = $xpath->query('activity:verb/text()', $activityobjects)->item(0)->nodeValue;
|
||||||
|
|
||||||
|
@ -800,6 +839,9 @@ class ostatus {
|
||||||
|
|
||||||
/// @todo This function is totally ugly and has to be rewritten totally
|
/// @todo This function is totally ugly and has to be rewritten totally
|
||||||
|
|
||||||
|
// Import all threads or only threads that were started by our followers?
|
||||||
|
$all_threads = !get_config('system','ostatus_full_threads');
|
||||||
|
|
||||||
$item_stored = -1;
|
$item_stored = -1;
|
||||||
|
|
||||||
$conversation_url = self::fetch_conversation($self, $conversation_url);
|
$conversation_url = self::fetch_conversation($self, $conversation_url);
|
||||||
|
@ -808,8 +850,8 @@ class ostatus {
|
||||||
// Don't do a completion on liked content
|
// Don't do a completion on liked content
|
||||||
if (((intval(get_config('system','ostatus_poll_interval')) == -2) AND (count($item) > 0)) OR
|
if (((intval(get_config('system','ostatus_poll_interval')) == -2) AND (count($item) > 0)) OR
|
||||||
($item["verb"] == ACTIVITY_LIKE) OR ($conversation_url == "")) {
|
($item["verb"] == ACTIVITY_LIKE) OR ($conversation_url == "")) {
|
||||||
$item_stored = item_store($item, true);
|
$item_stored = item_store($item, $all_threads);
|
||||||
return($item_stored);
|
return $item_stored;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the parent
|
// Get the parent
|
||||||
|
@ -889,7 +931,7 @@ class ostatus {
|
||||||
|
|
||||||
if (!sizeof($items)) {
|
if (!sizeof($items)) {
|
||||||
if (count($item) > 0) {
|
if (count($item) > 0) {
|
||||||
$item_stored = item_store($item, true);
|
$item_stored = item_store($item, $all_threads);
|
||||||
|
|
||||||
if ($item_stored) {
|
if ($item_stored) {
|
||||||
logger("Conversation ".$conversation_url." couldn't be fetched. Item uri ".$item["uri"]." stored: ".$item_stored, LOGGER_DEBUG);
|
logger("Conversation ".$conversation_url." couldn't be fetched. Item uri ".$item["uri"]." stored: ".$item_stored, LOGGER_DEBUG);
|
||||||
|
@ -1066,10 +1108,11 @@ class ostatus {
|
||||||
$arr["owner-name"] = $single_conv->actor->portablecontacts_net->displayName;
|
$arr["owner-name"] = $single_conv->actor->portablecontacts_net->displayName;
|
||||||
|
|
||||||
$arr["owner-link"] = $actor;
|
$arr["owner-link"] = $actor;
|
||||||
$arr["owner-avatar"] = $single_conv->actor->image->url;
|
$arr["owner-avatar"] = self::fix_avatar($single_conv->actor->image->url, $arr["owner-link"]);
|
||||||
|
|
||||||
$arr["author-name"] = $arr["owner-name"];
|
$arr["author-name"] = $arr["owner-name"];
|
||||||
$arr["author-link"] = $actor;
|
$arr["author-link"] = $arr["owner-link"];
|
||||||
$arr["author-avatar"] = $single_conv->actor->image->url;
|
$arr["author-avatar"] = $arr["owner-avatar"];
|
||||||
$arr["body"] = add_page_info_to_body(html2bbcode($single_conv->content));
|
$arr["body"] = add_page_info_to_body(html2bbcode($single_conv->content));
|
||||||
|
|
||||||
if (isset($single_conv->status_net->notice_info->source))
|
if (isset($single_conv->status_net->notice_info->source))
|
||||||
|
@ -1120,11 +1163,11 @@ class ostatus {
|
||||||
$arr["edited"] = $single_conv->object->published;
|
$arr["edited"] = $single_conv->object->published;
|
||||||
|
|
||||||
$arr["author-name"] = $single_conv->object->actor->displayName;
|
$arr["author-name"] = $single_conv->object->actor->displayName;
|
||||||
if ($arr["owner-name"] == '')
|
if ($arr["owner-name"] == '') {
|
||||||
$arr["author-name"] = $single_conv->object->actor->contact->displayName;
|
$arr["author-name"] = $single_conv->object->actor->contact->displayName;
|
||||||
|
}
|
||||||
$arr["author-link"] = $single_conv->object->actor->url;
|
$arr["author-link"] = $single_conv->object->actor->url;
|
||||||
$arr["author-avatar"] = $single_conv->object->actor->image->url;
|
$arr["author-avatar"] = self::fix_avatar($single_conv->object->actor->image->url, $arr["author-link"]);
|
||||||
|
|
||||||
$arr["app"] = $single_conv->object->provider->displayName."#";
|
$arr["app"] = $single_conv->object->provider->displayName."#";
|
||||||
//$arr["verb"] = $single_conv->object->verb;
|
//$arr["verb"] = $single_conv->object->verb;
|
||||||
|
@ -1187,7 +1230,7 @@ class ostatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$item_stored = item_store($item, true);
|
$item_stored = item_store($item, $all_threads);
|
||||||
if ($item_stored) {
|
if ($item_stored) {
|
||||||
logger("Uri ".$item["uri"]." wasn't found in conversation ".$conversation_url, LOGGER_DEBUG);
|
logger("Uri ".$item["uri"]." wasn't found in conversation ".$conversation_url, LOGGER_DEBUG);
|
||||||
self::store_conversation($item_stored, $conversation_url);
|
self::store_conversation($item_stored, $conversation_url);
|
||||||
|
|
|
@ -14,8 +14,13 @@ require_once("include/html2bbcode.php");
|
||||||
require_once("include/Contact.php");
|
require_once("include/Contact.php");
|
||||||
require_once("include/Photo.php");
|
require_once("include/Photo.php");
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* poco_load
|
* @brief Fetch POCO data
|
||||||
|
*
|
||||||
|
* @param integer $cid Contact ID
|
||||||
|
* @param integer $uid User ID
|
||||||
|
* @param integer $zcid Global Contact ID
|
||||||
|
* @param integer $url POCO address that should be polled
|
||||||
*
|
*
|
||||||
* Given a contact-id (minimum), load the PortableContacts friend list for that contact,
|
* Given a contact-id (minimum), load the PortableContacts friend list for that contact,
|
||||||
* and add the entries to the gcontact (Global Contact) table, or update existing entries
|
* and add the entries to the gcontact (Global Contact) table, or update existing entries
|
||||||
|
@ -27,12 +32,21 @@ require_once("include/Photo.php");
|
||||||
* pointing to the same global contact id.
|
* pointing to the same global contact id.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function poco_load($cid, $uid = 0, $zcid = 0, $url = null) {
|
function poco_load($cid, $uid = 0, $zcid = 0, $url = null) {
|
||||||
|
// Call the function "poco_load_worker" via the worker
|
||||||
|
proc_run(PRIORITY_LOW, "include/discover_poco.php", "poco_load", intval($cid), intval($uid), intval($zcid), base64_encode($url));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fetch POCO data from the worker
|
||||||
|
*
|
||||||
|
* @param integer $cid Contact ID
|
||||||
|
* @param integer $uid User ID
|
||||||
|
* @param integer $zcid Global Contact ID
|
||||||
|
* @param integer $url POCO address that should be polled
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function poco_load_worker($cid, $uid, $zcid, $url) {
|
||||||
$a = get_app();
|
$a = get_app();
|
||||||
|
|
||||||
if($cid) {
|
if($cid) {
|
||||||
|
@ -145,27 +159,27 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
||||||
if (isset($entry->contactType) AND ($entry->contactType >= 0))
|
if (isset($entry->contactType) AND ($entry->contactType >= 0))
|
||||||
$contact_type = $entry->contactType;
|
$contact_type = $entry->contactType;
|
||||||
|
|
||||||
// If you query a Friendica server for its profiles, the network has to be Friendica
|
$gcontact = array("url" => $profile_url,
|
||||||
/// TODO It could also be a Redmatrix server
|
"name" => $name,
|
||||||
//if ($uid == 0)
|
"network" => $network,
|
||||||
// $network = NETWORK_DFRN;
|
"photo" => $profile_photo,
|
||||||
|
"about" => $about,
|
||||||
|
"location" => $location,
|
||||||
|
"gender" => $gender,
|
||||||
|
"keywords" => $keywords,
|
||||||
|
"connect" => $connect_url,
|
||||||
|
"updated" => $updated,
|
||||||
|
"contact-type" => $contact_type,
|
||||||
|
"generation" => $generation);
|
||||||
|
|
||||||
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
|
try {
|
||||||
|
$gcontact = sanitize_gcontact($gcontact);
|
||||||
|
$gcid = update_gcontact($gcontact);
|
||||||
|
|
||||||
$gcontact = array("url" => $profile_url, "contact-type" => $contact_type, "generation" => $generation);
|
link_gcontact($gcid, $uid, $cid, $zcid);
|
||||||
update_gcontact($gcontact);
|
} catch (Exception $e) {
|
||||||
|
logger($e->getMessage(), LOGGER_DEBUG);
|
||||||
// Update the Friendica contacts. Diaspora is doing it via a message. (See include/diaspora.php)
|
}
|
||||||
// Deactivated because we now update Friendica contacts in dfrn.php
|
|
||||||
//if (($location != "") OR ($about != "") OR ($keywords != "") OR ($gender != ""))
|
|
||||||
// q("UPDATE `contact` SET `location` = '%s', `about` = '%s', `keywords` = '%s', `gender` = '%s'
|
|
||||||
// WHERE `nurl` = '%s' AND NOT `self` AND `network` = '%s'",
|
|
||||||
// dbesc($location),
|
|
||||||
// dbesc($about),
|
|
||||||
// dbesc($keywords),
|
|
||||||
// dbesc($gender),
|
|
||||||
// dbesc(normalise_link($profile_url)),
|
|
||||||
// dbesc(NETWORK_DFRN));
|
|
||||||
}
|
}
|
||||||
logger("poco_load: loaded $total entries",LOGGER_DEBUG);
|
logger("poco_load: loaded $total entries",LOGGER_DEBUG);
|
||||||
|
|
||||||
|
@ -176,172 +190,158 @@ function poco_load($cid,$uid = 0,$zcid = 0,$url = null) {
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @brief Sanitize the given gcontact data
|
||||||
|
*
|
||||||
|
* @param array $gcontact array with gcontact data
|
||||||
|
* @throw Exception
|
||||||
|
*
|
||||||
|
* Generation:
|
||||||
|
* 0: No definition
|
||||||
|
* 1: Profiles on this server
|
||||||
|
* 2: Contacts of profiles on this server
|
||||||
|
* 3: Contacts of contacts of profiles on this server
|
||||||
|
* 4: ...
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function sanitize_gcontact($gcontact) {
|
||||||
|
|
||||||
function poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid = 0, $uid = 0, $zcid = 0) {
|
if ($gcontact['url'] == "") {
|
||||||
|
throw new Exception('URL is empty');
|
||||||
|
}
|
||||||
|
|
||||||
// Generation:
|
$urlparts = parse_url($gcontact['url']);
|
||||||
// 0: No definition
|
if (!isset($urlparts["scheme"])) {
|
||||||
// 1: Profiles on this server
|
throw new Exception("This (".$gcontact['url'].") doesn't seem to be an url.");
|
||||||
// 2: Contacts of profiles on this server
|
}
|
||||||
// 3: Contacts of contacts of profiles on this server
|
|
||||||
// 4: ...
|
|
||||||
|
|
||||||
$gcid = "";
|
|
||||||
|
|
||||||
if ($profile_url == "")
|
|
||||||
return $gcid;
|
|
||||||
|
|
||||||
$urlparts = parse_url($profile_url);
|
|
||||||
if (!isset($urlparts["scheme"]))
|
|
||||||
return $gcid;
|
|
||||||
|
|
||||||
if (in_array($urlparts["host"], array("www.facebook.com", "facebook.com", "twitter.com",
|
if (in_array($urlparts["host"], array("www.facebook.com", "facebook.com", "twitter.com",
|
||||||
"identi.ca", "alpha.app.net")))
|
"identi.ca", "alpha.app.net"))) {
|
||||||
return $gcid;
|
throw new Exception('Contact from a non federated network ignored. ('.$gcontact['url'].')');
|
||||||
|
}
|
||||||
|
|
||||||
// Don't store the statusnet connector as network
|
// Don't store the statusnet connector as network
|
||||||
// We can't simply set this to NETWORK_OSTATUS since the connector could have fetched posts from friendica as well
|
// We can't simply set this to NETWORK_OSTATUS since the connector could have fetched posts from friendica as well
|
||||||
if ($network == NETWORK_STATUSNET)
|
if ($gcontact['network'] == NETWORK_STATUSNET) {
|
||||||
$network = "";
|
$gcontact['network'] = "";
|
||||||
|
}
|
||||||
|
|
||||||
// Assure that there are no parameter fragments in the profile url
|
// Assure that there are no parameter fragments in the profile url
|
||||||
if (in_array($network, array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, "")))
|
if (in_array($gcontact['network'], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_OSTATUS, ""))) {
|
||||||
$profile_url = clean_contact_url($profile_url);
|
$gcontact['url'] = clean_contact_url($gcontact['url']);
|
||||||
|
}
|
||||||
|
|
||||||
$alternate = poco_alternate_ostatus_url($profile_url);
|
$alternate = poco_alternate_ostatus_url($gcontact['url']);
|
||||||
|
|
||||||
$orig_updated = $updated;
|
|
||||||
|
|
||||||
// The global contacts should contain the original picture, not the cached one
|
// The global contacts should contain the original picture, not the cached one
|
||||||
if (($generation != 1) AND stristr(normalise_link($profile_photo), normalise_link(App::get_baseurl()."/photo/"))) {
|
if (($gcontact['generation'] != 1) AND stristr(normalise_link($gcontact['photo']), normalise_link(App::get_baseurl()."/photo/"))) {
|
||||||
$profile_photo = "";
|
$gcontact['photo'] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
$r = q("SELECT `network` FROM `contact` WHERE `nurl` = '%s' AND `network` != '' AND `network` != '%s' LIMIT 1",
|
if (!isset($gcontact['network'])) {
|
||||||
dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
|
$r = q("SELECT `network` FROM `contact` WHERE `uid` = 0 AND `nurl` = '%s' AND `network` != '' AND `network` != '%s' LIMIT 1",
|
||||||
|
dbesc(normalise_link($gcontact['url'])), dbesc(NETWORK_STATUSNET)
|
||||||
);
|
);
|
||||||
if (dbm::is_result($r)) {
|
if (dbm::is_result($r)) {
|
||||||
$network = $r[0]["network"];
|
$gcontact['network'] = $r[0]["network"];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($network == "") OR ($network == NETWORK_OSTATUS)) {
|
if (($gcontact['network'] == "") OR ($gcontact['network'] == NETWORK_OSTATUS)) {
|
||||||
$r = q("SELECT `network`, `url` FROM `contact` WHERE `alias` IN ('%s', '%s') AND `network` != '' AND `network` != '%s' LIMIT 1",
|
$r = q("SELECT `network`, `url` FROM `contact` WHERE `uid` = 0 AND `alias` IN ('%s', '%s') AND `network` != '' AND `network` != '%s' LIMIT 1",
|
||||||
dbesc($profile_url), dbesc(normalise_link($profile_url)), dbesc(NETWORK_STATUSNET)
|
dbesc($gcontact['url']), dbesc(normalise_link($gcontact['url'])), dbesc(NETWORK_STATUSNET)
|
||||||
);
|
);
|
||||||
if (dbm::is_result($r)) {
|
if (dbm::is_result($r)) {
|
||||||
$network = $r[0]["network"];
|
$gcontact['network'] = $r[0]["network"];
|
||||||
//$profile_url = $r[0]["url"];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$gcontact['server_url'] = '';
|
||||||
|
$gcontact['network'] = '';
|
||||||
|
|
||||||
$x = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
|
$x = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s' LIMIT 1",
|
||||||
dbesc(normalise_link($profile_url))
|
dbesc(normalise_link($gcontact['url']))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (count($x)) {
|
if (count($x)) {
|
||||||
if (($network == "") AND ($x[0]["network"] != NETWORK_STATUSNET)) {
|
if (!isset($gcontact['network']) AND ($x[0]["network"] != NETWORK_STATUSNET)) {
|
||||||
$network = $x[0]["network"];
|
$gcontact['network'] = $x[0]["network"];
|
||||||
}
|
}
|
||||||
if ($updated <= NULL_DATE) {
|
if ($gcontact['updated'] <= NULL_DATE) {
|
||||||
$updated = $x[0]["updated"];
|
$gcontact['updated'] = $x[0]["updated"];
|
||||||
|
}
|
||||||
|
if (!isset($gcontact['server_url']) AND (normalise_link($x[0]["server_url"]) != normalise_link($x[0]["url"]))) {
|
||||||
|
$gcontact['server_url'] = $x[0]["server_url"];
|
||||||
|
}
|
||||||
|
if (!isset($gcontact['addr'])) {
|
||||||
|
$gcontact['addr'] = $x[0]["addr"];
|
||||||
}
|
}
|
||||||
$created = $x[0]["created"];
|
|
||||||
$server_url = $x[0]["server_url"];
|
|
||||||
$nick = $x[0]["nick"];
|
|
||||||
$addr = $x[0]["addr"];
|
|
||||||
$alias = $x[0]["alias"];
|
|
||||||
$notify = $x[0]["notify"];
|
|
||||||
} else {
|
|
||||||
$created = NULL_DATE;
|
|
||||||
$server_url = "";
|
|
||||||
|
|
||||||
$urlparts = parse_url($profile_url);
|
|
||||||
$nick = end(explode("/", $urlparts["path"]));
|
|
||||||
$addr = "";
|
|
||||||
$alias = "";
|
|
||||||
$notify = "";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((($network == "") OR ($name == "") OR ($addr == "") OR ($profile_photo == "") OR ($server_url == "") OR $alternate)
|
if ((!isset($gcontact['network']) OR !isset($gcontact['name']) OR !isset($gcontact['addr']) OR !isset($gcontact['photo']) OR !isset($gcontact['server_url']) OR $alternate)
|
||||||
AND poco_reachable($profile_url, $server_url, $network, false)) {
|
AND poco_reachable($gcontact['url'], $gcontact['server_url'], $gcontact['network'], false)) {
|
||||||
$data = probe_url($profile_url);
|
$data = Probe::uri($gcontact['url']);
|
||||||
|
|
||||||
$orig_profile = $profile_url;
|
if ($data["network"] == NETWORK_PHANTOM) {
|
||||||
|
throw new Exception('Probing for URL '.$gcontact['url'].' failed');
|
||||||
|
}
|
||||||
|
|
||||||
$network = $data["network"];
|
$orig_profile = $gcontact['url'];
|
||||||
$name = $data["name"];
|
|
||||||
$nick = $data["nick"];
|
|
||||||
$addr = $data["addr"];
|
|
||||||
$alias = $data["alias"];
|
|
||||||
$notify = $data["notify"];
|
|
||||||
$profile_url = $data["url"];
|
|
||||||
$profile_photo = $data["photo"];
|
|
||||||
$server_url = $data["baseurl"];
|
|
||||||
|
|
||||||
if ($alternate AND ($network == NETWORK_OSTATUS)) {
|
$gcontact["server_url"] = $data["baseurl"];
|
||||||
|
|
||||||
|
$gcontact = array_merge($gcontact, $data);
|
||||||
|
|
||||||
|
if ($alternate AND ($gcontact['network'] == NETWORK_OSTATUS)) {
|
||||||
// Delete the old entry - if it exists
|
// Delete the old entry - if it exists
|
||||||
$r = q("SELECT `id` FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
|
$r = q("SELECT `id` FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
|
||||||
if ($r) {
|
if ($r) {
|
||||||
q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
|
q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($orig_profile)));
|
||||||
q("DELETE FROM `glink` WHERE `gcid` = %d", intval($r[0]["id"]));
|
q("DELETE FROM `glink` WHERE `gcid` = %d", intval($r[0]["id"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// possibly create a new entry
|
|
||||||
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, $cid, $uid, $zcid);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($alternate AND ($network == NETWORK_OSTATUS))
|
if (!isset($gcontact['name']) OR !isset($gcontact['photo'])) {
|
||||||
return $gcid;
|
throw new Exception('No name and photo for URL '.$gcontact['url']);
|
||||||
|
|
||||||
if (count($x) AND ($x[0]["network"] == "") AND ($network != "")) {
|
|
||||||
q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
|
|
||||||
dbesc($network),
|
|
||||||
dbesc(normalise_link($profile_url))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($name == "") OR ($profile_photo == ""))
|
if (!in_array($gcontact['network'], array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA))) {
|
||||||
return $gcid;
|
throw new Exception('No federated network ('.$gcontact['network'].') detected for URL '.$gcontact['url']);
|
||||||
|
}
|
||||||
if (!in_array($network, array(NETWORK_DFRN, NETWORK_OSTATUS, NETWORK_DIASPORA)))
|
|
||||||
return $gcid;
|
|
||||||
|
|
||||||
logger("profile-check generation: ".$generation." Network: ".$network." URL: ".$profile_url." name: ".$name." avatar: ".$profile_photo, LOGGER_DEBUG);
|
|
||||||
|
|
||||||
|
if (!isset($gcontact['server_url'])) {
|
||||||
// We check the server url to be sure that it is a real one
|
// We check the server url to be sure that it is a real one
|
||||||
$server_url2 = poco_detect_server($profile_url);
|
$server_url = poco_detect_server($gcontact['url']);
|
||||||
|
|
||||||
// We are no sure that it is a correct URL. So we use it in the future
|
// We are now sure that it is a correct URL. So we use it in the future
|
||||||
if ($server_url2 != "") {
|
if ($server_url != "") {
|
||||||
$server_url = $server_url2;
|
$gcontact['server_url'] = $server_url;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The server URL doesn't seem to be valid, so we don't store it.
|
// The server URL doesn't seem to be valid, so we don't store it.
|
||||||
if (!poco_check_server($server_url, $network)) {
|
if (!poco_check_server($gcontact['server_url'], $gcontact['network'])) {
|
||||||
$server_url = "";
|
$gcontact['server_url'] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
$gcontact = array("url" => $profile_url,
|
return $gcontact;
|
||||||
"addr" => $addr,
|
}
|
||||||
"alias" => $alias,
|
|
||||||
"name" => $name,
|
|
||||||
"network" => $network,
|
|
||||||
"photo" => $profile_photo,
|
|
||||||
"about" => $about,
|
|
||||||
"location" => $location,
|
|
||||||
"gender" => $gender,
|
|
||||||
"keywords" => $keywords,
|
|
||||||
"server_url" => $server_url,
|
|
||||||
"connect" => $connect_url,
|
|
||||||
"notify" => $notify,
|
|
||||||
"updated" => $updated,
|
|
||||||
"generation" => $generation);
|
|
||||||
|
|
||||||
$gcid = update_gcontact($gcontact);
|
/**
|
||||||
|
* @brief Link the gcontact entry with user, contact and global contact
|
||||||
|
*
|
||||||
|
* @param integer $gcid Global contact ID
|
||||||
|
* @param integer $cid Contact ID
|
||||||
|
* @param integer $uid User ID
|
||||||
|
* @param integer $zcid Global Contact ID
|
||||||
|
* *
|
||||||
|
*/
|
||||||
|
function link_gcontact($gcid, $uid = 0, $cid = 0, $zcid = 0) {
|
||||||
|
|
||||||
if(!$gcid)
|
if ($gcid <= 0) {
|
||||||
return $gcid;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$r = q("SELECT * FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d LIMIT 1",
|
$r = q("SELECT * FROM `glink` WHERE `cid` = %d AND `uid` = %d AND `gcid` = %d AND `zcid` = %d LIMIT 1",
|
||||||
intval($cid),
|
intval($cid),
|
||||||
|
@ -366,8 +366,6 @@ function poco_check($profile_url, $name, $network, $profile_photo, $about, $loca
|
||||||
intval($zcid)
|
intval($zcid)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $gcid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function poco_reachable($profile, $server = "", $network = "", $force = false) {
|
function poco_reachable($profile, $server = "", $network = "", $force = false) {
|
||||||
|
@ -465,15 +463,26 @@ function poco_last_updated($profile, $force = false) {
|
||||||
$gcontacts = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'",
|
$gcontacts = q("SELECT * FROM `gcontact` WHERE `nurl` = '%s'",
|
||||||
dbesc(normalise_link($profile)));
|
dbesc(normalise_link($profile)));
|
||||||
|
|
||||||
if ($gcontacts[0]["created"] <= NULL_DATE) {
|
if (!dbm::is_result($gcontacts)) {
|
||||||
q("UPDATE `gcontact` SET `created` = '%s' WHERE `nurl` = '%s'",
|
return false;
|
||||||
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
|
|
||||||
}
|
}
|
||||||
if ($gcontacts[0]["server_url"] != "") {
|
|
||||||
|
$contact = array("url" => $profile);
|
||||||
|
|
||||||
|
if ($gcontacts[0]["created"] <= NULL_DATE) {
|
||||||
|
$contact['created'] = datetime_convert();
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($force) {
|
||||||
|
$server_url = normalise_link(poco_detect_server($profile));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($server_url == '') AND ($gcontacts[0]["server_url"] != "")) {
|
||||||
$server_url = $gcontacts[0]["server_url"];
|
$server_url = $gcontacts[0]["server_url"];
|
||||||
}
|
}
|
||||||
if (($server_url == '') OR ($gcontacts[0]["server_url"] == $gcontacts[0]["nurl"])) {
|
|
||||||
$server_url = poco_detect_server($profile);
|
if (!$force AND (($server_url == '') OR ($gcontacts[0]["server_url"] == $gcontacts[0]["nurl"]))) {
|
||||||
|
$server_url = normalise_link(poco_detect_server($profile));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!in_array($gcontacts[0]["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_FEED, NETWORK_OSTATUS, ""))) {
|
if (!in_array($gcontacts[0]["network"], array(NETWORK_DFRN, NETWORK_DIASPORA, NETWORK_FEED, NETWORK_OSTATUS, ""))) {
|
||||||
|
@ -483,35 +492,33 @@ function poco_last_updated($profile, $force = false) {
|
||||||
|
|
||||||
if ($server_url != "") {
|
if ($server_url != "") {
|
||||||
if (!poco_check_server($server_url, $gcontacts[0]["network"], $force)) {
|
if (!poco_check_server($server_url, $gcontacts[0]["network"], $force)) {
|
||||||
|
if ($force) {
|
||||||
if ($force)
|
|
||||||
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
|
q("UPDATE `gcontact` SET `last_failure` = '%s' WHERE `nurl` = '%s'",
|
||||||
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
|
dbesc(datetime_convert()), dbesc(normalise_link($profile)));
|
||||||
|
}
|
||||||
|
|
||||||
logger("Profile ".$profile.": Server ".$server_url." wasn't reachable.", LOGGER_DEBUG);
|
logger("Profile ".$profile.": Server ".$server_url." wasn't reachable.", LOGGER_DEBUG);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
$contact['server_url'] = $server_url;
|
||||||
q("UPDATE `gcontact` SET `server_url` = '%s' WHERE `nurl` = '%s'",
|
|
||||||
dbesc($server_url), dbesc(normalise_link($profile)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (in_array($gcontacts[0]["network"], array("", NETWORK_FEED))) {
|
if (in_array($gcontacts[0]["network"], array("", NETWORK_FEED))) {
|
||||||
$server = q("SELECT `network` FROM `gserver` WHERE `nurl` = '%s' AND `network` != ''",
|
$server = q("SELECT `network` FROM `gserver` WHERE `nurl` = '%s' AND `network` != ''",
|
||||||
dbesc(normalise_link($server_url)));
|
dbesc(normalise_link($server_url)));
|
||||||
|
|
||||||
if ($server)
|
if ($server) {
|
||||||
q("UPDATE `gcontact` SET `network` = '%s' WHERE `nurl` = '%s'",
|
$contact['network'] = $server[0]["network"];
|
||||||
dbesc($server[0]["network"]), dbesc(normalise_link($profile)));
|
} else {
|
||||||
else
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// noscrape is really fast so we don't cache the call.
|
// noscrape is really fast so we don't cache the call.
|
||||||
if (($gcontacts[0]["server_url"] != "") AND ($gcontacts[0]["nick"] != "")) {
|
if (($server_url != "") AND ($gcontacts[0]["nick"] != "")) {
|
||||||
|
|
||||||
// Use noscrape if possible
|
// Use noscrape if possible
|
||||||
$server = q("SELECT `noscrape`, `network` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($gcontacts[0]["server_url"])));
|
$server = q("SELECT `noscrape`, `network` FROM `gserver` WHERE `nurl` = '%s' AND `noscrape` != ''", dbesc(normalise_link($server_url)));
|
||||||
|
|
||||||
if ($server) {
|
if ($server) {
|
||||||
$noscraperet = z_fetch_url($server[0]["noscrape"]."/".$gcontacts[0]["nick"]);
|
$noscraperet = z_fetch_url($server[0]["noscrape"]."/".$gcontacts[0]["nick"]);
|
||||||
|
@ -521,29 +528,28 @@ function poco_last_updated($profile, $force = false) {
|
||||||
$noscrape = json_decode($noscraperet["body"], true);
|
$noscrape = json_decode($noscraperet["body"], true);
|
||||||
|
|
||||||
if (is_array($noscrape)) {
|
if (is_array($noscrape)) {
|
||||||
$contact = array("url" => $profile,
|
$contact["network"] = $server[0]["network"];
|
||||||
"network" => $server[0]["network"],
|
|
||||||
"generation" => $gcontacts[0]["generation"]);
|
|
||||||
|
|
||||||
if (isset($noscrape["fn"]))
|
if (isset($noscrape["fn"])) {
|
||||||
$contact["name"] = $noscrape["fn"];
|
$contact["name"] = $noscrape["fn"];
|
||||||
|
}
|
||||||
if (isset($noscrape["comm"]))
|
if (isset($noscrape["comm"])) {
|
||||||
$contact["community"] = $noscrape["comm"];
|
$contact["community"] = $noscrape["comm"];
|
||||||
|
}
|
||||||
if (isset($noscrape["tags"])) {
|
if (isset($noscrape["tags"])) {
|
||||||
$keywords = implode(" ", $noscrape["tags"]);
|
$keywords = implode(" ", $noscrape["tags"]);
|
||||||
if ($keywords != "")
|
if ($keywords != "") {
|
||||||
$contact["keywords"] = $keywords;
|
$contact["keywords"] = $keywords;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$location = formatted_location($noscrape);
|
$location = formatted_location($noscrape);
|
||||||
if ($location)
|
if ($location) {
|
||||||
$contact["location"] = $location;
|
$contact["location"] = $location;
|
||||||
|
}
|
||||||
if (isset($noscrape["dfrn-notify"]))
|
if (isset($noscrape["dfrn-notify"])) {
|
||||||
$contact["notify"] = $noscrape["dfrn-notify"];
|
$contact["notify"] = $noscrape["dfrn-notify"];
|
||||||
|
}
|
||||||
// Remove all fields that are not present in the gcontact table
|
// Remove all fields that are not present in the gcontact table
|
||||||
unset($noscrape["fn"]);
|
unset($noscrape["fn"]);
|
||||||
unset($noscrape["key"]);
|
unset($noscrape["key"]);
|
||||||
|
@ -583,10 +589,12 @@ function poco_last_updated($profile, $force = false) {
|
||||||
// If we only can poll the feed, then we only do this once a while
|
// If we only can poll the feed, then we only do this once a while
|
||||||
if (!$force AND !poco_do_update($gcontacts[0]["created"], $gcontacts[0]["updated"], $gcontacts[0]["last_failure"], $gcontacts[0]["last_contact"])) {
|
if (!$force AND !poco_do_update($gcontacts[0]["created"], $gcontacts[0]["updated"], $gcontacts[0]["last_failure"], $gcontacts[0]["last_contact"])) {
|
||||||
logger("Profile ".$profile." was last updated at ".$gcontacts[0]["updated"]." (cached)", LOGGER_DEBUG);
|
logger("Profile ".$profile." was last updated at ".$gcontacts[0]["updated"]." (cached)", LOGGER_DEBUG);
|
||||||
|
|
||||||
|
update_gcontact($contact);
|
||||||
return $gcontacts[0]["updated"];
|
return $gcontacts[0]["updated"];
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = probe_url($profile);
|
$data = Probe::uri($profile);
|
||||||
|
|
||||||
// Is the profile link the alternate OStatus link notation? (http://domain.tld/user/4711)
|
// Is the profile link the alternate OStatus link notation? (http://domain.tld/user/4711)
|
||||||
// Then check the other link and delete this one
|
// Then check the other link and delete this one
|
||||||
|
@ -598,10 +606,18 @@ function poco_last_updated($profile, $force = false) {
|
||||||
q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($profile)));
|
q("DELETE FROM `gcontact` WHERE `nurl` = '%s'", dbesc(normalise_link($profile)));
|
||||||
q("DELETE FROM `glink` WHERE `gcid` = %d", intval($gcontacts[0]["id"]));
|
q("DELETE FROM `glink` WHERE `gcid` = %d", intval($gcontacts[0]["id"]));
|
||||||
|
|
||||||
poco_check($data["url"], $data["name"], $data["network"], $data["photo"], $gcontacts[0]["about"], $gcontacts[0]["location"],
|
$gcontact = array_merge($gcontacts[0], $data);
|
||||||
$gcontacts[0]["gender"], $gcontacts[0]["keywords"], $data["addr"], $gcontacts[0]["updated"], $gcontacts[0]["generation"]);
|
|
||||||
|
$gcontact["server_url"] = $data["baseurl"];
|
||||||
|
|
||||||
|
try {
|
||||||
|
$gcontact = sanitize_gcontact($gcontact);
|
||||||
|
update_gcontact($gcontact);
|
||||||
|
|
||||||
poco_last_updated($data["url"], $force);
|
poco_last_updated($data["url"], $force);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
logger($e->getMessage(), LOGGER_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
logger("Profile ".$profile." was deleted", LOGGER_DEBUG);
|
logger("Profile ".$profile." was deleted", LOGGER_DEBUG);
|
||||||
return false;
|
return false;
|
||||||
|
@ -615,21 +631,10 @@ function poco_last_updated($profile, $force = false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
$contact = array("generation" => $gcontacts[0]["generation"]);
|
|
||||||
|
|
||||||
$contact = array_merge($contact, $data);
|
$contact = array_merge($contact, $data);
|
||||||
|
|
||||||
$contact["server_url"] = $data["baseurl"];
|
$contact["server_url"] = $data["baseurl"];
|
||||||
|
|
||||||
unset($contact["batch"]);
|
|
||||||
unset($contact["poll"]);
|
|
||||||
unset($contact["request"]);
|
|
||||||
unset($contact["confirm"]);
|
|
||||||
unset($contact["poco"]);
|
|
||||||
unset($contact["priority"]);
|
|
||||||
unset($contact["pubkey"]);
|
|
||||||
unset($contact["baseurl"]);
|
|
||||||
|
|
||||||
update_gcontact($contact);
|
update_gcontact($contact);
|
||||||
|
|
||||||
$feedret = z_fetch_url($data["poll"]);
|
$feedret = z_fetch_url($data["poll"]);
|
||||||
|
@ -672,9 +677,10 @@ function poco_last_updated($profile, $force = false) {
|
||||||
q("UPDATE `gcontact` SET `updated` = '%s', `last_contact` = '%s' WHERE `nurl` = '%s'",
|
q("UPDATE `gcontact` SET `updated` = '%s', `last_contact` = '%s' WHERE `nurl` = '%s'",
|
||||||
dbesc(dbm::date($last_updated)), dbesc(dbm::date()), dbesc(normalise_link($profile)));
|
dbesc(dbm::date($last_updated)), dbesc(dbm::date()), dbesc(normalise_link($profile)));
|
||||||
|
|
||||||
if (($gcontacts[0]["generation"] == 0))
|
if (($gcontacts[0]["generation"] == 0)) {
|
||||||
q("UPDATE `gcontact` SET `generation` = 9 WHERE `nurl` = '%s'",
|
q("UPDATE `gcontact` SET `generation` = 9 WHERE `nurl` = '%s'",
|
||||||
dbesc(normalise_link($profile)));
|
dbesc(normalise_link($profile)));
|
||||||
|
}
|
||||||
|
|
||||||
logger("Profile ".$profile." was last updated at ".$last_updated, LOGGER_DEBUG);
|
logger("Profile ".$profile." was last updated at ".$last_updated, LOGGER_DEBUG);
|
||||||
|
|
||||||
|
@ -1668,37 +1674,21 @@ function poco_discover_federation() {
|
||||||
set_config('poco','last_federation_discovery', time());
|
set_config('poco','last_federation_discovery', time());
|
||||||
}
|
}
|
||||||
|
|
||||||
function poco_discover($complete = false) {
|
function poco_discover_single_server($id) {
|
||||||
|
$r = q("SELECT `poco`, `nurl`, `url`, `network` FROM `gserver` WHERE `id` = %d", intval($id));
|
||||||
// Update the server list
|
if (!dbm::is_result($r)) {
|
||||||
poco_discover_federation();
|
return false;
|
||||||
|
|
||||||
$no_of_queries = 5;
|
|
||||||
|
|
||||||
$requery_days = intval(get_config("system", "poco_requery_days"));
|
|
||||||
|
|
||||||
if ($requery_days == 0)
|
|
||||||
$requery_days = 7;
|
|
||||||
|
|
||||||
$last_update = date("c", time() - (60 * 60 * 24 * $requery_days));
|
|
||||||
|
|
||||||
$r = q("SELECT `poco`, `nurl`, `url`, `network` FROM `gserver` WHERE `last_contact` >= `last_failure` AND `poco` != '' AND `last_poco_query` < '%s' ORDER BY RAND()", dbesc($last_update));
|
|
||||||
if ($r)
|
|
||||||
foreach ($r AS $server) {
|
|
||||||
|
|
||||||
if (!poco_check_server($server["url"], $server["network"])) {
|
|
||||||
// The server is not reachable? Okay, then we will try it later
|
|
||||||
q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discover new servers out there
|
$server = $r[0];
|
||||||
|
|
||||||
|
// Discover new servers out there (Works from Friendica version 3.5.2)
|
||||||
poco_fetch_serverlist($server["poco"]);
|
poco_fetch_serverlist($server["poco"]);
|
||||||
|
|
||||||
// Fetch all users from the other server
|
// Fetch all users from the other server
|
||||||
$url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation";
|
$url = $server["poco"]."/?fields=displayName,urls,photos,updated,network,aboutMe,currentLocation,tags,gender,contactType,generation";
|
||||||
|
|
||||||
logger("Fetch all users from the server ".$server["nurl"], LOGGER_DEBUG);
|
logger("Fetch all users from the server ".$server["url"], LOGGER_DEBUG);
|
||||||
|
|
||||||
$retdata = z_fetch_url($url);
|
$retdata = z_fetch_url($url);
|
||||||
if ($retdata["success"]) {
|
if ($retdata["success"]) {
|
||||||
|
@ -1709,8 +1699,9 @@ function poco_discover($complete = false) {
|
||||||
if (get_config('system','poco_discovery') > 1) {
|
if (get_config('system','poco_discovery') > 1) {
|
||||||
|
|
||||||
$timeframe = get_config('system','poco_discovery_since');
|
$timeframe = get_config('system','poco_discovery_since');
|
||||||
if ($timeframe == 0)
|
if ($timeframe == 0) {
|
||||||
$timeframe = 30;
|
$timeframe = 30;
|
||||||
|
}
|
||||||
|
|
||||||
$updatedSince = date("Y-m-d H:i:s", time() - $timeframe * 86400);
|
$updatedSince = date("Y-m-d H:i:s", time() - $timeframe * 86400);
|
||||||
|
|
||||||
|
@ -1732,14 +1723,49 @@ function poco_discover($complete = false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
|
q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
|
||||||
if (!$complete AND (--$no_of_queries == 0))
|
|
||||||
break;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
// If the server hadn't replied correctly, then force a sanity check
|
// If the server hadn't replied correctly, then force a sanity check
|
||||||
poco_check_server($server["url"], $server["network"], true);
|
poco_check_server($server["url"], $server["network"], true);
|
||||||
|
|
||||||
// If we couldn't reach the server, we will try it some time later
|
// If we couldn't reach the server, we will try it some time later
|
||||||
q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
|
q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function poco_discover($complete = false) {
|
||||||
|
|
||||||
|
// Update the server list
|
||||||
|
poco_discover_federation();
|
||||||
|
|
||||||
|
$no_of_queries = 5;
|
||||||
|
|
||||||
|
$requery_days = intval(get_config("system", "poco_requery_days"));
|
||||||
|
|
||||||
|
if ($requery_days == 0) {
|
||||||
|
$requery_days = 7;
|
||||||
|
}
|
||||||
|
$last_update = date("c", time() - (60 * 60 * 24 * $requery_days));
|
||||||
|
|
||||||
|
$r = q("SELECT `id`, `url`, `network` FROM `gserver` WHERE `last_contact` >= `last_failure` AND `poco` != '' AND `last_poco_query` < '%s' ORDER BY RAND()", dbesc($last_update));
|
||||||
|
if (dbm::is_result($r)) {
|
||||||
|
foreach ($r AS $server) {
|
||||||
|
|
||||||
|
if (!poco_check_server($server["url"], $server["network"])) {
|
||||||
|
// The server is not reachable? Okay, then we will try it later
|
||||||
|
q("UPDATE `gserver` SET `last_poco_query` = '%s' WHERE `nurl` = '%s'", dbesc(datetime_convert()), dbesc($server["nurl"]));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger('Update directory from server '.$server['url'].' with ID '.$server['id'], LOGGER_DEBUG);
|
||||||
|
proc_run(PRIORITY_LOW, "include/discover_poco.php", "update_server_directory", intval($server['id']));
|
||||||
|
|
||||||
|
if (!$complete AND (--$no_of_queries == 0)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1855,10 +1881,26 @@ function poco_discover_server($data, $default_generation = 0) {
|
||||||
$success = true;
|
$success = true;
|
||||||
|
|
||||||
logger("Store profile ".$profile_url, LOGGER_DEBUG);
|
logger("Store profile ".$profile_url, LOGGER_DEBUG);
|
||||||
poco_check($profile_url, $name, $network, $profile_photo, $about, $location, $gender, $keywords, $connect_url, $updated, $generation, 0, 0, 0);
|
|
||||||
|
|
||||||
$gcontact = array("url" => $profile_url, "contact-type" => $contact_type, "generation" => $generation);
|
$gcontact = array("url" => $profile_url,
|
||||||
|
"name" => $name,
|
||||||
|
"network" => $network,
|
||||||
|
"photo" => $profile_photo,
|
||||||
|
"about" => $about,
|
||||||
|
"location" => $location,
|
||||||
|
"gender" => $gender,
|
||||||
|
"keywords" => $keywords,
|
||||||
|
"connect" => $connect_url,
|
||||||
|
"updated" => $updated,
|
||||||
|
"contact-type" => $contact_type,
|
||||||
|
"generation" => $generation);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$gcontact = sanitize_gcontact($gcontact);
|
||||||
update_gcontact($gcontact);
|
update_gcontact($gcontact);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
logger($e->getMessage(), LOGGER_DEBUG);
|
||||||
|
}
|
||||||
|
|
||||||
logger("Done for profile ".$profile_url, LOGGER_DEBUG);
|
logger("Done for profile ".$profile_url, LOGGER_DEBUG);
|
||||||
}
|
}
|
||||||
|
@ -2153,6 +2195,8 @@ function update_gcontact_from_probe($url) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$data["server_url"] = $data["baseurl"];
|
||||||
|
|
||||||
update_gcontact($data);
|
update_gcontact($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -97,13 +97,6 @@ function create_user($arr) {
|
||||||
if(mb_strlen($username) < 3)
|
if(mb_strlen($username) < 3)
|
||||||
$result['message'] .= t('Name too short.') . EOL;
|
$result['message'] .= t('Name too short.') . EOL;
|
||||||
|
|
||||||
// I don't really like having this rule, but it cuts down
|
|
||||||
// on the number of auto-registrations by Russian spammers
|
|
||||||
|
|
||||||
// Using preg_match was completely unreliable, due to mixed UTF-8 regex support
|
|
||||||
// $no_utf = get_config('system','no_utf');
|
|
||||||
// $pat = (($no_utf) ? '/^[a-zA-Z]* [a-zA-Z]*$/' : '/^\p{L}* \p{L}*$/u' );
|
|
||||||
|
|
||||||
// So now we are just looking for a space in the full name.
|
// So now we are just looking for a space in the full name.
|
||||||
|
|
||||||
$loose_reg = get_config('system','no_regfullname');
|
$loose_reg = get_config('system','no_regfullname');
|
||||||
|
@ -182,17 +175,7 @@ function create_user($arr) {
|
||||||
$prvkey = $keys['prvkey'];
|
$prvkey = $keys['prvkey'];
|
||||||
$pubkey = $keys['pubkey'];
|
$pubkey = $keys['pubkey'];
|
||||||
|
|
||||||
/**
|
// Create another keypair for signing/verifying salmon protocol messages.
|
||||||
*
|
|
||||||
* Create another keypair for signing/verifying
|
|
||||||
* salmon protocol messages. We have to use a slightly
|
|
||||||
* less robust key because this won't be using openssl
|
|
||||||
* but the phpseclib. Since it is PHP interpreted code
|
|
||||||
* it is not nearly as efficient, and the larger keys
|
|
||||||
* will take several minutes each to process.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
$sres = new_keypair(512);
|
$sres = new_keypair(512);
|
||||||
$sprvkey = $sres['prvkey'];
|
$sprvkey = $sres['prvkey'];
|
||||||
$spubkey = $sres['pubkey'];
|
$spubkey = $sres['pubkey'];
|
||||||
|
|
11
library/Mobile_Detect/.gitignore
vendored
11
library/Mobile_Detect/.gitignore
vendored
|
@ -1,11 +0,0 @@
|
||||||
vendor/
|
|
||||||
nbproject/
|
|
||||||
/*.buildpath
|
|
||||||
/*.project
|
|
||||||
/.settings
|
|
||||||
/error.log
|
|
||||||
/export/nicejson
|
|
||||||
.idea/
|
|
||||||
*.iml
|
|
||||||
/coverage
|
|
||||||
/phpunit.phar
|
|
3
library/Mobile_Detect/.gitmodules
vendored
3
library/Mobile_Detect/.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
||||||
[submodule "export/nicejson"]
|
|
||||||
path = export/nicejson
|
|
||||||
url = https://github.com/GerHobbelt/nicejson-php.git
|
|
|
@ -1,24 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
use Symfony\CS\FixerInterface;
|
|
||||||
|
|
||||||
$finder = Symfony\CS\Finder\DefaultFinder::create()
|
|
||||||
->notName('LICENSE')
|
|
||||||
->notName('README.md')
|
|
||||||
->notName('.php_cs')
|
|
||||||
->notName('composer.*')
|
|
||||||
->notName('phpunit.xml*')
|
|
||||||
->notName('*.phar')
|
|
||||||
->exclude('vendor')
|
|
||||||
->exclude('examples')
|
|
||||||
->exclude('Symfony/CS/Tests/Fixer')
|
|
||||||
->notName('ElseifFixer.php')
|
|
||||||
->exclude('data')
|
|
||||||
->in(__DIR__)
|
|
||||||
;
|
|
||||||
|
|
||||||
return Symfony\CS\Config\Config::create()
|
|
||||||
->finder($finder)
|
|
||||||
;
|
|
||||||
|
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
language: php
|
|
||||||
php:
|
|
||||||
- "5.2"
|
|
||||||
- "5.3"
|
|
||||||
- "5.4"
|
|
||||||
- "5.5"
|
|
||||||
- "5.6"
|
|
||||||
|
|
||||||
branches:
|
|
||||||
only:
|
|
||||||
- devel
|
|
||||||
|
|
||||||
script:
|
|
||||||
- phpunit -v -c tests/phpunit.xml
|
|
||||||
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
File diff suppressed because one or more lines are too long
|
@ -1,219 +0,0 @@
|
||||||
[![Build Status](https://travis-ci.org/serbanghita/Mobile-Detect.svg?branch=devel)](https://travis-ci.org/serbanghita/Mobile-Detect) [![Latest Stable Version](https://poser.pugx.org/mobiledetect/mobiledetectlib/v/stable.svg)](https://packagist.org/packages/mobiledetect/mobiledetectlib) [![Total Downloads](https://poser.pugx.org/mobiledetect/mobiledetectlib/downloads.svg)](https://packagist.org/packages/mobiledetect/mobiledetectlib) [![Daily Downloads](https://poser.pugx.org/mobiledetect/mobiledetectlib/d/daily.png)](https://packagist.org/packages/mobiledetect/mobiledetectlib) [![License](https://poser.pugx.org/mobiledetect/mobiledetectlib/license.svg)](https://packagist.org/packages/mobiledetect/mobiledetectlib)
|
|
||||||
[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/serbanghita/Mobile-Detect?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
|
||||||
|
|
||||||
![Mobile Detect](http://demo.mobiledetect.net/logo-github.png)
|
|
||||||
|
|
||||||
> Motto: "Every business should have a mobile detection script to detect mobile readers."
|
|
||||||
|
|
||||||
<i>Mobile_Detect is a lightweight PHP class for detecting mobile devices (including tablets).
|
|
||||||
It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.</i>
|
|
||||||
|
|
||||||
> We're commited to make Mobile_Detect the best open-source mobile detection resource and this is why before each release we're running [unit tests](./tests), we also research and update the detection rules on <b>daily</b> and <b>weekly</b> basis.
|
|
||||||
|
|
||||||
Your website's _content strategy_ is important! You need a complete toolkit to deliver an experience that is _optimized_, _fast_ and _relevant_ to your users. Mobile_Detect class is a [server-side detection](http://www.w3.org/TR/mwabp/#bp-devcap-detection) tool that can help you with your RWD strategy, it is not a replacement for CSS3 media queries or other forms of client-side feature detection.
|
|
||||||
|
|
||||||
##### This month updates
|
|
||||||
|
|
||||||
First of all a **BIG THANK YOU** to our growing community for your continuous support and for all the feedback received! I'm still working my way with the current issues and all the emails.
|
|
||||||
|
|
||||||
Nick is almost done with all the code for the upcoming `3.0.0` so that I only have to integrate the new parsing engine. We will release minor `2.8.xx` versions until a feature freeze where we will switch to the new branch. You will all be announced before this and hopefully we can make the transition smooth for everyone.
|
|
||||||
|
|
||||||
<a href="http://trycatch.us/"><img align="left" src="http://assets.mobiledetect.net/img/try_catch_logo_80px.jpg" hspace="20"></a> Last but not least, special thanks for supporting us to our friends from [TryCatch.us](http://trycatch.us/) who _are set to carefully curate the most talented developers in Europe_!
|
|
||||||
|
|
||||||
Thank you all and we're excited for the new release!
|
|
||||||
|
|
||||||
##### Download and demo
|
|
||||||
|
|
||||||
|Download|Docs|Examples|
|
|
||||||
|-------------|-------------|-------------|
|
|
||||||
|[Go to releases](../../tags)|[Become a contributor](../../wiki/Become-a-contributor)|[Code examples](../../wiki/Code-examples)
|
|
||||||
|[Mobile_Detect.php](./Mobile_Detect.php)|[History](../../wiki/History)|[:iphone: Live demo!](http://is.gd/mobiletest)
|
|
||||||
|[Composer package](https://packagist.org/packages/mobiledetect/mobiledetectlib)|
|
|
||||||
|
|
||||||
##### Help
|
|
||||||
|
|
||||||
|Pledgie|Paypal|
|
|
||||||
|-------|------|
|
|
||||||
|[Donate :+1:](http://pledgie.com/campaigns/21856)|[Donate :beer:](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=mobiledetectlib%40gmail%2ecom&lc=US&item_name=Mobile%20Detect¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donate_SM%2egif%3aNonHosted)|
|
|
||||||
|
|
||||||
|
|
||||||
I'm currently paying for hosting and spend a lot of my family time to maintain the project and planning the future releases.
|
|
||||||
I would highly appreciate any money donations that will keep the research going.
|
|
||||||
|
|
||||||
Special thanks to the community :+1: for donations, [BrowserStack](http://browserstack.com) - for providing access to their great platform, [Zend](http://zend.com) - for donating licenses, [Dragos Gavrila](https://twitter.com/grafician) who contributed with the logo.
|
|
||||||
|
|
||||||
##### 3rd party modules / [Submit new](../../issues/new?title=New%203rd%20party%20module&body=Name, Link and Description of the module.)
|
|
||||||
|
|
||||||
:point_right: Keep `Mobile_Detect.php` class in a separate `module` and do NOT include it in your script core because of the high frequency of updates.
|
|
||||||
:point_right: When including the class into you `web application` or `module` always use `include_once '../path/to/Mobile_Detect.php` to prevent conflicts.
|
|
||||||
|
|
||||||
<table>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Varnish Cache</td>
|
|
||||||
<td>
|
|
||||||
<p><a href="https://github.com/willemk/varnish-mobiletranslate">Varnish Mobile Detect</a> - Drop-in varnish solution to mobile user detection based on the Mobile-Detect library. Made by <a href="https://github.com/willemk">willemk</a></p>
|
|
||||||
<p><a href="https://github.com/carlosabalde/mobiledetect2vcl">mobiledetect2vcl</a> - Python script to transform the Mobile Detect JSON database into an UA-based mobile detection VCL subroutine easily integrable in any Varnish Cache configuration. Made by <a href="https://github.com/carlosabalde">Carlos Abalde</a></p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>WordPress</td>
|
|
||||||
<td>
|
|
||||||
<p><a href="http://wordpress.org/extend/plugins/wp-mobile-detect/">WordPress Mobile Detect</a> - Gives you the ability to wrap that infographic in a `[notdevice][/notdevice]` shortcode so at the server level <code>WordPress</code> will decide to show that content only if the user is NOT on a phone or tablet. Made by <a href="http://profiles.wordpress.org/professor44/">Jesse Friedman</a>.</p>
|
|
||||||
|
|
||||||
<p><a href="http://wordpress.org/plugins/mobble/">mobble</a> - provides mobile related conditional functions for your site. e.g. is_iphone(), is_mobile() and is_tablet(). Made by Scott Evans.</p>
|
|
||||||
|
|
||||||
<p><a href="https://github.com/iamspacehead/responsage">WordPress Responsage</a> - A small <code>WordPress</code> theme plugin that allows you to make your images responsive. Made by <a href="https://github.com/iamspacehead">Adrian Ciaschetti</a>.</p>
|
|
||||||
|
|
||||||
<p><a href="http://wordpress.org/plugins/social-popup/">Social PopUP</a> - This plugin will display a popup or splash screen when a new user visit your site showing a Google+, Twitter and Facebook follow links. It uses Mobile_Detect to detect mobile devices.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Drupal</td>
|
|
||||||
<td>
|
|
||||||
<p><a href="http://drupal.org/project/mobile_switch">Drupal Mobile Switch</a> - The Mobile Switch <code>Drupal</code> module provides a automatic theme switch functionality for mobile devices,
|
|
||||||
detected by Browscap or Mobile Detect. Made by <a href="http://drupal.org/user/45267">Siegfried Neumann</a>.</p>
|
|
||||||
|
|
||||||
<p><a href="http://drupal.org/project/context_mobile_detect">Drupal Context Mobile Detect</a> - This is a <code>Drupal context</code> module which integrates Context and PHP Mobile Detect library.
|
|
||||||
Created by <a href="http://drupal.org/user/432492">Artem Shymko</a>.</p>
|
|
||||||
|
|
||||||
<p><a href="http://drupal.org/project/mobile_detect">Drupal Mobile Detect</a> - Lightweight mobile detect module for <code>Drupal</code> created by <a href="http://drupal.org/user/325244">Matthew Donadio</a></p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Joomla</td>
|
|
||||||
<td><p><a href="http://www.yagendoo.com/en/blog/free-mobile-detection-plugin-for-joomla.html">yagendoo Joomla! Mobile Detection Plugin</a> - Lightweight PHP plugin for Joomla! that detects a mobile browser using the Mobile Detect class. Made by <a href="http://www.yagendoo.com/">yagendoo media</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Magento</td>
|
|
||||||
<td><p><a href="http://www.magentocommerce.com/magento-connect/catalog/product/view/id/16835/">Magento</a> - This <code>Magento helper</code> from Optimise Web enables the use of all functions provided by MobileDetect.net.
|
|
||||||
Made by <a href="http://www.kathirvel.com">Kathir Vel</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>PrestaShop</td>
|
|
||||||
<td><p><a href="http://www.prestashop.com/">PrestaShop</a> is a free, secure and open source shopping cart platform. Mobile_Detect is included in the default package since 1.5.x.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Zend Framework</td>
|
|
||||||
<td>
|
|
||||||
<p><a href="https://github.com/neilime/zf2-mobile-detect.git">ZF2 Mobile-Detect</a> - Zend Framework 2 module that provides Mobile-Detect features (Mobile_Detect class as a service, helper for views and plugin controllers). Made by <a href="https://github.com/neilime">neilime</a></p>
|
|
||||||
|
|
||||||
<p><a href="https://github.com/nikolaposa/MobileDetectModule">ZF2 MobileDetectModule</a> - Facilitates integration of a PHP MobileDetect class with some ZF2-based application. Has similar idea like the existing ZF2 Mobile-Detect module, but differs in initialization and provision routine of the actual Mobile_Detect class. Appropriate view helper and controller plugin also have different conceptions. Made by <a href="https://github.com/nikolaposa">Nikola Posa</a></p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Symfony</td>
|
|
||||||
<td><p><a href="https://github.com/suncat2000/MobileDetectBundle">Symfony2 Mobile Detect Bundle</a> - The bundle for detecting mobile devices, manage mobile view and redirect to the mobile and tablet version.
|
|
||||||
Made by <a href="https://github.com/suncat2000">Nikolay Ivlev</a>.</p>
|
|
||||||
<p><a href="https://github.com/jbinfo/MobileDetectServiceProvider">Silex Mobile Detect Service Provider</a> - <code>Silex</code> service provider to interact with Mobile detect class methods. Made by <a href="https://github.com/jbinfo">Lhassan Baazzi</a>.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Laravel</td>
|
|
||||||
<td>
|
|
||||||
<p><a href="https://github.com/jenssegers/Laravel-Agent">Laravel-Agent</a> a user agent class for Laravel, based on Mobile Detect with some additional functionality. Made by <a href="https://github.com/jenssegers">Jens Segers</a>.</p>
|
|
||||||
<p><a href="https://github.com/hisorange/browser-detect">BrowserDetect</a> is a browser & mobile detection package, collects and wrap together the best user-agent identifiers for Laravel. Created by <a href="https://github.com/hisorange">Varga Zsolt</a>.</p>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>ExpressionEngine</td>
|
|
||||||
<td><p><a href="https://github.com/garethtdavies/detect-mobile">EE2 Detect Mobile</a> - Lightweight PHP plugin for <code>EE2</code> that detects a mobile browser using the Mobile Detect class. Made by <a href="https://github.com/garethtdavies">Gareth Davies</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Yii Framework</td>
|
|
||||||
<td><p><a href="https://github.com/iamsalnikov/MobileDetect">Yii Extension</a> - Mobile detect plugin for Yii framework. Made by <a href="https://github.com/iamsalnikov">Alexey Salnikov</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>CakePHP</td>
|
|
||||||
<td><p><a href="https://github.com/chronon/CakePHP-MobileDetectComponent-Plugin">CakePHP MobileDetect</a> - <code>plugin</code> component for <code>CakePHP</code> 2.x. Made by <a href="https://github.com/chronon">Gregory Gaskill</a></p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>FuelPHP</td>
|
|
||||||
<td><a href="https://github.com/rob-bar/special_agent">Special Agent</a> is a FuelPHP package which uses php-mobile-detect to determine whether a device is mobile or not.
|
|
||||||
It overrides the Fuelphp Agent class its methods. Made by <a href="https://github.com/rob-bar">Robbie Bardjin</a>.</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Typo3</td>
|
|
||||||
<td><a href="http://docs.typo3.org/typo3cms/extensions/px_mobiledetect/1.0.2/">px_mobiledetect</a> is an extension that helps to detect visitor's mobile device class (if that’s tablet or mobile device like smartphone). Made by Alexander Tretyak.</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Statamic</td>
|
|
||||||
<td><p><a href="https://github.com/sergeifilippov/statamic-mobile-detect">Statamic CMS Mobile Detect</a> - <code>plugin</code>. Made by <a href="https://github.com/haikulab/statamic-mobile-detect">Sergei Filippov of Haiku Lab</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Kohana</td>
|
|
||||||
<td><p><a href="https://github.com/madeinnordeste/kohana-mobile-detect">Kohana Mobile Detect</a> - an example of implementation of <code>Mobile_Detect</code> class with Kohana framework. Written by <a href="https://github.com/madeinnordeste">Luiz Alberto S. Ribeiro</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>mobile-detect.js</td>
|
|
||||||
<td><p>A <a href="https://github.com/hgoebl/mobile-detect.js">JavaScript port</a> of Mobile-Detect class. Made by <a href="https://github.com/hgoebl">Heinrich Goebl</a></p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Perl</td>
|
|
||||||
<td><p><a href="http://www.buzzerstar.com/development/">MobileDetect.pm</a> - <code>Perl module</code> for Mobile Detect. Made by <a href="http://www.buzzerstar.com/">Sebastian Enger</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>python</td>
|
|
||||||
<td><p><a href="http://pypi.python.org/pypi/pymobiledetect">pymobiledetect</a> - Mobile detect <code>python package</code>. Made by Bas van Oostveen.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Ruby</td>
|
|
||||||
<td><p><a href="https://github.com/ktaragorn/mobile_detect">mobile_detect.rb</a> - A <code>Ruby gem</code> using the JSON data exposed by the php project and implementing a basic subset of the API (as much as can be done by the exposed data). Made by <a href="https://github.com/ktaragorn">Karthik T</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>GoMobileDetect</td>
|
|
||||||
<td><p><a href="https://github.com/Shaked/gomobiledetect">GoMobileDetect</a> - Go port of Mobile Detect class. Made by <a href="https://github.com/Shaked">Shaked</a>.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>MemHT</td>
|
|
||||||
<td><p><a href="http://www.memht.com/">MemHT</a> is a Free PHP CMS and Blog that permit the creation and the management online of websites with few and easy steps. Has the class included in the core.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>concrete5</td>
|
|
||||||
<td><p><a href="https://www.concrete5.org/">concrete5</a> is a CMS that is free and open source. The library is included in the core.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>engine7</td>
|
|
||||||
<td><p><a href="https://github.com/gchiappe/exengine7">ExEngine 7</a> PHP Open Source Framework. The Mobile_Detect class is included in the engine.</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Zikula</td>
|
|
||||||
<td><p><a href="http://zikula.org/">Zikula</a> is a free and open-source Content Management Framework, which allows you to run impressive websites and build powerful online applications. The core uses Mobile-Detect to switch to a special Mobile theme, using jQueryMobile</p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>UserAgentInfo</td>
|
|
||||||
<td><p><a href="https://github.com/quentin389/UserAgentInfo">UserAgentInfo</a> is a PHP class for parsing user agent strings (HTTP_USER_AGENT). Includes mobile checks, bot checks, browser types/versions and more.
|
|
||||||
Based on browscap, Mobile_Detect and ua-parser. Created for high traffic websites and fast batch processing. Made by <a href="https://github.com/quentin389">quentin389</a></p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr>
|
|
||||||
<td>Craft CMS</td>
|
|
||||||
<td><p><a href="https://github.com/lewisjenkins/craft-lj-mobiledetect">LJ Mobile Detect</a> is a simple implementation of Mobile Detect for Craft CMS. Made by <a href="https://github.com/lewisjenkins">Lewis Jenkins</a></p></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</table>
|
|
|
@ -342,9 +342,9 @@ final class Crypto
|
||||||
*/
|
*/
|
||||||
private static function SecureRandom($octets)
|
private static function SecureRandom($octets)
|
||||||
{
|
{
|
||||||
self::EnsureFunctionExists("mcrypt_create_iv");
|
self::EnsureFunctionExists("openssl_random_pseudo_bytes");
|
||||||
$random = mcrypt_create_iv($octets, MCRYPT_DEV_URANDOM);
|
$random = openssl_random_pseudo_bytes($octets, $crypto_strong);
|
||||||
if ($random === FALSE) {
|
if ($crypto_strong === FALSE) {
|
||||||
throw new CannotPerformOperationException();
|
throw new CannotPerformOperationException();
|
||||||
} else {
|
} else {
|
||||||
return $random;
|
return $random;
|
||||||
|
|
|
@ -1,24 +1,17 @@
|
||||||
<?php
|
<?php
|
||||||
//# Install PSR-0-compatible class autoloader
|
require_once "library/php-markdown/Michelf/MarkdownExtra.inc.php";
|
||||||
//spl_autoload_register(function($class){
|
|
||||||
// require preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php';
|
|
||||||
//});
|
|
||||||
|
|
||||||
require_once("library/php-markdown/Michelf/MarkdownExtra.inc.php");
|
|
||||||
# Get Markdown class
|
|
||||||
use \Michelf\MarkdownExtra;
|
use \Michelf\MarkdownExtra;
|
||||||
|
|
||||||
function Markdown($text) {
|
function Markdown($text) {
|
||||||
|
|
||||||
$a = get_app();
|
$a = get_app();
|
||||||
|
|
||||||
$stamp1 = microtime(true);
|
$stamp1 = microtime(true);
|
||||||
|
|
||||||
# Read file and pass content through the Markdown parser
|
$MarkdownParser = new MarkdownExtra();
|
||||||
$html = MarkdownExtra::defaultTransform($text);
|
$MarkdownParser->hard_wrap = true;
|
||||||
|
$html = $MarkdownParser->transform($text);
|
||||||
|
|
||||||
$a->save_timestamp($stamp1, "parser");
|
$a->save_timestamp($stamp1, "parser");
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
|
|
|
@ -1,728 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
//ini_set('display_errors', 1);
|
|
||||||
//error_reporting(E_ALL | E_STRICT);
|
|
||||||
|
|
||||||
// Regex to filter out the client identifier
|
|
||||||
// (described in Section 2 of IETF draft)
|
|
||||||
// IETF draft does not prescribe a format for these, however
|
|
||||||
// I've arbitrarily chosen alphanumeric strings with hyphens and underscores, 3-12 characters long
|
|
||||||
// Feel free to change.
|
|
||||||
define("REGEX_CLIENT_ID", "/^[a-z0-9-_]{3,12}$/i");
|
|
||||||
|
|
||||||
// Used to define the name of the OAuth access token parameter (POST/GET/etc.)
|
|
||||||
// IETF Draft sections 5.2 and 5.3 specify that it should be called "oauth_token"
|
|
||||||
// but other implementations use things like "access_token"
|
|
||||||
// I won't be heartbroken if you change it, but it might be better to adhere to the spec
|
|
||||||
define("OAUTH_TOKEN_PARAM_NAME", "oauth_token");
|
|
||||||
|
|
||||||
// Client types (for client authorization)
|
|
||||||
//define("WEB_SERVER_CLIENT_TYPE", "web_server");
|
|
||||||
//define("USER_AGENT_CLIENT_TYPE", "user_agent");
|
|
||||||
//define("REGEX_CLIENT_TYPE", "/^(web_server|user_agent)$/");
|
|
||||||
define("ACCESS_TOKEN_AUTH_RESPONSE_TYPE", "token");
|
|
||||||
define("AUTH_CODE_AUTH_RESPONSE_TYPE", "code");
|
|
||||||
define("CODE_AND_TOKEN_AUTH_RESPONSE_TYPE", "code-and-token");
|
|
||||||
define("REGEX_AUTH_RESPONSE_TYPE", "/^(token|code|code-and-token)$/");
|
|
||||||
|
|
||||||
// Grant Types (for token obtaining)
|
|
||||||
define("AUTH_CODE_GRANT_TYPE", "authorization-code");
|
|
||||||
define("USER_CREDENTIALS_GRANT_TYPE", "basic-credentials");
|
|
||||||
define("ASSERTION_GRANT_TYPE", "assertion");
|
|
||||||
define("REFRESH_TOKEN_GRANT_TYPE", "refresh-token");
|
|
||||||
define("NONE_GRANT_TYPE", "none");
|
|
||||||
define("REGEX_TOKEN_GRANT_TYPE", "/^(authorization-code|basic-credentials|assertion|refresh-token|none)$/");
|
|
||||||
|
|
||||||
/* Error handling constants */
|
|
||||||
|
|
||||||
// HTTP status codes
|
|
||||||
define("ERROR_NOT_FOUND", "404 Not Found");
|
|
||||||
define("ERROR_BAD_REQUEST", "400 Bad Request");
|
|
||||||
|
|
||||||
// TODO: Extend for i18n
|
|
||||||
|
|
||||||
// "Official" OAuth 2.0 errors
|
|
||||||
define("ERROR_REDIRECT_URI_MISMATCH", "redirect-uri-mismatch");
|
|
||||||
define("ERROR_INVALID_CLIENT_CREDENTIALS", "invalid-client-credentials");
|
|
||||||
define("ERROR_UNAUTHORIZED_CLIENT", "unauthorized-client");
|
|
||||||
define("ERROR_USER_DENIED", "access-denied");
|
|
||||||
define("ERROR_INVALID_REQUEST", "invalid-request");
|
|
||||||
define("ERROR_INVALID_CLIENT_ID", "invalid-client-id");
|
|
||||||
define("ERROR_UNSUPPORTED_RESPONSE_TYPE", "unsupported-response-type");
|
|
||||||
define("ERROR_INVALID_SCOPE", "invalid-scope");
|
|
||||||
define("ERROR_INVALID_GRANT", "invalid-grant");
|
|
||||||
|
|
||||||
// Protected resource errors
|
|
||||||
define("ERROR_INVALID_TOKEN", "invalid-token");
|
|
||||||
define("ERROR_EXPIRED_TOKEN", "expired-token");
|
|
||||||
define("ERROR_INSUFFICIENT_SCOPE", "insufficient-scope");
|
|
||||||
|
|
||||||
// Messages
|
|
||||||
define("ERROR_INVALID_RESPONSE_TYPE", "Invalid response type.");
|
|
||||||
|
|
||||||
// Errors that we made up
|
|
||||||
|
|
||||||
// Error for trying to use a grant type that we haven't implemented
|
|
||||||
define("ERROR_UNSUPPORTED_GRANT_TYPE", "unsupported-grant-type");
|
|
||||||
|
|
||||||
abstract class OAuth2 {
|
|
||||||
|
|
||||||
/* Subclasses must implement the following functions */
|
|
||||||
|
|
||||||
// Make sure that the client id is valid
|
|
||||||
// If a secret is required, check that they've given the right one
|
|
||||||
// Must return false if the client credentials are invalid
|
|
||||||
abstract protected function auth_client_credentials($client_id, $client_secret = null);
|
|
||||||
|
|
||||||
// OAuth says we should store request URIs for each registered client
|
|
||||||
// Implement this function to grab the stored URI for a given client id
|
|
||||||
// Must return false if the given client does not exist or is invalid
|
|
||||||
abstract protected function get_redirect_uri($client_id);
|
|
||||||
|
|
||||||
// We need to store and retrieve access token data as we create and verify tokens
|
|
||||||
// Implement these functions to do just that
|
|
||||||
|
|
||||||
// Look up the supplied token id from storage, and return an array like:
|
|
||||||
//
|
|
||||||
// array(
|
|
||||||
// "client_id" => <stored client id>,
|
|
||||||
// "expires" => <stored expiration timestamp>,
|
|
||||||
// "scope" => <stored scope (may be null)
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// Return null if the supplied token is invalid
|
|
||||||
//
|
|
||||||
abstract protected function get_access_token($token_id);
|
|
||||||
|
|
||||||
// Store the supplied values
|
|
||||||
abstract protected function store_access_token($token_id, $client_id, $expires, $scope = null);
|
|
||||||
|
|
||||||
/*
|
|
||||||
*
|
|
||||||
* Stuff that should get overridden by subclasses
|
|
||||||
*
|
|
||||||
* I don't want to make these abstract, because then subclasses would have
|
|
||||||
* to implement all of them, which is too much work.
|
|
||||||
*
|
|
||||||
* So they're just stubs. Override the ones you need.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// You should override this function with something,
|
|
||||||
// or else your OAuth provider won't support any grant types!
|
|
||||||
protected function get_supported_grant_types() {
|
|
||||||
// If you support all grant types, then you'd do:
|
|
||||||
// return array(
|
|
||||||
// AUTH_CODE_GRANT_TYPE,
|
|
||||||
// USER_CREDENTIALS_GRANT_TYPE,
|
|
||||||
// ASSERTION_GRANT_TYPE,
|
|
||||||
// REFRESH_TOKEN_GRANT_TYPE,
|
|
||||||
// NONE_GRANT_TYPE
|
|
||||||
// );
|
|
||||||
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
// You should override this function with your supported response types
|
|
||||||
protected function get_supported_auth_response_types() {
|
|
||||||
return array(
|
|
||||||
AUTH_CODE_AUTH_RESPONSE_TYPE,
|
|
||||||
ACCESS_TOKEN_AUTH_RESPONSE_TYPE,
|
|
||||||
CODE_AND_TOKEN_AUTH_RESPONSE_TYPE
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you want to support scope use, then have this function return a list
|
|
||||||
// of all acceptable scopes (used to throw the invalid-scope error)
|
|
||||||
protected function get_supported_scopes() {
|
|
||||||
// Example:
|
|
||||||
// return array("my-friends", "photos", "whatever-else");
|
|
||||||
return array();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you want to restrict clients to certain authorization response types,
|
|
||||||
// override this function
|
|
||||||
// Given a client identifier and auth type, return true or false
|
|
||||||
// (auth type would be one of the values contained in REGEX_AUTH_RESPONSE_TYPE)
|
|
||||||
protected function authorize_client_response_type($client_id, $response_type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If you want to restrict clients to certain grant types, override this function
|
|
||||||
// Given a client identifier and grant type, return true or false
|
|
||||||
protected function authorize_client($client_id, $grant_type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Functions that help grant access tokens for various grant types */
|
|
||||||
|
|
||||||
// Fetch authorization code data (probably the most common grant type)
|
|
||||||
// IETF Draft 4.1.1: http://tools.ietf.org/html/draft-ietf-oauth-v2-08#section-4.1.1
|
|
||||||
// Required for AUTH_CODE_GRANT_TYPE
|
|
||||||
protected function get_stored_auth_code($code) {
|
|
||||||
// Retrieve the stored data for the given authorization code
|
|
||||||
// Should return:
|
|
||||||
//
|
|
||||||
// array (
|
|
||||||
// "client_id" => <stored client id>,
|
|
||||||
// "redirect_uri" => <stored redirect URI>,
|
|
||||||
// "expires" => <stored code expiration time>,
|
|
||||||
// "scope" => <stored scope values (space-separated string), or can be omitted if scope is unused>
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// Return null if the code is invalid.
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Take the provided authorization code values and store them somewhere (db, etc.)
|
|
||||||
// Required for AUTH_CODE_GRANT_TYPE
|
|
||||||
protected function store_auth_code($code, $client_id, $redirect_uri, $expires, $scope) {
|
|
||||||
// This function should be the storage counterpart to get_stored_auth_code
|
|
||||||
|
|
||||||
// If storage fails for some reason, we're not currently checking
|
|
||||||
// for any sort of success/failure, so you should bail out of the
|
|
||||||
// script and provide a descriptive fail message
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant access tokens for basic user credentials
|
|
||||||
// IETF Draft 4.1.2: http://tools.ietf.org/html/draft-ietf-oauth-v2-08#section-4.1.2
|
|
||||||
// Required for USER_CREDENTIALS_GRANT_TYPE
|
|
||||||
protected function check_user_credentials($client_id, $username, $password) {
|
|
||||||
// Check the supplied username and password for validity
|
|
||||||
// You can also use the $client_id param to do any checks required
|
|
||||||
// based on a client, if you need that
|
|
||||||
// If the username and password are invalid, return false
|
|
||||||
|
|
||||||
// If the username and password are valid, and you want to verify the scope of
|
|
||||||
// a user's access, return an array with the scope values, like so:
|
|
||||||
//
|
|
||||||
// array (
|
|
||||||
// "scope" => <stored scope values (space-separated string)>
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// We'll check the scope you provide against the requested scope before
|
|
||||||
// providing an access token.
|
|
||||||
//
|
|
||||||
// Otherwise, just return true.
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant access tokens for assertions
|
|
||||||
// IETF Draft 4.1.3: http://tools.ietf.org/html/draft-ietf-oauth-v2-08#section-4.1.3
|
|
||||||
// Required for ASSERTION_GRANT_TYPE
|
|
||||||
protected function check_assertion($client_id, $assertion_type, $assertion) {
|
|
||||||
// Check the supplied assertion for validity
|
|
||||||
// You can also use the $client_id param to do any checks required
|
|
||||||
// based on a client, if you need that
|
|
||||||
// If the assertion is invalid, return false
|
|
||||||
|
|
||||||
// If the assertion is valid, and you want to verify the scope of
|
|
||||||
// an access request, return an array with the scope values, like so:
|
|
||||||
//
|
|
||||||
// array (
|
|
||||||
// "scope" => <stored scope values (space-separated string)>
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// We'll check the scope you provide against the requested scope before
|
|
||||||
// providing an access token.
|
|
||||||
//
|
|
||||||
// Otherwise, just return true.
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant refresh access tokens
|
|
||||||
// IETF Draft 4.1.4: http://tools.ietf.org/html/draft-ietf-oauth-v2-08#section-4.1.4
|
|
||||||
// Required for REFRESH_TOKEN_GRANT_TYPE
|
|
||||||
protected function get_refresh_token($refresh_token) {
|
|
||||||
// Retrieve the stored data for the given refresh token
|
|
||||||
// Should return:
|
|
||||||
//
|
|
||||||
// array (
|
|
||||||
// "client_id" => <stored client id>,
|
|
||||||
// "expires" => <refresh token expiration time>,
|
|
||||||
// "scope" => <stored scope values (space-separated string), or can be omitted if scope is unused>
|
|
||||||
// )
|
|
||||||
//
|
|
||||||
// Return null if the token id is invalid.
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store refresh access tokens
|
|
||||||
// Required for REFRESH_TOKEN_GRANT_TYPE
|
|
||||||
protected function store_refresh_token($token, $client_id, $expires, $scope = null) {
|
|
||||||
// If storage fails for some reason, we're not currently checking
|
|
||||||
// for any sort of success/failure, so you should bail out of the
|
|
||||||
// script and provide a descriptive fail message
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Grant access tokens for the "none" grant type
|
|
||||||
// Not really described in the IETF Draft, so I just left a method stub...do whatever you want!
|
|
||||||
// Required for NONE_GRANT_TYPE
|
|
||||||
protected function check_none_access($client_id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function get_default_authentication_realm() {
|
|
||||||
// Change this to whatever authentication realm you want to send in a WWW-Authenticate header
|
|
||||||
return "Service";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* End stuff that should get overridden */
|
|
||||||
|
|
||||||
private $access_token_lifetime = 3600;
|
|
||||||
private $auth_code_lifetime = 30;
|
|
||||||
private $refresh_token_lifetime = 1209600; // Two weeks
|
|
||||||
|
|
||||||
public function __construct($access_token_lifetime = 3600, $auth_code_lifetime = 30, $refresh_token_lifetime = 1209600) {
|
|
||||||
$this->access_token_lifetime = $access_token_lifetime;
|
|
||||||
$this->auth_code_lifetime = $auth_code_lifetime;
|
|
||||||
$this->refresh_token_lifetime = $refresh_token_lifetime;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Resource protecting (Section 5) */
|
|
||||||
|
|
||||||
// Check that a valid access token has been provided
|
|
||||||
//
|
|
||||||
// The scope parameter defines any required scope that the token must have
|
|
||||||
// If a scope param is provided and the token does not have the required scope,
|
|
||||||
// we bounce the request
|
|
||||||
//
|
|
||||||
// Some implementations may choose to return a subset of the protected resource
|
|
||||||
// (i.e. "public" data) if the user has not provided an access token
|
|
||||||
// or if the access token is invalid or expired
|
|
||||||
//
|
|
||||||
// The IETF spec says that we should send a 401 Unauthorized header and bail immediately
|
|
||||||
// so that's what the defaults are set to
|
|
||||||
//
|
|
||||||
// Here's what each parameter does:
|
|
||||||
// $scope = A space-separated string of required scope(s), if you want to check for scope
|
|
||||||
// $exit_not_present = If true and no access token is provided, send a 401 header and exit, otherwise return false
|
|
||||||
// $exit_invalid = If true and the implementation of get_access_token returns null, exit, otherwise return false
|
|
||||||
// $exit_expired = If true and the access token has expired, exit, otherwise return false
|
|
||||||
// $exit_scope = If true the access token does not have the required scope(s), exit, otherwise return false
|
|
||||||
// $realm = If you want to specify a particular realm for the WWW-Authenticate header, supply it here
|
|
||||||
public function verify_access_token($scope = null, $exit_not_present = true, $exit_invalid = true, $exit_expired = true, $exit_scope = true, $realm = null) {
|
|
||||||
$token_param = $this->get_access_token_param();
|
|
||||||
if ($token_param === false) // Access token was not provided
|
|
||||||
return $exit_not_present ? $this->send_401_unauthorized($realm, $scope) : false;
|
|
||||||
|
|
||||||
// Get the stored token data (from the implementing subclass)
|
|
||||||
$token = $this->get_access_token($token_param);
|
|
||||||
if ($token === null)
|
|
||||||
return $exit_invalid ? $this->send_401_unauthorized($realm, $scope, ERROR_INVALID_TOKEN) : false;
|
|
||||||
|
|
||||||
// Check token expiration (I'm leaving this check separated, later we'll fill in better error messages)
|
|
||||||
if (isset($token["expires"]) && time() > $token["expires"])
|
|
||||||
return $exit_expired ? $this->send_401_unauthorized($realm, $scope, ERROR_EXPIRED_TOKEN) : false;
|
|
||||||
|
|
||||||
// Check scope, if provided
|
|
||||||
// If token doesn't have a scope, it's null/empty, or it's insufficient, then throw an error
|
|
||||||
if ($scope &&
|
|
||||||
(!isset($token["scope"]) || !$token["scope"] || !$this->check_scope($scope, $token["scope"])))
|
|
||||||
return $exit_scope ? $this->send_401_unauthorized($realm, $scope, ERROR_INSUFFICIENT_SCOPE) : false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Returns true if everything in required scope is contained in available scope
|
|
||||||
// False if something in required scope is not in available scope
|
|
||||||
private function check_scope($required_scope, $available_scope) {
|
|
||||||
// The required scope should match or be a subset of the available scope
|
|
||||||
if (!is_array($required_scope))
|
|
||||||
$required_scope = explode(" ", $required_scope);
|
|
||||||
|
|
||||||
if (!is_array($available_scope))
|
|
||||||
$available_scope = explode(" ", $available_scope);
|
|
||||||
|
|
||||||
return (count(array_diff($required_scope, $available_scope)) == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send a 401 unauthorized header with the given realm
|
|
||||||
// and an error, if provided
|
|
||||||
private function send_401_unauthorized($realm, $scope, $error = null) {
|
|
||||||
$realm = $realm === null ? $this->get_default_authentication_realm() : $realm;
|
|
||||||
|
|
||||||
$auth_header = "WWW-Authenticate: Token realm='".$realm."'";
|
|
||||||
|
|
||||||
if ($scope)
|
|
||||||
$auth_header .= ", scope='".$scope."'";
|
|
||||||
|
|
||||||
if ($error !== null)
|
|
||||||
$auth_header .= ", error='".$error."'";
|
|
||||||
|
|
||||||
header("HTTP/1.1 401 Unauthorized");
|
|
||||||
header($auth_header);
|
|
||||||
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pulls the access token out of the HTTP request
|
|
||||||
// Either from the Authorization header or GET/POST/etc.
|
|
||||||
// Returns false if no token is present
|
|
||||||
// TODO: Support POST or DELETE
|
|
||||||
private function get_access_token_param() {
|
|
||||||
$auth_header = $this->get_authorization_header();
|
|
||||||
|
|
||||||
if ($auth_header !== false) {
|
|
||||||
// Make sure only the auth header is set
|
|
||||||
if (isset($_GET[OAUTH_TOKEN_PARAM_NAME]) || isset($_POST[OAUTH_TOKEN_PARAM_NAME]))
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
$auth_header = trim($auth_header);
|
|
||||||
|
|
||||||
// Make sure it's Token authorization
|
|
||||||
if (strcmp(substr($auth_header, 0, 6),"Token ") !== 0)
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
// Parse the rest of the header
|
|
||||||
if (preg_match('/\s*token\s*="(.+)"/', substr($auth_header, 6), $matches) == 0 || count($matches) < 2)
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
return $matches[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_GET[OAUTH_TOKEN_PARAM_NAME])) {
|
|
||||||
if (isset($_POST[OAUTH_TOKEN_PARAM_NAME])) // Both GET and POST are not allowed
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
return $_GET[OAUTH_TOKEN_PARAM_NAME];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_POST[OAUTH_TOKEN_PARAM_NAME]))
|
|
||||||
return $_POST[OAUTH_TOKEN_PARAM_NAME];
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Access token granting (Section 4) */
|
|
||||||
|
|
||||||
// Grant or deny a requested access token
|
|
||||||
// This would be called from the "/token" endpoint as defined in the spec
|
|
||||||
// Obviously, you can call your endpoint whatever you want
|
|
||||||
public function grant_access_token() {
|
|
||||||
$filters = array(
|
|
||||||
"grant_type" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => REGEX_TOKEN_GRANT_TYPE), "flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"scope" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"code" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"redirect_uri" => array("filter" => FILTER_VALIDATE_URL, "flags" => array(FILTER_FLAG_SCHEME_REQUIRED, FILTER_REQUIRE_SCALAR)),
|
|
||||||
"username" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"password" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"assertion_type" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"assertion" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"refresh_token" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
);
|
|
||||||
|
|
||||||
$input = filter_input_array(INPUT_POST, $filters);
|
|
||||||
|
|
||||||
// Grant Type must be specified.
|
|
||||||
if (!$input["grant_type"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
// Make sure we've implemented the requested grant type
|
|
||||||
if (!in_array($input["grant_type"], $this->get_supported_grant_types()))
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_UNSUPPORTED_GRANT_TYPE);
|
|
||||||
|
|
||||||
// Authorize the client
|
|
||||||
$client = $this->get_client_credentials();
|
|
||||||
|
|
||||||
if ($this->auth_client_credentials($client[0], $client[1]) === false)
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_CLIENT_CREDENTIALS);
|
|
||||||
|
|
||||||
if (!$this->authorize_client($client[0], $input["grant_type"]))
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_UNAUTHORIZED_CLIENT);
|
|
||||||
|
|
||||||
// Do the granting
|
|
||||||
switch ($input["grant_type"]) {
|
|
||||||
case AUTH_CODE_GRANT_TYPE:
|
|
||||||
if (!$input["code"] || !$input["redirect_uri"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
$stored = $this->get_stored_auth_code($input["code"]);
|
|
||||||
|
|
||||||
if ($stored === null || $input["redirect_uri"] != $stored["redirect_uri"] || $client[0] != $stored["client_id"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_GRANT);
|
|
||||||
|
|
||||||
if ($stored["expires"] > time())
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_GRANT);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case USER_CREDENTIALS_GRANT_TYPE:
|
|
||||||
if (!$input["username"] || !$input["password"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
$stored = $this->check_user_credentials($client[0], $input["username"], $input["password"]);
|
|
||||||
|
|
||||||
if ($stored === false)
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_GRANT);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case ASSERTION_GRANT_TYPE:
|
|
||||||
if (!$input["assertion_type"] || !$input["assertion"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
$stored = $this->check_assertion($client[0], $input["assertion_type"], $input["assertion"]);
|
|
||||||
|
|
||||||
if ($stored === false)
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_GRANT);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case REFRESH_TOKEN_GRANT_TYPE:
|
|
||||||
if (!$input["refresh_token"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
$stored = $this->get_refresh_token($input["refresh_token"]);
|
|
||||||
|
|
||||||
if ($stored === null || $client[0] != $stored["client_id"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_GRANT);
|
|
||||||
|
|
||||||
if ($stored["expires"] > time())
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_GRANT);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case NONE_GRANT_TYPE:
|
|
||||||
$stored = $this->check_none_access($client[0]);
|
|
||||||
|
|
||||||
if ($stored === false)
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check scope, if provided
|
|
||||||
if ($input["scope"] && (!is_array($stored) || !isset($stored["scope"]) || !$this->check_scope($input["scope"], $stored["scope"])))
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_SCOPE);
|
|
||||||
|
|
||||||
if (!$input["scope"])
|
|
||||||
$input["scope"] = null;
|
|
||||||
|
|
||||||
$token = $this->create_access_token($client[0], $input["scope"]);
|
|
||||||
|
|
||||||
$this->send_json_headers();
|
|
||||||
echo json_encode($token);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Internal function used to get the client credentials from HTTP basic auth or POST data
|
|
||||||
// See http://tools.ietf.org/html/draft-ietf-oauth-v2-08#section-2
|
|
||||||
private function get_client_credentials() {
|
|
||||||
if (isset($_SERVER["PHP_AUTH_USER"]) && $_POST && isset($_POST["client_id"]))
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_CLIENT_CREDENTIALS);
|
|
||||||
|
|
||||||
// Try basic auth
|
|
||||||
if (isset($_SERVER["PHP_AUTH_USER"]))
|
|
||||||
return array($_SERVER["PHP_AUTH_USER"], $_SERVER["PHP_AUTH_PW"]);
|
|
||||||
|
|
||||||
// Try POST
|
|
||||||
if ($_POST && isset($_POST["client_id"])) {
|
|
||||||
if (isset($_POST["client_secret"]))
|
|
||||||
return array($_POST["client_id"], $_POST["client_secret"]);
|
|
||||||
|
|
||||||
return array($_POST["client_id"], NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
// No credentials were specified
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_CLIENT_CREDENTIALS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* End-user/client Authorization (Section 3 of IETF Draft) */
|
|
||||||
|
|
||||||
// Pull the authorization request data out of the HTTP request
|
|
||||||
// and return it so the authorization server can prompt the user
|
|
||||||
// for approval
|
|
||||||
public function get_authorize_params() {
|
|
||||||
$filters = array(
|
|
||||||
"client_id" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => REGEX_CLIENT_ID), "flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"response_type" => array("filter" => FILTER_VALIDATE_REGEXP, "options" => array("regexp" => REGEX_AUTH_RESPONSE_TYPE), "flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"redirect_uri" => array("filter" => FILTER_VALIDATE_URL, "flags" => array(FILTER_FLAG_SCHEME_REQUIRED, FILTER_REQUIRE_SCALAR)),
|
|
||||||
"state" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
"scope" => array("flags" => FILTER_REQUIRE_SCALAR),
|
|
||||||
);
|
|
||||||
|
|
||||||
$input = filter_input_array(INPUT_GET, $filters);
|
|
||||||
|
|
||||||
// Make sure a valid client id was supplied
|
|
||||||
if (!$input["client_id"]) {
|
|
||||||
if ($input["redirect_uri"])
|
|
||||||
$this->callback_error($input["redirect_uri"], ERROR_INVALID_CLIENT_ID, $input["state"]);
|
|
||||||
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_CLIENT_ID); // We don't have a good URI to use
|
|
||||||
}
|
|
||||||
|
|
||||||
// redirect_uri is not required if already established via other channels
|
|
||||||
// check an existing redirect URI against the one supplied
|
|
||||||
$redirect_uri = $this->get_redirect_uri($input["client_id"]);
|
|
||||||
|
|
||||||
// At least one of: existing redirect URI or input redirect URI must be specified
|
|
||||||
if (!$redirect_uri && !$input["redirect_uri"])
|
|
||||||
$this->error(ERROR_BAD_REQUEST, ERROR_INVALID_REQUEST);
|
|
||||||
|
|
||||||
// get_redirect_uri should return false if the given client ID is invalid
|
|
||||||
// this probably saves us from making a separate db call, and simplifies the method set
|
|
||||||
if ($redirect_uri === false)
|
|
||||||
$this->callback_error($input["redirect_uri"], ERROR_INVALID_CLIENT_ID, $input["state"]);
|
|
||||||
|
|
||||||
// If there's an existing uri and one from input, verify that they match
|
|
||||||
if ($redirect_uri && $input["redirect_uri"]) {
|
|
||||||
// Ensure that the input uri starts with the stored uri
|
|
||||||
if (strcasecmp(substr($input["redirect_uri"], 0, strlen($redirect_uri)),$redirect_uri) !== 0)
|
|
||||||
$this->callback_error($input["redirect_uri"], ERROR_REDIRECT_URI_MISMATCH, $input["state"]);
|
|
||||||
} elseif ($redirect_uri) { // They did not provide a uri from input, so use the stored one
|
|
||||||
$input["redirect_uri"] = $redirect_uri;
|
|
||||||
}
|
|
||||||
|
|
||||||
// type and client_id are required
|
|
||||||
if (!$input["response_type"])
|
|
||||||
$this->callback_error($input["redirect_uri"], ERROR_INVALID_REQUEST, $input["state"], ERROR_INVALID_RESPONSE_TYPE);
|
|
||||||
|
|
||||||
// Check requested auth response type against the list of supported types
|
|
||||||
if (array_search($input["response_type"], $this->get_supported_auth_response_types()) === false)
|
|
||||||
$this->callback_error($input["redirect_uri"], ERROR_UNSUPPORTED_RESPONSE_TYPE, $input["state"]);
|
|
||||||
|
|
||||||
// Validate that the requested scope is supported
|
|
||||||
if ($input["scope"] && !$this->check_scope($input["scope"], $this->get_supported_scopes()))
|
|
||||||
$this->callback_error($input["redirect_uri"], ERROR_INVALID_SCOPE, $input["state"]);
|
|
||||||
|
|
||||||
return $input;
|
|
||||||
}
|
|
||||||
|
|
||||||
// After the user has approved or denied the access request
|
|
||||||
// the authorization server should call this function to redirect
|
|
||||||
// the user appropriately
|
|
||||||
|
|
||||||
// The params all come from the results of get_authorize_params
|
|
||||||
// except for $is_authorized -- this is true or false depending on whether
|
|
||||||
// the user authorized the access
|
|
||||||
public function finish_client_authorization($is_authorized, $type, $client_id, $redirect_uri, $state, $scope = null) {
|
|
||||||
if ($state !== null)
|
|
||||||
$result["query"]["state"] = $state;
|
|
||||||
|
|
||||||
if ($is_authorized === false) {
|
|
||||||
$result["query"]["error"] = ERROR_USER_DENIED;
|
|
||||||
} else {
|
|
||||||
if ($type == AUTH_CODE_AUTH_RESPONSE_TYPE || $type == CODE_AND_TOKEN_AUTH_RESPONSE_TYPE)
|
|
||||||
$result["query"]["code"] = $this->create_auth_code($client_id, $redirect_uri, $scope);
|
|
||||||
|
|
||||||
if ($type == ACCESS_TOKEN_AUTH_RESPONSE_TYPE || $type == CODE_AND_TOKEN_AUTH_RESPONSE_TYPE)
|
|
||||||
$result["fragment"] = $this->create_access_token($client_id, $scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->do_redirect_uri_callback($redirect_uri, $result);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Other/utility functions */
|
|
||||||
|
|
||||||
private function do_redirect_uri_callback($redirect_uri, $result) {
|
|
||||||
header("HTTP/1.1 302 Found");
|
|
||||||
header("Location: " . $this->build_uri($redirect_uri, $result));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function build_uri($uri, $data) {
|
|
||||||
$parse_url = parse_url($uri);
|
|
||||||
|
|
||||||
// Add our data to the parsed uri
|
|
||||||
foreach ($data as $k => $v) {
|
|
||||||
if (isset($parse_url[$k]))
|
|
||||||
$parse_url[$k] .= "&" . http_build_query($v);
|
|
||||||
else
|
|
||||||
$parse_url[$k] = http_build_query($v);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put humpty dumpty back together
|
|
||||||
return
|
|
||||||
((isset($parse_url["scheme"])) ? $parse_url["scheme"] . "://" : "")
|
|
||||||
.((isset($parse_url["user"])) ? $parse_url["user"] . ((isset($parse_url["pass"])) ? ":" . $parse_url["pass"] : "") ."@" : "")
|
|
||||||
.((isset($parse_url["host"])) ? $parse_url["host"] : "")
|
|
||||||
.((isset($parse_url["port"])) ? ":" . $parse_url["port"] : "")
|
|
||||||
.((isset($parse_url["path"])) ? $parse_url["path"] : "")
|
|
||||||
.((isset($parse_url["query"])) ? "?" . $parse_url["query"] : "")
|
|
||||||
.((isset($parse_url["fragment"])) ? "#" . $parse_url["fragment"] : "");
|
|
||||||
}
|
|
||||||
|
|
||||||
// This belongs in a separate factory, but to keep it simple, I'm just keeping it here.
|
|
||||||
private function create_access_token($client_id, $scope) {
|
|
||||||
$token = array(
|
|
||||||
"access_token" => $this->gen_access_token(),
|
|
||||||
"expires_in" => $this->access_token_lifetime,
|
|
||||||
"scope" => $scope
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->store_access_token($token["access_token"], $client_id, time() + $this->access_token_lifetime, $scope);
|
|
||||||
|
|
||||||
// Issue a refresh token also, if we support them
|
|
||||||
if (in_array(REFRESH_TOKEN_GRANT_TYPE, $this->get_supported_grant_types())) {
|
|
||||||
$token["refresh_token"] = $this->gen_access_token();
|
|
||||||
$this->store_refresh_token($token["refresh_token"], $client_id, time() + $this->refresh_token_lifetime, $scope);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function create_auth_code($client_id, $redirect_uri, $scope) {
|
|
||||||
$code = $this->gen_auth_code();
|
|
||||||
$this->store_auth_code($code, $client_id, $redirect_uri, time() + $this->auth_code_lifetime, $scope);
|
|
||||||
return $code;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementing classes may want to override these two functions
|
|
||||||
// to implement other access token or auth code generation schemes
|
|
||||||
private function gen_access_token() {
|
|
||||||
return base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private function gen_auth_code() {
|
|
||||||
return base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implementing classes may need to override this function for use on non-Apache web servers
|
|
||||||
// Just pull out the Authorization HTTP header and return it
|
|
||||||
// Return false if the Authorization header does not exist
|
|
||||||
private function get_authorization_header() {
|
|
||||||
if (array_key_exists("HTTP_AUTHORIZATION", $_SERVER))
|
|
||||||
return $_SERVER["HTTP_AUTHORIZATION"];
|
|
||||||
|
|
||||||
if (function_exists("apache_request_headers")) {
|
|
||||||
$headers = apache_request_headers();
|
|
||||||
|
|
||||||
if (array_key_exists("Authorization", $headers))
|
|
||||||
return $headers["Authorization"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private function send_json_headers() {
|
|
||||||
header("Content-Type: application/json");
|
|
||||||
header("Cache-Control: no-store");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function error($code, $message = null) {
|
|
||||||
header("HTTP/1.1 " . $code);
|
|
||||||
|
|
||||||
if ($message) {
|
|
||||||
$this->send_json_headers();
|
|
||||||
echo json_encode(array("error" => $message));
|
|
||||||
}
|
|
||||||
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function callback_error($redirect_uri, $error, $state, $message = null, $error_uri = null) {
|
|
||||||
$result["query"]["error"] = $error;
|
|
||||||
|
|
||||||
if ($state)
|
|
||||||
$result["query"]["state"] = $state;
|
|
||||||
|
|
||||||
if ($message)
|
|
||||||
$result["query"]["error_description"] = $message;
|
|
||||||
|
|
||||||
if ($error_uri)
|
|
||||||
$result["query"]["error_uri"] = $error_uri;
|
|
||||||
|
|
||||||
$this->do_redirect_uri_callback($redirect_uri, $result);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,11 +1,11 @@
|
||||||
PHP Markdown Lib
|
PHP Markdown Lib
|
||||||
Copyright (c) 2004-2014 Michel Fortin
|
Copyright (c) 2004-2016 Michel Fortin
|
||||||
<http://michelf.ca/>
|
<https://michelf.ca/>
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Based on Markdown
|
Based on Markdown
|
||||||
Copyright (c) 2003-2006 John Gruber
|
Copyright (c) 2003-2006 John Gruber
|
||||||
<http://daringfireball.net/>
|
<https://daringfireball.net/>
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
# Use this file if you cannot use class autoloading. It will include all the
|
// Use this file if you cannot use class autoloading. It will include all the
|
||||||
# files needed for the Markdown parser.
|
// files needed for the Markdown parser.
|
||||||
#
|
//
|
||||||
# Take a look at the PSR-0-compatible class autoloading implementation
|
// Take a look at the PSR-0-compatible class autoloading implementation
|
||||||
# in the Readme.php file if you want a simple autoloader setup.
|
// in the Readme.php file if you want a simple autoloader setup.
|
||||||
|
|
||||||
require_once dirname(__FILE__) . '/MarkdownInterface.php';
|
require_once dirname(__FILE__) . '/MarkdownInterface.php';
|
||||||
require_once dirname(__FILE__) . '/Markdown.php';
|
require_once dirname(__FILE__) . '/Markdown.php';
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,10 +1,10 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
# Use this file if you cannot use class autoloading. It will include all the
|
// Use this file if you cannot use class autoloading. It will include all the
|
||||||
# files needed for the MarkdownExtra parser.
|
// files needed for the MarkdownExtra parser.
|
||||||
#
|
//
|
||||||
# Take a look at the PSR-0-compatible class autoloading implementation
|
// Take a look at the PSR-0-compatible class autoloading implementation
|
||||||
# in the Readme.php file if you want a simple autoloader setup.
|
// in the Readme.php file if you want a simple autoloader setup.
|
||||||
|
|
||||||
require_once dirname(__FILE__) . '/MarkdownInterface.php';
|
require_once dirname(__FILE__) . '/MarkdownInterface.php';
|
||||||
require_once dirname(__FILE__) . '/Markdown.php';
|
require_once dirname(__FILE__) . '/Markdown.php';
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,9 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
# Use this file if you cannot use class autoloading. It will include all the
|
// Use this file if you cannot use class autoloading. It will include all the
|
||||||
# files needed for the MarkdownInterface interface.
|
// files needed for the MarkdownInterface interface.
|
||||||
#
|
//
|
||||||
# Take a look at the PSR-0-compatible class autoloading implementation
|
// Take a look at the PSR-0-compatible class autoloading implementation
|
||||||
# in the Readme.php file if you want a simple autoloader setup.
|
// in the Readme.php file if you want a simple autoloader setup.
|
||||||
|
|
||||||
require_once dirname(__FILE__) . '/MarkdownInterface.php';
|
require_once dirname(__FILE__) . '/MarkdownInterface.php';
|
||||||
|
|
|
@ -1,34 +1,38 @@
|
||||||
<?php
|
<?php
|
||||||
#
|
/**
|
||||||
# Markdown - A text-to-HTML conversion tool for web writers
|
* Markdown - A text-to-HTML conversion tool for web writers
|
||||||
#
|
*
|
||||||
# PHP Markdown
|
* @package php-markdown
|
||||||
# Copyright (c) 2004-2014 Michel Fortin
|
* @author Michel Fortin <michel.fortin@michelf.com>
|
||||||
# <http://michelf.com/projects/php-markdown/>
|
* @copyright 2004-2016 Michel Fortin <https://michelf.com/projects/php-markdown/>
|
||||||
#
|
* @copyright (Original Markdown) 2004-2006 John Gruber <https://daringfireball.net/projects/markdown/>
|
||||||
# Original Markdown
|
*/
|
||||||
# Copyright (c) 2004-2006 John Gruber
|
|
||||||
# <http://daringfireball.net/projects/markdown/>
|
|
||||||
#
|
|
||||||
namespace Michelf;
|
namespace Michelf;
|
||||||
|
|
||||||
|
/**
|
||||||
#
|
* Markdown Parser Interface
|
||||||
# Markdown Parser Interface
|
*/
|
||||||
#
|
|
||||||
|
|
||||||
interface MarkdownInterface {
|
interface MarkdownInterface {
|
||||||
|
/**
|
||||||
#
|
* Initialize the parser and return the result of its transform method.
|
||||||
# Initialize the parser and return the result of its transform method.
|
* This will work fine for derived classes too.
|
||||||
# This will work fine for derived classes too.
|
*
|
||||||
#
|
* @api
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public static function defaultTransform($text);
|
public static function defaultTransform($text);
|
||||||
|
|
||||||
#
|
/**
|
||||||
# Main function. Performs some preprocessing on the input text
|
* Main function. Performs some preprocessing on the input text
|
||||||
# and pass it through the document gamut.
|
* and pass it through the document gamut.
|
||||||
#
|
*
|
||||||
|
* @api
|
||||||
|
*
|
||||||
|
* @param string $text
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
public function transform($text);
|
public function transform($text);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
PHP Markdown
|
PHP Markdown
|
||||||
============
|
============
|
||||||
|
|
||||||
PHP Markdown Lib 1.4.1 - 4 May 2013
|
PHP Markdown Lib 1.7.0 - 29 Oct 2016
|
||||||
|
|
||||||
by Michel Fortin
|
by Michel Fortin
|
||||||
<http://michelf.ca/>
|
<https://michelf.ca/>
|
||||||
|
|
||||||
based on Markdown by John Gruber
|
based on Markdown by John Gruber
|
||||||
<http://daringfireball.net/>
|
<https://daringfireball.net/>
|
||||||
|
|
||||||
|
|
||||||
Introduction
|
Introduction
|
||||||
|
@ -25,10 +25,10 @@ software tool, originally written in Perl, that converts the plain text
|
||||||
markup to HTML. PHP Markdown is a port to PHP of the original Markdown
|
markup to HTML. PHP Markdown is a port to PHP of the original Markdown
|
||||||
program by John Gruber.
|
program by John Gruber.
|
||||||
|
|
||||||
* [Full documentation of the Markdown syntax](<http://daringfireball.net/projects/markdown/>)
|
* [Full documentation of the Markdown syntax](<https://daringfireball.net/projects/markdown/>)
|
||||||
- Daring Fireball (John Gruber)
|
— Daring Fireball (John Gruber)
|
||||||
* [Markdown Extra syntax additions](<http://michelf.ca/projects/php-markdown/extra/>)
|
* [Markdown Extra syntax additions](<https://michelf.ca/projects/php-markdown/extra/>)
|
||||||
- Michel Fortin
|
— Michel Fortin
|
||||||
|
|
||||||
|
|
||||||
Requirement
|
Requirement
|
||||||
|
@ -83,7 +83,7 @@ configuration variables:
|
||||||
|
|
||||||
To learn more, see the full list of [configuration variables].
|
To learn more, see the full list of [configuration variables].
|
||||||
|
|
||||||
[configuration variables]: http://michelf.ca/projects/php-markdown/configuration/
|
[configuration variables]: https://michelf.ca/projects/php-markdown/configuration/
|
||||||
|
|
||||||
|
|
||||||
### Usage without an autoloader
|
### Usage without an autoloader
|
||||||
|
@ -149,7 +149,7 @@ Development and Testing
|
||||||
-----------------------
|
-----------------------
|
||||||
|
|
||||||
Pull requests for fixing bugs are welcome. Proposed new features are
|
Pull requests for fixing bugs are welcome. Proposed new features are
|
||||||
going meticulously reviewed -- taking into account backward compatibility,
|
going to be meticulously reviewed -- taking into account backward compatibility,
|
||||||
potential side effects, and future extensibility -- before deciding on
|
potential side effects, and future extensibility -- before deciding on
|
||||||
acceptance or rejection.
|
acceptance or rejection.
|
||||||
|
|
||||||
|
@ -174,11 +174,80 @@ PHP Markdown, please visit [michelf.ca/donate] or send Bitcoin to
|
||||||
Version History
|
Version History
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Unreleased
|
PHP Markdown Lib 1.7.0 (29 Oct 2016)
|
||||||
|
|
||||||
* Added the ability to insert custom HTML attributes everywhere an extra
|
* Added a `hard_wrap` configuration variable to make all newline characters
|
||||||
attribute block is allowed (links, images, headers). Credits to
|
in the text become `<br>` tags in the HTML output. By default, according
|
||||||
Peter Droogmans for providing the implementation.
|
to the standard Markdown syntax these newlines are ignored unless they a
|
||||||
|
preceded by two spaces. Thanks to Jonathan Cohlmeyer for the implementation.
|
||||||
|
|
||||||
|
* Improved the parsing of list items to fix problematic cases that came to
|
||||||
|
light with the addition of `hard_wrap`. This should have no effect on the
|
||||||
|
output except span-level list items that ended with two spaces (and thus
|
||||||
|
ended with a line break).
|
||||||
|
|
||||||
|
* Added a `code_span_content_func` configuration variable which takes a
|
||||||
|
function that will convert the content of the code span to HTML. This can
|
||||||
|
be useful to implement syntax highlighting. Although contrary to its
|
||||||
|
code block equivalent, there is no syntax for specifying a language.
|
||||||
|
Credits to styxit for the implementation.
|
||||||
|
|
||||||
|
* Fixed a Markdown Extra issue where two-space-at-end-of-line hard breaks
|
||||||
|
wouldn't work inside of HTML block elements such as `<p markdown="1">`
|
||||||
|
where the element expects only span-level content.
|
||||||
|
|
||||||
|
* In the parser code, switched to PHPDoc comment format. Thanks to
|
||||||
|
Robbie Averill for the help.
|
||||||
|
|
||||||
|
|
||||||
|
PHP Markdown Lib 1.6.0 (23 Dec 2015)
|
||||||
|
|
||||||
|
Note: this version was incorrectly released as 1.5.1 on Dec 22, a number
|
||||||
|
that contradicted the versioning policy.
|
||||||
|
|
||||||
|
* For fenced code blocks in Markdown Extra, can now set a class name for the
|
||||||
|
code block's language before the special attribute block. Previously, this
|
||||||
|
class name was only allowed in the absence of the special attribute block.
|
||||||
|
|
||||||
|
* Added a `code_block_content_func` configuration variable which takes a
|
||||||
|
function that will convert the content of the code block to HTML. This is
|
||||||
|
most useful for syntax highlighting. For fenced code blocks in Markdown
|
||||||
|
Extra, the function has access to the language class name (the one outside
|
||||||
|
of the special attribute block). Credits to Mario Konrad for providing the
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
* The curled arrow character for the backlink in footnotes is now followed
|
||||||
|
by a Unicode variant selector to prevent it from being displayed in emoji
|
||||||
|
form on iOS.
|
||||||
|
|
||||||
|
Note that in older browsers the variant selector is often interpreted as a
|
||||||
|
separate character, making it visible after the arrow. So there is now a
|
||||||
|
also a `fn_backlink_html` configuration variable that can be used to set
|
||||||
|
the link text to something else. Credits to Dana for providing the
|
||||||
|
implementation.
|
||||||
|
|
||||||
|
* Fixed an issue in MarkdownExtra where long header lines followed by a
|
||||||
|
special attribute block would hit the backtrack limit an cause an empty
|
||||||
|
string to be returned.
|
||||||
|
|
||||||
|
|
||||||
|
PHP Markdown Lib 1.5.0 (1 Mar 2015)
|
||||||
|
|
||||||
|
* Added the ability start ordered lists with a number different from 1 and
|
||||||
|
and have that reflected in the HTML output. This can be enabled with
|
||||||
|
the `enhanced_ordered_lists` configuration variable for the Markdown
|
||||||
|
parser; it is enabled by default for Markdown Extra.
|
||||||
|
Credits to Matt Gorle for providing the implementation.
|
||||||
|
|
||||||
|
* Added the ability to insert custom HTML attributes with simple values
|
||||||
|
everywhere an extra attribute block is allowed (links, images, headers).
|
||||||
|
The value must be unquoted, cannot contains spaces and is limited to
|
||||||
|
alphanumeric ASCII characters.
|
||||||
|
Credits to Peter Droogmans for providing the implementation.
|
||||||
|
|
||||||
|
* Added a `header_id_func` configuration variable which takes a function
|
||||||
|
that can generate an `id` attribute value from the header text.
|
||||||
|
Credits to Evert Pot for providing the implementation.
|
||||||
|
|
||||||
* Added a `url_filter_func` configuration variable which takes a function
|
* Added a `url_filter_func` configuration variable which takes a function
|
||||||
that can rewrite any link or image URL to something different.
|
that can rewrite any link or image URL to something different.
|
||||||
|
@ -239,7 +308,7 @@ PHP Markdown Extra 1.2.6:
|
||||||
|
|
||||||
* Plugin interface for WordPress and other systems is no longer present in
|
* Plugin interface for WordPress and other systems is no longer present in
|
||||||
the Lib package. The classic package is still available if you need it:
|
the Lib package. The classic package is still available if you need it:
|
||||||
<http://michelf.ca/projects/php-markdown/classic/>
|
<https://michelf.ca/projects/php-markdown/classic/>
|
||||||
|
|
||||||
* Added `public` and `protected` protection attributes, plus a section about
|
* Added `public` and `protected` protection attributes, plus a section about
|
||||||
what is "public API" and what isn't in the Readme file.
|
what is "public API" and what isn't in the Readme file.
|
||||||
|
@ -277,13 +346,13 @@ Copyright and License
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
PHP Markdown Lib
|
PHP Markdown Lib
|
||||||
Copyright (c) 2004-2014 Michel Fortin
|
Copyright (c) 2004-2016 Michel Fortin
|
||||||
<http://michelf.ca/>
|
<https://michelf.ca/>
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Based on Markdown
|
Based on Markdown
|
||||||
Copyright (c) 2003-2005 John Gruber
|
Copyright (c) 2003-2005 John Gruber
|
||||||
<http://daringfireball.net/>
|
<https://daringfireball.net/>
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
# This file passes the content of the Readme.md file in the same directory
|
// This file passes the content of the Readme.md file in the same directory
|
||||||
# through the Markdown filter. You can adapt this sample code in any way
|
// through the Markdown filter. You can adapt this sample code in any way
|
||||||
# you like.
|
// you like.
|
||||||
|
|
||||||
# Install PSR-0-compatible class autoloader
|
// Install PSR-0-compatible class autoloader
|
||||||
spl_autoload_register(function($class){
|
spl_autoload_register(function($class){
|
||||||
require preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php';
|
require preg_replace('{\\\\|_(?!.*\\\\)}', DIRECTORY_SEPARATOR, ltrim($class, '\\')).'.php';
|
||||||
});
|
});
|
||||||
|
|
||||||
# Get Markdown class
|
// Get Markdown class
|
||||||
use \Michelf\Markdown;
|
use \Michelf\Markdown;
|
||||||
|
|
||||||
# Read file and pass content through the Markdown parser
|
// Read file and pass content through the Markdown parser
|
||||||
$text = file_get_contents('Readme.md');
|
$text = file_get_contents('Readme.md');
|
||||||
$html = Markdown::defaultTransform($text);
|
$html = Markdown::defaultTransform($text);
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ $html = Markdown::defaultTransform($text);
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<?php
|
<?php
|
||||||
# Put HTML content in the document
|
// Put HTML content in the document
|
||||||
echo $html;
|
echo $html;
|
||||||
?>
|
?>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -2,19 +2,19 @@
|
||||||
"name": "michelf/php-markdown",
|
"name": "michelf/php-markdown",
|
||||||
"type": "library",
|
"type": "library",
|
||||||
"description": "PHP Markdown",
|
"description": "PHP Markdown",
|
||||||
"homepage": "http://michelf.ca/projects/php-markdown/",
|
"homepage": "https://michelf.ca/projects/php-markdown/",
|
||||||
"keywords": ["markdown"],
|
"keywords": ["markdown"],
|
||||||
"license": "BSD-3-Clause",
|
"license": "BSD-3-Clause",
|
||||||
"authors": [
|
"authors": [
|
||||||
{
|
{
|
||||||
"name": "Michel Fortin",
|
"name": "Michel Fortin",
|
||||||
"email": "michel.fortin@michelf.ca",
|
"email": "michel.fortin@michelf.ca",
|
||||||
"homepage": "http://michelf.ca/",
|
"homepage": "https://michelf.ca/",
|
||||||
"role": "Developer"
|
"role": "Developer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "John Gruber",
|
"name": "John Gruber",
|
||||||
"homepage": "http://daringfireball.net/"
|
"homepage": "https://daringfireball.net/"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
|
@ -22,10 +22,5 @@
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
"psr-0": { "Michelf": "" }
|
"psr-0": { "Michelf": "" }
|
||||||
},
|
|
||||||
"extra": {
|
|
||||||
"branch-alias": {
|
|
||||||
"dev-lib": "1.4.x-dev"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,479 +0,0 @@
|
||||||
<?php
|
|
||||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of AES.
|
|
||||||
*
|
|
||||||
* Uses mcrypt, if available, and an internal implementation, otherwise.
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* If {@link Crypt_AES::setKeyLength() setKeyLength()} isn't called, it'll be calculated from
|
|
||||||
* {@link Crypt_AES::setKey() setKey()}. ie. if the key is 128-bits, the key length will be 128-bits. If it's 136-bits
|
|
||||||
* it'll be null-padded to 160-bits and 160 bits will be the key length until {@link Crypt_Rijndael::setKey() setKey()}
|
|
||||||
* is called, again, at which point, it'll be recalculated.
|
|
||||||
*
|
|
||||||
* Since Crypt_AES extends Crypt_Rijndael, some functions are available to be called that, in the context of AES, don't
|
|
||||||
* make a whole lot of sense. {@link Crypt_AES::setBlockLength() setBlockLength()}, for instance. Calling that function,
|
|
||||||
* however possible, won't do anything (AES has a fixed block length whereas Rijndael has a variable one).
|
|
||||||
*
|
|
||||||
* Here's a short example of how to use this library:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
* include('Crypt/AES.php');
|
|
||||||
*
|
|
||||||
* $aes = new Crypt_AES();
|
|
||||||
*
|
|
||||||
* $aes->setKey('abcdefghijklmnop');
|
|
||||||
*
|
|
||||||
* $size = 10 * 1024;
|
|
||||||
* $plaintext = '';
|
|
||||||
* for ($i = 0; $i < $size; $i++) {
|
|
||||||
* $plaintext.= 'a';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* echo $aes->decrypt($aes->encrypt($plaintext));
|
|
||||||
* ?>
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* 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_AES
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @copyright MMVIII Jim Wigginton
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl.txt
|
|
||||||
* @version $Id: AES.php,v 1.7 2010/02/09 06:10:25 terrafrost Exp $
|
|
||||||
* @link http://phpseclib.sourceforge.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Include Crypt_Rijndael
|
|
||||||
*/
|
|
||||||
require_once 'Rijndael.php';
|
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* @access public
|
|
||||||
* @see Crypt_AES::encrypt()
|
|
||||||
* @see Crypt_AES::decrypt()
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using the Counter mode.
|
|
||||||
*
|
|
||||||
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
|
|
||||||
*
|
|
||||||
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
|
|
||||||
*/
|
|
||||||
define('CRYPT_AES_MODE_CTR', -1);
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using the Electronic Code Book mode.
|
|
||||||
*
|
|
||||||
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
|
|
||||||
*/
|
|
||||||
define('CRYPT_AES_MODE_ECB', 1);
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using the Code Book Chaining mode.
|
|
||||||
*
|
|
||||||
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
|
|
||||||
*/
|
|
||||||
define('CRYPT_AES_MODE_CBC', 2);
|
|
||||||
/**#@-*/
|
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* @access private
|
|
||||||
* @see Crypt_AES::Crypt_AES()
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Toggles the internal implementation
|
|
||||||
*/
|
|
||||||
define('CRYPT_AES_MODE_INTERNAL', 1);
|
|
||||||
/**
|
|
||||||
* Toggles the mcrypt implementation
|
|
||||||
*/
|
|
||||||
define('CRYPT_AES_MODE_MCRYPT', 2);
|
|
||||||
/**#@-*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of AES.
|
|
||||||
*
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @version 0.1.0
|
|
||||||
* @access public
|
|
||||||
* @package Crypt_AES
|
|
||||||
*/
|
|
||||||
class Crypt_AES extends Crypt_Rijndael {
|
|
||||||
/**
|
|
||||||
* mcrypt resource for encryption
|
|
||||||
*
|
|
||||||
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
|
|
||||||
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::encrypt()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $enmcrypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mcrypt resource for decryption
|
|
||||||
*
|
|
||||||
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
|
|
||||||
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::decrypt()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $demcrypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Constructor.
|
|
||||||
*
|
|
||||||
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
|
|
||||||
* CRYPT_AES_MODE_ECB or CRYPT_AES_MODE_CBC. If not explictly set, CRYPT_AES_MODE_CBC will be used.
|
|
||||||
*
|
|
||||||
* @param optional Integer $mode
|
|
||||||
* @return Crypt_AES
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function Crypt_AES($mode = CRYPT_AES_MODE_CBC)
|
|
||||||
{
|
|
||||||
if ( !defined('CRYPT_AES_MODE') ) {
|
|
||||||
switch (true) {
|
|
||||||
case extension_loaded('mcrypt'):
|
|
||||||
// i'd check to see if aes was supported, by doing in_array('des', mcrypt_list_algorithms('')),
|
|
||||||
// but since that can be changed after the object has been created, there doesn't seem to be
|
|
||||||
// a lot of point...
|
|
||||||
define('CRYPT_AES_MODE', CRYPT_AES_MODE_MCRYPT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
define('CRYPT_AES_MODE', CRYPT_AES_MODE_INTERNAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( CRYPT_AES_MODE ) {
|
|
||||||
case CRYPT_AES_MODE_MCRYPT:
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_AES_MODE_ECB:
|
|
||||||
$this->mode = MCRYPT_MODE_ECB;
|
|
||||||
break;
|
|
||||||
case CRYPT_AES_MODE_CTR:
|
|
||||||
// ctr doesn't have a constant associated with it even though it appears to be fairly widely
|
|
||||||
// supported. in lieu of knowing just how widely supported it is, i've, for now, opted not to
|
|
||||||
// include a compatibility layer. the layer has been implemented but, for now, is commented out.
|
|
||||||
$this->mode = 'ctr';
|
|
||||||
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_AES_MODE_CTR;
|
|
||||||
break;
|
|
||||||
case CRYPT_AES_MODE_CBC:
|
|
||||||
default:
|
|
||||||
$this->mode = MCRYPT_MODE_CBC;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_AES_MODE_ECB:
|
|
||||||
$this->mode = CRYPT_RIJNDAEL_MODE_ECB;
|
|
||||||
break;
|
|
||||||
case CRYPT_AES_MODE_CTR:
|
|
||||||
$this->mode = CRYPT_RIJNDAEL_MODE_CTR;
|
|
||||||
break;
|
|
||||||
case CRYPT_AES_MODE_CBC:
|
|
||||||
default:
|
|
||||||
$this->mode = CRYPT_RIJNDAEL_MODE_CBC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CRYPT_AES_MODE == CRYPT_AES_MODE_INTERNAL) {
|
|
||||||
parent::Crypt_Rijndael($this->mode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy function
|
|
||||||
*
|
|
||||||
* Since Crypt_AES extends Crypt_Rijndael, this function is, technically, available, but it doesn't do anything.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param Integer $length
|
|
||||||
*/
|
|
||||||
function setBlockLength($length)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a message.
|
|
||||||
*
|
|
||||||
* $plaintext will be padded with up to 16 additional bytes. Other AES implementations may or may not pad in the
|
|
||||||
* same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
|
|
||||||
* URL:
|
|
||||||
*
|
|
||||||
* {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
|
|
||||||
*
|
|
||||||
* An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
|
|
||||||
* strlen($plaintext) will still need to be a multiple of 16, however, arbitrary values can be added to make it that
|
|
||||||
* length.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::decrypt()
|
|
||||||
* @access public
|
|
||||||
* @param String $plaintext
|
|
||||||
*/
|
|
||||||
function encrypt($plaintext)
|
|
||||||
{
|
|
||||||
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
|
|
||||||
$this->_mcryptSetup();
|
|
||||||
/*
|
|
||||||
if ($this->mode == CRYPT_AES_MODE_CTR) {
|
|
||||||
$iv = $this->encryptIV;
|
|
||||||
$xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($plaintext), $iv));
|
|
||||||
$ciphertext = $plaintext ^ $xor;
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->encryptIV = $iv;
|
|
||||||
}
|
|
||||||
return $ciphertext;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ($this->mode != 'ctr') {
|
|
||||||
$plaintext = $this->_pad($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
|
|
||||||
|
|
||||||
if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::encrypt($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a message.
|
|
||||||
*
|
|
||||||
* If strlen($ciphertext) is not a multiple of 16, null bytes will be added to the end of the string until it is.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::encrypt()
|
|
||||||
* @access public
|
|
||||||
* @param String $ciphertext
|
|
||||||
*/
|
|
||||||
function decrypt($ciphertext)
|
|
||||||
{
|
|
||||||
if ( CRYPT_AES_MODE == CRYPT_AES_MODE_MCRYPT ) {
|
|
||||||
$this->_mcryptSetup();
|
|
||||||
/*
|
|
||||||
if ($this->mode == CRYPT_AES_MODE_CTR) {
|
|
||||||
$iv = $this->decryptIV;
|
|
||||||
$xor = mcrypt_generic($this->enmcrypt, $this->_generate_xor(strlen($ciphertext), $iv));
|
|
||||||
$plaintext = $ciphertext ^ $xor;
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->decryptIV = $iv;
|
|
||||||
}
|
|
||||||
return $plaintext;
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ($this->mode != 'ctr') {
|
|
||||||
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
|
|
||||||
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
|
|
||||||
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 15) & 0xFFFFFFF0, chr(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
|
|
||||||
|
|
||||||
if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::decrypt($ciphertext);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Setup mcrypt
|
|
||||||
*
|
|
||||||
* Validates all the variables.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _mcryptSetup()
|
|
||||||
{
|
|
||||||
if (!$this->changed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->explicit_key_length) {
|
|
||||||
// this just copied from Crypt_Rijndael::_setup()
|
|
||||||
$length = strlen($this->key) >> 2;
|
|
||||||
if ($length > 8) {
|
|
||||||
$length = 8;
|
|
||||||
} else if ($length < 4) {
|
|
||||||
$length = 4;
|
|
||||||
}
|
|
||||||
$this->Nk = $length;
|
|
||||||
$this->key_size = $length << 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($this->Nk) {
|
|
||||||
case 4: // 128
|
|
||||||
$this->key_size = 16;
|
|
||||||
break;
|
|
||||||
case 5: // 160
|
|
||||||
case 6: // 192
|
|
||||||
$this->key_size = 24;
|
|
||||||
break;
|
|
||||||
case 7: // 224
|
|
||||||
case 8: // 256
|
|
||||||
$this->key_size = 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->key = substr($this->key, 0, $this->key_size);
|
|
||||||
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($this->iv, 0, 16), 16, chr(0));
|
|
||||||
|
|
||||||
if (!isset($this->enmcrypt)) {
|
|
||||||
$mode = $this->mode;
|
|
||||||
//$mode = $this->mode == CRYPT_AES_MODE_CTR ? MCRYPT_MODE_ECB : $this->mode;
|
|
||||||
|
|
||||||
$this->demcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
|
|
||||||
$this->enmcrypt = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', $mode, '');
|
|
||||||
} // else should mcrypt_generic_deinit be called?
|
|
||||||
|
|
||||||
mcrypt_generic_init($this->demcrypt, $this->key, $this->iv);
|
|
||||||
mcrypt_generic_init($this->enmcrypt, $this->key, $this->iv);
|
|
||||||
|
|
||||||
$this->changed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a block
|
|
||||||
*
|
|
||||||
* Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
|
|
||||||
*
|
|
||||||
* @see Crypt_Rijndael::_encryptBlock()
|
|
||||||
* @access private
|
|
||||||
* @param String $in
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
function _encryptBlock($in)
|
|
||||||
{
|
|
||||||
$state = unpack('N*word', $in);
|
|
||||||
|
|
||||||
$Nr = $this->Nr;
|
|
||||||
$w = $this->w;
|
|
||||||
$t0 = $this->t0;
|
|
||||||
$t1 = $this->t1;
|
|
||||||
$t2 = $this->t2;
|
|
||||||
$t3 = $this->t3;
|
|
||||||
|
|
||||||
// addRoundKey and reindex $state
|
|
||||||
$state = array(
|
|
||||||
$state['word1'] ^ $w[0][0],
|
|
||||||
$state['word2'] ^ $w[0][1],
|
|
||||||
$state['word3'] ^ $w[0][2],
|
|
||||||
$state['word4'] ^ $w[0][3]
|
|
||||||
);
|
|
||||||
|
|
||||||
// shiftRows + subWord + mixColumns + addRoundKey
|
|
||||||
// we could loop unroll this and use if statements to do more rounds as necessary, but, in my tests, that yields
|
|
||||||
// only a marginal improvement. since that also, imho, hinders the readability of the code, i've opted not to do it.
|
|
||||||
for ($round = 1; $round < $this->Nr; $round++) {
|
|
||||||
$state = array(
|
|
||||||
$t0[$state[0] & 0xFF000000] ^ $t1[$state[1] & 0x00FF0000] ^ $t2[$state[2] & 0x0000FF00] ^ $t3[$state[3] & 0x000000FF] ^ $w[$round][0],
|
|
||||||
$t0[$state[1] & 0xFF000000] ^ $t1[$state[2] & 0x00FF0000] ^ $t2[$state[3] & 0x0000FF00] ^ $t3[$state[0] & 0x000000FF] ^ $w[$round][1],
|
|
||||||
$t0[$state[2] & 0xFF000000] ^ $t1[$state[3] & 0x00FF0000] ^ $t2[$state[0] & 0x0000FF00] ^ $t3[$state[1] & 0x000000FF] ^ $w[$round][2],
|
|
||||||
$t0[$state[3] & 0xFF000000] ^ $t1[$state[0] & 0x00FF0000] ^ $t2[$state[1] & 0x0000FF00] ^ $t3[$state[2] & 0x000000FF] ^ $w[$round][3]
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// subWord
|
|
||||||
$state = array(
|
|
||||||
$this->_subWord($state[0]),
|
|
||||||
$this->_subWord($state[1]),
|
|
||||||
$this->_subWord($state[2]),
|
|
||||||
$this->_subWord($state[3])
|
|
||||||
);
|
|
||||||
|
|
||||||
// shiftRows + addRoundKey
|
|
||||||
$state = array(
|
|
||||||
($state[0] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[3] & 0x000000FF) ^ $this->w[$this->Nr][0],
|
|
||||||
($state[1] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[0] & 0x000000FF) ^ $this->w[$this->Nr][1],
|
|
||||||
($state[2] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[1] & 0x000000FF) ^ $this->w[$this->Nr][2],
|
|
||||||
($state[3] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[2] & 0x000000FF) ^ $this->w[$this->Nr][3]
|
|
||||||
);
|
|
||||||
|
|
||||||
return pack('N*', $state[0], $state[1], $state[2], $state[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a block
|
|
||||||
*
|
|
||||||
* Optimized over Crypt_Rijndael's implementation by means of loop unrolling.
|
|
||||||
*
|
|
||||||
* @see Crypt_Rijndael::_decryptBlock()
|
|
||||||
* @access private
|
|
||||||
* @param String $in
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
function _decryptBlock($in)
|
|
||||||
{
|
|
||||||
$state = unpack('N*word', $in);
|
|
||||||
|
|
||||||
$Nr = $this->Nr;
|
|
||||||
$dw = $this->dw;
|
|
||||||
$dt0 = $this->dt0;
|
|
||||||
$dt1 = $this->dt1;
|
|
||||||
$dt2 = $this->dt2;
|
|
||||||
$dt3 = $this->dt3;
|
|
||||||
|
|
||||||
// addRoundKey and reindex $state
|
|
||||||
$state = array(
|
|
||||||
$state['word1'] ^ $dw[$this->Nr][0],
|
|
||||||
$state['word2'] ^ $dw[$this->Nr][1],
|
|
||||||
$state['word3'] ^ $dw[$this->Nr][2],
|
|
||||||
$state['word4'] ^ $dw[$this->Nr][3]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// invShiftRows + invSubBytes + invMixColumns + addRoundKey
|
|
||||||
for ($round = $this->Nr - 1; $round > 0; $round--) {
|
|
||||||
$state = array(
|
|
||||||
$dt0[$state[0] & 0xFF000000] ^ $dt1[$state[3] & 0x00FF0000] ^ $dt2[$state[2] & 0x0000FF00] ^ $dt3[$state[1] & 0x000000FF] ^ $dw[$round][0],
|
|
||||||
$dt0[$state[1] & 0xFF000000] ^ $dt1[$state[0] & 0x00FF0000] ^ $dt2[$state[3] & 0x0000FF00] ^ $dt3[$state[2] & 0x000000FF] ^ $dw[$round][1],
|
|
||||||
$dt0[$state[2] & 0xFF000000] ^ $dt1[$state[1] & 0x00FF0000] ^ $dt2[$state[0] & 0x0000FF00] ^ $dt3[$state[3] & 0x000000FF] ^ $dw[$round][2],
|
|
||||||
$dt0[$state[3] & 0xFF000000] ^ $dt1[$state[2] & 0x00FF0000] ^ $dt2[$state[1] & 0x0000FF00] ^ $dt3[$state[0] & 0x000000FF] ^ $dw[$round][3]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// invShiftRows + invSubWord + addRoundKey
|
|
||||||
$state = array(
|
|
||||||
$this->_invSubWord(($state[0] & 0xFF000000) ^ ($state[3] & 0x00FF0000) ^ ($state[2] & 0x0000FF00) ^ ($state[1] & 0x000000FF)) ^ $dw[0][0],
|
|
||||||
$this->_invSubWord(($state[1] & 0xFF000000) ^ ($state[0] & 0x00FF0000) ^ ($state[3] & 0x0000FF00) ^ ($state[2] & 0x000000FF)) ^ $dw[0][1],
|
|
||||||
$this->_invSubWord(($state[2] & 0xFF000000) ^ ($state[1] & 0x00FF0000) ^ ($state[0] & 0x0000FF00) ^ ($state[3] & 0x000000FF)) ^ $dw[0][2],
|
|
||||||
$this->_invSubWord(($state[3] & 0xFF000000) ^ ($state[2] & 0x00FF0000) ^ ($state[1] & 0x0000FF00) ^ ($state[0] & 0x000000FF)) ^ $dw[0][3]
|
|
||||||
);
|
|
||||||
|
|
||||||
return pack('N*', $state[0], $state[1], $state[2], $state[3]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: ts=4:sw=4:et:
|
|
||||||
// vim6: fdl=1:
|
|
|
@ -1,945 +0,0 @@
|
||||||
<?php
|
|
||||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of DES.
|
|
||||||
*
|
|
||||||
* Uses mcrypt, if available, and an internal implementation, otherwise.
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* Useful resources are as follows:
|
|
||||||
*
|
|
||||||
* - {@link http://en.wikipedia.org/wiki/DES_supplementary_material Wikipedia: DES supplementary material}
|
|
||||||
* - {@link http://www.itl.nist.gov/fipspubs/fip46-2.htm FIPS 46-2 - (DES), Data Encryption Standard}
|
|
||||||
* - {@link http://www.cs.eku.edu/faculty/styer/460/Encrypt/JS-DES.html JavaScript DES Example}
|
|
||||||
*
|
|
||||||
* Here's a short example of how to use this library:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
* include('Crypt/DES.php');
|
|
||||||
*
|
|
||||||
* $des = new Crypt_DES();
|
|
||||||
*
|
|
||||||
* $des->setKey('abcdefgh');
|
|
||||||
*
|
|
||||||
* $size = 10 * 1024;
|
|
||||||
* $plaintext = '';
|
|
||||||
* for ($i = 0; $i < $size; $i++) {
|
|
||||||
* $plaintext.= 'a';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* echo $des->decrypt($des->encrypt($plaintext));
|
|
||||||
* ?>
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* 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_DES
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @copyright MMVII Jim Wigginton
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl.txt
|
|
||||||
* @version $Id: DES.php,v 1.12 2010/02/09 06:10:26 terrafrost Exp $
|
|
||||||
* @link http://phpseclib.sourceforge.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* @access private
|
|
||||||
* @see Crypt_DES::_prepareKey()
|
|
||||||
* @see Crypt_DES::_processBlock()
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Contains array_reverse($keys[CRYPT_DES_DECRYPT])
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_ENCRYPT', 0);
|
|
||||||
/**
|
|
||||||
* Contains array_reverse($keys[CRYPT_DES_ENCRYPT])
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_DECRYPT', 1);
|
|
||||||
/**#@-*/
|
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* @access public
|
|
||||||
* @see Crypt_DES::encrypt()
|
|
||||||
* @see Crypt_DES::decrypt()
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using the Counter mode.
|
|
||||||
*
|
|
||||||
* Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
|
|
||||||
*
|
|
||||||
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_MODE_CTR', -1);
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using the Electronic Code Book mode.
|
|
||||||
*
|
|
||||||
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_MODE_ECB', 1);
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using the Code Book Chaining mode.
|
|
||||||
*
|
|
||||||
* @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_MODE_CBC', 2);
|
|
||||||
/**#@-*/
|
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* @access private
|
|
||||||
* @see Crypt_DES::Crypt_DES()
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Toggles the internal implementation
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_MODE_INTERNAL', 1);
|
|
||||||
/**
|
|
||||||
* Toggles the mcrypt implementation
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_MODE_MCRYPT', 2);
|
|
||||||
/**#@-*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of DES.
|
|
||||||
*
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @version 0.1.0
|
|
||||||
* @access public
|
|
||||||
* @package Crypt_DES
|
|
||||||
*/
|
|
||||||
class Crypt_DES {
|
|
||||||
/**
|
|
||||||
* The Key Schedule
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::setKey()
|
|
||||||
* @var Array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $keys = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Encryption Mode
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::Crypt_DES()
|
|
||||||
* @var Integer
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $mode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Continuous Buffer status
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::enableContinuousBuffer()
|
|
||||||
* @var Boolean
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $continuousBuffer = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Padding status
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::enablePadding()
|
|
||||||
* @var Boolean
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $padding = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Initialization Vector
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::setIV()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $iv = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "sliding" Initialization Vector
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::enableContinuousBuffer()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $encryptIV = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "sliding" Initialization Vector
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::enableContinuousBuffer()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $decryptIV = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mcrypt resource for encryption
|
|
||||||
*
|
|
||||||
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
|
|
||||||
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::encrypt()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $enmcrypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mcrypt resource for decryption
|
|
||||||
*
|
|
||||||
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
|
|
||||||
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::decrypt()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $demcrypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the (en|de)mcrypt resource need to be (re)initialized?
|
|
||||||
*
|
|
||||||
* @see setKey()
|
|
||||||
* @see setIV()
|
|
||||||
* @var Boolean
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $changed = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Constructor.
|
|
||||||
*
|
|
||||||
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
|
|
||||||
* CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
|
|
||||||
*
|
|
||||||
* @param optional Integer $mode
|
|
||||||
* @return Crypt_DES
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function Crypt_DES($mode = CRYPT_MODE_DES_CBC)
|
|
||||||
{
|
|
||||||
if ( !defined('CRYPT_DES_MODE') ) {
|
|
||||||
switch (true) {
|
|
||||||
case extension_loaded('mcrypt'):
|
|
||||||
// i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
|
|
||||||
// but since that can be changed after the object has been created, there doesn't seem to be
|
|
||||||
// a lot of point...
|
|
||||||
define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( CRYPT_DES_MODE ) {
|
|
||||||
case CRYPT_DES_MODE_MCRYPT:
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
$this->mode = MCRYPT_MODE_ECB;
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
$this->mode = 'ctr';
|
|
||||||
//$this->mode = in_array('ctr', mcrypt_list_modes()) ? 'ctr' : CRYPT_DES_MODE_CTR;
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
default:
|
|
||||||
$this->mode = MCRYPT_MODE_CBC;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
$this->mode = $mode;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$this->mode = CRYPT_DES_MODE_CBC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the key.
|
|
||||||
*
|
|
||||||
* Keys can be of any length. DES, itself, uses 64-bit keys (eg. strlen($key) == 8), however, we
|
|
||||||
* only use the first eight, if $key has more then eight characters in it, and pad $key with the
|
|
||||||
* null byte if it is less then eight characters long.
|
|
||||||
*
|
|
||||||
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
|
|
||||||
*
|
|
||||||
* If the key is not explicitly set, it'll be assumed to be all zero's.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param String $key
|
|
||||||
*/
|
|
||||||
function setKey($key)
|
|
||||||
{
|
|
||||||
$this->keys = ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) ? substr($key, 0, 8) : $this->_prepareKey($key);
|
|
||||||
$this->changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the initialization vector. (optional)
|
|
||||||
*
|
|
||||||
* SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
|
|
||||||
* to be all zero's.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param String $iv
|
|
||||||
*/
|
|
||||||
function setIV($iv)
|
|
||||||
{
|
|
||||||
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
|
|
||||||
$this->changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate CTR XOR encryption key
|
|
||||||
*
|
|
||||||
* Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
|
|
||||||
* plaintext / ciphertext in CTR mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::decrypt()
|
|
||||||
* @see Crypt_DES::encrypt()
|
|
||||||
* @access public
|
|
||||||
* @param Integer $length
|
|
||||||
* @param String $iv
|
|
||||||
*/
|
|
||||||
function _generate_xor($length, &$iv)
|
|
||||||
{
|
|
||||||
$xor = '';
|
|
||||||
$num_blocks = ($length + 7) >> 3;
|
|
||||||
for ($i = 0; $i < $num_blocks; $i++) {
|
|
||||||
$xor.= $iv;
|
|
||||||
for ($j = 4; $j <= 8; $j+=4) {
|
|
||||||
$temp = substr($iv, -$j, 4);
|
|
||||||
switch ($temp) {
|
|
||||||
case "\xFF\xFF\xFF\xFF":
|
|
||||||
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
|
|
||||||
break;
|
|
||||||
case "\x7F\xFF\xFF\xFF":
|
|
||||||
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
|
|
||||||
break 2;
|
|
||||||
default:
|
|
||||||
extract(unpack('Ncount', $temp));
|
|
||||||
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $xor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a message.
|
|
||||||
*
|
|
||||||
* $plaintext will be padded with up to 8 additional bytes. Other DES implementations may or may not pad in the
|
|
||||||
* same manner. Other common approaches to padding and the reasons why it's necessary are discussed in the following
|
|
||||||
* URL:
|
|
||||||
*
|
|
||||||
* {@link http://www.di-mgt.com.au/cryptopad.html http://www.di-mgt.com.au/cryptopad.html}
|
|
||||||
*
|
|
||||||
* An alternative to padding is to, separately, send the length of the file. This is what SSH, in fact, does.
|
|
||||||
* strlen($plaintext) will still need to be a multiple of 8, however, arbitrary values can be added to make it that
|
|
||||||
* length.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::decrypt()
|
|
||||||
* @access public
|
|
||||||
* @param String $plaintext
|
|
||||||
*/
|
|
||||||
function encrypt($plaintext)
|
|
||||||
{
|
|
||||||
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
|
|
||||||
$plaintext = $this->_pad($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
|
|
||||||
if ($this->changed) {
|
|
||||||
if (!isset($this->enmcrypt)) {
|
|
||||||
$this->enmcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
|
|
||||||
}
|
|
||||||
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
|
|
||||||
$this->changed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
|
|
||||||
|
|
||||||
if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_init($this->enmcrypt, $this->keys, $this->encryptIV);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($this->keys)) {
|
|
||||||
$this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
|
|
||||||
}
|
|
||||||
|
|
||||||
$ciphertext = '';
|
|
||||||
switch ($this->mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
for ($i = 0; $i < strlen($plaintext); $i+=8) {
|
|
||||||
$ciphertext.= $this->_processBlock(substr($plaintext, $i, 8), CRYPT_DES_ENCRYPT);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
$xor = $this->encryptIV;
|
|
||||||
for ($i = 0; $i < strlen($plaintext); $i+=8) {
|
|
||||||
$block = substr($plaintext, $i, 8);
|
|
||||||
$block = $this->_processBlock($block ^ $xor, CRYPT_DES_ENCRYPT);
|
|
||||||
$xor = $block;
|
|
||||||
$ciphertext.= $block;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->encryptIV = $xor;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
$xor = $this->encryptIV;
|
|
||||||
for ($i = 0; $i < strlen($plaintext); $i+=8) {
|
|
||||||
$block = substr($plaintext, $i, 8);
|
|
||||||
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
|
|
||||||
$ciphertext.= $block ^ $key;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->encryptIV = $xor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a message.
|
|
||||||
*
|
|
||||||
* If strlen($ciphertext) is not a multiple of 8, null bytes will be added to the end of the string until it is.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::encrypt()
|
|
||||||
* @access public
|
|
||||||
* @param String $ciphertext
|
|
||||||
*/
|
|
||||||
function decrypt($ciphertext)
|
|
||||||
{
|
|
||||||
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
|
|
||||||
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
|
|
||||||
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
|
|
||||||
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
|
|
||||||
if ($this->changed) {
|
|
||||||
if (!isset($this->demcrypt)) {
|
|
||||||
$this->demcrypt = mcrypt_module_open(MCRYPT_DES, '', $this->mode, '');
|
|
||||||
}
|
|
||||||
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
|
|
||||||
$this->changed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
|
|
||||||
|
|
||||||
if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_init($this->demcrypt, $this->keys, $this->decryptIV);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_array($this->keys)) {
|
|
||||||
$this->keys = $this->_prepareKey("\0\0\0\0\0\0\0\0");
|
|
||||||
}
|
|
||||||
|
|
||||||
$plaintext = '';
|
|
||||||
switch ($this->mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
|
|
||||||
$plaintext.= $this->_processBlock(substr($ciphertext, $i, 8), CRYPT_DES_DECRYPT);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
$xor = $this->decryptIV;
|
|
||||||
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
|
|
||||||
$block = substr($ciphertext, $i, 8);
|
|
||||||
$plaintext.= $this->_processBlock($block, CRYPT_DES_DECRYPT) ^ $xor;
|
|
||||||
$xor = $block;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->decryptIV = $xor;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
$xor = $this->decryptIV;
|
|
||||||
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
|
|
||||||
$block = substr($ciphertext, $i, 8);
|
|
||||||
$key = $this->_processBlock($this->_generate_xor(8, $xor), CRYPT_DES_ENCRYPT);
|
|
||||||
$plaintext.= $block ^ $key;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->decryptIV = $xor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Treat consecutive "packets" as if they are a continuous buffer.
|
|
||||||
*
|
|
||||||
* Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
|
|
||||||
* will yield different outputs:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* echo $des->encrypt(substr($plaintext, 0, 8));
|
|
||||||
* echo $des->encrypt(substr($plaintext, 8, 8));
|
|
||||||
* </code>
|
|
||||||
* <code>
|
|
||||||
* echo $des->encrypt($plaintext);
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
|
|
||||||
* another, as demonstrated with the following:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* $des->encrypt(substr($plaintext, 0, 8));
|
|
||||||
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
|
|
||||||
* </code>
|
|
||||||
* <code>
|
|
||||||
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
|
|
||||||
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
|
|
||||||
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
|
|
||||||
*
|
|
||||||
* Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
|
|
||||||
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
|
|
||||||
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
|
|
||||||
* however, they are also less intuitive and more likely to cause you problems.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::disableContinuousBuffer()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function enableContinuousBuffer()
|
|
||||||
{
|
|
||||||
$this->continuousBuffer = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Treat consecutive packets as if they are a discontinuous buffer.
|
|
||||||
*
|
|
||||||
* The default behavior.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::enableContinuousBuffer()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function disableContinuousBuffer()
|
|
||||||
{
|
|
||||||
$this->continuousBuffer = false;
|
|
||||||
$this->encryptIV = $this->iv;
|
|
||||||
$this->decryptIV = $this->iv;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pad "packets".
|
|
||||||
*
|
|
||||||
* DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
|
|
||||||
* a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
|
|
||||||
*
|
|
||||||
* Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
|
|
||||||
* where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
|
|
||||||
* away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
|
|
||||||
* transmitted separately)
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::disablePadding()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function enablePadding()
|
|
||||||
{
|
|
||||||
$this->padding = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do not pad packets.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::enablePadding()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function disablePadding()
|
|
||||||
{
|
|
||||||
$this->padding = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pads a string
|
|
||||||
*
|
|
||||||
* Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
|
|
||||||
* 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
|
|
||||||
*
|
|
||||||
* If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
|
|
||||||
* and padding will, hence forth, be enabled.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::_unpad()
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _pad($text)
|
|
||||||
{
|
|
||||||
$length = strlen($text);
|
|
||||||
|
|
||||||
if (!$this->padding) {
|
|
||||||
if (($length & 7) == 0) {
|
|
||||||
return $text;
|
|
||||||
} else {
|
|
||||||
user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
|
|
||||||
$this->padding = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$pad = 8 - ($length & 7);
|
|
||||||
return str_pad($text, $length + $pad, chr($pad));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unpads a string
|
|
||||||
*
|
|
||||||
* If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
|
|
||||||
* and false will be returned.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::_pad()
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _unpad($text)
|
|
||||||
{
|
|
||||||
if (!$this->padding) {
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
$length = ord($text[strlen($text) - 1]);
|
|
||||||
|
|
||||||
if (!$length || $length > 8) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return substr($text, 0, -$length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts or decrypts a 64-bit block
|
|
||||||
*
|
|
||||||
* $mode should be either CRYPT_DES_ENCRYPT or CRYPT_DES_DECRYPT. See
|
|
||||||
* {@link http://en.wikipedia.org/wiki/Image:Feistel.png Feistel.png} to get a general
|
|
||||||
* idea of what this function does.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param String $block
|
|
||||||
* @param Integer $mode
|
|
||||||
* @return String
|
|
||||||
*/
|
|
||||||
function _processBlock($block, $mode)
|
|
||||||
{
|
|
||||||
// s-boxes. in the official DES docs, they're described as being matrices that
|
|
||||||
// one accesses by using the first and last bits to determine the row and the
|
|
||||||
// middle four bits to determine the column. in this implementation, they've
|
|
||||||
// been converted to vectors
|
|
||||||
static $sbox = array(
|
|
||||||
array(
|
|
||||||
14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1,
|
|
||||||
3, 10 ,10, 6, 6, 12, 12, 11, 5, 9, 9, 5, 0, 3, 7, 8,
|
|
||||||
4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1, 11, 7,
|
|
||||||
15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14,
|
|
||||||
9, 12, 7, 0, 2, 1, 13, 10, 12, 6, 0, 9, 5, 11, 10, 5,
|
|
||||||
0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13, 4, 1, 2,
|
|
||||||
5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10,
|
|
||||||
1, 2, 13, 8, 12, 5, 7, 14, 11, 12, 4, 11, 2, 15, 8, 1,
|
|
||||||
13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8, 0, 7,
|
|
||||||
11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3,
|
|
||||||
1, 4, 2, 7, 8, 2, 5, 12, 11, 1, 12, 10, 4, 14, 15, 9,
|
|
||||||
10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13, 13, 8,
|
|
||||||
15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1,
|
|
||||||
8, 5, 5, 0, 3, 15, 15, 10, 13, 3, 0, 9, 14, 8, 9, 6,
|
|
||||||
4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2, 8, 13,
|
|
||||||
15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5,
|
|
||||||
0, 6, 13, 1, 3, 13, 4, 14, 14, 0, 7, 11, 5, 3, 11, 8,
|
|
||||||
9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15, 3, 10,
|
|
||||||
7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10,
|
|
||||||
3, 14, 12, 3, 9, 5, 7, 12, 5, 2, 10, 15, 6, 8, 1, 6,
|
|
||||||
1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10, 14, 7,
|
|
||||||
10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12
|
|
||||||
),
|
|
||||||
array(
|
|
||||||
13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4,
|
|
||||||
10, 12, 9, 5, 3, 6, 14, 11, 5, 0, 0, 14, 12, 9, 7, 2,
|
|
||||||
7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8, 2, 13,
|
|
||||||
0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
$keys = $this->keys;
|
|
||||||
|
|
||||||
$temp = unpack('Na/Nb', $block);
|
|
||||||
$block = array($temp['a'], $temp['b']);
|
|
||||||
|
|
||||||
// because php does arithmetic right shifts, if the most significant bits are set, right
|
|
||||||
// shifting those into the correct position will add 1's - not 0's. this will intefere
|
|
||||||
// with the | operation unless a second & is done. so we isolate these bits and left shift
|
|
||||||
// them into place. we then & each block with 0x7FFFFFFF to prevennt 1's from being added
|
|
||||||
// for any other shifts.
|
|
||||||
$msb = array(
|
|
||||||
($block[0] >> 31) & 1,
|
|
||||||
($block[1] >> 31) & 1
|
|
||||||
);
|
|
||||||
$block[0] &= 0x7FFFFFFF;
|
|
||||||
$block[1] &= 0x7FFFFFFF;
|
|
||||||
|
|
||||||
// we isolate the appropriate bit in the appropriate integer and shift as appropriate. in
|
|
||||||
// some cases, there are going to be multiple bits in the same integer that need to be shifted
|
|
||||||
// in the same way. we combine those into one shift operation.
|
|
||||||
$block = array(
|
|
||||||
(($block[1] & 0x00000040) << 25) | (($block[1] & 0x00004000) << 16) |
|
|
||||||
(($block[1] & 0x00400001) << 7) | (($block[1] & 0x40000100) >> 2) |
|
|
||||||
(($block[0] & 0x00000040) << 21) | (($block[0] & 0x00004000) << 12) |
|
|
||||||
(($block[0] & 0x00400001) << 3) | (($block[0] & 0x40000100) >> 6) |
|
|
||||||
(($block[1] & 0x00000010) << 19) | (($block[1] & 0x00001000) << 10) |
|
|
||||||
(($block[1] & 0x00100000) << 1) | (($block[1] & 0x10000000) >> 8) |
|
|
||||||
(($block[0] & 0x00000010) << 15) | (($block[0] & 0x00001000) << 6) |
|
|
||||||
(($block[0] & 0x00100000) >> 3) | (($block[0] & 0x10000000) >> 12) |
|
|
||||||
(($block[1] & 0x00000004) << 13) | (($block[1] & 0x00000400) << 4) |
|
|
||||||
(($block[1] & 0x00040000) >> 5) | (($block[1] & 0x04000000) >> 14) |
|
|
||||||
(($block[0] & 0x00000004) << 9) | ( $block[0] & 0x00000400 ) |
|
|
||||||
(($block[0] & 0x00040000) >> 9) | (($block[0] & 0x04000000) >> 18) |
|
|
||||||
(($block[1] & 0x00010000) >> 11) | (($block[1] & 0x01000000) >> 20) |
|
|
||||||
(($block[0] & 0x00010000) >> 15) | (($block[0] & 0x01000000) >> 24)
|
|
||||||
,
|
|
||||||
(($block[1] & 0x00000080) << 24) | (($block[1] & 0x00008000) << 15) |
|
|
||||||
(($block[1] & 0x00800002) << 6) | (($block[0] & 0x00000080) << 20) |
|
|
||||||
(($block[0] & 0x00008000) << 11) | (($block[0] & 0x00800002) << 2) |
|
|
||||||
(($block[1] & 0x00000020) << 18) | (($block[1] & 0x00002000) << 9) |
|
|
||||||
( $block[1] & 0x00200000 ) | (($block[1] & 0x20000000) >> 9) |
|
|
||||||
(($block[0] & 0x00000020) << 14) | (($block[0] & 0x00002000) << 5) |
|
|
||||||
(($block[0] & 0x00200000) >> 4) | (($block[0] & 0x20000000) >> 13) |
|
|
||||||
(($block[1] & 0x00000008) << 12) | (($block[1] & 0x00000800) << 3) |
|
|
||||||
(($block[1] & 0x00080000) >> 6) | (($block[1] & 0x08000000) >> 15) |
|
|
||||||
(($block[0] & 0x00000008) << 8) | (($block[0] & 0x00000800) >> 1) |
|
|
||||||
(($block[0] & 0x00080000) >> 10) | (($block[0] & 0x08000000) >> 19) |
|
|
||||||
(($block[1] & 0x00000200) >> 3) | (($block[0] & 0x00000200) >> 7) |
|
|
||||||
(($block[1] & 0x00020000) >> 12) | (($block[1] & 0x02000000) >> 21) |
|
|
||||||
(($block[0] & 0x00020000) >> 16) | (($block[0] & 0x02000000) >> 25) |
|
|
||||||
($msb[1] << 28) | ($msb[0] << 24)
|
|
||||||
);
|
|
||||||
|
|
||||||
for ($i = 0; $i < 16; $i++) {
|
|
||||||
// start of "the Feistel (F) function" - see the following URL:
|
|
||||||
// http://en.wikipedia.org/wiki/Image:Data_Encryption_Standard_InfoBox_Diagram.png
|
|
||||||
$temp = (($sbox[0][((($block[1] >> 27) & 0x1F) | (($block[1] & 1) << 5)) ^ $keys[$mode][$i][0]]) << 28)
|
|
||||||
| (($sbox[1][(($block[1] & 0x1F800000) >> 23) ^ $keys[$mode][$i][1]]) << 24)
|
|
||||||
| (($sbox[2][(($block[1] & 0x01F80000) >> 19) ^ $keys[$mode][$i][2]]) << 20)
|
|
||||||
| (($sbox[3][(($block[1] & 0x001F8000) >> 15) ^ $keys[$mode][$i][3]]) << 16)
|
|
||||||
| (($sbox[4][(($block[1] & 0x0001F800) >> 11) ^ $keys[$mode][$i][4]]) << 12)
|
|
||||||
| (($sbox[5][(($block[1] & 0x00001F80) >> 7) ^ $keys[$mode][$i][5]]) << 8)
|
|
||||||
| (($sbox[6][(($block[1] & 0x000001F8) >> 3) ^ $keys[$mode][$i][6]]) << 4)
|
|
||||||
| ( $sbox[7][((($block[1] & 0x1F) << 1) | (($block[1] >> 31) & 1)) ^ $keys[$mode][$i][7]]);
|
|
||||||
|
|
||||||
$msb = ($temp >> 31) & 1;
|
|
||||||
$temp &= 0x7FFFFFFF;
|
|
||||||
$newBlock = (($temp & 0x00010000) << 15) | (($temp & 0x02020120) << 5)
|
|
||||||
| (($temp & 0x00001800) << 17) | (($temp & 0x01000000) >> 10)
|
|
||||||
| (($temp & 0x00000008) << 24) | (($temp & 0x00100000) << 6)
|
|
||||||
| (($temp & 0x00000010) << 21) | (($temp & 0x00008000) << 9)
|
|
||||||
| (($temp & 0x00000200) << 12) | (($temp & 0x10000000) >> 27)
|
|
||||||
| (($temp & 0x00000040) << 14) | (($temp & 0x08000000) >> 8)
|
|
||||||
| (($temp & 0x00004000) << 4) | (($temp & 0x00000002) << 16)
|
|
||||||
| (($temp & 0x00442000) >> 6) | (($temp & 0x40800000) >> 15)
|
|
||||||
| (($temp & 0x00000001) << 11) | (($temp & 0x20000000) >> 20)
|
|
||||||
| (($temp & 0x00080000) >> 13) | (($temp & 0x00000004) << 3)
|
|
||||||
| (($temp & 0x04000000) >> 22) | (($temp & 0x00000480) >> 7)
|
|
||||||
| (($temp & 0x00200000) >> 19) | ($msb << 23);
|
|
||||||
// end of "the Feistel (F) function" - $newBlock is F's output
|
|
||||||
|
|
||||||
$temp = $block[1];
|
|
||||||
$block[1] = $block[0] ^ $newBlock;
|
|
||||||
$block[0] = $temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
$msb = array(
|
|
||||||
($block[0] >> 31) & 1,
|
|
||||||
($block[1] >> 31) & 1
|
|
||||||
);
|
|
||||||
$block[0] &= 0x7FFFFFFF;
|
|
||||||
$block[1] &= 0x7FFFFFFF;
|
|
||||||
|
|
||||||
$block = array(
|
|
||||||
(($block[0] & 0x01000004) << 7) | (($block[1] & 0x01000004) << 6) |
|
|
||||||
(($block[0] & 0x00010000) << 13) | (($block[1] & 0x00010000) << 12) |
|
|
||||||
(($block[0] & 0x00000100) << 19) | (($block[1] & 0x00000100) << 18) |
|
|
||||||
(($block[0] & 0x00000001) << 25) | (($block[1] & 0x00000001) << 24) |
|
|
||||||
(($block[0] & 0x02000008) >> 2) | (($block[1] & 0x02000008) >> 3) |
|
|
||||||
(($block[0] & 0x00020000) << 4) | (($block[1] & 0x00020000) << 3) |
|
|
||||||
(($block[0] & 0x00000200) << 10) | (($block[1] & 0x00000200) << 9) |
|
|
||||||
(($block[0] & 0x00000002) << 16) | (($block[1] & 0x00000002) << 15) |
|
|
||||||
(($block[0] & 0x04000000) >> 11) | (($block[1] & 0x04000000) >> 12) |
|
|
||||||
(($block[0] & 0x00040000) >> 5) | (($block[1] & 0x00040000) >> 6) |
|
|
||||||
(($block[0] & 0x00000400) << 1) | ( $block[1] & 0x00000400 ) |
|
|
||||||
(($block[0] & 0x08000000) >> 20) | (($block[1] & 0x08000000) >> 21) |
|
|
||||||
(($block[0] & 0x00080000) >> 14) | (($block[1] & 0x00080000) >> 15) |
|
|
||||||
(($block[0] & 0x00000800) >> 8) | (($block[1] & 0x00000800) >> 9)
|
|
||||||
,
|
|
||||||
(($block[0] & 0x10000040) << 3) | (($block[1] & 0x10000040) << 2) |
|
|
||||||
(($block[0] & 0x00100000) << 9) | (($block[1] & 0x00100000) << 8) |
|
|
||||||
(($block[0] & 0x00001000) << 15) | (($block[1] & 0x00001000) << 14) |
|
|
||||||
(($block[0] & 0x00000010) << 21) | (($block[1] & 0x00000010) << 20) |
|
|
||||||
(($block[0] & 0x20000080) >> 6) | (($block[1] & 0x20000080) >> 7) |
|
|
||||||
( $block[0] & 0x00200000 ) | (($block[1] & 0x00200000) >> 1) |
|
|
||||||
(($block[0] & 0x00002000) << 6) | (($block[1] & 0x00002000) << 5) |
|
|
||||||
(($block[0] & 0x00000020) << 12) | (($block[1] & 0x00000020) << 11) |
|
|
||||||
(($block[0] & 0x40000000) >> 15) | (($block[1] & 0x40000000) >> 16) |
|
|
||||||
(($block[0] & 0x00400000) >> 9) | (($block[1] & 0x00400000) >> 10) |
|
|
||||||
(($block[0] & 0x00004000) >> 3) | (($block[1] & 0x00004000) >> 4) |
|
|
||||||
(($block[0] & 0x00800000) >> 18) | (($block[1] & 0x00800000) >> 19) |
|
|
||||||
(($block[0] & 0x00008000) >> 12) | (($block[1] & 0x00008000) >> 13) |
|
|
||||||
($msb[0] << 7) | ($msb[1] << 6)
|
|
||||||
);
|
|
||||||
|
|
||||||
return pack('NN', $block[0], $block[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the key schedule.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param String $key
|
|
||||||
* @return Array
|
|
||||||
*/
|
|
||||||
function _prepareKey($key)
|
|
||||||
{
|
|
||||||
static $shifts = array( // number of key bits shifted per round
|
|
||||||
1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
|
|
||||||
);
|
|
||||||
|
|
||||||
// pad the key and remove extra characters as appropriate.
|
|
||||||
$key = str_pad(substr($key, 0, 8), 8, chr(0));
|
|
||||||
|
|
||||||
$temp = unpack('Na/Nb', $key);
|
|
||||||
$key = array($temp['a'], $temp['b']);
|
|
||||||
$msb = array(
|
|
||||||
($key[0] >> 31) & 1,
|
|
||||||
($key[1] >> 31) & 1
|
|
||||||
);
|
|
||||||
$key[0] &= 0x7FFFFFFF;
|
|
||||||
$key[1] &= 0x7FFFFFFF;
|
|
||||||
|
|
||||||
$key = array(
|
|
||||||
(($key[1] & 0x00000002) << 26) | (($key[1] & 0x00000204) << 17) |
|
|
||||||
(($key[1] & 0x00020408) << 8) | (($key[1] & 0x02040800) >> 1) |
|
|
||||||
(($key[0] & 0x00000002) << 22) | (($key[0] & 0x00000204) << 13) |
|
|
||||||
(($key[0] & 0x00020408) << 4) | (($key[0] & 0x02040800) >> 5) |
|
|
||||||
(($key[1] & 0x04080000) >> 10) | (($key[0] & 0x04080000) >> 14) |
|
|
||||||
(($key[1] & 0x08000000) >> 19) | (($key[0] & 0x08000000) >> 23) |
|
|
||||||
(($key[0] & 0x00000010) >> 1) | (($key[0] & 0x00001000) >> 10) |
|
|
||||||
(($key[0] & 0x00100000) >> 19) | (($key[0] & 0x10000000) >> 28)
|
|
||||||
,
|
|
||||||
(($key[1] & 0x00000080) << 20) | (($key[1] & 0x00008000) << 11) |
|
|
||||||
(($key[1] & 0x00800000) << 2) | (($key[0] & 0x00000080) << 16) |
|
|
||||||
(($key[0] & 0x00008000) << 7) | (($key[0] & 0x00800000) >> 2) |
|
|
||||||
(($key[1] & 0x00000040) << 13) | (($key[1] & 0x00004000) << 4) |
|
|
||||||
(($key[1] & 0x00400000) >> 5) | (($key[1] & 0x40000000) >> 14) |
|
|
||||||
(($key[0] & 0x00000040) << 9) | ( $key[0] & 0x00004000 ) |
|
|
||||||
(($key[0] & 0x00400000) >> 9) | (($key[0] & 0x40000000) >> 18) |
|
|
||||||
(($key[1] & 0x00000020) << 6) | (($key[1] & 0x00002000) >> 3) |
|
|
||||||
(($key[1] & 0x00200000) >> 12) | (($key[1] & 0x20000000) >> 21) |
|
|
||||||
(($key[0] & 0x00000020) << 2) | (($key[0] & 0x00002000) >> 7) |
|
|
||||||
(($key[0] & 0x00200000) >> 16) | (($key[0] & 0x20000000) >> 25) |
|
|
||||||
(($key[1] & 0x00000010) >> 1) | (($key[1] & 0x00001000) >> 10) |
|
|
||||||
(($key[1] & 0x00100000) >> 19) | (($key[1] & 0x10000000) >> 28) |
|
|
||||||
($msb[1] << 24) | ($msb[0] << 20)
|
|
||||||
);
|
|
||||||
|
|
||||||
$keys = array();
|
|
||||||
for ($i = 0; $i < 16; $i++) {
|
|
||||||
$key[0] <<= $shifts[$i];
|
|
||||||
$temp = ($key[0] & 0xF0000000) >> 28;
|
|
||||||
$key[0] = ($key[0] | $temp) & 0x0FFFFFFF;
|
|
||||||
|
|
||||||
$key[1] <<= $shifts[$i];
|
|
||||||
$temp = ($key[1] & 0xF0000000) >> 28;
|
|
||||||
$key[1] = ($key[1] | $temp) & 0x0FFFFFFF;
|
|
||||||
|
|
||||||
$temp = array(
|
|
||||||
(($key[1] & 0x00004000) >> 9) | (($key[1] & 0x00000800) >> 7) |
|
|
||||||
(($key[1] & 0x00020000) >> 14) | (($key[1] & 0x00000010) >> 2) |
|
|
||||||
(($key[1] & 0x08000000) >> 26) | (($key[1] & 0x00800000) >> 23)
|
|
||||||
,
|
|
||||||
(($key[1] & 0x02400000) >> 20) | (($key[1] & 0x00000001) << 4) |
|
|
||||||
(($key[1] & 0x00002000) >> 10) | (($key[1] & 0x00040000) >> 18) |
|
|
||||||
(($key[1] & 0x00000080) >> 6)
|
|
||||||
,
|
|
||||||
( $key[1] & 0x00000020 ) | (($key[1] & 0x00000200) >> 5) |
|
|
||||||
(($key[1] & 0x00010000) >> 13) | (($key[1] & 0x01000000) >> 22) |
|
|
||||||
(($key[1] & 0x00000004) >> 1) | (($key[1] & 0x00100000) >> 20)
|
|
||||||
,
|
|
||||||
(($key[1] & 0x00001000) >> 7) | (($key[1] & 0x00200000) >> 17) |
|
|
||||||
(($key[1] & 0x00000002) << 2) | (($key[1] & 0x00000100) >> 6) |
|
|
||||||
(($key[1] & 0x00008000) >> 14) | (($key[1] & 0x04000000) >> 26)
|
|
||||||
,
|
|
||||||
(($key[0] & 0x00008000) >> 10) | ( $key[0] & 0x00000010 ) |
|
|
||||||
(($key[0] & 0x02000000) >> 22) | (($key[0] & 0x00080000) >> 17) |
|
|
||||||
(($key[0] & 0x00000200) >> 8) | (($key[0] & 0x00000002) >> 1)
|
|
||||||
,
|
|
||||||
(($key[0] & 0x04000000) >> 21) | (($key[0] & 0x00010000) >> 12) |
|
|
||||||
(($key[0] & 0x00000020) >> 2) | (($key[0] & 0x00000800) >> 9) |
|
|
||||||
(($key[0] & 0x00800000) >> 22) | (($key[0] & 0x00000100) >> 8)
|
|
||||||
,
|
|
||||||
(($key[0] & 0x00001000) >> 7) | (($key[0] & 0x00000088) >> 3) |
|
|
||||||
(($key[0] & 0x00020000) >> 14) | (($key[0] & 0x00000001) << 2) |
|
|
||||||
(($key[0] & 0x00400000) >> 21)
|
|
||||||
,
|
|
||||||
(($key[0] & 0x00000400) >> 5) | (($key[0] & 0x00004000) >> 10) |
|
|
||||||
(($key[0] & 0x00000040) >> 3) | (($key[0] & 0x00100000) >> 18) |
|
|
||||||
(($key[0] & 0x08000000) >> 26) | (($key[0] & 0x01000000) >> 24)
|
|
||||||
);
|
|
||||||
|
|
||||||
$keys[] = $temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
$temp = array(
|
|
||||||
CRYPT_DES_ENCRYPT => $keys,
|
|
||||||
CRYPT_DES_DECRYPT => array_reverse($keys)
|
|
||||||
);
|
|
||||||
|
|
||||||
return $temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: ts=4:sw=4:et:
|
|
||||||
// vim6: fdl=1:
|
|
|
@ -1,816 +0,0 @@
|
||||||
<?php
|
|
||||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementations of keyed-hash message authentication codes (HMACs) and various cryptographic hashing functions.
|
|
||||||
*
|
|
||||||
* Uses hash() or mhash() if available and an internal implementation, otherwise. Currently supports the following:
|
|
||||||
*
|
|
||||||
* md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512
|
|
||||||
*
|
|
||||||
* If {@link Crypt_Hash::setKey() setKey()} is called, {@link Crypt_Hash::hash() hash()} will return the HMAC as opposed to
|
|
||||||
* the hash. If no valid algorithm is provided, sha1 will be used.
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* {@internal The variable names are the same as those in
|
|
||||||
* {@link http://tools.ietf.org/html/rfc2104#section-2 RFC2104}.}}
|
|
||||||
*
|
|
||||||
* Here's a short example of how to use this library:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
* include('Crypt/Hash.php');
|
|
||||||
*
|
|
||||||
* $hash = new Crypt_Hash('sha1');
|
|
||||||
*
|
|
||||||
* $hash->setKey('abcdefg');
|
|
||||||
*
|
|
||||||
* echo base64_encode($hash->hash('abcdefg'));
|
|
||||||
* ?>
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* 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 <terrafrost@php.net>
|
|
||||||
* @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 <terrafrost@php.net>
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,493 +0,0 @@
|
||||||
<?php
|
|
||||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of RC4.
|
|
||||||
*
|
|
||||||
* Uses mcrypt, if available, and an internal implementation, otherwise.
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* Useful resources are as follows:
|
|
||||||
*
|
|
||||||
* - {@link http://www.mozilla.org/projects/security/pki/nss/draft-kaukonen-cipher-arcfour-03.txt ARCFOUR Algorithm}
|
|
||||||
* - {@link http://en.wikipedia.org/wiki/RC4 - Wikipedia: RC4}
|
|
||||||
*
|
|
||||||
* RC4 is also known as ARCFOUR or ARC4. The reason is elaborated upon at Wikipedia. This class is named RC4 and not
|
|
||||||
* ARCFOUR or ARC4 because RC4 is how it is refered to in the SSH1 specification.
|
|
||||||
*
|
|
||||||
* Here's a short example of how to use this library:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
* include('Crypt/RC4.php');
|
|
||||||
*
|
|
||||||
* $rc4 = new Crypt_RC4();
|
|
||||||
*
|
|
||||||
* $rc4->setKey('abcdefgh');
|
|
||||||
*
|
|
||||||
* $size = 10 * 1024;
|
|
||||||
* $plaintext = '';
|
|
||||||
* for ($i = 0; $i < $size; $i++) {
|
|
||||||
* $plaintext.= 'a';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* echo $rc4->decrypt($rc4->encrypt($plaintext));
|
|
||||||
* ?>
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* 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_RC4
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @copyright MMVII Jim Wigginton
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl.txt
|
|
||||||
* @version $Id: RC4.php,v 1.8 2009/06/09 04:00:38 terrafrost Exp $
|
|
||||||
* @link http://phpseclib.sourceforge.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* @access private
|
|
||||||
* @see Crypt_RC4::Crypt_RC4()
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Toggles the internal implementation
|
|
||||||
*/
|
|
||||||
define('CRYPT_RC4_MODE_INTERNAL', 1);
|
|
||||||
/**
|
|
||||||
* Toggles the mcrypt implementation
|
|
||||||
*/
|
|
||||||
define('CRYPT_RC4_MODE_MCRYPT', 2);
|
|
||||||
/**#@-*/
|
|
||||||
|
|
||||||
/**#@+
|
|
||||||
* @access private
|
|
||||||
* @see Crypt_RC4::_crypt()
|
|
||||||
*/
|
|
||||||
define('CRYPT_RC4_ENCRYPT', 0);
|
|
||||||
define('CRYPT_RC4_DECRYPT', 1);
|
|
||||||
/**#@-*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of RC4.
|
|
||||||
*
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @version 0.1.0
|
|
||||||
* @access public
|
|
||||||
* @package Crypt_RC4
|
|
||||||
*/
|
|
||||||
class Crypt_RC4 {
|
|
||||||
/**
|
|
||||||
* The Key
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::setKey()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $key = "\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Key Stream for encryption
|
|
||||||
*
|
|
||||||
* If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::setKey()
|
|
||||||
* @var Array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $encryptStream = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Key Stream for decryption
|
|
||||||
*
|
|
||||||
* If CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT, this will be equal to the mcrypt object
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::setKey()
|
|
||||||
* @var Array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $decryptStream = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The $i and $j indexes for encryption
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::_crypt()
|
|
||||||
* @var Integer
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $encryptIndex = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The $i and $j indexes for decryption
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::_crypt()
|
|
||||||
* @var Integer
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $decryptIndex = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MCrypt parameters
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::setMCrypt()
|
|
||||||
* @var Array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $mcrypt = array('', '');
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Encryption Algorithm
|
|
||||||
*
|
|
||||||
* Only used if CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT. Only possible values are MCRYPT_RC4 or MCRYPT_ARCFOUR.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::Crypt_RC4()
|
|
||||||
* @var Integer
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $mode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Constructor.
|
|
||||||
*
|
|
||||||
* Determines whether or not the mcrypt extension should be used.
|
|
||||||
*
|
|
||||||
* @param optional Integer $mode
|
|
||||||
* @return Crypt_RC4
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function Crypt_RC4()
|
|
||||||
{
|
|
||||||
if ( !defined('CRYPT_RC4_MODE') ) {
|
|
||||||
switch (true) {
|
|
||||||
case extension_loaded('mcrypt') && (defined('MCRYPT_ARCFOUR') || defined('MCRYPT_RC4')):
|
|
||||||
// i'd check to see if rc4 was supported, by doing in_array('arcfour', mcrypt_list_algorithms('')),
|
|
||||||
// but since that can be changed after the object has been created, there doesn't seem to be
|
|
||||||
// a lot of point...
|
|
||||||
define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_MCRYPT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
define('CRYPT_RC4_MODE', CRYPT_RC4_MODE_INTERNAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( CRYPT_RC4_MODE ) {
|
|
||||||
case CRYPT_RC4_MODE_MCRYPT:
|
|
||||||
switch (true) {
|
|
||||||
case defined('MCRYPT_ARCFOUR'):
|
|
||||||
$this->mode = MCRYPT_ARCFOUR;
|
|
||||||
break;
|
|
||||||
case defined('MCRYPT_RC4');
|
|
||||||
$this->mode = MCRYPT_RC4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the key.
|
|
||||||
*
|
|
||||||
* Keys can be between 1 and 256 bytes long. If they are longer then 256 bytes, the first 256 bytes will
|
|
||||||
* be used. If no key is explicitly set, it'll be assumed to be a single null byte.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param String $key
|
|
||||||
*/
|
|
||||||
function setKey($key)
|
|
||||||
{
|
|
||||||
$this->key = $key;
|
|
||||||
|
|
||||||
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$keyLength = strlen($key);
|
|
||||||
$keyStream = array();
|
|
||||||
for ($i = 0; $i < 256; $i++) {
|
|
||||||
$keyStream[$i] = $i;
|
|
||||||
}
|
|
||||||
$j = 0;
|
|
||||||
for ($i = 0; $i < 256; $i++) {
|
|
||||||
$j = ($j + $keyStream[$i] + ord($key[$i % $keyLength])) & 255;
|
|
||||||
$temp = $keyStream[$i];
|
|
||||||
$keyStream[$i] = $keyStream[$j];
|
|
||||||
$keyStream[$j] = $temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->encryptIndex = $this->decryptIndex = array(0, 0);
|
|
||||||
$this->encryptStream = $this->decryptStream = $keyStream;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy function.
|
|
||||||
*
|
|
||||||
* Some protocols, such as WEP, prepend an "initialization vector" to the key, effectively creating a new key [1].
|
|
||||||
* If you need to use an initialization vector in this manner, feel free to prepend it to the key, yourself, before
|
|
||||||
* calling setKey().
|
|
||||||
*
|
|
||||||
* [1] WEP's initialization vectors (IV's) are used in a somewhat insecure way. Since, in that protocol,
|
|
||||||
* the IV's are relatively easy to predict, an attack described by
|
|
||||||
* {@link http://www.drizzle.com/~aboba/IEEE/rc4_ksaproc.pdf Scott Fluhrer, Itsik Mantin, and Adi Shamir}
|
|
||||||
* can be used to quickly guess at the rest of the key. The following links elaborate:
|
|
||||||
*
|
|
||||||
* {@link http://www.rsa.com/rsalabs/node.asp?id=2009 http://www.rsa.com/rsalabs/node.asp?id=2009}
|
|
||||||
* {@link http://en.wikipedia.org/wiki/Related_key_attack http://en.wikipedia.org/wiki/Related_key_attack}
|
|
||||||
*
|
|
||||||
* @param String $iv
|
|
||||||
* @see Crypt_RC4::setKey()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function setIV($iv)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets MCrypt parameters. (optional)
|
|
||||||
*
|
|
||||||
* If MCrypt is being used, empty strings will be used, unless otherwise specified.
|
|
||||||
*
|
|
||||||
* @link http://php.net/function.mcrypt-module-open#function.mcrypt-module-open
|
|
||||||
* @access public
|
|
||||||
* @param optional Integer $algorithm_directory
|
|
||||||
* @param optional Integer $mode_directory
|
|
||||||
*/
|
|
||||||
function setMCrypt($algorithm_directory = '', $mode_directory = '')
|
|
||||||
{
|
|
||||||
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
|
|
||||||
$this->mcrypt = array($algorithm_directory, $mode_directory);
|
|
||||||
$this->_closeMCrypt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a message.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::_crypt()
|
|
||||||
* @access public
|
|
||||||
* @param String $plaintext
|
|
||||||
*/
|
|
||||||
function encrypt($plaintext)
|
|
||||||
{
|
|
||||||
return $this->_crypt($plaintext, CRYPT_RC4_ENCRYPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a message.
|
|
||||||
*
|
|
||||||
* $this->decrypt($this->encrypt($plaintext)) == $this->encrypt($this->encrypt($plaintext)).
|
|
||||||
* Atleast if the continuous buffer is disabled.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::_crypt()
|
|
||||||
* @access public
|
|
||||||
* @param String $ciphertext
|
|
||||||
*/
|
|
||||||
function decrypt($ciphertext)
|
|
||||||
{
|
|
||||||
return $this->_crypt($ciphertext, CRYPT_RC4_DECRYPT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts or decrypts a message.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::encrypt()
|
|
||||||
* @see Crypt_RC4::decrypt()
|
|
||||||
* @access private
|
|
||||||
* @param String $text
|
|
||||||
* @param Integer $mode
|
|
||||||
*/
|
|
||||||
function _crypt($text, $mode)
|
|
||||||
{
|
|
||||||
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
|
|
||||||
$keyStream = $mode == CRYPT_RC4_ENCRYPT ? 'encryptStream' : 'decryptStream';
|
|
||||||
|
|
||||||
if ($this->$keyStream === false) {
|
|
||||||
$this->$keyStream = mcrypt_module_open($this->mode, $this->mcrypt[0], MCRYPT_MODE_STREAM, $this->mcrypt[1]);
|
|
||||||
mcrypt_generic_init($this->$keyStream, $this->key, '');
|
|
||||||
} else if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_init($this->$keyStream, $this->key, '');
|
|
||||||
}
|
|
||||||
$newText = mcrypt_generic($this->$keyStream, $text);
|
|
||||||
if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_deinit($this->$keyStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $newText;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->encryptStream === false) {
|
|
||||||
$this->setKey($this->key);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_RC4_ENCRYPT:
|
|
||||||
$keyStream = $this->encryptStream;
|
|
||||||
list($i, $j) = $this->encryptIndex;
|
|
||||||
break;
|
|
||||||
case CRYPT_RC4_DECRYPT:
|
|
||||||
$keyStream = $this->decryptStream;
|
|
||||||
list($i, $j) = $this->decryptIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
$newText = '';
|
|
||||||
for ($k = 0; $k < strlen($text); $k++) {
|
|
||||||
$i = ($i + 1) & 255;
|
|
||||||
$j = ($j + $keyStream[$i]) & 255;
|
|
||||||
$temp = $keyStream[$i];
|
|
||||||
$keyStream[$i] = $keyStream[$j];
|
|
||||||
$keyStream[$j] = $temp;
|
|
||||||
$temp = $keyStream[($keyStream[$i] + $keyStream[$j]) & 255];
|
|
||||||
$newText.= chr(ord($text[$k]) ^ $temp);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_RC4_ENCRYPT:
|
|
||||||
$this->encryptStream = $keyStream;
|
|
||||||
$this->encryptIndex = array($i, $j);
|
|
||||||
break;
|
|
||||||
case CRYPT_RC4_DECRYPT:
|
|
||||||
$this->decryptStream = $keyStream;
|
|
||||||
$this->decryptIndex = array($i, $j);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $newText;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Treat consecutive "packets" as if they are a continuous buffer.
|
|
||||||
*
|
|
||||||
* Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
|
|
||||||
* will yield different outputs:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* echo $rc4->encrypt(substr($plaintext, 0, 8));
|
|
||||||
* echo $rc4->encrypt(substr($plaintext, 8, 8));
|
|
||||||
* </code>
|
|
||||||
* <code>
|
|
||||||
* echo $rc4->encrypt($plaintext);
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
|
|
||||||
* another, as demonstrated with the following:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* $rc4->encrypt(substr($plaintext, 0, 8));
|
|
||||||
* echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
|
|
||||||
* </code>
|
|
||||||
* <code>
|
|
||||||
* echo $rc4->decrypt($des->encrypt(substr($plaintext, 8, 8)));
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
|
|
||||||
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
|
|
||||||
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
|
|
||||||
*
|
|
||||||
* Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
|
|
||||||
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
|
|
||||||
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
|
|
||||||
* however, they are also less intuitive and more likely to cause you problems.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::disableContinuousBuffer()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function enableContinuousBuffer()
|
|
||||||
{
|
|
||||||
$this->continuousBuffer = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Treat consecutive packets as if they are a discontinuous buffer.
|
|
||||||
*
|
|
||||||
* The default behavior.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::enableContinuousBuffer()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function disableContinuousBuffer()
|
|
||||||
{
|
|
||||||
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_INTERNAL ) {
|
|
||||||
$this->encryptIndex = $this->decryptIndex = array(0, 0);
|
|
||||||
$this->setKey($this->key);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->continuousBuffer = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy function.
|
|
||||||
*
|
|
||||||
* Since RC4 is a stream cipher and not a block cipher, no padding is necessary. The only reason this function is
|
|
||||||
* included is so that you can switch between a block cipher and a stream cipher transparently.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::disablePadding()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function enablePadding()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dummy function.
|
|
||||||
*
|
|
||||||
* @see Crypt_RC4::enablePadding()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function disablePadding()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class destructor.
|
|
||||||
*
|
|
||||||
* Will be called, automatically, if you're using PHP5. If you're using PHP4, call it yourself. Only really
|
|
||||||
* needs to be called if mcrypt is being used.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function __destruct()
|
|
||||||
{
|
|
||||||
if ( CRYPT_RC4_MODE == CRYPT_RC4_MODE_MCRYPT ) {
|
|
||||||
$this->_closeMCrypt();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Properly close the MCrypt objects.
|
|
||||||
*
|
|
||||||
* @access prviate
|
|
||||||
*/
|
|
||||||
function _closeMCrypt()
|
|
||||||
{
|
|
||||||
if ( $this->encryptStream !== false ) {
|
|
||||||
if ( $this->continuousBuffer ) {
|
|
||||||
mcrypt_generic_deinit($this->encryptStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
mcrypt_module_close($this->encryptStream);
|
|
||||||
|
|
||||||
$this->encryptStream = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $this->decryptStream !== false ) {
|
|
||||||
if ( $this->continuousBuffer ) {
|
|
||||||
mcrypt_generic_deinit($this->decryptStream);
|
|
||||||
}
|
|
||||||
|
|
||||||
mcrypt_module_close($this->decryptStream);
|
|
||||||
|
|
||||||
$this->decryptStream = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,130 +0,0 @@
|
||||||
<?php
|
|
||||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Random Number Generator
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* Here's a short example of how to use this library:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
* include('Crypt/Random.php');
|
|
||||||
*
|
|
||||||
* echo crypt_random();
|
|
||||||
* ?>
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* 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 <terrafrost@php.net>
|
|
||||||
* @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;
|
|
||||||
}
|
|
||||||
?>
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,690 +0,0 @@
|
||||||
<?php
|
|
||||||
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of Triple DES.
|
|
||||||
*
|
|
||||||
* Uses mcrypt, if available, and an internal implementation, otherwise. Operates in the EDE3 mode (encrypt-decrypt-encrypt).
|
|
||||||
*
|
|
||||||
* PHP versions 4 and 5
|
|
||||||
*
|
|
||||||
* Here's a short example of how to use this library:
|
|
||||||
* <code>
|
|
||||||
* <?php
|
|
||||||
* include('Crypt/TripleDES.php');
|
|
||||||
*
|
|
||||||
* $des = new Crypt_TripleDES();
|
|
||||||
*
|
|
||||||
* $des->setKey('abcdefghijklmnopqrstuvwx');
|
|
||||||
*
|
|
||||||
* $size = 10 * 1024;
|
|
||||||
* $plaintext = '';
|
|
||||||
* for ($i = 0; $i < $size; $i++) {
|
|
||||||
* $plaintext.= 'a';
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* echo $des->decrypt($des->encrypt($plaintext));
|
|
||||||
* ?>
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* 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_TripleDES
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @copyright MMVII Jim Wigginton
|
|
||||||
* @license http://www.gnu.org/licenses/lgpl.txt
|
|
||||||
* @version $Id: TripleDES.php,v 1.13 2010/02/26 03:40:25 terrafrost Exp $
|
|
||||||
* @link http://phpseclib.sourceforge.net
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Include Crypt_DES
|
|
||||||
*/
|
|
||||||
require_once 'DES.php';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using inner chaining
|
|
||||||
*
|
|
||||||
* Inner chaining is used by SSH-1 and is generally considered to be less secure then outer chaining (CRYPT_DES_MODE_CBC3).
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_MODE_3CBC', 3);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt / decrypt using outer chaining
|
|
||||||
*
|
|
||||||
* Outer chaining is used by SSH-2 and when the mode is set to CRYPT_DES_MODE_CBC.
|
|
||||||
*/
|
|
||||||
define('CRYPT_DES_MODE_CBC3', CRYPT_DES_MODE_CBC);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pure-PHP implementation of Triple DES.
|
|
||||||
*
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @version 0.1.0
|
|
||||||
* @access public
|
|
||||||
* @package Crypt_TerraDES
|
|
||||||
*/
|
|
||||||
class Crypt_TripleDES {
|
|
||||||
/**
|
|
||||||
* The Three Keys
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::setKey()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $key = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Encryption Mode
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::Crypt_TripleDES()
|
|
||||||
* @var Integer
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $mode = CRYPT_DES_MODE_CBC;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Continuous Buffer status
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::enableContinuousBuffer()
|
|
||||||
* @var Boolean
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $continuousBuffer = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Padding status
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::enablePadding()
|
|
||||||
* @var Boolean
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $padding = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Initialization Vector
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::setIV()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $iv = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "sliding" Initialization Vector
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::enableContinuousBuffer()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $encryptIV = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A "sliding" Initialization Vector
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::enableContinuousBuffer()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $decryptIV = "\0\0\0\0\0\0\0\0";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Crypt_DES objects
|
|
||||||
*
|
|
||||||
* @var Array
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $des;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mcrypt resource for encryption
|
|
||||||
*
|
|
||||||
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
|
|
||||||
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::encrypt()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $enmcrypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* mcrypt resource for decryption
|
|
||||||
*
|
|
||||||
* The mcrypt resource can be recreated every time something needs to be created or it can be created just once.
|
|
||||||
* Since mcrypt operates in continuous mode, by default, it'll need to be recreated when in non-continuous mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_AES::decrypt()
|
|
||||||
* @var String
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $demcrypt;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the (en|de)mcrypt resource need to be (re)initialized?
|
|
||||||
*
|
|
||||||
* @see setKey()
|
|
||||||
* @see setIV()
|
|
||||||
* @var Boolean
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
var $changed = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Constructor.
|
|
||||||
*
|
|
||||||
* Determines whether or not the mcrypt extension should be used. $mode should only, at present, be
|
|
||||||
* CRYPT_DES_MODE_ECB or CRYPT_DES_MODE_CBC. If not explictly set, CRYPT_DES_MODE_CBC will be used.
|
|
||||||
*
|
|
||||||
* @param optional Integer $mode
|
|
||||||
* @return Crypt_TripleDES
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function Crypt_TripleDES($mode = CRYPT_DES_MODE_CBC)
|
|
||||||
{
|
|
||||||
if ( !defined('CRYPT_DES_MODE') ) {
|
|
||||||
switch (true) {
|
|
||||||
case extension_loaded('mcrypt'):
|
|
||||||
// i'd check to see if des was supported, by doing in_array('des', mcrypt_list_algorithms('')),
|
|
||||||
// but since that can be changed after the object has been created, there doesn't seem to be
|
|
||||||
// a lot of point...
|
|
||||||
define('CRYPT_DES_MODE', CRYPT_DES_MODE_MCRYPT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
define('CRYPT_DES_MODE', CRYPT_DES_MODE_INTERNAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( $mode == CRYPT_DES_MODE_3CBC ) {
|
|
||||||
$this->mode = CRYPT_DES_MODE_3CBC;
|
|
||||||
$this->des = array(
|
|
||||||
new Crypt_DES(CRYPT_DES_MODE_CBC),
|
|
||||||
new Crypt_DES(CRYPT_DES_MODE_CBC),
|
|
||||||
new Crypt_DES(CRYPT_DES_MODE_CBC)
|
|
||||||
);
|
|
||||||
|
|
||||||
// we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
|
|
||||||
$this->des[0]->disablePadding();
|
|
||||||
$this->des[1]->disablePadding();
|
|
||||||
$this->des[2]->disablePadding();
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch ( CRYPT_DES_MODE ) {
|
|
||||||
case CRYPT_DES_MODE_MCRYPT:
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
$this->mode = MCRYPT_MODE_ECB;
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
$this->mode = 'ctr';
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
default:
|
|
||||||
$this->mode = MCRYPT_MODE_CBC;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$this->des = array(
|
|
||||||
new Crypt_DES(CRYPT_DES_MODE_ECB),
|
|
||||||
new Crypt_DES(CRYPT_DES_MODE_ECB),
|
|
||||||
new Crypt_DES(CRYPT_DES_MODE_ECB)
|
|
||||||
);
|
|
||||||
|
|
||||||
// we're going to be doing the padding, ourselves, so disable it in the Crypt_DES objects
|
|
||||||
$this->des[0]->disablePadding();
|
|
||||||
$this->des[1]->disablePadding();
|
|
||||||
$this->des[2]->disablePadding();
|
|
||||||
|
|
||||||
switch ($mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
$this->mode = $mode;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
$this->mode = CRYPT_DES_MODE_CBC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the key.
|
|
||||||
*
|
|
||||||
* Keys can be of any length. Triple DES, itself, can use 128-bit (eg. strlen($key) == 16) or
|
|
||||||
* 192-bit (eg. strlen($key) == 24) keys. This function pads and truncates $key as appropriate.
|
|
||||||
*
|
|
||||||
* DES also requires that every eighth bit be a parity bit, however, we'll ignore that.
|
|
||||||
*
|
|
||||||
* If the key is not explicitly set, it'll be assumed to be all zero's.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param String $key
|
|
||||||
*/
|
|
||||||
function setKey($key)
|
|
||||||
{
|
|
||||||
$length = strlen($key);
|
|
||||||
if ($length > 8) {
|
|
||||||
$key = str_pad($key, 24, chr(0));
|
|
||||||
// if $key is between 64 and 128-bits, use the first 64-bits as the last, per this:
|
|
||||||
// http://php.net/function.mcrypt-encrypt#47973
|
|
||||||
//$key = $length <= 16 ? substr_replace($key, substr($key, 0, 8), 16) : substr($key, 0, 24);
|
|
||||||
}
|
|
||||||
$this->key = $key;
|
|
||||||
switch (true) {
|
|
||||||
case CRYPT_DES_MODE == CRYPT_DES_MODE_INTERNAL:
|
|
||||||
case $this->mode == CRYPT_DES_MODE_3CBC:
|
|
||||||
$this->des[0]->setKey(substr($key, 0, 8));
|
|
||||||
$this->des[1]->setKey(substr($key, 8, 8));
|
|
||||||
$this->des[2]->setKey(substr($key, 16, 8));
|
|
||||||
}
|
|
||||||
$this->changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the initialization vector. (optional)
|
|
||||||
*
|
|
||||||
* SetIV is not required when CRYPT_DES_MODE_ECB is being used. If not explictly set, it'll be assumed
|
|
||||||
* to be all zero's.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param String $iv
|
|
||||||
*/
|
|
||||||
function setIV($iv)
|
|
||||||
{
|
|
||||||
$this->encryptIV = $this->decryptIV = $this->iv = str_pad(substr($iv, 0, 8), 8, chr(0));
|
|
||||||
if ($this->mode == CRYPT_DES_MODE_3CBC) {
|
|
||||||
$this->des[0]->setIV($iv);
|
|
||||||
$this->des[1]->setIV($iv);
|
|
||||||
$this->des[2]->setIV($iv);
|
|
||||||
}
|
|
||||||
$this->changed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate CTR XOR encryption key
|
|
||||||
*
|
|
||||||
* Encrypt the output of this and XOR it against the ciphertext / plaintext to get the
|
|
||||||
* plaintext / ciphertext in CTR mode.
|
|
||||||
*
|
|
||||||
* @see Crypt_DES::decrypt()
|
|
||||||
* @see Crypt_DES::encrypt()
|
|
||||||
* @access public
|
|
||||||
* @param Integer $length
|
|
||||||
* @param String $iv
|
|
||||||
*/
|
|
||||||
function _generate_xor($length, &$iv)
|
|
||||||
{
|
|
||||||
$xor = '';
|
|
||||||
$num_blocks = ($length + 7) >> 3;
|
|
||||||
for ($i = 0; $i < $num_blocks; $i++) {
|
|
||||||
$xor.= $iv;
|
|
||||||
for ($j = 4; $j <= 8; $j+=4) {
|
|
||||||
$temp = substr($iv, -$j, 4);
|
|
||||||
switch ($temp) {
|
|
||||||
case "\xFF\xFF\xFF\xFF":
|
|
||||||
$iv = substr_replace($iv, "\x00\x00\x00\x00", -$j, 4);
|
|
||||||
break;
|
|
||||||
case "\x7F\xFF\xFF\xFF":
|
|
||||||
$iv = substr_replace($iv, "\x80\x00\x00\x00", -$j, 4);
|
|
||||||
break 2;
|
|
||||||
default:
|
|
||||||
extract(unpack('Ncount', $temp));
|
|
||||||
$iv = substr_replace($iv, pack('N', $count + 1), -$j, 4);
|
|
||||||
break 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $xor;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypts a message.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param String $plaintext
|
|
||||||
*/
|
|
||||||
function encrypt($plaintext)
|
|
||||||
{
|
|
||||||
if ($this->mode != CRYPT_DES_MODE_CTR && $this->mode != 'ctr') {
|
|
||||||
$plaintext = $this->_pad($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if the key is smaller then 8, do what we'd normally do
|
|
||||||
if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
|
|
||||||
$ciphertext = $this->des[2]->encrypt($this->des[1]->decrypt($this->des[0]->encrypt($plaintext)));
|
|
||||||
|
|
||||||
return $ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
|
|
||||||
if ($this->changed) {
|
|
||||||
if (!isset($this->enmcrypt)) {
|
|
||||||
$this->enmcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
|
|
||||||
}
|
|
||||||
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
|
|
||||||
$this->changed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ciphertext = mcrypt_generic($this->enmcrypt, $plaintext);
|
|
||||||
|
|
||||||
if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_init($this->enmcrypt, $this->key, $this->encryptIV);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($this->key) <= 8) {
|
|
||||||
$this->des[0]->mode = $this->mode;
|
|
||||||
|
|
||||||
return $this->des[0]->encrypt($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
|
|
||||||
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
|
|
||||||
$plaintext = str_pad($plaintext, ceil(strlen($plaintext) / 8) * 8, chr(0));
|
|
||||||
|
|
||||||
$des = $this->des;
|
|
||||||
|
|
||||||
$ciphertext = '';
|
|
||||||
switch ($this->mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
for ($i = 0; $i < strlen($plaintext); $i+=8) {
|
|
||||||
$block = substr($plaintext, $i, 8);
|
|
||||||
$block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
|
|
||||||
$block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
|
|
||||||
$block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
|
|
||||||
$ciphertext.= $block;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
$xor = $this->encryptIV;
|
|
||||||
for ($i = 0; $i < strlen($plaintext); $i+=8) {
|
|
||||||
$block = substr($plaintext, $i, 8) ^ $xor;
|
|
||||||
$block = $des[0]->_processBlock($block, CRYPT_DES_ENCRYPT);
|
|
||||||
$block = $des[1]->_processBlock($block, CRYPT_DES_DECRYPT);
|
|
||||||
$block = $des[2]->_processBlock($block, CRYPT_DES_ENCRYPT);
|
|
||||||
$xor = $block;
|
|
||||||
$ciphertext.= $block;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->encryptIV = $xor;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
$xor = $this->encryptIV;
|
|
||||||
for ($i = 0; $i < strlen($plaintext); $i+=8) {
|
|
||||||
$key = $this->_generate_xor(8, $xor);
|
|
||||||
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
|
|
||||||
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
|
|
||||||
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
|
|
||||||
$block = substr($plaintext, $i, 8);
|
|
||||||
$ciphertext.= $block ^ $key;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->encryptIV = $xor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $ciphertext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypts a message.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param String $ciphertext
|
|
||||||
*/
|
|
||||||
function decrypt($ciphertext)
|
|
||||||
{
|
|
||||||
if ($this->mode == CRYPT_DES_MODE_3CBC && strlen($this->key) > 8) {
|
|
||||||
$plaintext = $this->des[0]->decrypt($this->des[1]->encrypt($this->des[2]->decrypt($ciphertext)));
|
|
||||||
|
|
||||||
return $this->_unpad($plaintext);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we pad with chr(0) since that's what mcrypt_generic does. to quote from http://php.net/function.mcrypt-generic :
|
|
||||||
// "The data is padded with "\0" to make sure the length of the data is n * blocksize."
|
|
||||||
$ciphertext = str_pad($ciphertext, (strlen($ciphertext) + 7) & 0xFFFFFFF8, chr(0));
|
|
||||||
|
|
||||||
if ( CRYPT_DES_MODE == CRYPT_DES_MODE_MCRYPT ) {
|
|
||||||
if ($this->changed) {
|
|
||||||
if (!isset($this->demcrypt)) {
|
|
||||||
$this->demcrypt = mcrypt_module_open(MCRYPT_3DES, '', $this->mode, '');
|
|
||||||
}
|
|
||||||
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
|
|
||||||
$this->changed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$plaintext = mdecrypt_generic($this->demcrypt, $ciphertext);
|
|
||||||
|
|
||||||
if (!$this->continuousBuffer) {
|
|
||||||
mcrypt_generic_init($this->demcrypt, $this->key, $this->decryptIV);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->mode != 'ctr' ? $this->_unpad($plaintext) : $plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strlen($this->key) <= 8) {
|
|
||||||
$this->des[0]->mode = $this->mode;
|
|
||||||
|
|
||||||
return $this->_unpad($this->des[0]->decrypt($plaintext));
|
|
||||||
}
|
|
||||||
|
|
||||||
$des = $this->des;
|
|
||||||
|
|
||||||
$plaintext = '';
|
|
||||||
switch ($this->mode) {
|
|
||||||
case CRYPT_DES_MODE_ECB:
|
|
||||||
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
|
|
||||||
$block = substr($ciphertext, $i, 8);
|
|
||||||
$block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
|
|
||||||
$block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
|
|
||||||
$block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
|
|
||||||
$plaintext.= $block;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CBC:
|
|
||||||
$xor = $this->decryptIV;
|
|
||||||
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
|
|
||||||
$orig = $block = substr($ciphertext, $i, 8);
|
|
||||||
$block = $des[2]->_processBlock($block, CRYPT_DES_DECRYPT);
|
|
||||||
$block = $des[1]->_processBlock($block, CRYPT_DES_ENCRYPT);
|
|
||||||
$block = $des[0]->_processBlock($block, CRYPT_DES_DECRYPT);
|
|
||||||
$plaintext.= $block ^ $xor;
|
|
||||||
$xor = $orig;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->decryptIV = $xor;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CRYPT_DES_MODE_CTR:
|
|
||||||
$xor = $this->decryptIV;
|
|
||||||
for ($i = 0; $i < strlen($ciphertext); $i+=8) {
|
|
||||||
$key = $this->_generate_xor(8, $xor);
|
|
||||||
$key = $des[0]->_processBlock($key, CRYPT_DES_ENCRYPT);
|
|
||||||
$key = $des[1]->_processBlock($key, CRYPT_DES_DECRYPT);
|
|
||||||
$key = $des[2]->_processBlock($key, CRYPT_DES_ENCRYPT);
|
|
||||||
$block = substr($ciphertext, $i, 8);
|
|
||||||
$plaintext.= $block ^ $key;
|
|
||||||
}
|
|
||||||
if ($this->continuousBuffer) {
|
|
||||||
$this->decryptIV = $xor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->mode != CRYPT_DES_MODE_CTR ? $this->_unpad($plaintext) : $plaintext;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Treat consecutive "packets" as if they are a continuous buffer.
|
|
||||||
*
|
|
||||||
* Say you have a 16-byte plaintext $plaintext. Using the default behavior, the two following code snippets
|
|
||||||
* will yield different outputs:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* echo $des->encrypt(substr($plaintext, 0, 8));
|
|
||||||
* echo $des->encrypt(substr($plaintext, 8, 8));
|
|
||||||
* </code>
|
|
||||||
* <code>
|
|
||||||
* echo $des->encrypt($plaintext);
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
|
|
||||||
* another, as demonstrated with the following:
|
|
||||||
*
|
|
||||||
* <code>
|
|
||||||
* $des->encrypt(substr($plaintext, 0, 8));
|
|
||||||
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
|
|
||||||
* </code>
|
|
||||||
* <code>
|
|
||||||
* echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
|
|
||||||
* outputs. The reason is due to the fact that the initialization vector's change after every encryption /
|
|
||||||
* decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
|
|
||||||
*
|
|
||||||
* Put another way, when the continuous buffer is enabled, the state of the Crypt_DES() object changes after each
|
|
||||||
* encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
|
|
||||||
* continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
|
|
||||||
* however, they are also less intuitive and more likely to cause you problems.
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::disableContinuousBuffer()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function enableContinuousBuffer()
|
|
||||||
{
|
|
||||||
$this->continuousBuffer = true;
|
|
||||||
if ($this->mode == CRYPT_DES_MODE_3CBC) {
|
|
||||||
$this->des[0]->enableContinuousBuffer();
|
|
||||||
$this->des[1]->enableContinuousBuffer();
|
|
||||||
$this->des[2]->enableContinuousBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Treat consecutive packets as if they are a discontinuous buffer.
|
|
||||||
*
|
|
||||||
* The default behavior.
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::enableContinuousBuffer()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function disableContinuousBuffer()
|
|
||||||
{
|
|
||||||
$this->continuousBuffer = false;
|
|
||||||
$this->encryptIV = $this->iv;
|
|
||||||
$this->decryptIV = $this->iv;
|
|
||||||
|
|
||||||
if ($this->mode == CRYPT_DES_MODE_3CBC) {
|
|
||||||
$this->des[0]->disableContinuousBuffer();
|
|
||||||
$this->des[1]->disableContinuousBuffer();
|
|
||||||
$this->des[2]->disableContinuousBuffer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pad "packets".
|
|
||||||
*
|
|
||||||
* DES works by encrypting eight bytes at a time. If you ever need to encrypt or decrypt something that's not
|
|
||||||
* a multiple of eight, it becomes necessary to pad the input so that it's length is a multiple of eight.
|
|
||||||
*
|
|
||||||
* Padding is enabled by default. Sometimes, however, it is undesirable to pad strings. Such is the case in SSH1,
|
|
||||||
* where "packets" are padded with random bytes before being encrypted. Unpad these packets and you risk stripping
|
|
||||||
* away characters that shouldn't be stripped away. (SSH knows how many bytes are added because the length is
|
|
||||||
* transmitted separately)
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::disablePadding()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function enablePadding()
|
|
||||||
{
|
|
||||||
$this->padding = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do not pad packets.
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::enablePadding()
|
|
||||||
* @access public
|
|
||||||
*/
|
|
||||||
function disablePadding()
|
|
||||||
{
|
|
||||||
$this->padding = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pads a string
|
|
||||||
*
|
|
||||||
* Pads a string using the RSA PKCS padding standards so that its length is a multiple of the blocksize (8).
|
|
||||||
* 8 - (strlen($text) & 7) bytes are added, each of which is equal to chr(8 - (strlen($text) & 7)
|
|
||||||
*
|
|
||||||
* If padding is disabled and $text is not a multiple of the blocksize, the string will be padded regardless
|
|
||||||
* and padding will, hence forth, be enabled.
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::_unpad()
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _pad($text)
|
|
||||||
{
|
|
||||||
$length = strlen($text);
|
|
||||||
|
|
||||||
if (!$this->padding) {
|
|
||||||
if (($length & 7) == 0) {
|
|
||||||
return $text;
|
|
||||||
} else {
|
|
||||||
user_error("The plaintext's length ($length) is not a multiple of the block size (8)", E_USER_NOTICE);
|
|
||||||
$this->padding = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$pad = 8 - ($length & 7);
|
|
||||||
return str_pad($text, $length + $pad, chr($pad));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unpads a string
|
|
||||||
*
|
|
||||||
* If padding is enabled and the reported padding length is invalid the encryption key will be assumed to be wrong
|
|
||||||
* and false will be returned.
|
|
||||||
*
|
|
||||||
* @see Crypt_TripleDES::_pad()
|
|
||||||
* @access private
|
|
||||||
*/
|
|
||||||
function _unpad($text)
|
|
||||||
{
|
|
||||||
if (!$this->padding) {
|
|
||||||
return $text;
|
|
||||||
}
|
|
||||||
|
|
||||||
$length = ord($text[strlen($text) - 1]);
|
|
||||||
|
|
||||||
if (!$length || $length > 8) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return substr($text, 0, -$length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: ts=4:sw=4:et:
|
|
||||||
// vim6: fdl=1:
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,43 +0,0 @@
|
||||||
<?php
|
|
||||||
// $Id: array_fill.php,v 1.1 2007/07/02 04:19:55 terrafrost Exp $
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace array_fill()
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_Compat
|
|
||||||
* @license LGPL - http://www.gnu.org/licenses/lgpl.html
|
|
||||||
* @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
|
|
||||||
* @link http://php.net/function.array_fill
|
|
||||||
* @author Jim Wigginton <terrafrost@php.net>
|
|
||||||
* @version $Revision: 1.1 $
|
|
||||||
* @since PHP 4.2.0
|
|
||||||
*/
|
|
||||||
function php_compat_array_fill($start_index, $num, $value)
|
|
||||||
{
|
|
||||||
if ($num <= 0) {
|
|
||||||
user_error('array_fill(): Number of elements must be positive', E_USER_WARNING);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$temp = array();
|
|
||||||
|
|
||||||
$end_index = $start_index + $num;
|
|
||||||
for ($i = (int) $start_index; $i < $end_index; $i++) {
|
|
||||||
$temp[$i] = $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Define
|
|
||||||
if (!function_exists('array_fill')) {
|
|
||||||
function array_fill($start_index, $num, $value)
|
|
||||||
{
|
|
||||||
return php_compat_array_fill($start_index, $num, $value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,67 +0,0 @@
|
||||||
<?php
|
|
||||||
// $Id: bcpowmod.php,v 1.1 2007/07/02 04:19:55 terrafrost Exp $
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace bcpowmod()
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_Compat
|
|
||||||
* @license LGPL - http://www.gnu.org/licenses/lgpl.html
|
|
||||||
* @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
|
|
||||||
* @link http://php.net/function.bcpowmod
|
|
||||||
* @author Sara Golemon <pollita@php.net>
|
|
||||||
* @version $Revision: 1.1 $
|
|
||||||
* @since PHP 5.0.0
|
|
||||||
* @require PHP 4.0.0 (user_error)
|
|
||||||
*/
|
|
||||||
function php_compat_bcpowmod($x, $y, $modulus, $scale = 0)
|
|
||||||
{
|
|
||||||
// Sanity check
|
|
||||||
if (!is_scalar($x)) {
|
|
||||||
user_error('bcpowmod() expects parameter 1 to be string, ' .
|
|
||||||
gettype($x) . ' given', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_scalar($y)) {
|
|
||||||
user_error('bcpowmod() expects parameter 2 to be string, ' .
|
|
||||||
gettype($y) . ' given', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_scalar($modulus)) {
|
|
||||||
user_error('bcpowmod() expects parameter 3 to be string, ' .
|
|
||||||
gettype($modulus) . ' given', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!is_scalar($scale)) {
|
|
||||||
user_error('bcpowmod() expects parameter 4 to be integer, ' .
|
|
||||||
gettype($scale) . ' given', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$t = '1';
|
|
||||||
while (bccomp($y, '0')) {
|
|
||||||
if (bccomp(bcmod($y, '2'), '0')) {
|
|
||||||
$t = bcmod(bcmul($t, $x), $modulus);
|
|
||||||
$y = bcsub($y, '1');
|
|
||||||
}
|
|
||||||
|
|
||||||
$x = bcmod(bcmul($x, $x), $modulus);
|
|
||||||
$y = bcdiv($y, '2');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $t;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Define
|
|
||||||
if (!function_exists('bcpowmod')) {
|
|
||||||
function bcpowmod($x, $y, $modulus, $scale = 0)
|
|
||||||
{
|
|
||||||
return php_compat_bcpowmod($x, $y, $modulus, $scale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
|
|
@ -1,59 +0,0 @@
|
||||||
<?php
|
|
||||||
/**
|
|
||||||
* Replace str_split()
|
|
||||||
*
|
|
||||||
* @category PHP
|
|
||||||
* @package PHP_Compat
|
|
||||||
* @license LGPL - http://www.gnu.org/licenses/lgpl.html
|
|
||||||
* @copyright 2004-2007 Aidan Lister <aidan@php.net>, Arpad Ray <arpad@php.net>
|
|
||||||
* @link http://php.net/function.str_split
|
|
||||||
* @author Aidan Lister <aidan@php.net>
|
|
||||||
* @version $Revision: 1.1 $
|
|
||||||
* @since PHP 5
|
|
||||||
* @require PHP 4.0.0 (user_error)
|
|
||||||
*/
|
|
||||||
function php_compat_str_split($string, $split_length = 1)
|
|
||||||
{
|
|
||||||
if (!is_scalar($split_length)) {
|
|
||||||
user_error('str_split() expects parameter 2 to be long, ' .
|
|
||||||
gettype($split_length) . ' given', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$split_length = (int) $split_length;
|
|
||||||
if ($split_length < 1) {
|
|
||||||
user_error('str_split() The length of each segment must be greater than zero', E_USER_WARNING);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Select split method
|
|
||||||
if ($split_length < 65536) {
|
|
||||||
// Faster, but only works for less than 2^16
|
|
||||||
preg_match_all('/.{1,' . $split_length . '}/s', $string, $matches);
|
|
||||||
return $matches[0];
|
|
||||||
} else {
|
|
||||||
// Required due to preg limitations
|
|
||||||
$arr = array();
|
|
||||||
$idx = 0;
|
|
||||||
$pos = 0;
|
|
||||||
$len = strlen($string);
|
|
||||||
|
|
||||||
while ($len > 0) {
|
|
||||||
$blk = ($len < $split_length) ? $len : $split_length;
|
|
||||||
$arr[$idx++] = substr($string, $pos, $blk);
|
|
||||||
$pos += $blk;
|
|
||||||
$len -= $blk;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $arr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Define
|
|
||||||
if (!function_exists('str_split')) {
|
|
||||||
function str_split($string, $split_length = 1)
|
|
||||||
{
|
|
||||||
return php_compat_str_split($string, $split_length);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,135 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 3. Cryptography</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="math.html" title="Chapter 2. Math" /><link rel="next" href="net.html" title="Chapter 4. Networking" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 3. Cryptography</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="crypt"></a>Chapter 3. Cryptography</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="crypt.html#crypt_intro">3.1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_dependencies">3.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_set">3.1.2. setKey() and setIV()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_setmcrypt">3.1.3. setMCrypt()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_encrypt">3.1.4. encrypt() and decrypt()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_continuousbuffer">3.1.5. enableContinuousBuffer() and disableContinuousBuffer()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_padding">3.1.6. enablePadding() and disablePadding()</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_des">3.2. Crypt_DES</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_des_constructor">3.2.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_tripledes">3.3. Crypt_TripleDES</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_tripledes_constructor">3.3.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_rc4">3.4. Crypt_RC4</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_rc4_constructor">3.4.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="crypt.html#crypt_aes">3.5. Crypt_Rijndael & Crypt_AES</a></span></dt><dd><dl><dt><span class="section"><a href="crypt.html#crypt_aes_constructor">3.5.1. The constructor</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_vs_rijndael">3.5.2. AES vs. Rijndael</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_setkeylength">3.5.3. setKeyLength()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_setblocklength">3.5.4. setBlockLength()</a></span></dt><dt><span class="section"><a href="crypt.html#crypt_aes_benchmarks">3.5.5. Speed Comparisons</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_intro"></a>3.1. Introduction</h2></div></div></div><p>
|
|
||||||
All of the cryptographic libraries included in phpseclib use mcrypt, if available, and an internal implementation
|
|
||||||
if it's not. The libraries all use a common interface although some functions, for some algorithms, carry with
|
|
||||||
with them certain caveats. Those that do not have caveats attached (or have relatively few attached) are
|
|
||||||
described below. If you don't know which one to use, try <code class="code">Crypt_TripleDES</code>.
|
|
||||||
</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_dependencies"></a>3.1.1. Dependencies</h3></div></div></div><p>
|
|
||||||
The Crypt_* functions require, minimally, PHP 4.0.0. Crypt_TripleDES additionally requires Crypt/DES.php.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_set"></a>3.1.2. setKey() and setIV()</h3></div></div></div><p>
|
|
||||||
Sets the key and the initialization vector, respectively. If neither are set, each assumed to be equal to
|
|
||||||
some amount of null bytes. The initialization vector is only used in block ciphers and even then only
|
|
||||||
in CBC mode. If the key or the initialization vector are larger then the block size, they're truncated.
|
|
||||||
If they're smaller, they're padded with null bytes.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_setmcrypt"></a>3.1.3. setMCrypt()</h3></div></div></div><p>
|
|
||||||
See php.net's entry on <a class="ulink" href="http://php.net/function.mcrypt-module-open#function.mcrypt-module-open" target="_top">mcrypt_module_open</a>.
|
|
||||||
The first parameter is equal to <code class="code">$algorithm_directory</code> and the second, to <code class="code">$mode_directory</code>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_encrypt"></a>3.1.4. encrypt() and decrypt()</h3></div></div></div><p>
|
|
||||||
Self-explanatory. Encrypts or decrypts messages. See the examples in the subsequent sections.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_continuousbuffer"></a>3.1.5. enableContinuousBuffer() and disableContinuousBuffer()</h3></div></div></div><p>
|
|
||||||
Say you have a 16-byte plaintext $plaintext and that you're using <code class="code">Crypt_DES</code>. Using the default behavior, the two following code snippets
|
|
||||||
will yield different outputs:
|
|
||||||
</p><pre class="programlisting"> echo $des->encrypt(substr($plaintext, 0, 8));
|
|
||||||
echo $des->encrypt(substr($plaintext, 8, 8));</pre><pre class="programlisting"> echo $des->encrypt($plaintext);</pre><p>
|
|
||||||
The solution is to enable the continuous buffer. Although this will resolve the above discrepancy, it creates
|
|
||||||
another, as demonstrated with the following:
|
|
||||||
</p><pre class="programlisting"> $des->encrypt(substr($plaintext, 0, 8));
|
|
||||||
echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));</pre><pre class="programlisting"> echo $des->decrypt($des->encrypt(substr($plaintext, 8, 8)));</pre><p>
|
|
||||||
With the continuous buffer disabled, these would yield the same output. With it enabled, they yield different
|
|
||||||
outputs. The reason is due to the fact that the initialization vector's change after every encryption /
|
|
||||||
decryption round when the continuous buffer is enabled. When it's disabled, they remain constant.
|
|
||||||
|
|
||||||
Put another way, when the continuous buffer is enabled, the state of the <code class="code">Crypt_DES()</code> object changes after each
|
|
||||||
encryption / decryption round, whereas otherwise, it'd remain constant. For this reason, it's recommended that
|
|
||||||
continuous buffers not be used. They do offer better security and are, in fact, sometimes required (SSH uses them),
|
|
||||||
however, they are also less intuitive and more likely to cause you problems.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_padding"></a>3.1.6. enablePadding() and disablePadding()</h3></div></div></div><p>
|
|
||||||
Enables / disables PKCS padding on block ciphers. Stream ciphers (<code class="code">Crypt_RC4</code> is the only stream
|
|
||||||
cipher currently included) ignore this.
|
|
||||||
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_des"></a>3.2. Crypt_DES</h2></div></div></div><p>
|
|
||||||
Implements DES (a block cipher). Here's an example of how to use it:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/DES.php');
|
|
||||||
|
|
||||||
$des = new Crypt_DES();
|
|
||||||
|
|
||||||
$des->setKey('abcdefgh');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $des->decrypt($des->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_des_constructor"></a>3.2.1. The constructor</h3></div></div></div><p>
|
|
||||||
The constructor takes one optional parameter - $mode. $mode can be equal to <code class="code">CRYPT_DES_MODE_ECB</code>
|
|
||||||
or <code class="code">CRYPT_DES_MODE_CBC</code>. <code class="code">CRYPT_DES_MODE_CBC</code> is generally considered more secure
|
|
||||||
and is what <code class="code">Crypt_DES</code> uses by default. If you don't know the difference between ECB or CBC,
|
|
||||||
just use the default settings.
|
|
||||||
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_tripledes"></a>3.3. Crypt_TripleDES</h2></div></div></div><p>
|
|
||||||
Implements TripleDES (a block cipher). Here's an example of how to use it:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/TripleDES.php');
|
|
||||||
|
|
||||||
$des = new Crypt_TripleDES();
|
|
||||||
|
|
||||||
$des->setKey('abcdefghijklmnopqrstuvwx');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $des->decrypt($des->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_tripledes_constructor"></a>3.3.1. The constructor</h3></div></div></div><p>
|
|
||||||
The constructor takes one optional parameter - $mode. $mode can be equal to <code class="code">CRYPT_DES_MODE_ECB</code>,
|
|
||||||
<code class="code">CRYPT_DES_MODE_CBC</code>, <code class="code">CRYPT_DES_MODE_3CBC</code>, or <code class="code">CRYPT_DES_MODE_CBC3</code>.
|
|
||||||
<code class="code">CRYPT_DES_MODE_CBC3</code> is an alias <code class="code">CRYPT_DES_MODE_CBC</code>. It's defined to distinguish
|
|
||||||
it from <code class="code">CRYPT_DES_MODE_3CBC</code>, which uses inner chaining to propogate the initialization vector.
|
|
||||||
SSH-1 uses this and it is generally considered to be less secure then <code class="code">CRYPT_DES_MODE_CBC3</code>,
|
|
||||||
which uses outer chaining (and is what SSH-2 uses).
|
|
||||||
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_rc4"></a>3.4. Crypt_RC4</h2></div></div></div><p>
|
|
||||||
Implements RC4 (a stream cipher). Here's an example of how to use it:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/RC4.php');
|
|
||||||
|
|
||||||
$rc4 = new Crypt_RC4();
|
|
||||||
|
|
||||||
$rc4->setKey('abcdefghijklmnopqrstuvwx');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $rc4->decrypt($rc4->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_rc4_constructor"></a>3.4.1. The constructor</h3></div></div></div><p>
|
|
||||||
Not much to say about this constructor. Since it's a stream cipher, you don't need to worry about which
|
|
||||||
mode of operation to use.
|
|
||||||
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="crypt_aes"></a>3.5. Crypt_Rijndael & Crypt_AES</h2></div></div></div><p>
|
|
||||||
Implements Rijndael / AES. Here's an example of how to use Crypt_AES:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/AES.php');
|
|
||||||
|
|
||||||
$aes = new Crypt_AES();
|
|
||||||
|
|
||||||
$aes->setKey('abcdefghijklmnop');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $aes->decrypt($aes->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_constructor"></a>3.5.1. The constructor</h3></div></div></div><p>
|
|
||||||
<code class="code">Crypt_AES</code>'s constructor takes <code class="code">CRYPT_AES_MODE_ECB</code> and <code class="code">CRYPT_AES_MODE_CBC</code> as parameters. <code class="code">Crypt_Rijndael</code>, <code class="code">CRYPT_RIJNDAEL_MODE_ECB</code> and <code class="code">CRYPT_RIJNDAEL_MODE_CBC</code>. In both cases, if no valid mode is defined, CBC will be used.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_vs_rijndael"></a>3.5.2. AES vs. Rijndael</h3></div></div></div><p>
|
|
||||||
AES is a subset of Rijndael. Both have variable key sizes, however, AES's block size is fixed at 128 bytes, whereas Rijndael's is variable. Also, Rijndael supports, by means of an extension to the specification, two key sizes that AES does not - 160 bits and 224 bits.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_setkeylength"></a>3.5.3. setKeyLength()</h3></div></div></div><p>
|
|
||||||
Valid key lengths for AES are 128 bits, 192 bits, and 256 bits. If the key that is assigned is invalid and less than 256 bits, they key length is rounded up to the next closest valid size and the key will be null padded to that amount. If the key length is greater than 256 bits, it will be truncated to 256 bits.
|
|
||||||
</p><p>
|
|
||||||
As an example, if the key is 136 bits, it will be null padded to 192 bits (or 160 bits if Rijndael is being used).
|
|
||||||
</p><p>
|
|
||||||
If <code class="code">setKeyLength()</code> has been called, this behavior changes somewhat. Say you've set the key length, via this function, to 256 bits. Then, instead of an invalid key being null padded to 192 or 160 bits, it will be null padded to 256 bits.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_setblocklength"></a>3.5.4. setBlockLength()</h3></div></div></div><p>
|
|
||||||
<code class="code">setBlockLength()</code> operates in a manner similar to <code class="code">setKeyLength()</code>, with one exception. <code class="code">setBlockLength()</code> only works on Rijndael. Although <code class="code">Crypt_AES</code> inherits <code class="code">setBlockLength()</code> as a function, the function doesn't do anything in AES.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="crypt_aes_benchmarks"></a>3.5.5. Speed Comparisons</h3></div></div></div><p>
|
|
||||||
The following table compares the speed of five different pure-PHP implementations of AES (one of which is Crypt_Rijndael and one of which is Crypt_AES) when ran on 150KB of text on a 1.8GHz Pentium 4-M. The numbers listed are averaged from five different trials and are measured in seconds. phpseclib's two implementations are highlighted. All implementations can be viewed by clicking on their names.
|
|
||||||
</p><div class="table"><a id="crypt_aes_benchmarks_table"></a><p class="title"><b>Table 3.1. AES Speed Comparisons</b></p><div class="table-contents"><table summary="AES Speed Comparisons" border="1"><colgroup><col /><col /><col /><col /><col /></colgroup><thead><tr><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/movable-type.phps" target="_top">movable-type.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpaes.phps" target="_top">phpaes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses1.phps" target="_top">phpclasses1.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses2.phps" target="_top">phpclasses2.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-aes.phps" target="_top">phpseclib-aes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-rijndael.phps" target="_top">phpseclib-rijndael.phps</a></th></tr></thead><tbody><tr><td align="right">15.6844158172</td><td align="right">39.9537248135</td><td align="right">15.0100150108</td><td align="right">62.591713190079</td><td class="highlight" align="right">3.5728311081</td><td class="highlight" align="right">5.24388728142</td></tr></tbody></table></div></div><br class="table-break" /><p>
|
|
||||||
As can be seen, phpseclib's implementations are the fastest. phpseclib-aes.phps is faster than phpseclib-rijndael.phps because phpseclib-rijndael.phps has to contend with multiple block sizes whereas phpseclib-aes.phps does not.
|
|
||||||
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 2. Math </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 4. Networking</td></tr></table></div></body></html>
|
|
|
@ -1,43 +0,0 @@
|
||||||
body {
|
|
||||||
font-family: Georgia, "Times New Roman", Times, serif
|
|
||||||
}
|
|
||||||
|
|
||||||
div.author h3 {
|
|
||||||
display: none
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
text-decoration: none;
|
|
||||||
color: #369
|
|
||||||
}
|
|
||||||
|
|
||||||
a:hover {
|
|
||||||
text-decoration: underline
|
|
||||||
}
|
|
||||||
|
|
||||||
.programlisting {
|
|
||||||
font-family: Monaco, "Andale Mono","Courier New", Courier, mono;
|
|
||||||
font-size: 10pt
|
|
||||||
}
|
|
||||||
|
|
||||||
.programlisting, .code {
|
|
||||||
background: #eee;
|
|
||||||
color: #181;
|
|
||||||
font-weight: bold
|
|
||||||
}
|
|
||||||
|
|
||||||
.red {
|
|
||||||
color: #e11
|
|
||||||
}
|
|
||||||
|
|
||||||
.highlight {
|
|
||||||
background: #ee1
|
|
||||||
}
|
|
||||||
|
|
||||||
thead {
|
|
||||||
background: #ccc
|
|
||||||
}
|
|
||||||
|
|
||||||
#crypt_aes_benchmarks_table.tbody {
|
|
||||||
font-weight: bold
|
|
||||||
}
|
|
File diff suppressed because one or more lines are too long
|
@ -1,20 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 1. Introduction</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="index.html" title="PHP Secure Communications Library" /><link rel="next" href="math.html" title="Chapter 2. Math" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 1. Introduction</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="math.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="intro"></a>Chapter 1. Introduction</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="intro.html#intro_intro">1.1. Who should use phpseclib</a></span></dt><dt><span class="section"><a href="intro.html#intro_usage">1.2. Using phpseclib</a></span></dt></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="intro_intro"></a>1.1. Who should use phpseclib</h2></div></div></div><p>
|
|
||||||
Although many of the features this library implements are implemented in PHP via optional extensions, what are
|
|
||||||
you, as a developer, going to do when a user tries to run your software on a host which, coincidentally, doesn't
|
|
||||||
happen to have that optional extension installed? You could, flat-out, tell that user to look for another
|
|
||||||
software package that does work on their server (or to get another host, or whatever), which is liable to leave
|
|
||||||
a bad impression on the user, or you could use a library like this - a library that uses those optional
|
|
||||||
extensions if they're available and falls back on an internal PHP implementation if they're not.
|
|
||||||
</p><p>
|
|
||||||
Another advantage of using this library over optional PHP extensions is that you, as a developer, may find this
|
|
||||||
libraries API easier to use then extensions API.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="intro_usage"></a>1.2. Using phpseclib</h2></div></div></div><p>
|
|
||||||
This library is written using the same conventions that libraries in the PHP Extension and Application Repository (PEAR)
|
|
||||||
have been written in. In particular, this library expects to be in your <code class="code">include_path</code>:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
set_include_path(get_include_path() . PATH_SEPARATOR . 'phpseclib');
|
|
||||||
|
|
||||||
include('Net/SSH2.php');
|
|
||||||
?></pre></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="index.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="math.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">PHP <span class="red">Secure</span> Communications Library </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 2. Math</td></tr></table></div></body></html>
|
|
|
@ -1,157 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 2. Math</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="intro.html" title="Chapter 1. Introduction" /><link rel="next" href="sym_crypt.html" title="Chapter 3. Symmetric-key Cryptography" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 2. Math</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="intro.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="sym_crypt.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="math"></a>Chapter 2. Math</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="math.html#math_biginteger">2.1. Math_BigInteger</a></span></dt><dd><dl><dt><span class="section"><a href="math.html#math_biginteger_dependencies">2.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_constructor">2.1.2. The constructor</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_output">2.1.3. toString(), toBytes(), toHex() and toBits()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_fourfunctions">2.1.4. add(), subtract(), multiply() and divide()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_modulo">2.1.5. powMod() and modInverse()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_gcd">2.1.6. gcd() and extendedGCD()</a></span></dt><dt><span class="section"><a href="math.html#math_abs">2.1.7. abs()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_compare">2.1.8. equals() and compare()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_precision">2.1.9. setPrecision()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_bitwise">2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_shifts">2.1.11. bitwise_rightShift() and bitwise_leftShift()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_rotates">2.1.12. bitwise_rightRotate() and bitwise_leftRotate()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_setrandom">2.1.13. setRandomGenerator()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_prime">2.1.14. isPrime()</a></span></dt><dt><span class="section"><a href="math.html#math_biginteger_random">2.1.15. random() and randomPrime()</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="math_biginteger"></a>2.1. Math_BigInteger</h2></div></div></div><p>
|
|
||||||
Implements an arbitrary precision integer arithmetic library. Uses gmp or bcmath, if available, and an
|
|
||||||
internal implementation, otherwise. Here's an example:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Math/BigInteger.php');
|
|
||||||
|
|
||||||
$a = new Math_BigInteger(2);
|
|
||||||
$b = new Math_BigInteger(3);
|
|
||||||
|
|
||||||
$c = $a->add($b);
|
|
||||||
|
|
||||||
echo $c->toString(); // outputs 5
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_dependencies"></a>2.1.1. Dependencies</h3></div></div></div><p>
|
|
||||||
If you're running PHP 5, Math_BigInteger's only dependancy is the PCRE extension (which is enabled by default). Math_BigInteger also works on PHP 4 if PHP/Compat/Function/array_fill.php and PHP/Compat/Function/bcpowmod.php are included.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_constructor"></a>2.1.2. The constructor</h3></div></div></div><p>
|
|
||||||
The constructor takes two parameters. The first is the number and the second represents the base. Both
|
|
||||||
are optional (if they're not provided, the Math_BigInteger object will assume a value of 0).
|
|
||||||
</p><p>
|
|
||||||
The supported bases are base-2, base-10 (default), base-16, and base-256. To set $a, in the
|
|
||||||
above example, to 2, using base-2, we'd do <code class="code">new Math_BigInteger('10', 2)</code>. To do it using
|
|
||||||
base-16, you could do <code class="code">new Math_BigInteger('2', 16)</code> or <code class="code">new Math_BigInteger('0x2', 16)</code>.
|
|
||||||
To set it to 2 using base-256, you'd do <code class="code">new Math_BigInteger(chr(2), 256)</code>.
|
|
||||||
</p><p>
|
|
||||||
If the base is negative (eg. -256), two's compliment will be used. Thus, <code class="code">new Math_BigInteger(chr(0xFF), -256)</code>
|
|
||||||
is equal to -1, as is <code class="code">new Math_BigInteger('0xFFFFFFFF', -16)</code> and <code class="code">new Math_BigInteger('11', -2)</code>.
|
|
||||||
Basically, if the leading bit is 1, the number is assumed to be negative.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_output"></a>2.1.3. toString(), toBytes(), toHex() and toBits()</h3></div></div></div><p>
|
|
||||||
<code class="code">toString()</code> returns the base-10 form of a number. <code class="code">toBytes()</code> returns the base-256
|
|
||||||
form of a number, <code class="code">toHex()</code> returns the base-16 form, and <code class="code">toBits()</code> the base-2 form.
|
|
||||||
<code class="code">toBytes()</code>, <code class="code">toHex()</code>, and <code class="code">toBits()</code> also take an optional parameter which,
|
|
||||||
if set, will return the two's compliment of a number. So if, for example, $a is equal to -1,
|
|
||||||
<code class="code">toBytes(true)</code> will return <code class="code">chr(0xFF)</code>.
|
|
||||||
</p><p>
|
|
||||||
On PHP 5, <code class="code">toString()</code> is called automatically when used in a string context via the
|
|
||||||
<a class="ulink" href="http://php.net/language.oop5.magic#language.oop5.magic.tostring" target="_top">__toString() magic method</a>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_fourfunctions"></a>2.1.4. add(), subtract(), multiply() and divide()</h3></div></div></div><p>
|
|
||||||
<code class="code">subtract()</code> and <code class="code">multiply()</code> operate similarly to <code class="code">add()</code>. <code class="code">divide()</code>,
|
|
||||||
however, does not. Namely, it 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:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Math/BigInteger.php');
|
|
||||||
|
|
||||||
$a = new Math_BigInteger('10');
|
|
||||||
$b = new Math_BigInteger('20');
|
|
||||||
|
|
||||||
list($quotient, $remainder) = $a->divide($b);
|
|
||||||
|
|
||||||
echo $quotient->toString(); // outputs 0
|
|
||||||
echo "\r\n";
|
|
||||||
echo $remainder->toString(); // outputs 10
|
|
||||||
?></pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_modulo"></a>2.1.5. powMod() and modInverse()</h3></div></div></div><p>
|
|
||||||
Examples of each follow:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Math/BigInteger.php');
|
|
||||||
|
|
||||||
$a = new Math_BigInteger('10');
|
|
||||||
$b = new Math_BigInteger('20');
|
|
||||||
$c = new Math_BigInteger('30');
|
|
||||||
|
|
||||||
$c = $a->powMod($b, $c);
|
|
||||||
|
|
||||||
echo $c->toString(); // outputs 10
|
|
||||||
?></pre><pre class="programlisting"><?php
|
|
||||||
include('Math/BigInteger.php');
|
|
||||||
|
|
||||||
$a = new Math_BigInteger(30);
|
|
||||||
$b = new Math_BigInteger(17);
|
|
||||||
|
|
||||||
$c = $a->modInverse($b);
|
|
||||||
|
|
||||||
echo $c->toString(); // outputs 4
|
|
||||||
?></pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_gcd"></a>2.1.6. gcd() and extendedGCD()</h3></div></div></div><p>
|
|
||||||
<code class="code">extendedGCD()</code> returns an array containing three Math_BigInteger values indexed with x, y,
|
|
||||||
and gcd. x and y represent Bézout's identity. <code class="code">gcd()</code> returns a Math_BigInteger value
|
|
||||||
equal to the gcd. An example of each follows:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Math/BigInteger.php');
|
|
||||||
|
|
||||||
$a = new Math_BigInteger(693);
|
|
||||||
$b = new Math_BigInteger(609);
|
|
||||||
|
|
||||||
extract($a->extendedGCD($b));
|
|
||||||
$c = $a->gcd($b);
|
|
||||||
|
|
||||||
echo $gcd->toString() . "\r\n"; // outputs 21
|
|
||||||
echo $c->toString() . "\r\n"; // outputs 21
|
|
||||||
echo $a->toString() * $x->toString() + $b->toString() * $y->toString(); // outputs 21
|
|
||||||
?></pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_abs"></a>2.1.7. abs()</h3></div></div></div><p>
|
|
||||||
<code class="code">$x->abs()</code> returns the absolute value of <code class="code">$x</code>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_compare"></a>2.1.8. equals() and compare()</h3></div></div></div><p>
|
|
||||||
<code class="code">$x->equals($y)</code> returns true or false depending on whether or not <code class="code">$x</code> and
|
|
||||||
<code class="code">$y</code> are equal.
|
|
||||||
</p><p>
|
|
||||||
<code class="code">$x->compare($y)</code> returns 1 if $x > $y, 0 if $x == $y, and -1 if $x < $y. The reason for this
|
|
||||||
is demonstrated thusly:
|
|
||||||
</p><pre class="programlisting">$x > $y: $x->compare($y) > 0
|
|
||||||
$x < $y: $x->compare($y) < 0
|
|
||||||
$x == $y: $x->compare($y) == 0
|
|
||||||
$x >= $y: $x->compare($y) >= 0
|
|
||||||
$x <= $y: $x->compare($y) <= 0</pre><p>
|
|
||||||
As a consequence of this, <code class="code">!$x->compare($y)</code> does not mean <code class="code">$x != $y</code> but rather
|
|
||||||
<code class="code">$x == $y</code>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_precision"></a>2.1.9. setPrecision()</h3></div></div></div><p>
|
|
||||||
Some bitwise operations give different results depending on the precision being used. Examples include
|
|
||||||
left shift, not, and rotates, as discussed for <a class="link" href="math.html#math_biginteger_bitwise" title="2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()">bitwise_not()</a>.
|
|
||||||
This function lets you control the precision.
|
|
||||||
</p><p>
|
|
||||||
Whenever a new Math_BigInteger object is created it's precision is set to the same precision as the
|
|
||||||
calling object. In other words, if you do <code class="code">$b = $a->bitwise_not()</code> then <code class="code">$b</code> will
|
|
||||||
have the same precision as <code class="code">$a</code>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_bitwise"></a>2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()</h3></div></div></div><p>
|
|
||||||
<code class="code">bitwise_and()</code>, <code class="code">bitwise_or()</code> and <code class="code">bitwise_xor()</code> operate similar to
|
|
||||||
<code class="code">add()</code>. <code class="code">bitwise_not()</code> is a bit more complicated. To elaborate, if the
|
|
||||||
precision (see <a class="link" href="math.html#math_biginteger_precision" title="2.1.9. setPrecision()">setPrecision</a>) is arbitrary,
|
|
||||||
<code class="code">$x->bitwise_not()</code> will always yield a smaller value since the most significant bit is
|
|
||||||
assumed to have a value of one. With fixed precision, however, the leading bit can be anything.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_shifts"></a>2.1.11. bitwise_rightShift() and bitwise_leftShift()</h3></div></div></div><p>
|
|
||||||
<code class="code">$a->bitwise_rightShift($shift)</code> shifts $a by $shift bits, effectively dividing by 2**$shift.
|
|
||||||
<code class="code">$a->bitwise_leftShift($shift)</code> shifts $a by $shift bits, effectively multiplying by 2**$shift.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_rotates"></a>2.1.12. bitwise_rightRotate() and bitwise_leftRotate()</h3></div></div></div><p>
|
|
||||||
<code class="code">$a->bitwise_rightRotate($shift)</code> and <code class="code">$a->bitwise_leftRotate($shift)</code> are
|
|
||||||
demonstrated thusly:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Math/BigInteger.php');
|
|
||||||
|
|
||||||
$a = new Math_BigInteger('00111000', 2);
|
|
||||||
$a->setPrecision(8);
|
|
||||||
$b = $a->bitwise_leftRotate(2);
|
|
||||||
echo $b->toBits(); // returns 11100000
|
|
||||||
|
|
||||||
echo "\r\n";
|
|
||||||
|
|
||||||
$a = new Math_BigInteger('00111000', 2);
|
|
||||||
$b = $a->bitwise_leftRotate(2);
|
|
||||||
echo $b->toBits(); // returns 100011
|
|
||||||
?></pre><p>
|
|
||||||
Just as with <a class="link" href="math.html#math_biginteger_bitwise" title="2.1.10. bitwise_and(), bitwise_or(), bitwise_xor() and bitwise_not()">bitwise_not()</a>, these operations are
|
|
||||||
precision dependant.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_setrandom"></a>2.1.13. setRandomGenerator()</h3></div></div></div><p>
|
|
||||||
Sets the random generator. To set it to <code class="code">mt_rand()</code> (which is what it is by default), call
|
|
||||||
<code class="code">$x->setRandomGenerator('mt_rand')</code>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_prime"></a>2.1.14. isPrime()</h3></div></div></div><p>
|
|
||||||
Returns true if a number is prime and false if it isn't.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="math_biginteger_random"></a>2.1.15. random() and randomPrime()</h3></div></div></div><p>
|
|
||||||
<code class="code">random($min, $max)</code> generates a random number between <code class="code">$min</code> and <code class="code">$max</code>.
|
|
||||||
<code class="code">randomPrime($min, $max)</code> generates a random prime number between <code class="code">$min</code> and <code class="code">$max</code>.
|
|
||||||
If no prime number exists between <code class="code">$min</code> and <code class="code">$max</code> false is returned.
|
|
||||||
</p><p>
|
|
||||||
<code class="code">randomPrime()</code> has an optional third parameter, as well - $timeout. Generating prime numbers
|
|
||||||
is a particurarly expensive operation and although in certain environments even 512-bit primes can be
|
|
||||||
generated in a less than a second it can take other environments upwards of around a minute if not more.
|
|
||||||
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="intro.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="sym_crypt.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 1. Introduction </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 3. Symmetric-key Cryptography</td></tr></table></div></body></html>
|
|
|
@ -1,155 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 4. Miscellaneous Cryptography</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="sym_crypt.html" title="Chapter 3. Symmetric-key Cryptography" /><link rel="next" href="net.html" title="Chapter 5. Networking" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 4. Miscellaneous Cryptography</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="sym_crypt.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="misc_crypt"></a>Chapter 4. Miscellaneous Cryptography</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="misc_crypt.html#misc_crypt_hash">4.1. Crypt_Hash</a></span></dt><dd><dl><dt><span class="section"><a href="misc_crypt.html#misc_crypt_hash_supported">4.1.1. Supported Algorithms and Dependencies</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_hash_example">4.1.2. Example</a></span></dt></dl></dd><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa">4.2. Crypt_RSA</a></span></dt><dd><dl><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_dependencies">4.2.1. Dependencies</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_examples">4.2.2. Examples</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_createkey">4.2.3. createKey()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_format">4.2.4. setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_getpublickey">4.2.5. setPublicKey() and getPublicKey()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_encrypt">4.2.6. encrypt(), decrypt() and setEncryptionMode()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_sign">4.2.7. sign(), verify(), and setSignatureMode()</a></span></dt><dt><span class="section"><a href="misc_crypt.html#misc_crypt_rsa_params">4.2.8. setHash(), setMGFHash() and setSaltLength()</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="misc_crypt_hash"></a>4.1. Crypt_Hash</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_hash_supported"></a>4.1.1. Supported Algorithms and Dependencies</h3></div></div></div><p>The following algorithms are supported:</p><p>md2, md5, md5-96, sha1, sha1-96, sha256, sha384, and sha512</p><p>
|
|
||||||
Crypt_Hash requires, minimally, PHP 4.3.0 (due to its use of
|
|
||||||
<a class="ulink" href="http://php.net/function.sha1" target="_top">sha1()</a>). If sha384 or sha512 are being used and
|
|
||||||
you're not running PHP 5.1.2 or greater then Math/BigInteger.php is also required.
|
|
||||||
</p><p>
|
|
||||||
Crypt_Hash uses the hash extension if it's available (> 5.1.2), mhash if it's not, and it's own
|
|
||||||
internal implementation if not even mhash is available.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_hash_example"></a>4.1.2. Example</h3></div></div></div><pre class="programlisting"><?php
|
|
||||||
include('Crypt/Hash.php');
|
|
||||||
|
|
||||||
$hash = new Crypt_Hash('sha1');
|
|
||||||
//$hash->setKey('abcdefg');
|
|
||||||
echo bin2hex($hash->hash('abcdefg'));
|
|
||||||
?></pre><p>If <code class="code">$hash->setKey()</code> had been called <code class="code">$hash->hash()</code> would have returned an HMAC.</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="misc_crypt_rsa"></a>4.2. Crypt_RSA</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_dependencies"></a>4.2.1. Dependencies</h3></div></div></div>
|
|
||||||
If you're running PHP 5, Crypt_RSA requires Math/BigInteger.php and Crypt/Hash.php. If you're running
|
|
||||||
PHP 4, Crypt_RSA also requires PHP/Compat/Function/array_fill.php, PHP/Compat/Function/bcpowmod.php, and
|
|
||||||
PHP/Compat/Function/str_split.php
|
|
||||||
</div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_examples"></a>4.2.2. Examples</h3></div></div></div><p>Here's an example of how to encrypt / decrypt with Crypt_RSA:</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/RSA.php');
|
|
||||||
|
|
||||||
$rsa = new Crypt_RSA();
|
|
||||||
extract($rsa->createKey());
|
|
||||||
|
|
||||||
$plaintext = 'terrafrost';
|
|
||||||
|
|
||||||
$rsa->loadKey($privatekey);
|
|
||||||
$ciphertext = $rsa->encrypt($plaintext);
|
|
||||||
|
|
||||||
$rsa->loadKey($publickey);
|
|
||||||
echo $rsa->decrypt($ciphertext);
|
|
||||||
?></pre><p>Here's an example of how to create / verify a signature with Crypt_RSA:</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/RSA.php');
|
|
||||||
|
|
||||||
$rsa = new Crypt_RSA();
|
|
||||||
extract($rsa->createKey());
|
|
||||||
|
|
||||||
$plaintext = 'terrafrost';
|
|
||||||
|
|
||||||
$rsa->loadKey($privatekey);
|
|
||||||
$signature = $rsa->sign($plaintext);
|
|
||||||
|
|
||||||
$rsa->loadKey($publickey);
|
|
||||||
echo $rsa->verify($plaintext, $signature) ? 'verified' : 'unverified';
|
|
||||||
></pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_createkey"></a>4.2.3. createKey()</h3></div></div></div><p>
|
|
||||||
<code class="code">createKey()</code> takes three parameters - <code class="code">$bits</code>, <code class="code">$timeout</code>,
|
|
||||||
and <code class="code">$primes</code>. <code class="code">$timeout</code> is present since creating a key has the potential to be
|
|
||||||
fairly time consuming and will guarantee that <code class="code">createKey()</code> does not run for more than
|
|
||||||
<code class="code">$timeout</code> seconds. <code class="code">$primes</code> lets provide pre-computed prime numbers to speed
|
|
||||||
things up.
|
|
||||||
</p><p>
|
|
||||||
<code class="code">extract($rsa->createKey())</code> creates three variables - <code class="code">$publickey</code>,
|
|
||||||
<code class="code">$privatekey</code>, and <code class="code">$partialkey</code>. If <code class="code">createKey</code> hit the timeout then
|
|
||||||
it'll return all the primes that it had managed to compute so that you might pass them back to
|
|
||||||
<code class="code">createKey()</code> on a subsequent call.
|
|
||||||
</p><p>
|
|
||||||
The exponent can be set by defining <code class="code">CRYPT_RSA_EXPONENT</code> and multi-prime RSA can be utilized
|
|
||||||
by adjusting <code class="code">CRYPT_RSA_SMALLEST_PRIME</code>. Note that these must be done before a Crypt_RSA()
|
|
||||||
object is initialized.
|
|
||||||
</p><p>
|
|
||||||
Smaller values for <code class="code">CRYPT_RSA_SMALLEST_PRIME</code> result in increased speed at the cost of security.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_format"></a>4.2.4. setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()</h3></div></div></div><p>Crypt_RSA supports the following formats:</p><p>CRYPT_RSA_PRIVATE_FORMAT_PKCS1:</p><pre class="programlisting">-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
MIICWgIBAAKBgHx5XHa3LjiugtNq2xkd0oFf2SdsJ04hQYLoeRR3bqAei3Gc+PSy
|
|
||||||
AvynCIh/03JCvBsUHaCe8BwjwaTYrpq5QunGo/wvIzvx2d3G9dlrpOIFLiatZYOf
|
|
||||||
h07+CkSfaRXhBUKkul/gU87WPhKEcbnPDJS10uD1HqLsHfSKLNitGOf7AgElAoGA
|
|
||||||
ENIhQHmedlzFkjEI2eFveURNxw6dhxlANEjtxH7XmRjiaUyQWGsVKQ+nNQpa2Bbb
|
|
||||||
JkD9FbSc/OI8wz/gPmwP9eJN29CriebhaV3ebM1L1gbb5r7Vf/D/6rxB0BG/h2lA
|
|
||||||
jyZWEZrV/Gi9ZCaw/J+IUu1pAskKid84yHphvszywCUCQQDigrtr+cVkwkUsxOGd
|
|
||||||
B378yQCroXmybAD7FQHwVslafuFfTHkaMQSU/ZZLVY1ioMs1VVzzq/vOu0RstZOY
|
|
||||||
AfHFAkEAjK3mIWdG4JOM44/SrDkACNatsMtXKOi4K3SlXu9ie6ikXPD+GSZ+bWCX
|
|
||||||
GstFaXr9cHRvZPF3qYtK+j2N9UXOvwJBALeoRO/DmSFDkgifoixLRF5CHDgiD6Vs
|
|
||||||
U9J/vGIBLaNSHoSe3rtKVr3+CyhTNF3Oe0AABi1bA4UGioGn+yFNr0UCQBbQF3sJ
|
|
||||||
1CRq9ECT3PlVWfOYbzFtFQ2NhaYul1uAw9yzkEZsROF73SZ+XbFRZTOzFFds08su
|
|
||||||
E2eaDCiUXDWcnhECQQCRUQn2huHlssj8kt35NAVwiHCNfaeSQ5tiDcwfOywA4YXl
|
|
||||||
Q+kpuWq5U3V8j/9/n7pE/DL0nXEG/3QpKHJEYV5T
|
|
||||||
-----END RSA PRIVATE KEY-----</pre><p>CRYPT_RSA_PRIVATE_FORMAT_PKCS1 (with password):</p><pre class="programlisting">-----BEGIN RSA PRIVATE KEY-----
|
|
||||||
Proc-Type: 4,ENCRYPTED
|
|
||||||
DEK-Info: DES-EDE3-CBC,0AE1DB47E71463BE
|
|
||||||
|
|
||||||
pI2Kk5ceURbMYNo1xQqqA5rm2/QP4hgj/HuvrACtPSz/aesbG+h4lYXGpQ9os6Ha
|
|
||||||
AyFW+iX2UWS6BRwJj1ztO20sKT6ckg7eINSfiSSAeOOiG5aHLxOYayO9aQ5UrrJX
|
|
||||||
r0QmwRJRiHTW/82PLBNzfFHYskslNI9EWA5L/Gg4NAXDWwDooGvGkDq3ex7WkWLr
|
|
||||||
k7DN2JoZuWsUZxwpgTDouRQMsygrsdSjwRDSgbnTn6luEBrL9fc5/oAWf0xoTk5h
|
|
||||||
XMiOOHPBNPiZ1883ayq91HL/6895g8U9oIR1wQmdl0USViYYp5jI19ueowCyblzP
|
|
||||||
xD3Bfpb6RPaZ/yqECOysPk6PDz257SGDMNk/QrQJ/eZkeniNXHJ8d+nJGuajZeBu
|
|
||||||
6A/bglvKGNNNWe8UJMb5P2OAliD7y7F9wXrkV5FnQ/Q49tGxdBl7WXNuGp4x2d9s
|
|
||||||
ZEnv3mOtrr1lM+2QE0Zg8mjqSem5b6Dp0LxOj5j45j5IbBrrd3MKu87jJVzp8yHy
|
|
||||||
sBC6NMYYtO03qxV/j1kJR+MmAcCF1+4GGRWdFcoc0sXGVqmEOmK4QfYx3T0Vb6Hk
|
|
||||||
oLdlh6ofZogezzJ8A1BvV382sTsJ90eqbgz3E+fDl8iR86+EV9bUujFE4IaBgZJP
|
|
||||||
gxikVItdTcq1frNKTCSH/RPeRwk+oKWTpCYGgNA+bl641onW1DCLYcd14N6TDKmY
|
|
||||||
77cOTf2ZDGOYNPycAF/FnNJJyLO3IYpU63aKBshB4dYeVrfH0FvG6g5Xt0geIkiD
|
|
||||||
5W9El4ks7/3r97x443SagDRt6Mceo5TtzzFfAo7cZeA=
|
|
||||||
-----END RSA PRIVATE KEY-----</pre><p>CRYPT_RSA_PUBLIC_FORMAT_PKCS1:</p><pre class="programlisting">-----BEGIN PUBLIC KEY-----
|
|
||||||
MIGGAoGAfHlcdrcuOK6C02rbGR3SgV/ZJ2wnTiFBguh5FHduoB6LcZz49LIC/KcIiH/TckK8GxQd
|
|
||||||
oJ7wHCPBpNiumrlC6caj/C8jO/HZ3cb12Wuk4gUuJq1lg5+HTv4KRJ9pFeEFQqS6X+BTztY+EoRx
|
|
||||||
uc8MlLXS4PUeouwd9Ios2K0Y5/sCASU=
|
|
||||||
-----END PUBLIC KEY-----</pre><p>CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:</p><pre class="programlisting">ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAIB8eVx2ty44roLTatsZHdKBX9knbCdOIUGC6HkUd26gHotx
|
|
||||||
nPj0sgL8pwiIf9NyQrwbFB2gnvAcI8Gk2K6auULpxqP8LyM78dndxvXZa6TiBS4mrWWDn4dO/gpEn2kV
|
|
||||||
4QVCpLpf4FPO1j4ShHG5zwyUtdLg9R6i7B30iizYrRjn+w== phpseclib-generated-key</pre><p>
|
|
||||||
Passwords can be set via <code class="code">setPassword()</code> and are only supported on private keys.
|
|
||||||
CRYPT_RSA_PUBLIC_FORMAT_OPENSSH generates keys that are intended to go in $HOME/.ssh/authorized_keys
|
|
||||||
for use with OpenSSH. Another format - CRYPT_RSA_PUBLIC_FORMAT_RAW - is stored as an array with two
|
|
||||||
indexes - one for the modulus and one for the exponent. Indexes accepted by <code class="code">loadkey()</code>
|
|
||||||
are as follows:
|
|
||||||
</p><p>
|
|
||||||
e, exponent, publicExponent, modulus, modulo, n
|
|
||||||
</p><p>
|
|
||||||
<code class="code">loadKey()</code> has two parameters - <code class="code">$key</code> and the optional <code class="code">$type</code>.
|
|
||||||
The default type, if <code class="code">$type</code> is not explicitely set, is CRYPT_RSA_PRIVATE_FORMAT_PKCS1.
|
|
||||||
It should, at this point, be noted that Crypt_RSA treats public and private keys largelly identically.
|
|
||||||
A key can be formatted as a CRYPT_RSA_PUBLIC_FORMAT_PKCS1 and still conform to the
|
|
||||||
CRYPT_RSA_PRIVATE_FORMAT_PKCS1 format and vice versa. The only real difference between private keys and
|
|
||||||
public keys is that private keys *can* contain their public key counterparts whereas public keys cannot.
|
|
||||||
That said, this distinction is, for the most part, irrelevant and academic. For a more thorough
|
|
||||||
discussion of this see <a class="link" href="misc_crypt.html#misc_crypt_rsa_getpublickey" title="4.2.5. setPublicKey() and getPublicKey()">setPublicKey() and getPublicKey()</a>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_getpublickey"></a>4.2.5. setPublicKey() and getPublicKey()</h3></div></div></div><p>
|
|
||||||
As noted in <a class="link" href="misc_crypt.html#misc_crypt_rsa_format" title="4.2.4. setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()">setPrivateKeyFormat(), setPublicKeyFormat(), loadKey() and setPassword()</a>,
|
|
||||||
Crypt_RSA treats public and private keys largely identically. The only real difference is that some
|
|
||||||
private key formats contain the public key within them whereas no public key format does. The reason
|
|
||||||
you'd want to do this is for indexing purposes. For example, in SSH-2, RSA authentication works by
|
|
||||||
sending your public key along with a signature created by your private key. The SSH-2 server then looks
|
|
||||||
the public key up in an index of public keys to see if it's an allowed key and then verifies the signature.
|
|
||||||
To that end, <code class="code">setPublicKey()</code> defines the public key if it hasn't already been defined and
|
|
||||||
<code class="code">getPublicKey()</code> returns it. <code class="code">getPublicKey()</code> has an optional parameter - $type -
|
|
||||||
that sets the format.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_encrypt"></a>4.2.6. encrypt(), decrypt() and setEncryptionMode()</h3></div></div></div><p>
|
|
||||||
Crypt_RSA supports two encryption modes - <code class="code">CRYPT_RSA_ENCRYPTION_OAEP</code> and
|
|
||||||
<code class="code">CRYPT_RSA_ENCRYPTION_PKCS1</code>. <code class="code">CRYPT_RSA_ENCRYPTION_OAEP</code> uses
|
|
||||||
<a class="ulink" href="http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding" target="_top">Optimal Asymmetric Encryption Padding</a>
|
|
||||||
and provides more security than <code class="code">CRYPT_RSA_ENCRYPTION_PKCS1</code>.
|
|
||||||
</p><p>
|
|
||||||
Both <code class="code">CRYPT_RSA_ENCRYPTION_OAEP</code> and <code class="code">CRYPT_RSA_ENCRYPTION_PKCS1</code> impose limits
|
|
||||||
on how large the plaintext can be. If the plaintext exceeds these limits the plaintext will be split
|
|
||||||
up such that each block falls within those limits.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_sign"></a>4.2.7. sign(), verify(), and setSignatureMode()</h3></div></div></div><p>
|
|
||||||
Crypt_RSA supports two signature modes - <code class="code">CRYPT_RSA_SIGNATURE_PSS</code> and
|
|
||||||
<code class="code">CRYPT_RSA_SIGNATURE_PKCS1</code>. The former is assumed to provide more security than the latter.
|
|
||||||
See <a class="link" href="misc_crypt.html#misc_crypt_rsa_examples" title="4.2.2. Examples">Examples</a> for examples.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="misc_crypt_rsa_params"></a>4.2.8. setHash(), setMGFHash() and setSaltLength()</h3></div></div></div><p>
|
|
||||||
In all likelihood, calling these functions will be unnecessary as the default values should be sufficient.
|
|
||||||
None-the-less a discussion of them follows.
|
|
||||||
</p><p>
|
|
||||||
<code class="code">setHash()</code> is used with signature production / verification and (if the encryption mode is
|
|
||||||
CRYPT_RSA_ENCRYPTION_OAEP) encryption and decryption. If the specified hash isn't supported sha1 will
|
|
||||||
be used.
|
|
||||||
</p><p>
|
|
||||||
<code class="code">setMGFHash()</code> determines which hashing function should be used for the mask generation
|
|
||||||
function as utilized in CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS. PKCS#1 recommends
|
|
||||||
but does not require that the MGFHash and the Hash be set to the same thing.
|
|
||||||
</p><p>
|
|
||||||
<code class="code">setSaltLength()</code> is only utilized with CRYPT_RSA_SIGNATURE_PSS. PKCS#1 recommends this
|
|
||||||
value either be 0 (which is what it is by default) or the length of the output of the hash function as
|
|
||||||
set via <code class="code">setHash()</code>
|
|
||||||
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="sym_crypt.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="net.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 3. Symmetric-key Cryptography </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 5. Networking</td></tr></table></div></body></html>
|
|
|
@ -1,153 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 5. Networking</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="misc_crypt.html" title="Chapter 4. Miscellaneous Cryptography" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 5. Networking</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="misc_crypt.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> </td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="net"></a>Chapter 5. Networking</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="net.html#net_ssh">5.1. Net_SSH</a></span></dt><dd><dl><dt><span class="section"><a href="net.html#net_ssh_dependencies">5.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_ssh1">5.1.2. Net_SSH1 Examples</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_ssh2">5.1.3. Net_SSH2 Examples</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_host_key_verify">5.1.4. Host Key Verification</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_interactive">5.1.5. interactiveRead() / interactiveWrite() vs. exec()</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_exec">5.1.6. SSH-1's exec() vs. SSH-2's exec()</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_successive">5.1.7. Successive calls to SSH-2's exec()</a></span></dt><dt><span class="section"><a href="net.html#net_ssh_debug">5.1.8. Debugging SSH-2</a></span></dt></dl></dd><dt><span class="section"><a href="net.html#net_sftp">5.2. Net_SFTP</a></span></dt><dd><dl><dt><span class="section"><a href="net.html#net_sftp_intro">5.2.1. Introduction</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_dependencies">5.2.2. Dependencies</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_example">5.2.3. Net_SFTP Example</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_put">5.2.4. put($remote_file, $data [, $mode])</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_get">5.2.5. get($remote_file [, $local_file])</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_pwd">5.2.6. pwd(), chdir(), mkdir() and rmdir()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_chmod">5.2.7. chmod() and size()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_nlist">5.2.8. nlist() and rawlist()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_delete">5.2.9. delete() and rename()</a></span></dt><dt><span class="section"><a href="net.html#net_sftp_debug">5.2.10. Debugging SFTP</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="net_ssh"></a>5.1. Net_SSH</h2></div></div></div><p>
|
|
||||||
The Net_SSH1 and Net_SSH2 libraries have, for the most part, an identical API. Some functions, however, do behave differently.
|
|
||||||
</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_dependencies"></a>5.1.1. Dependencies</h3></div></div></div><p>
|
|
||||||
Net_SSH1/2 require, minimally, Math/BigInteger.php, Crypt/*.php, and PHP/Compat/Function/*.php. Net_SSH1 requires PHP 4.0.0 unless you're using the interactive functions, which require PHP 4.3.0. Net_SSH2 requires PHP 4.3.0 due to it's use of <a class="ulink" href="http://php.net/function.sha1" target="_top">sha1()</a>.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_ssh1"></a>5.1.2. Net_SSH1 Examples</h3></div></div></div><pre class="programlisting"><?php
|
|
||||||
include('Net/SSH1.php');
|
|
||||||
|
|
||||||
$ssh = new Net_SSH1('www.domain.tld');
|
|
||||||
if (!$ssh->login('username', 'password')) {
|
|
||||||
exit('Login Failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
echo $ssh->interactiveRead();
|
|
||||||
|
|
||||||
$read = array(STDIN);
|
|
||||||
$write = $except = NULL;
|
|
||||||
if (stream_select($read, $write, $except, 0)) {
|
|
||||||
$ssh->interactiveWrite(fread(STDIN, 1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?></pre><pre class="programlisting"><?php
|
|
||||||
include('Net/SSH1.php');
|
|
||||||
|
|
||||||
$ssh = new Net_SSH1('www.domain.tld');
|
|
||||||
if (!$ssh->login('username', 'password')) {
|
|
||||||
exit('Login Failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $ssh->exec('ls -la');
|
|
||||||
?></pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_ssh2"></a>5.1.3. Net_SSH2 Examples</h3></div></div></div><pre class="programlisting"><?php
|
|
||||||
include('Net/SSH2.php');
|
|
||||||
|
|
||||||
$ssh = new Net_SSH2('www.domain.tld');
|
|
||||||
if (!$ssh->login('username', 'password')) {
|
|
||||||
exit('Login Failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $ssh->exec('pwd');
|
|
||||||
echo $ssh->exec('ls -la');
|
|
||||||
?></pre><pre class="programlisting"><?php
|
|
||||||
include('Crypt/RSA.php');
|
|
||||||
include('Net/SSH2.php');
|
|
||||||
|
|
||||||
$key = new Crypt_RSA();
|
|
||||||
//$key->setPassword('whatever');
|
|
||||||
$key->loadKey(file_get_contents('privatekey'));
|
|
||||||
|
|
||||||
$ssh = new Net_SSH2('www.domain.tld');
|
|
||||||
if (!$ssh->login('username', $key)) {
|
|
||||||
exit('Login Failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $ssh->exec('pwd');
|
|
||||||
echo $ssh->exec('ls -la');
|
|
||||||
?<</pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_host_key_verify"></a>5.1.4. Host Key Verification</h3></div></div></div><p>
|
|
||||||
SSH protects itself against active eavesdroppers by providing a host key. The first time you connect the host key is supposed to be cached in some manner. On subsequent connections, the host key being used for this connection should be checked against the cached host key. If they match, it's the same server. If not, it's a different one.
|
|
||||||
</p><p>
|
|
||||||
In SSH-1, <code class="code">getHostKeyPublicModulus()</code> and <code class="code">getHostKeyPublicExponent()</code> will provide you with the host key. In SSH-2, <code class="code">getServerPublicHostKey()</code> gets you the key.
|
|
||||||
</p><p>
|
|
||||||
The Net_SSH1 and Net_SSH2 examples omit the key verification stage for brevity. Also, depending on the context in which this library is used, it may even be unnecessary. For example, if you're connecting to www.example.com:22 from www.example.com:80, eavesdroppers are not something you need to worry about.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_interactive"></a>5.1.5. interactiveRead() / interactiveWrite() vs. exec()</h3></div></div></div><p>
|
|
||||||
Say you wanted to use SSH to get the contents of a directory. If you used <code class="code">interactiveWrite('ls')</code> to do this, and then <code class="code">interactiveRead()</code> to get the output, you, in all likelihood, wouldn't get the whole output. You'd have to call <code class="code">interactiveRead()</code> multiple times - you'd have to call it as many times as it took for you to get the complete output, which, in turn, begs the question... how do you know when you have the complete output?
|
|
||||||
You could assume that whenever the prompt (eg. <code class="code">root@desktop:/root#</code>) showed up, that that'd mean you had the complete output, but that's not fool proof. And what about messages of the day? The first <code class="code">interactiveRead()</code> you do is liable to include a part of that rather than the directory listing. So, not only do you have to make some sort of guestimate as to when the output ends - you often may have to guestimate as to when it begins.
|
|
||||||
</p><p>
|
|
||||||
To top it all off, you may also get <a class="ulink" href="http://en.wikipedia.org/wiki/ANSI_escape_code" target="_top">ANSI escape codes</a> interspersed amongst the output, which would need to be removed.
|
|
||||||
</p><p>
|
|
||||||
Using <code class="code">exec('ls')</code> resolves all of these issues. If you're implementing an interactive client, the interactive functions are the ones you'll want to use. Otherwise, <code class="code">exec()</code> is likely what you'll want to use.
|
|
||||||
</p><p>
|
|
||||||
interactiveRead() / interactiveWrite() are not implemented in Net_SSH2. The SSH-2 protocol supports them, however, phpseclib does not. The reasons are discussed in the <a class="link" href="net.html#net_ssh_exec" title="5.1.6. SSH-1's exec() vs. SSH-2's exec()">SSH-1's exec() vs. SSH-2's exec()</a> section.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_exec"></a>5.1.6. SSH-1's exec() vs. SSH-2's exec()</h3></div></div></div><p>
|
|
||||||
<code class="code">exec()</code> works by creating a channel, issuing a command, and then subsequently destroying that channel. Since SSH-1 <a class="ulink" href="http://www.snailbook.com/faq/ssh-1-vs-2.auto.html" target="_top">only allows one channel</a>, exec() can only be called once. SSH-2, in contrast, allows an unlimited number of channels, and as such, you can perform as many <code class="code">exec()</code>'s as you see fit.
|
|
||||||
</p><p>
|
|
||||||
As a consequence of this difference, Net_SSH2 does not implement <a class="link" href="net.html#net_ssh_interactive" title="5.1.5. interactiveRead() / interactiveWrite() vs. exec()">interactiveRead() / interactiveWrite()</a>, even though the SSH-2 specifications provide for those functions. Simply put, in SSH-1, those functions are necessary to do multiple commands. In SSH-2, they're not, and so they're not implemented.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_successive"></a>5.1.7. Successive calls to SSH-2's exec()</h3></div></div></div><p>
|
|
||||||
Successive calls to SSH-2's exec() may not work as expected. Consider the following:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Net/SSH2.php');
|
|
||||||
|
|
||||||
$ssh = new Net_SSH2('www.domain.tld');
|
|
||||||
if (!$ssh->login('username', 'password')) {
|
|
||||||
exit('Login Failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $ssh->exec('pwd');
|
|
||||||
echo $ssh->exec('cd /');
|
|
||||||
echo $ssh->exec('pwd');
|
|
||||||
?></pre><p>
|
|
||||||
If done on an interactive shell, the output you'd receive for the first <code class="code">pwd</code> would (depending on how your system is setup) be different than the output of the second <code class="code">pwd</code>. The above code snippet, however, will yield two identical lines. The reason for this is that any "state changes" you make to the one-time shell are gone once the <code class="code">exec()</code> has been ran and the channel has been deleted.
|
|
||||||
As such, if you want to support <code class="code">cd</code> in your program, it'd be best to just handle that internally and rewrite all commands, before they're passed to <code class="code">exec()</code> such that the relative paths are expanded to the absolute paths.
|
|
||||||
Alternatively, one could always run a shell script, however, that may not always be an option.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_ssh_debug"></a>5.1.8. Debugging SSH-2</h3></div></div></div><p>
|
|
||||||
To log output, the NET_SSH2_LOGGING constant will need to be defined. If you want full logs, you'll need to do <code class="code">define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX)</code>. <code class="code">$ssh->getLog()</code> will then return a string containing the unencrypted packets in hex and ASCII. If you want to just record the packet types that are being sent to and fro, you'll need to do <code class="code">define('NET_SSH2_LOGGING', NET_SSH2_LOG_SIMPLE)</code>. <code class="code">$ssh->getLog()</code> will then return an array. Both log types include the amount of time it took to send the packet in question. The former is useful for general diagnostics and the latter is more useful for profiling. An example follows:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Net/SSH2.php');
|
|
||||||
define('NET_SSH2_LOGGING', NET_SSH2_LOG_COMPLEX);
|
|
||||||
|
|
||||||
$ssh = new Net_SSH2('www.domain.tld');
|
|
||||||
if (!$ssh->login('username', 'password')) {
|
|
||||||
exit('Login Failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $ssh->exec('pwd');
|
|
||||||
echo $ssh->getLog();
|
|
||||||
?></pre><p>
|
|
||||||
Depending on the problem, it may be more effective to just look at the output of <code class="code">$ssh->getLastError()</code> (which returns a string) and <code class="code">$ssh->getErrors()</code> (which returns an array) than to sift through the logs.
|
|
||||||
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="net_sftp"></a>5.2. Net_SFTP</h2></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_intro"></a>5.2.1. Introduction</h3></div></div></div><p>
|
|
||||||
Net_SFTP currently only supports SFTPv3, which, according to wikipedia.org, "is the most widely used
|
|
||||||
version, implemented by the popular OpenSSH SFTP server".
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_dependencies"></a>5.2.2. Dependencies</h3></div></div></div><p>
|
|
||||||
Net_SFTP requires, minimally, PHP 4.3.0 and Net/SSH2.php, Math/BigInteger.php, Crypt/*.php, and PHP/Compat/Function/*.php.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_example"></a>5.2.3. Net_SFTP Example</h3></div></div></div><pre class="programlisting"><?php
|
|
||||||
include('Net/SFTP.php');
|
|
||||||
|
|
||||||
$sftp = new Net_SFTP('www.domain.tld');
|
|
||||||
if (!$sftp->login('username', 'password')) {
|
|
||||||
exit('Login Failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $sftp->pwd() . "\r\n";
|
|
||||||
$sftp->put('filename.ext', 'hello, world!');
|
|
||||||
print_r($sftp->nlist());
|
|
||||||
?></pre></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_put"></a>5.2.4. put($remote_file, $data [, $mode])</h3></div></div></div><p>
|
|
||||||
By default, put() does not read from the local filesystem. $data is dumped directly into $remote_file.
|
|
||||||
So, for example, if you set $data to 'filename.ext' and then do get(), you will get a file, twelve bytes
|
|
||||||
long, containing 'filename.ext' as its contents.
|
|
||||||
</p><p>
|
|
||||||
Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will
|
|
||||||
contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
|
|
||||||
large $remote_file will be, as well.
|
|
||||||
</p><p>
|
|
||||||
Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
|
|
||||||
care of that, yourself.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_get"></a>5.2.5. get($remote_file [, $local_file])</h3></div></div></div><p>
|
|
||||||
Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
|
|
||||||
the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
|
|
||||||
operation
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_pwd"></a>5.2.6. pwd(), chdir(), mkdir() and rmdir()</h3></div></div></div><p>
|
|
||||||
pwd() returns the current directory, chdir() changes directories, mkdir() creates direcotires, and rmdir() removes directories.
|
|
||||||
In the event of failure, they all return false. chdir(), mkdir(), and rmdir() return true on successful completion of the operation.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_chmod"></a>5.2.7. chmod() and size()</h3></div></div></div><p>
|
|
||||||
chmod() sets the permissions on a file and returns the new file permissions on success or false on error. Permissions are expected to be in octal so to set a file to 777 do <code class="code">$sftp->chmod(0777, $filename)</code>
|
|
||||||
</p><p>
|
|
||||||
size() returns the size, in bytes, of an arbitrary file.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_nlist"></a>5.2.8. nlist() and rawlist()</h3></div></div></div><p>
|
|
||||||
nlist($dir = '.') returns the contents of the current directory as a numerically indexed array and rawlist() returns an associate array where the filenames are the array keys and the array values are, themselves, arrays containing the file attributes. The directory can be changed with the first parameter.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_delete"></a>5.2.9. delete() and rename()</h3></div></div></div><p>
|
|
||||||
The purpose of both functions should be easy enough to glean - delete() deletes files or directories and rename() renames them. Both return true on success and false on failure.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="net_sftp_debug"></a>5.2.10. Debugging SFTP</h3></div></div></div><p>
|
|
||||||
Debbuging SFTP connections in phpseclib works in a manner similar to <a class="link" href="net.html#net_ssh_debug" title="5.1.8. Debugging SSH-2">debugging SSH-2</a> Instead of the constant being NET_SSH2_LOGGING, however, it's <code class="code">NET_SFTP_LOGGING</code>. And instead of NET_SSH2_LOG_COMPLEX or NET_SSH2_LOG_SIMPLE it's NET_SFTP_LOG_COMPLEX or NET_SFTP_LOG_SIMPLE respectively. And instead of calling $sftp->getLog() you call <code class="code">$sftp->getSFTPLog()</code> or <code class="code">$sftp->getLastSFTPError()</code> or whatever.
|
|
||||||
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="misc_crypt.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> </td></tr><tr><td width="40%" align="left" valign="top">Chapter 4. Miscellaneous Cryptography </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> </td></tr></table></div></body></html>
|
|
|
@ -1,118 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /><title>Chapter 3. Symmetric-key Cryptography</title><link rel="stylesheet" href="docbook.css" type="text/css" /><meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /><link rel="start" href="index.html" title="PHP Secure Communications Library" /><link rel="up" href="index.html" title="PHP Secure Communications Library" /><link rel="prev" href="math.html" title="Chapter 2. Math" /><link rel="next" href="misc_crypt.html" title="Chapter 4. Miscellaneous Cryptography" /></head><body><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Chapter 3. Symmetric-key Cryptography</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><th width="60%" align="center"> </th><td width="20%" align="right"> <a accesskey="n" href="misc_crypt.html">Next</a></td></tr></table><hr /></div><div class="chapter" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title"><a id="sym_crypt"></a>Chapter 3. Symmetric-key Cryptography</h2></div></div></div><div class="toc"><p><b>Table of Contents</b></p><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_intro">3.1. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_dependencies">3.1.1. Dependencies</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_set">3.1.2. setKey() and setIV()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_encrypt">3.1.3. encrypt() and decrypt()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_continuousbuffer">3.1.4. enableContinuousBuffer() and disableContinuousBuffer()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_padding">3.1.5. enablePadding() and disablePadding()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_caution">3.1.6. A word of caution about stream ciphers and CTR mode</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_des">3.2. Crypt_DES</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_des_constructor">3.2.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_tripledes">3.3. Crypt_TripleDES</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_tripledes_constructor">3.3.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_rc4">3.4. Crypt_RC4</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_rc4_constructor">3.4.1. The constructor</a></span></dt></dl></dd><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes">3.5. Crypt_Rijndael & Crypt_AES</a></span></dt><dd><dl><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_constructor">3.5.1. The constructor</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_vs_rijndael">3.5.2. AES vs. Rijndael</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_setkeylength">3.5.3. setKeyLength()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_setblocklength">3.5.4. setBlockLength()</a></span></dt><dt><span class="section"><a href="sym_crypt.html#sym_crypt_aes_benchmarks">3.5.5. Speed Comparisons</a></span></dt></dl></dd></dl></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_intro"></a>3.1. Introduction</h2></div></div></div><p>
|
|
||||||
All of the cryptographic libraries included in phpseclib use mcrypt, if available, and an internal implementation
|
|
||||||
if it's not. The libraries all use a common interface although some functions, for some algorithms, carry with
|
|
||||||
with them certain caveats. Those that do not have caveats attached (or have relatively few attached) are
|
|
||||||
described below. If you don't know which one to use, try <code class="code">Crypt_TripleDES</code>.
|
|
||||||
</p><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_dependencies"></a>3.1.1. Dependencies</h3></div></div></div><p>
|
|
||||||
The Crypt_* functions require, minimally, PHP 4.0.0. Crypt_TripleDES additionally requires Crypt/DES.php.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_set"></a>3.1.2. setKey() and setIV()</h3></div></div></div><p>
|
|
||||||
Sets the key and the initialization vector, respectively. If neither are set, each assumed to be equal to
|
|
||||||
some amount of null bytes. The initialization vector is only used in block ciphers and even then only
|
|
||||||
in CBC mode. If the key or the initialization vector are larger then the block size, they're truncated.
|
|
||||||
If they're smaller, they're padded with null bytes.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_encrypt"></a>3.1.3. encrypt() and decrypt()</h3></div></div></div><p>
|
|
||||||
Self-explanatory. Encrypts or decrypts messages. See the examples in the subsequent sections.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_continuousbuffer"></a>3.1.4. enableContinuousBuffer() and disableContinuousBuffer()</h3></div></div></div><p>
|
|
||||||
If the continuous buffer is enabled and you're using a stream cipher or a block cipher mode other than ECB then encrypting the same string twice will yield different ciphertexts.
|
|
||||||
The reason being that the IV doesn't reset after each encryption / decryption round when the continuous buffer is used.
|
|
||||||
This provides better security but it may also make for less intuitive behavior.
|
|
||||||
For this reason, the continuous buffer is disabled by default.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_padding"></a>3.1.5. enablePadding() and disablePadding()</h3></div></div></div><p>
|
|
||||||
Enables / disables PKCS padding on block ciphers. Stream ciphers (<code class="code">Crypt_RC4</code> is the only stream
|
|
||||||
cipher currently included) ignore this.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_caution"></a>3.1.6. A word of caution about stream ciphers and CTR mode</h3></div></div></div><p>
|
|
||||||
Most stream ciphers (and block ciphers operating in a mode - like CTR - that turns them into stream ciphers) work by generating a stream of pseudorandom characters called a <a class="ulink" href="http://en.wikipedia.org/wiki/Keystream" target="_top">keystream</a> and then XOR'ing that with the plaintext.
|
|
||||||
This *effectively* makes them <a class="ulink" href="http://en.wikipedia.org/wiki/One-time_pad" target="_top">one-time pads</a> which, in theory, can provide perfect secrecy. The problem with one-time pads is that they're not as versatile as one might desire.
|
|
||||||
Among other things, a keystream must never be reset, lest it be possible for an attacker to recover the keystream via a <a class="ulink" href="http://en.wikipedia.org/wiki/Known-plaintext_attack" target="_top">known-plaintext attack</a>. ie. <code class="code">$ciphertext ^ $plaintext = $key</code>. If <code class="code">$key</code> is constant (because the keystream's being reset or something) than an attacker can recover any <code class="code">$plaintext</code>, but if not - if it's dynamic - then the only key that an attacker could recover is their own.
|
|
||||||
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_des"></a>3.2. Crypt_DES</h2></div></div></div><p>
|
|
||||||
Implements DES (a block cipher). Here's an example of how to use it:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/DES.php');
|
|
||||||
|
|
||||||
$des = new Crypt_DES();
|
|
||||||
|
|
||||||
$des->setKey('abcdefgh');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $des->decrypt($des->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_des_constructor"></a>3.2.1. The constructor</h3></div></div></div><p>
|
|
||||||
The constructor takes one optional parameter - $mode. Valid values for $mode are as follows:
|
|
||||||
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_DES_MODE_ECB</code></li><li><code class="code">CRYPT_DES_MODE_CBC</code>: The default value.</li><li><code class="code">CRYPT_DES_MODE_CTR</code></li></ul></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_tripledes"></a>3.3. Crypt_TripleDES</h2></div></div></div><p>
|
|
||||||
Implements TripleDES (a block cipher). Here's an example of how to use it:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/TripleDES.php');
|
|
||||||
|
|
||||||
$des = new Crypt_TripleDES();
|
|
||||||
|
|
||||||
$des->setKey('abcdefghijklmnopqrstuvwx');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $des->decrypt($des->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_tripledes_constructor"></a>3.3.1. The constructor</h3></div></div></div><p>
|
|
||||||
The constructor takes one optional parameter - $mode. Valid values for $mode are as follows:
|
|
||||||
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_DES_MODE_ECB</code></li><li><code class="code">CRYPT_DES_MODE_CBC3</code>: Employs outer chaining to propogate the initialization vector. Used by SSH-2 and generally considered more secure than inner chaining.</li><li><code class="code">CRYPT_DES_MODE_3CBC</code>: Employs inner chaining to propogate the initialization vector. Used by SSH-1.</li><li><code class="code">CRYPT_DES_MODE_CBC</code>: The default value. An alias for <code class="code">CRYPT_DES_MODE_CBC3</code>.</li><li><code class="code">CRYPT_DES_MODE_CTR</code></li></ul></div></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_rc4"></a>3.4. Crypt_RC4</h2></div></div></div><p>
|
|
||||||
Implements RC4 (a stream cipher). Here's an example of how to use it:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/RC4.php');
|
|
||||||
|
|
||||||
$rc4 = new Crypt_RC4();
|
|
||||||
|
|
||||||
$rc4->setKey('abcdefghijklmnopqrstuvwx');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $rc4->decrypt($rc4->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_rc4_constructor"></a>3.4.1. The constructor</h3></div></div></div><p>
|
|
||||||
Not much to say about this constructor. Since it's a stream cipher, you don't need to worry about which
|
|
||||||
mode of operation to use.
|
|
||||||
</p></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a id="sym_crypt_aes"></a>3.5. Crypt_Rijndael & Crypt_AES</h2></div></div></div><p>
|
|
||||||
Implements Rijndael / AES. Here's an example of how to use Crypt_AES:
|
|
||||||
</p><pre class="programlisting"><?php
|
|
||||||
include('Crypt/AES.php');
|
|
||||||
|
|
||||||
$aes = new Crypt_AES();
|
|
||||||
|
|
||||||
$aes->setKey('abcdefghijklmnop');
|
|
||||||
|
|
||||||
$size = 10 * 1024;
|
|
||||||
$plaintext = '';
|
|
||||||
for ($i = 0; $i < $size; $i++) {
|
|
||||||
$plaintext.= 'a';
|
|
||||||
}
|
|
||||||
|
|
||||||
echo $aes->decrypt($aes->encrypt($plaintext));
|
|
||||||
?></pre><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_constructor"></a>3.5.1. The constructor</h3></div></div></div><p>
|
|
||||||
<code class="code">Crypt_AES</code>'s constructor's optional parameter can take the following values:
|
|
||||||
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_AES_MODE_ECB</code></li><li><code class="code">CRYPT_AES_MODE_CBC</code>: The default value.</li><li><code class="code">CRYPT_AES_MODE_CTR</code></li></ul></div><p>
|
|
||||||
<code class="code">Crypt_Rijndael</code> takes the following:
|
|
||||||
</p><div class="itemizedlist"><ul type="disc"><li><code class="code">CRYPT_RIJNDAEL_MODE_ECB</code></li><li><code class="code">CRYPT_RIJNDAEL_MODE_CBC</code>: The default value.</li><li><code class="code">CRYPT_RIJNDAEL_MODE_CTR</code></li></ul></div></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_vs_rijndael"></a>3.5.2. AES vs. Rijndael</h3></div></div></div><p>
|
|
||||||
AES is a subset of Rijndael. Both have variable key sizes, however, AES's block size is fixed at 128 bits, whereas Rijndael's is variable. Also, Rijndael supports, by means of an extension to the specification, two key sizes that AES does not - 160 bits and 224 bits.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_setkeylength"></a>3.5.3. setKeyLength()</h3></div></div></div><p>
|
|
||||||
Valid key lengths for AES are 128 bits, 192 bits, and 256 bits. If the key that is assigned is invalid and less than 256 bits, they key length is rounded up to the next closest valid size and the key will be null padded to that amount. If the key length is greater than 256 bits, it will be truncated to 256 bits.
|
|
||||||
</p><p>
|
|
||||||
As an example, if the key is 136 bits, it will be null padded to 192 bits (or 160 bits if Rijndael is being used).
|
|
||||||
</p><p>
|
|
||||||
If <code class="code">setKeyLength()</code> has been called, this behavior changes somewhat. Say you've set the key length, via this function, to 256 bits. Then, instead of an invalid key being null padded to 192 or 160 bits, it will be null padded to 256 bits.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_setblocklength"></a>3.5.4. setBlockLength()</h3></div></div></div><p>
|
|
||||||
<code class="code">setBlockLength()</code> operates in a manner similar to <code class="code">setKeyLength()</code>, with one exception. <code class="code">setBlockLength()</code> only works on Rijndael. Although <code class="code">Crypt_AES</code> inherits <code class="code">setBlockLength()</code> as a function, the function doesn't do anything in AES.
|
|
||||||
</p></div><div class="section" lang="en" xml:lang="en"><div class="titlepage"><div><div><h3 class="title"><a id="sym_crypt_aes_benchmarks"></a>3.5.5. Speed Comparisons</h3></div></div></div><p>
|
|
||||||
The following table compares the speed of five different pure-PHP implementations of AES (one of which is Crypt_Rijndael and one of which is Crypt_AES) when ran on 150KB of text on a 1.8GHz Pentium 4-M. The numbers listed are averaged from five different trials and are measured in seconds. phpseclib's two implementations are highlighted. All implementations can be viewed by clicking on their names.
|
|
||||||
</p><div class="table"><a id="sym_crypt_aes_benchmarks_table"></a><p class="title"><b>Table 3.1. AES Speed Comparisons</b></p><div class="table-contents"><table summary="AES Speed Comparisons" border="1"><colgroup><col /><col /><col /><col /><col /></colgroup><thead><tr><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/movable-type.phps" target="_top">movable-type.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpaes.phps" target="_top">phpaes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses1.phps" target="_top">phpclasses1.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpclasses2.phps" target="_top">phpclasses2.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-aes.phps" target="_top">phpseclib-aes.phps</a></th><th align="right"><a class="ulink" href="http://phpseclib.sourceforge.net/phpseclib-rijndael.phps" target="_top">phpseclib-rijndael.phps</a></th></tr></thead><tbody><tr><td align="right">15.6844158172</td><td align="right">39.9537248135</td><td align="right">15.0100150108</td><td align="right">62.591713190079</td><td class="highlight" align="right">2.03581542968752</td><td class="highlight" align="right">2.62501101493836</td></tr></tbody></table></div></div><br class="table-break" /><p>
|
|
||||||
As can be seen, phpseclib's implementations are the fastest. phpseclib-aes.phps is faster than phpseclib-rijndael.phps because phpseclib-rijndael.phps has to contend with multiple block sizes whereas phpseclib-aes.phps does not. Note that if mcrypt weren't explicitily disabled phpseclib would have been even faster.
|
|
||||||
</p></div></div></div><div class="navfooter"><hr /><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="math.html">Prev</a> </td><td width="20%" align="center"> </td><td width="40%" align="right"> <a accesskey="n" href="misc_crypt.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Chapter 2. Math </td><td width="20%" align="center"><a accesskey="h" href="index.html">Home</a></td><td width="40%" align="right" valign="top"> Chapter 4. Miscellaneous Cryptography</td></tr></table></div></body></html>
|
|
|
@ -1,12 +0,0 @@
|
||||||
B8 for Friendica
|
|
||||||
|
|
||||||
B8 is an excellent bayesian spam implementation for PHP. However when evaluating it for use in Friendica there were a few shortcomings. B8's primary audience is guestbooks and blogs - single user situations.
|
|
||||||
|
|
||||||
Friendica is a multi-user distributed social environment. So the first thing we need to add to b8 is a concept of user ID.
|
|
||||||
|
|
||||||
Second we don't want to use a second stored set of DB login credentials so we're going to implemetn Friendica's MySQL driver and use our existing connection and credentials.
|
|
||||||
|
|
||||||
The third requirement is that the B8 processing model is to load a set of word/data sets from the DB, perform processing (which may change the value of the data) and then store the results back to the DB. We're in a highly dynamic environment with lots of sometimes concurrent message processing. So the plan is to alter the storage architecture to read data in, do processing, and then apply a somewhat atomic change operation where the changes are performed in a single query using the current data in storage rather than something passed through outside processing and where the data may be outdated come time to store it.
|
|
||||||
|
|
||||||
In accordance with the LGPL of the B8 package these changes are available in source form at http://github.com/friendica/friendica in the directory library/spam
|
|
||||||
|
|
|
@ -1,503 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# b8 - A Bayesian spam filter written in PHP 5
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Tobias Leupold
|
|
||||||
* @author Oliver Lillie (aka buggedcom) (original PHP 5 port)
|
|
||||||
*/
|
|
||||||
|
|
||||||
class b8
|
|
||||||
{
|
|
||||||
|
|
||||||
public $config = array(
|
|
||||||
'min_size' => 3,
|
|
||||||
'max_size' => 30,
|
|
||||||
'allow_numbers' => FALSE,
|
|
||||||
'lexer' => 'default',
|
|
||||||
'degenerator' => 'default',
|
|
||||||
'storage' => 'dba',
|
|
||||||
'use_relevant' => 15,
|
|
||||||
'min_dev' => 0.2,
|
|
||||||
'rob_s' => 0.3,
|
|
||||||
'rob_x' => 0.5
|
|
||||||
);
|
|
||||||
|
|
||||||
private $_lexer = NULL;
|
|
||||||
private $_database = NULL;
|
|
||||||
private $_token_data = NULL;
|
|
||||||
|
|
||||||
const SPAM = 'spam';
|
|
||||||
const HAM = 'ham';
|
|
||||||
const LEARN = 'learn';
|
|
||||||
const UNLEARN = 'unlearn';
|
|
||||||
|
|
||||||
const STARTUP_FAIL_DATABASE = 'STARTUP_FAIL_DATABASE';
|
|
||||||
const STARTUP_FAIL_LEXER = 'STARTUP_FAIL_LEXER';
|
|
||||||
const TRAINER_CATEGORY_FAIL = 'TRAINER_CATEGORY_FAIL';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs b8
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __construct($config = array(), $database_config)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate config data
|
|
||||||
|
|
||||||
if(count($config) > 0) {
|
|
||||||
|
|
||||||
foreach ($config as $name=>$value) {
|
|
||||||
|
|
||||||
switch($name) {
|
|
||||||
|
|
||||||
case 'min_dev':
|
|
||||||
case 'rob_s':
|
|
||||||
case 'rob_x':
|
|
||||||
$this->config[$name] = (float) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'min_size':
|
|
||||||
case 'max_size':
|
|
||||||
case 'use_relevant':
|
|
||||||
$this->config[$name] = (int) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'allow_numbers':
|
|
||||||
$this->config[$name] = (bool) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'lexer':
|
|
||||||
$value = (string) strtolower($value);
|
|
||||||
$this->config[$name] = is_file(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lexer' . DIRECTORY_SEPARATOR . "lexer_" . $value . '.php') === TRUE ? $value : 'default';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'storage':
|
|
||||||
$this->config[$name] = (string) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Setup the database backend
|
|
||||||
|
|
||||||
# Get the basic storage class used by all backends
|
|
||||||
if($this->load_class('b8_storage_base', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'storage_base.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
# Get the degenerator we need
|
|
||||||
if($this->load_class('b8_degenerator_' . $this->config['degenerator'], dirname(__FILE__) . DIRECTORY_SEPARATOR . 'degenerator' . DIRECTORY_SEPARATOR . 'degenerator_' . $this->config['degenerator'] . '.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
# Get the actual storage backend we need
|
|
||||||
if($this->load_class('b8_storage_' . $this->config['storage'], dirname(__FILE__) . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'storage_' . $this->config['storage'] . '.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
# Setup the backend
|
|
||||||
$class = 'b8_storage_' . $this->config['storage'];
|
|
||||||
$this->_database = new $class(
|
|
||||||
$database_config,
|
|
||||||
$this->config['degenerator'], date('ymd')
|
|
||||||
);
|
|
||||||
|
|
||||||
# Setup the lexer class
|
|
||||||
|
|
||||||
if($this->load_class('b8_lexer_' . $this->config['lexer'], dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lexer' . DIRECTORY_SEPARATOR . 'lexer_' . $this->config['lexer'] . '.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$class = 'b8_lexer_' . $this->config['lexer'];
|
|
||||||
$this->_lexer = new $class(
|
|
||||||
array(
|
|
||||||
'min_size' => $this->config['min_size'],
|
|
||||||
'max_size' => $this->config['max_size'],
|
|
||||||
'allow_numbers' => $this->config['allow_numbers']
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a class file if a class has not been defined yet.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return boolean Returns TRUE if everything is okay, otherwise FALSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function load_class($class_name, $class_file)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(class_exists($class_name, FALSE) === FALSE) {
|
|
||||||
|
|
||||||
$included = require_once $class_file;
|
|
||||||
|
|
||||||
if($included === FALSE or class_exists($class_name, FALSE) === FALSE)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the class has all it needs to work.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return mixed Returns TRUE if everything is okay, otherwise an error code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function validate()
|
|
||||||
{
|
|
||||||
|
|
||||||
if($this->_database === NULL)
|
|
||||||
return self::STARTUP_FAIL_DATABASE;
|
|
||||||
|
|
||||||
# Connect the database backend if we aren't connected yet
|
|
||||||
|
|
||||||
elseif($this->_database->connected === FALSE) {
|
|
||||||
|
|
||||||
$connection = $this->_database->connect();
|
|
||||||
|
|
||||||
if($connection !== TRUE)
|
|
||||||
return $connection;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->_lexer === NULL)
|
|
||||||
return self::STARTUP_FAIL_LEXER;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classifies a text
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @package default
|
|
||||||
* @param string $text
|
|
||||||
* @return float The rating between 0 (ham) and 1 (spam)
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function classify($uid,$text)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# Get the internal database variables, containing the number of ham and
|
|
||||||
# spam texts so the spam probability can be calculated in relation to them
|
|
||||||
$internals = $this->_database->get_internals($uid);
|
|
||||||
|
|
||||||
# Calculate the spamminess of all tokens
|
|
||||||
|
|
||||||
# Get all tokens we want to rate
|
|
||||||
|
|
||||||
$tokens = $this->_lexer->get_tokens($text);
|
|
||||||
|
|
||||||
# Check if the lexer failed
|
|
||||||
# (if so, $tokens will be a lexer error code, if not, $tokens will be an array)
|
|
||||||
if(!is_array($tokens))
|
|
||||||
return $tokens;
|
|
||||||
|
|
||||||
# Fetch all availible data for the token set from the database
|
|
||||||
$this->_token_data = $this->_database->get(array_keys($tokens),$uid);
|
|
||||||
|
|
||||||
# Calculate the spamminess and importance for each token (or a degenerated form of it)
|
|
||||||
|
|
||||||
$word_count = array();
|
|
||||||
$rating = array();
|
|
||||||
$importance = array();
|
|
||||||
|
|
||||||
foreach($tokens as $word => $count) {
|
|
||||||
|
|
||||||
$word_count[$word] = $count;
|
|
||||||
|
|
||||||
# Although we only call this function only here ... let's do the
|
|
||||||
# calculation stuff in a function to make this a bit less confusing ;-)
|
|
||||||
$rating[$word] = $this->_get_probability($word, $internals['texts_ham'], $internals['texts_spam']);
|
|
||||||
|
|
||||||
$importance[$word] = abs(0.5 - $rating[$word]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Order by importance
|
|
||||||
arsort($importance);
|
|
||||||
reset($importance);
|
|
||||||
|
|
||||||
# Get the most interesting tokens (use all if we have less than the given number)
|
|
||||||
|
|
||||||
$relevant = array();
|
|
||||||
|
|
||||||
for($i = 0; $i < $this->config['use_relevant']; $i++) {
|
|
||||||
|
|
||||||
if($tmp = each($importance)) {
|
|
||||||
|
|
||||||
# Important tokens remain
|
|
||||||
|
|
||||||
# If the token's rating is relevant enough, use it
|
|
||||||
|
|
||||||
if(abs(0.5 - $rating[$tmp['key']]) > $this->config['min_dev']) {
|
|
||||||
|
|
||||||
# Tokens that appear more than once also count more than once
|
|
||||||
|
|
||||||
for($x = 0, $l = $word_count[$tmp['key']]; $x < $l; $x++)
|
|
||||||
array_push($relevant, $rating[$tmp['key']]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
# We have less than words to use, so we already
|
|
||||||
# use what we have and can break here
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Calculate the spamminess of the text (thanks to Mr. Robinson ;-)
|
|
||||||
# We set both hamminess and Spamminess to 1 for the first multiplying
|
|
||||||
$hamminess = 1;
|
|
||||||
$spamminess = 1;
|
|
||||||
|
|
||||||
# Consider all relevant ratings
|
|
||||||
foreach($relevant as $value) {
|
|
||||||
$hamminess *= (1.0 - $value);
|
|
||||||
$spamminess *= $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
# If no token was good for calculation, we really don't know how
|
|
||||||
# to rate this text; so we assume a spam and ham probability of 0.5
|
|
||||||
|
|
||||||
if($hamminess === 1 and $spamminess === 1) {
|
|
||||||
$hamminess = 0.5;
|
|
||||||
$spamminess = 0.5;
|
|
||||||
$n = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Get the number of relevant ratings
|
|
||||||
$n = count($relevant);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Calculate the combined rating
|
|
||||||
|
|
||||||
# The actual hamminess and spamminess
|
|
||||||
$hamminess = 1 - pow($hamminess, (1 / $n));
|
|
||||||
$spamminess = 1 - pow($spamminess, (1 / $n));
|
|
||||||
|
|
||||||
# Calculate the combined indicator
|
|
||||||
$probability = ($hamminess - $spamminess) / ($hamminess + $spamminess);
|
|
||||||
|
|
||||||
# We want a value between 0 and 1, not between -1 and +1, so ...
|
|
||||||
$probability = (1 + $probability) / 2;
|
|
||||||
|
|
||||||
# Alea iacta est
|
|
||||||
return $probability;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the spamminess of a single token also considering "degenerated" versions
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $word
|
|
||||||
* @param string $texts_ham
|
|
||||||
* @param string $texts_spam
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _get_probability($word, $texts_ham, $texts_spam)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Let's see what we have!
|
|
||||||
|
|
||||||
if(isset($this->_token_data['tokens'][$word]) === TRUE) {
|
|
||||||
# The token was in the database, so we can use it's data as-is
|
|
||||||
# and calculate the spamminess of this token directly
|
|
||||||
return $this->_calc_probability($this->_token_data['tokens'][$word], $texts_ham, $texts_spam);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Damn. The token was not found, so do we have at least similar words?
|
|
||||||
|
|
||||||
if(isset($this->_token_data['degenerates'][$word]) === TRUE) {
|
|
||||||
|
|
||||||
# We found similar words, so calculate the spamminess for each one
|
|
||||||
# and choose the most important one for the further calculation
|
|
||||||
|
|
||||||
# The default rating is 0.5 simply saying nothing
|
|
||||||
$rating = 0.5;
|
|
||||||
|
|
||||||
foreach($this->_token_data['degenerates'][$word] as $degenerate => $count) {
|
|
||||||
|
|
||||||
# Calculate the rating of the current degenerated token
|
|
||||||
$rating_tmp = $this->_calc_probability($count, $texts_ham, $texts_spam);
|
|
||||||
|
|
||||||
# Is it more important than the rating of another degenerated version?
|
|
||||||
if(abs(0.5 - $rating_tmp) > abs(0.5 - $rating))
|
|
||||||
$rating = $rating_tmp;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rating;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
# The token is really unknown, so choose the default rating
|
|
||||||
# for completely unknown tokens. This strips down to the
|
|
||||||
# robX parameter so we can cheap out the freaky math ;-)
|
|
||||||
return $this->config['rob_x'];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do the actual spamminess calculation of a single token
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param array $data
|
|
||||||
* @param string $texts_ham
|
|
||||||
* @param string $texts_spam
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _calc_probability($data, $texts_ham, $texts_spam)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Calculate the basic probability by Mr. Graham
|
|
||||||
|
|
||||||
# But: consider the number of ham and spam texts saved instead of the
|
|
||||||
# number of entries where the token appeared to calculate a relative
|
|
||||||
# spamminess because we count tokens appearing multiple times not just
|
|
||||||
# once but as often as they appear in the learned texts
|
|
||||||
|
|
||||||
$rel_ham = $data['count_ham'];
|
|
||||||
$rel_spam = $data['count_spam'];
|
|
||||||
|
|
||||||
if($texts_ham > 0)
|
|
||||||
$rel_ham = $data['count_ham'] / $texts_ham;
|
|
||||||
|
|
||||||
if($texts_spam > 0)
|
|
||||||
$rel_spam = $data['count_spam'] / $texts_spam;
|
|
||||||
|
|
||||||
$rating = $rel_spam / ($rel_ham + $rel_spam);
|
|
||||||
|
|
||||||
# Calculate the better probability proposed by Mr. Robinson
|
|
||||||
$all = $data['count_ham'] + $data['count_spam'];
|
|
||||||
return (($this->config['rob_s'] * $this->config['rob_x']) + ($all * $rating)) / ($this->config['rob_s'] + $all);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the validity of the category of a request
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $category
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _check_category($category)
|
|
||||||
{
|
|
||||||
return $category === self::HAM or $category === self::SPAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Learn a reference text
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $text
|
|
||||||
* @param const $category Either b8::SPAM or b8::HAM
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function learn($text, $category, $uid)
|
|
||||||
{
|
|
||||||
return $this->_process_text($text, $category, self::LEARN, $uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlearn a reference text
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $text
|
|
||||||
* @param const $category Either b8::SPAM or b8::HAM
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function unlearn($text, $category, $uid)
|
|
||||||
{
|
|
||||||
return $this->_process_text($text, $category, self::UNLEARN, $uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the actual interaction with the storage backend for learning or unlearning texts
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $text
|
|
||||||
* @param const $category Either b8::SPAM or b8::HAM
|
|
||||||
* @param const $action Either b8::LEARN or b8::UNLEARN
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _process_text($text, $category, $action, $uid = 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# Look if the request is okay
|
|
||||||
if($this->_check_category($category) === FALSE)
|
|
||||||
return self::TRAINER_CATEGORY_FAIL;
|
|
||||||
|
|
||||||
# Get all tokens from $text
|
|
||||||
|
|
||||||
$tokens = $this->_lexer->get_tokens($text);
|
|
||||||
|
|
||||||
# Check if the lexer failed
|
|
||||||
# (if so, $tokens will be a lexer error code, if not, $tokens will be an array)
|
|
||||||
if(!is_array($tokens))
|
|
||||||
return $tokens;
|
|
||||||
|
|
||||||
# Pass the tokens and what to do with it to the storage backend
|
|
||||||
return $this->_database->process_text($tokens, $category, $action, $uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,503 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# b8 - A Bayesian spam filter written in PHP 5
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Tobias Leupold
|
|
||||||
* @author Oliver Lillie (aka buggedcom) (original PHP 5 port)
|
|
||||||
*/
|
|
||||||
|
|
||||||
class b8
|
|
||||||
{
|
|
||||||
|
|
||||||
public $config = array(
|
|
||||||
'min_size' => 3,
|
|
||||||
'max_size' => 30,
|
|
||||||
'allow_numbers' => FALSE,
|
|
||||||
'lexer' => 'default',
|
|
||||||
'degenerator' => 'default',
|
|
||||||
'storage' => 'dba',
|
|
||||||
'use_relevant' => 15,
|
|
||||||
'min_dev' => 0.2,
|
|
||||||
'rob_s' => 0.3,
|
|
||||||
'rob_x' => 0.5
|
|
||||||
);
|
|
||||||
|
|
||||||
private $_lexer = NULL;
|
|
||||||
private $_database = NULL;
|
|
||||||
private $_token_data = NULL;
|
|
||||||
|
|
||||||
const SPAM = 'spam';
|
|
||||||
const HAM = 'ham';
|
|
||||||
const LEARN = 'learn';
|
|
||||||
const UNLEARN = 'unlearn';
|
|
||||||
|
|
||||||
const STARTUP_FAIL_DATABASE = 'STARTUP_FAIL_DATABASE';
|
|
||||||
const STARTUP_FAIL_LEXER = 'STARTUP_FAIL_LEXER';
|
|
||||||
const TRAINER_CATEGORY_FAIL = 'TRAINER_CATEGORY_FAIL';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs b8
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __construct($config = array(), $database_config)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate config data
|
|
||||||
|
|
||||||
if(count($config) > 0) {
|
|
||||||
|
|
||||||
foreach ($config as $name=>$value) {
|
|
||||||
|
|
||||||
switch($name) {
|
|
||||||
|
|
||||||
case 'min_dev':
|
|
||||||
case 'rob_s':
|
|
||||||
case 'rob_x':
|
|
||||||
$this->config[$name] = (float) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'min_size':
|
|
||||||
case 'max_size':
|
|
||||||
case 'use_relevant':
|
|
||||||
$this->config[$name] = (int) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'allow_numbers':
|
|
||||||
$this->config[$name] = (bool) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'lexer':
|
|
||||||
$value = (string) strtolower($value);
|
|
||||||
$this->config[$name] = is_file(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lexer' . DIRECTORY_SEPARATOR . "lexer_" . $value . '.php') === TRUE ? $value : 'default';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'storage':
|
|
||||||
$this->config[$name] = (string) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Setup the database backend
|
|
||||||
|
|
||||||
# Get the basic storage class used by all backends
|
|
||||||
if($this->load_class('b8_storage_base', dirname(__FILE__) . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'storage_base.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
# Get the degenerator we need
|
|
||||||
if($this->load_class('b8_degenerator_' . $this->config['degenerator'], dirname(__FILE__) . DIRECTORY_SEPARATOR . 'degenerator' . DIRECTORY_SEPARATOR . 'degenerator_' . $this->config['degenerator'] . '.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
# Get the actual storage backend we need
|
|
||||||
if($this->load_class('b8_storage_' . $this->config['storage'], dirname(__FILE__) . DIRECTORY_SEPARATOR . 'storage' . DIRECTORY_SEPARATOR . 'storage_' . $this->config['storage'] . '.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
# Setup the backend
|
|
||||||
$class = 'b8_storage_' . $this->config['storage'];
|
|
||||||
$this->_database = new $class(
|
|
||||||
$database_config,
|
|
||||||
$this->config['degenerator'], date('ymd')
|
|
||||||
);
|
|
||||||
|
|
||||||
# Setup the lexer class
|
|
||||||
|
|
||||||
if($this->load_class('b8_lexer_' . $this->config['lexer'], dirname(__FILE__) . DIRECTORY_SEPARATOR . 'lexer' . DIRECTORY_SEPARATOR . 'lexer_' . $this->config['lexer'] . '.php') === FALSE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
$class = 'b8_lexer_' . $this->config['lexer'];
|
|
||||||
$this->_lexer = new $class(
|
|
||||||
array(
|
|
||||||
'min_size' => $this->config['min_size'],
|
|
||||||
'max_size' => $this->config['max_size'],
|
|
||||||
'allow_numbers' => $this->config['allow_numbers']
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load a class file if a class has not been defined yet.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return boolean Returns TRUE if everything is okay, otherwise FALSE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function load_class($class_name, $class_file)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(class_exists($class_name, FALSE) === FALSE) {
|
|
||||||
|
|
||||||
$included = require_once $class_file;
|
|
||||||
|
|
||||||
if($included === FALSE or class_exists($class_name, FALSE) === FALSE)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the class has all it needs to work.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return mixed Returns TRUE if everything is okay, otherwise an error code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function validate()
|
|
||||||
{
|
|
||||||
|
|
||||||
if($this->_database === NULL)
|
|
||||||
return self::STARTUP_FAIL_DATABASE;
|
|
||||||
|
|
||||||
# Connect the database backend if we aren't connected yet
|
|
||||||
|
|
||||||
elseif($this->_database->connected === FALSE) {
|
|
||||||
|
|
||||||
$connection = $this->_database->connect();
|
|
||||||
|
|
||||||
if($connection !== TRUE)
|
|
||||||
return $connection;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if($this->_lexer === NULL)
|
|
||||||
return self::STARTUP_FAIL_LEXER;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Classifies a text
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @package default
|
|
||||||
* @param string $text
|
|
||||||
* @return float The rating between 0 (ham) and 1 (spam)
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function classify($text)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# Get the internal database variables, containing the number of ham and
|
|
||||||
# spam texts so the spam probability can be calculated in relation to them
|
|
||||||
$internals = $this->_database->get_internals();
|
|
||||||
|
|
||||||
# Calculate the spamminess of all tokens
|
|
||||||
|
|
||||||
# Get all tokens we want to rate
|
|
||||||
|
|
||||||
$tokens = $this->_lexer->get_tokens($text);
|
|
||||||
|
|
||||||
# Check if the lexer failed
|
|
||||||
# (if so, $tokens will be a lexer error code, if not, $tokens will be an array)
|
|
||||||
if(!is_array($tokens))
|
|
||||||
return $tokens;
|
|
||||||
|
|
||||||
# Fetch all availible data for the token set from the database
|
|
||||||
$this->_token_data = $this->_database->get(array_keys($tokens));
|
|
||||||
|
|
||||||
# Calculate the spamminess and importance for each token (or a degenerated form of it)
|
|
||||||
|
|
||||||
$word_count = array();
|
|
||||||
$rating = array();
|
|
||||||
$importance = array();
|
|
||||||
|
|
||||||
foreach($tokens as $word => $count) {
|
|
||||||
|
|
||||||
$word_count[$word] = $count;
|
|
||||||
|
|
||||||
# Although we only call this function only here ... let's do the
|
|
||||||
# calculation stuff in a function to make this a bit less confusing ;-)
|
|
||||||
$rating[$word] = $this->_get_probability($word, $internals['texts_ham'], $internals['texts_spam']);
|
|
||||||
|
|
||||||
$importance[$word] = abs(0.5 - $rating[$word]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Order by importance
|
|
||||||
arsort($importance);
|
|
||||||
reset($importance);
|
|
||||||
|
|
||||||
# Get the most interesting tokens (use all if we have less than the given number)
|
|
||||||
|
|
||||||
$relevant = array();
|
|
||||||
|
|
||||||
for($i = 0; $i < $this->config['use_relevant']; $i++) {
|
|
||||||
|
|
||||||
if($tmp = each($importance)) {
|
|
||||||
|
|
||||||
# Important tokens remain
|
|
||||||
|
|
||||||
# If the token's rating is relevant enough, use it
|
|
||||||
|
|
||||||
if(abs(0.5 - $rating[$tmp['key']]) > $this->config['min_dev']) {
|
|
||||||
|
|
||||||
# Tokens that appear more than once also count more than once
|
|
||||||
|
|
||||||
for($x = 0, $l = $word_count[$tmp['key']]; $x < $l; $x++)
|
|
||||||
array_push($relevant, $rating[$tmp['key']]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
# We have less than words to use, so we already
|
|
||||||
# use what we have and can break here
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Calculate the spamminess of the text (thanks to Mr. Robinson ;-)
|
|
||||||
# We set both hamminess and Spamminess to 1 for the first multiplying
|
|
||||||
$hamminess = 1;
|
|
||||||
$spamminess = 1;
|
|
||||||
|
|
||||||
# Consider all relevant ratings
|
|
||||||
foreach($relevant as $value) {
|
|
||||||
$hamminess *= (1.0 - $value);
|
|
||||||
$spamminess *= $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
# If no token was good for calculation, we really don't know how
|
|
||||||
# to rate this text; so we assume a spam and ham probability of 0.5
|
|
||||||
|
|
||||||
if($hamminess === 1 and $spamminess === 1) {
|
|
||||||
$hamminess = 0.5;
|
|
||||||
$spamminess = 0.5;
|
|
||||||
$n = 1;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
# Get the number of relevant ratings
|
|
||||||
$n = count($relevant);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Calculate the combined rating
|
|
||||||
|
|
||||||
# The actual hamminess and spamminess
|
|
||||||
$hamminess = 1 - pow($hamminess, (1 / $n));
|
|
||||||
$spamminess = 1 - pow($spamminess, (1 / $n));
|
|
||||||
|
|
||||||
# Calculate the combined indicator
|
|
||||||
$probability = ($hamminess - $spamminess) / ($hamminess + $spamminess);
|
|
||||||
|
|
||||||
# We want a value between 0 and 1, not between -1 and +1, so ...
|
|
||||||
$probability = (1 + $probability) / 2;
|
|
||||||
|
|
||||||
# Alea iacta est
|
|
||||||
return $probability;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculate the spamminess of a single token also considering "degenerated" versions
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $word
|
|
||||||
* @param string $texts_ham
|
|
||||||
* @param string $texts_spam
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _get_probability($word, $texts_ham, $texts_spam)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Let's see what we have!
|
|
||||||
|
|
||||||
if(isset($this->_token_data['tokens'][$word]) === TRUE) {
|
|
||||||
# The token was in the database, so we can use it's data as-is
|
|
||||||
# and calculate the spamminess of this token directly
|
|
||||||
return $this->_calc_probability($this->_token_data['tokens'][$word], $texts_ham, $texts_spam);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Damn. The token was not found, so do we have at least similar words?
|
|
||||||
|
|
||||||
if(isset($this->_token_data['degenerates'][$word]) === TRUE) {
|
|
||||||
|
|
||||||
# We found similar words, so calculate the spamminess for each one
|
|
||||||
# and choose the most important one for the further calculation
|
|
||||||
|
|
||||||
# The default rating is 0.5 simply saying nothing
|
|
||||||
$rating = 0.5;
|
|
||||||
|
|
||||||
foreach($this->_token_data['degenerates'][$word] as $degenerate => $count) {
|
|
||||||
|
|
||||||
# Calculate the rating of the current degenerated token
|
|
||||||
$rating_tmp = $this->_calc_probability($count, $texts_ham, $texts_spam);
|
|
||||||
|
|
||||||
# Is it more important than the rating of another degenerated version?
|
|
||||||
if(abs(0.5 - $rating_tmp) > abs(0.5 - $rating))
|
|
||||||
$rating = $rating_tmp;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $rating;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
# The token is really unknown, so choose the default rating
|
|
||||||
# for completely unknown tokens. This strips down to the
|
|
||||||
# robX parameter so we can cheap out the freaky math ;-)
|
|
||||||
return $this->config['rob_x'];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do the actual spamminess calculation of a single token
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param array $data
|
|
||||||
* @param string $texts_ham
|
|
||||||
* @param string $texts_spam
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _calc_probability($data, $texts_ham, $texts_spam)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Calculate the basic probability by Mr. Graham
|
|
||||||
|
|
||||||
# But: consider the number of ham and spam texts saved instead of the
|
|
||||||
# number of entries where the token appeared to calculate a relative
|
|
||||||
# spamminess because we count tokens appearing multiple times not just
|
|
||||||
# once but as often as they appear in the learned texts
|
|
||||||
|
|
||||||
$rel_ham = $data['count_ham'];
|
|
||||||
$rel_spam = $data['count_spam'];
|
|
||||||
|
|
||||||
if($texts_ham > 0)
|
|
||||||
$rel_ham = $data['count_ham'] / $texts_ham;
|
|
||||||
|
|
||||||
if($texts_spam > 0)
|
|
||||||
$rel_spam = $data['count_spam'] / $texts_spam;
|
|
||||||
|
|
||||||
$rating = $rel_spam / ($rel_ham + $rel_spam);
|
|
||||||
|
|
||||||
# Calculate the better probability proposed by Mr. Robinson
|
|
||||||
$all = $data['count_ham'] + $data['count_spam'];
|
|
||||||
return (($this->config['rob_s'] * $this->config['rob_x']) + ($all * $rating)) / ($this->config['rob_s'] + $all);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check the validity of the category of a request
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $category
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _check_category($category)
|
|
||||||
{
|
|
||||||
return $category === self::HAM or $category === self::SPAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Learn a reference text
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $text
|
|
||||||
* @param const $category Either b8::SPAM or b8::HAM
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function learn($text, $category)
|
|
||||||
{
|
|
||||||
return $this->_process_text($text, $category, self::LEARN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unlearn a reference text
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $text
|
|
||||||
* @param const $category Either b8::SPAM or b8::HAM
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function unlearn($text, $category)
|
|
||||||
{
|
|
||||||
return $this->_process_text($text, $category, self::UNLEARN);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the actual interaction with the storage backend for learning or unlearning texts
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $text
|
|
||||||
* @param const $category Either b8::SPAM or b8::HAM
|
|
||||||
* @param const $action Either b8::LEARN or b8::UNLEARN
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _process_text($text, $category, $action)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# Look if the request is okay
|
|
||||||
if($this->_check_category($category) === FALSE)
|
|
||||||
return self::TRAINER_CATEGORY_FAIL;
|
|
||||||
|
|
||||||
# Get all tokens from $text
|
|
||||||
|
|
||||||
$tokens = $this->_lexer->get_tokens($text);
|
|
||||||
|
|
||||||
# Check if the lexer failed
|
|
||||||
# (if so, $tokens will be a lexer error code, if not, $tokens will be an array)
|
|
||||||
if(!is_array($tokens))
|
|
||||||
return $tokens;
|
|
||||||
|
|
||||||
# Pass the tokens and what to do with it to the storage backend
|
|
||||||
return $this->_database->process_text($tokens, $category, $action);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,127 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Tobias Leupold
|
|
||||||
*/
|
|
||||||
|
|
||||||
class b8_degenerator_default
|
|
||||||
{
|
|
||||||
|
|
||||||
public $degenerates = array();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a list of "degenerated" words for a list of words.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param array $tokens
|
|
||||||
* @return array An array containing an array of degenerated tokens for each token
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function degenerate(array $words)
|
|
||||||
{
|
|
||||||
|
|
||||||
$degenerates = array();
|
|
||||||
|
|
||||||
foreach($words as $word)
|
|
||||||
$degenerates[$word] = $this->_degenerate_word($word);
|
|
||||||
|
|
||||||
return $degenerates;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the original word is not found in the database then
|
|
||||||
* we build "degenerated" versions of the word to lookup.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $word
|
|
||||||
* @return array An array of degenerated words
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _degenerate_word($word)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Check for any stored words so the process doesn't have to repeat
|
|
||||||
if(isset($this->degenerates[$word]) === TRUE)
|
|
||||||
return $this->degenerates[$word];
|
|
||||||
|
|
||||||
$degenerate = array();
|
|
||||||
|
|
||||||
# Add different version of upper and lower case and ucfirst
|
|
||||||
array_push($degenerate, strtolower($word));
|
|
||||||
array_push($degenerate, strtoupper($word));
|
|
||||||
array_push($degenerate, ucfirst($word));
|
|
||||||
|
|
||||||
# Degenerate all versions
|
|
||||||
|
|
||||||
foreach($degenerate as $alt_word) {
|
|
||||||
|
|
||||||
# Look for stuff like !!! and ???
|
|
||||||
|
|
||||||
if(preg_match('/[!?]$/', $alt_word) > 0) {
|
|
||||||
|
|
||||||
# Add versions with different !s and ?s
|
|
||||||
|
|
||||||
if(preg_match('/[!?]{2,}$/', $alt_word) > 0) {
|
|
||||||
$tmp = preg_replace('/([!?])+$/', '$1', $alt_word);
|
|
||||||
array_push($degenerate, $tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmp = preg_replace('/([!?])+$/', '', $alt_word);
|
|
||||||
array_push($degenerate, $tmp);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Look for ... at the end of the word
|
|
||||||
|
|
||||||
$alt_word_int = $alt_word;
|
|
||||||
|
|
||||||
while(preg_match('/[\.]$/', $alt_word_int) > 0) {
|
|
||||||
$alt_word_int = substr($alt_word_int, 0, strlen($alt_word_int) - 1);
|
|
||||||
array_push($degenerate, $alt_word_int);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Some degenerates are the same as the original word. These don't have
|
|
||||||
# to be fetched, so we create a new array with only new tokens
|
|
||||||
|
|
||||||
$real_degenerate = array();
|
|
||||||
|
|
||||||
foreach($degenerate as $deg_word) {
|
|
||||||
if($word != $deg_word)
|
|
||||||
array_push($real_degenerate, $deg_word);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Store the list of degenerates for the token
|
|
||||||
$this->degenerates[$word] = $real_degenerate;
|
|
||||||
|
|
||||||
return $real_degenerate;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,205 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Tobias Leupold
|
|
||||||
* @author Oliver Lillie (aka buggedcom) (original PHP 5 port)
|
|
||||||
*/
|
|
||||||
|
|
||||||
class b8_lexer_default
|
|
||||||
{
|
|
||||||
|
|
||||||
const LEXER_TEXT_NOT_STRING = 'LEXER_TEXT_NOT_STRING';
|
|
||||||
const LEXER_TEXT_EMPTY = 'LEXER_TEXT_EMPTY';
|
|
||||||
|
|
||||||
public $config = NULL;
|
|
||||||
|
|
||||||
# The regular expressions we use to split the text to tokens
|
|
||||||
|
|
||||||
public $regexp = array(
|
|
||||||
'ip' => '/([A-Za-z0-9\_\-\.]+)/',
|
|
||||||
'raw_split' => '/[\s,\.\/"\:;\|<>\-_\[\]{}\+=\)\(\*\&\^%]+/',
|
|
||||||
'html' => '/(<.+?>)/',
|
|
||||||
'tagname' => '/(.+?)\s/',
|
|
||||||
'numbers' => '/^[0-9]+$/'
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the lexer.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __construct($config)
|
|
||||||
{
|
|
||||||
$this->config = $config;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates the tokens required for the bayesian filter.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $text
|
|
||||||
* @return array Returns the list of tokens
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function get_tokens($text)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Check that we actually have a string ...
|
|
||||||
if(is_string($text) === FALSE)
|
|
||||||
return self::LEXER_TEXT_NOT_STRING;
|
|
||||||
|
|
||||||
# ... and that it's not empty
|
|
||||||
if(empty($text) === TRUE)
|
|
||||||
return self::LEXER_TEXT_EMPTY;
|
|
||||||
|
|
||||||
# Re-convert the text to the original characters coded in UTF-8, as
|
|
||||||
# they have been coded in html entities during the post process
|
|
||||||
$text = html_entity_decode($text, ENT_QUOTES, 'UTF-8');
|
|
||||||
|
|
||||||
$tokens = array();
|
|
||||||
|
|
||||||
# Find URLs and IP addresses
|
|
||||||
|
|
||||||
preg_match_all($this->regexp['ip'], $text, $raw_tokens);
|
|
||||||
|
|
||||||
foreach($raw_tokens[1] as $word) {
|
|
||||||
|
|
||||||
# Check for a dot
|
|
||||||
if(strpos($word, '.') === FALSE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
# Check that the word is valid, min and max sizes, etc.
|
|
||||||
if($this->_is_valid($word) === FALSE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(isset($tokens[$word]) === FALSE)
|
|
||||||
$tokens[$word] = 1;
|
|
||||||
else
|
|
||||||
$tokens[$word] += 1;
|
|
||||||
|
|
||||||
# Delete the word from the text so it doesn't get re-added.
|
|
||||||
$text = str_replace($word, '', $text);
|
|
||||||
|
|
||||||
# Also process the parts of the URLs
|
|
||||||
$url_parts = preg_split($this->regexp['raw_split'], $word);
|
|
||||||
|
|
||||||
foreach($url_parts as $word) {
|
|
||||||
|
|
||||||
# Again validate the part
|
|
||||||
|
|
||||||
if($this->_is_valid($word) === FALSE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(isset($tokens[$word]) === FALSE)
|
|
||||||
$tokens[$word] = 1;
|
|
||||||
else
|
|
||||||
$tokens[$word] += 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Split the remaining text
|
|
||||||
|
|
||||||
$raw_tokens = preg_split($this->regexp['raw_split'], $text);
|
|
||||||
|
|
||||||
foreach($raw_tokens as $word) {
|
|
||||||
|
|
||||||
# Again validate the part
|
|
||||||
|
|
||||||
if($this->_is_valid($word) === FALSE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if(isset($tokens[$word]) === FALSE)
|
|
||||||
$tokens[$word] = 1;
|
|
||||||
else
|
|
||||||
$tokens[$word] += 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Process the HTML
|
|
||||||
|
|
||||||
preg_match_all($this->regexp['html'], $text, $raw_tokens);
|
|
||||||
|
|
||||||
foreach($raw_tokens[1] as $word) {
|
|
||||||
|
|
||||||
# Again validate the part
|
|
||||||
|
|
||||||
if($this->_is_valid($word) === FALSE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
# If the tag has parameters, just use the tag itself
|
|
||||||
|
|
||||||
if(strpos($word, ' ') !== FALSE) {
|
|
||||||
preg_match($this->regexp['tagname'], $word, $tmp);
|
|
||||||
$word = "{$tmp[1]}...>";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isset($tokens[$word]) === FALSE)
|
|
||||||
$tokens[$word] = 1;
|
|
||||||
else
|
|
||||||
$tokens[$word] += 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Return a list of all found tokens
|
|
||||||
return $tokens;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates a token.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $token The token string.
|
|
||||||
* @return boolean Returns TRUE if the token is valid, otherwise returns FALSE
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _is_valid($token)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the size of the token
|
|
||||||
|
|
||||||
$len = strlen($token);
|
|
||||||
|
|
||||||
if($len < $this->config['min_size'] or $len > $this->config['max_size'])
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
# We may want to exclude pure numbers
|
|
||||||
if($this->config['allow_numbers'] === FALSE) {
|
|
||||||
if(preg_match($this->regexp['numbers'], $token) > 0)
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Token is okay
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,396 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functions used by all storage backends
|
|
||||||
* Copyright (C) 2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Tobias Leupold
|
|
||||||
*/
|
|
||||||
|
|
||||||
abstract class b8_storage_base
|
|
||||||
{
|
|
||||||
|
|
||||||
public $connected = FALSE;
|
|
||||||
|
|
||||||
protected $_degenerator = NULL;
|
|
||||||
|
|
||||||
const INTERNALS_TEXTS_HAM = 'bayes*texts.ham';
|
|
||||||
const INTERNALS_TEXTS_SPAM = 'bayes*texts.spam';
|
|
||||||
const INTERNALS_DBVERSION = 'bayes*dbversion';
|
|
||||||
|
|
||||||
const BACKEND_NOT_CONNECTED = 'BACKEND_NOT_CONNECTED';
|
|
||||||
const DATABASE_WRONG_VERSION = 'DATABASE_WRONG_VERSION';
|
|
||||||
const DATABASE_NOT_B8 = 'DATABASE_NOT_B8';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the class has all it needs to work.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @return mixed Returns TRUE if everything is okay, otherwise an error code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function validate()
|
|
||||||
{
|
|
||||||
|
|
||||||
# We set up the degenerator here, as we would have to duplicate code if it
|
|
||||||
# was done in the constructor of the respective storage backend.
|
|
||||||
$class = 'b8_degenerator_' . $this->b8_config['degenerator'];
|
|
||||||
$this->_degenerator = new $class();
|
|
||||||
|
|
||||||
if($this->connected !== TRUE)
|
|
||||||
return self::BACKEND_NOT_CONNECTED;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a b8 database is used and if it's version is okay
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @return mixed Returns TRUE if everything is okay, otherwise an error code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function check_database($uid)
|
|
||||||
{
|
|
||||||
|
|
||||||
$internals = $this->get_internals($uid);
|
|
||||||
|
|
||||||
if(isset($internals['dbversion'])) {
|
|
||||||
if($internals['dbversion'] == "2") {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_WRONG_VERSION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_NOT_B8;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the "count" data of a token.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $data
|
|
||||||
* @return array Returns an array of the parsed data: array(count_ham, count_spam, lastseen).
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _parse_count($data)
|
|
||||||
{
|
|
||||||
|
|
||||||
list($count_ham, $count_spam, $lastseen) = explode(' ', $data);
|
|
||||||
|
|
||||||
$count_ham = (int) $count_ham;
|
|
||||||
$count_spam = (int) $count_spam;
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'count_ham' => $count_ham,
|
|
||||||
'count_spam' => $count_spam
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the database's internal variables.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return array Returns an array of all internals.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function get_internals($uid)
|
|
||||||
{
|
|
||||||
|
|
||||||
$internals = $this->_get_query(
|
|
||||||
array(
|
|
||||||
self::INTERNALS_TEXTS_HAM,
|
|
||||||
self::INTERNALS_TEXTS_SPAM,
|
|
||||||
self::INTERNALS_DBVERSION
|
|
||||||
),
|
|
||||||
$uid
|
|
||||||
);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'texts_ham' => (int) $internals[self::INTERNALS_TEXTS_HAM],
|
|
||||||
'texts_spam' => (int) $internals[self::INTERNALS_TEXTS_SPAM],
|
|
||||||
'dbversion' => (int) $internals[self::INTERNALS_DBVERSION]
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all data about a list of tags from the database.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param array $tokens
|
|
||||||
* @return mixed Returns FALSE on failure, otherwise returns array of returned data in the format array('tokens' => array(token => count), 'degenerates' => array(token => array(degenerate => count))).
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function get($tokens, $uid)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# First we see what we have in the database.
|
|
||||||
$token_data = $this->_get_query($tokens, $uid);
|
|
||||||
|
|
||||||
# Check if we have to degenerate some tokens
|
|
||||||
|
|
||||||
$missing_tokens = array();
|
|
||||||
|
|
||||||
foreach($tokens as $token) {
|
|
||||||
if(!isset($token_data[$token]))
|
|
||||||
$missing_tokens[] = $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($missing_tokens) > 0) {
|
|
||||||
|
|
||||||
# We have to degenerate some tokens
|
|
||||||
$degenerates_list = array();
|
|
||||||
|
|
||||||
# Generate a list of degenerated tokens for the missing tokens ...
|
|
||||||
$degenerates = $this->_degenerator->degenerate($missing_tokens);
|
|
||||||
|
|
||||||
# ... and look them up
|
|
||||||
|
|
||||||
foreach($degenerates as $token => $token_degenerates)
|
|
||||||
$degenerates_list = array_merge($degenerates_list, $token_degenerates);
|
|
||||||
|
|
||||||
$token_data = array_merge($token_data, $this->_get_query($degenerates_list));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Here, we have all availible data in $token_data.
|
|
||||||
|
|
||||||
$return_data_tokens = array();
|
|
||||||
$return_data_degenerates = array();
|
|
||||||
|
|
||||||
foreach($tokens as $token) {
|
|
||||||
|
|
||||||
if(isset($token_data[$token]) === TRUE) {
|
|
||||||
|
|
||||||
# The token was found in the database
|
|
||||||
|
|
||||||
# Add the data ...
|
|
||||||
$return_data_tokens[$token] = $this->_parse_count($token_data[$token]);
|
|
||||||
|
|
||||||
# ... and update it's lastseen parameter
|
|
||||||
$this->_update($token, "{$return_data_tokens[$token]['count_ham']} {$return_data_tokens[$token]['count_spam']} " . $this->b8_config['today'], $uid );
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
# The token was not found, so we look if we
|
|
||||||
# can return data for degenerated tokens
|
|
||||||
|
|
||||||
# Check all degenerated forms of the token
|
|
||||||
|
|
||||||
foreach($this->_degenerator->degenerates[$token] as $degenerate) {
|
|
||||||
|
|
||||||
if(isset($token_data[$degenerate]) === TRUE) {
|
|
||||||
|
|
||||||
# A degeneration of the token way found in the database
|
|
||||||
|
|
||||||
# Add the data ...
|
|
||||||
$return_data_degenerates[$token][$degenerate] = $this->_parse_count($token_data[$degenerate]);
|
|
||||||
|
|
||||||
# ... and update it's lastseen parameter
|
|
||||||
$this->_update($degenerate, "{$return_data_degenerates[$token][$degenerate]['count_ham']} {$return_data_degenerates[$token][$degenerate]['count_spam']} " . $this->b8_config['today'], $uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Now, all token data directly found in the database is in $return_data_tokens
|
|
||||||
# and all data for degenerated versions is in $return_data_degenerates
|
|
||||||
|
|
||||||
# First, we commit the changes to the lastseen parameters
|
|
||||||
$this->_commit();
|
|
||||||
|
|
||||||
# Then, we return what we have
|
|
||||||
return array(
|
|
||||||
'tokens' => $return_data_tokens,
|
|
||||||
'degenerates' => $return_data_degenerates
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores or deletes a list of tokens from the given category.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param array $tokens
|
|
||||||
* @param const $category Either b8::HAM or b8::SPAM
|
|
||||||
* @param const $action Either b8::LEARN or b8::UNLEARN
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function process_text($tokens, $category, $action, $uid)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# No matter what we do, we first have to check what data we have.
|
|
||||||
|
|
||||||
# First get the internals, including the ham texts and spam texts counter
|
|
||||||
$internals = $this->get_internals($uid);
|
|
||||||
|
|
||||||
# Then, fetch all data for all tokens we have (and update their lastseen parameters)
|
|
||||||
$token_data = $this->_get_query(array_keys($tokens), $uid);
|
|
||||||
|
|
||||||
# Process all tokens to learn/unlearn
|
|
||||||
|
|
||||||
foreach($tokens as $token => $count) {
|
|
||||||
|
|
||||||
if(isset($token_data[$token])) {
|
|
||||||
|
|
||||||
# We already have this token, so update it's data
|
|
||||||
|
|
||||||
# Get the existing data
|
|
||||||
list($count_ham, $count_spam, $lastseen) = explode(' ', $token_data[$token]);
|
|
||||||
$count_ham = (int) $count_ham;
|
|
||||||
$count_spam = (int) $count_spam;
|
|
||||||
|
|
||||||
# Increase or decrease the right counter
|
|
||||||
|
|
||||||
if($action === b8::LEARN) {
|
|
||||||
if($category === b8::HAM)
|
|
||||||
$count_ham += $count;
|
|
||||||
elseif($category === b8::SPAM)
|
|
||||||
$count_spam += $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($action == b8::UNLEARN) {
|
|
||||||
if($category === b8::HAM)
|
|
||||||
$count_ham -= $count;
|
|
||||||
elseif($category === b8::SPAM)
|
|
||||||
$count_spam -= $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
# We don't want to have negative values
|
|
||||||
|
|
||||||
if($count_ham < 0)
|
|
||||||
$count_ham = 0;
|
|
||||||
|
|
||||||
if($count_spam < 0)
|
|
||||||
$count_spam = 0;
|
|
||||||
|
|
||||||
# Now let's see if we have to update or delete the token
|
|
||||||
if($count_ham !== 0 or $count_spam !== 0)
|
|
||||||
$this->_update($token, "$count_ham $count_spam " . $this->b8_config['today'], $uid);
|
|
||||||
else
|
|
||||||
$this->_del($token, $uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
# We don't have the token. If we unlearn a text, we can't delete it
|
|
||||||
# as we don't have it anyway, so just do something if we learn a text
|
|
||||||
|
|
||||||
if($action === b8::LEARN) {
|
|
||||||
|
|
||||||
if($category === b8::HAM)
|
|
||||||
$data = '1 0 ';
|
|
||||||
elseif($category === b8::SPAM)
|
|
||||||
$data = '0 1 ';
|
|
||||||
|
|
||||||
$data .= $this->b8_config['today'];
|
|
||||||
|
|
||||||
$this->_put($token, $data, $uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Now, all token have been processed, so let's update the right text
|
|
||||||
|
|
||||||
if($action === b8::LEARN) {
|
|
||||||
|
|
||||||
if($category === b8::HAM) {
|
|
||||||
$internals['texts_ham']++;
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_HAM, $internals['texts_ham'], $uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($category === b8::SPAM) {
|
|
||||||
$internals['texts_spam']++;
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_SPAM, $internals['texts_spam'], $uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($action == b8::UNLEARN) {
|
|
||||||
|
|
||||||
if($category === b8::HAM) {
|
|
||||||
|
|
||||||
$internals['texts_ham']--;
|
|
||||||
|
|
||||||
if($internals['texts_ham'] < 0)
|
|
||||||
$internals['texts_ham'] = 0;
|
|
||||||
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_HAM, $internals['texts_ham'], $uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($category === b8::SPAM) {
|
|
||||||
|
|
||||||
$internals['texts_spam']--;
|
|
||||||
|
|
||||||
if($internals['texts_spam'] < 0)
|
|
||||||
$internals['texts_spam'] = 0;
|
|
||||||
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_SPAM, $internals['texts_spam'], $uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# We're done and can commit all changes to the database now
|
|
||||||
$this->_commit($uid);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,395 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Functions used by all storage backends
|
|
||||||
* Copyright (C) 2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Tobias Leupold
|
|
||||||
*/
|
|
||||||
|
|
||||||
abstract class b8_storage_base
|
|
||||||
{
|
|
||||||
|
|
||||||
public $connected = FALSE;
|
|
||||||
|
|
||||||
protected $_degenerator = NULL;
|
|
||||||
|
|
||||||
const INTERNALS_TEXTS_HAM = 'bayes*texts.ham';
|
|
||||||
const INTERNALS_TEXTS_SPAM = 'bayes*texts.spam';
|
|
||||||
const INTERNALS_DBVERSION = 'bayes*dbversion';
|
|
||||||
|
|
||||||
const BACKEND_NOT_CONNECTED = 'BACKEND_NOT_CONNECTED';
|
|
||||||
const DATABASE_WRONG_VERSION = 'DATABASE_WRONG_VERSION';
|
|
||||||
const DATABASE_NOT_B8 = 'DATABASE_NOT_B8';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the class has all it needs to work.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @return mixed Returns TRUE if everything is okay, otherwise an error code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function validate()
|
|
||||||
{
|
|
||||||
|
|
||||||
# We set up the degenerator here, as we would have to duplicate code if it
|
|
||||||
# was done in the constructor of the respective storage backend.
|
|
||||||
$class = 'b8_degenerator_' . $this->b8_config['degenerator'];
|
|
||||||
$this->_degenerator = new $class();
|
|
||||||
|
|
||||||
if($this->connected !== TRUE)
|
|
||||||
return self::BACKEND_NOT_CONNECTED;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if a b8 database is used and if it's version is okay
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @return mixed Returns TRUE if everything is okay, otherwise an error code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function check_database()
|
|
||||||
{
|
|
||||||
|
|
||||||
$internals = $this->get_internals();
|
|
||||||
|
|
||||||
if(isset($internals['dbversion'])) {
|
|
||||||
if($internals['dbversion'] == "2") {
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_WRONG_VERSION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_NOT_B8;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses the "count" data of a token.
|
|
||||||
*
|
|
||||||
* @access private
|
|
||||||
* @param string $data
|
|
||||||
* @return array Returns an array of the parsed data: array(count_ham, count_spam, lastseen).
|
|
||||||
*/
|
|
||||||
|
|
||||||
private function _parse_count($data)
|
|
||||||
{
|
|
||||||
|
|
||||||
list($count_ham, $count_spam, $lastseen) = explode(' ', $data);
|
|
||||||
|
|
||||||
$count_ham = (int) $count_ham;
|
|
||||||
$count_spam = (int) $count_spam;
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'count_ham' => $count_ham,
|
|
||||||
'count_spam' => $count_spam
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the database's internal variables.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return array Returns an array of all internals.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function get_internals()
|
|
||||||
{
|
|
||||||
|
|
||||||
$internals = $this->_get_query(
|
|
||||||
array(
|
|
||||||
self::INTERNALS_TEXTS_HAM,
|
|
||||||
self::INTERNALS_TEXTS_SPAM,
|
|
||||||
self::INTERNALS_DBVERSION
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return array(
|
|
||||||
'texts_ham' => (int) $internals[self::INTERNALS_TEXTS_HAM],
|
|
||||||
'texts_spam' => (int) $internals[self::INTERNALS_TEXTS_SPAM],
|
|
||||||
'dbversion' => (int) $internals[self::INTERNALS_DBVERSION]
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get all data about a list of tags from the database.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param array $tokens
|
|
||||||
* @return mixed Returns FALSE on failure, otherwise returns array of returned data in the format array('tokens' => array(token => count), 'degenerates' => array(token => array(degenerate => count))).
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function get($tokens)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# First we see what we have in the database.
|
|
||||||
$token_data = $this->_get_query($tokens);
|
|
||||||
|
|
||||||
# Check if we have to degenerate some tokens
|
|
||||||
|
|
||||||
$missing_tokens = array();
|
|
||||||
|
|
||||||
foreach($tokens as $token) {
|
|
||||||
if(!isset($token_data[$token]))
|
|
||||||
$missing_tokens[] = $token;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($missing_tokens) > 0) {
|
|
||||||
|
|
||||||
# We have to degenerate some tokens
|
|
||||||
$degenerates_list = array();
|
|
||||||
|
|
||||||
# Generate a list of degenerated tokens for the missing tokens ...
|
|
||||||
$degenerates = $this->_degenerator->degenerate($missing_tokens);
|
|
||||||
|
|
||||||
# ... and look them up
|
|
||||||
|
|
||||||
foreach($degenerates as $token => $token_degenerates)
|
|
||||||
$degenerates_list = array_merge($degenerates_list, $token_degenerates);
|
|
||||||
|
|
||||||
$token_data = array_merge($token_data, $this->_get_query($degenerates_list));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Here, we have all availible data in $token_data.
|
|
||||||
|
|
||||||
$return_data_tokens = array();
|
|
||||||
$return_data_degenerates = array();
|
|
||||||
|
|
||||||
foreach($tokens as $token) {
|
|
||||||
|
|
||||||
if(isset($token_data[$token]) === TRUE) {
|
|
||||||
|
|
||||||
# The token was found in the database
|
|
||||||
|
|
||||||
# Add the data ...
|
|
||||||
$return_data_tokens[$token] = $this->_parse_count($token_data[$token]);
|
|
||||||
|
|
||||||
# ... and update it's lastseen parameter
|
|
||||||
$this->_update($token, "{$return_data_tokens[$token]['count_ham']} {$return_data_tokens[$token]['count_spam']} " . $this->b8_config['today']);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
# The token was not found, so we look if we
|
|
||||||
# can return data for degenerated tokens
|
|
||||||
|
|
||||||
# Check all degenerated forms of the token
|
|
||||||
|
|
||||||
foreach($this->_degenerator->degenerates[$token] as $degenerate) {
|
|
||||||
|
|
||||||
if(isset($token_data[$degenerate]) === TRUE) {
|
|
||||||
|
|
||||||
# A degeneration of the token way found in the database
|
|
||||||
|
|
||||||
# Add the data ...
|
|
||||||
$return_data_degenerates[$token][$degenerate] = $this->_parse_count($token_data[$degenerate]);
|
|
||||||
|
|
||||||
# ... and update it's lastseen parameter
|
|
||||||
$this->_update($degenerate, "{$return_data_degenerates[$token][$degenerate]['count_ham']} {$return_data_degenerates[$token][$degenerate]['count_spam']} " . $this->b8_config['today']);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Now, all token data directly found in the database is in $return_data_tokens
|
|
||||||
# and all data for degenerated versions is in $return_data_degenerates
|
|
||||||
|
|
||||||
# First, we commit the changes to the lastseen parameters
|
|
||||||
$this->_commit();
|
|
||||||
|
|
||||||
# Then, we return what we have
|
|
||||||
return array(
|
|
||||||
'tokens' => $return_data_tokens,
|
|
||||||
'degenerates' => $return_data_degenerates
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores or deletes a list of tokens from the given category.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param array $tokens
|
|
||||||
* @param const $category Either b8::HAM or b8::SPAM
|
|
||||||
* @param const $action Either b8::LEARN or b8::UNLEARN
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function process_text($tokens, $category, $action)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Validate the startup
|
|
||||||
|
|
||||||
$started_up = $this->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE)
|
|
||||||
return $started_up;
|
|
||||||
|
|
||||||
# No matter what we do, we first have to check what data we have.
|
|
||||||
|
|
||||||
# First get the internals, including the ham texts and spam texts counter
|
|
||||||
$internals = $this->get_internals();
|
|
||||||
|
|
||||||
# Then, fetch all data for all tokens we have (and update their lastseen parameters)
|
|
||||||
$token_data = $this->_get_query(array_keys($tokens));
|
|
||||||
|
|
||||||
# Process all tokens to learn/unlearn
|
|
||||||
|
|
||||||
foreach($tokens as $token => $count) {
|
|
||||||
|
|
||||||
if(isset($token_data[$token])) {
|
|
||||||
|
|
||||||
# We already have this token, so update it's data
|
|
||||||
|
|
||||||
# Get the existing data
|
|
||||||
list($count_ham, $count_spam, $lastseen) = explode(' ', $token_data[$token]);
|
|
||||||
$count_ham = (int) $count_ham;
|
|
||||||
$count_spam = (int) $count_spam;
|
|
||||||
|
|
||||||
# Increase or decrease the right counter
|
|
||||||
|
|
||||||
if($action === b8::LEARN) {
|
|
||||||
if($category === b8::HAM)
|
|
||||||
$count_ham += $count;
|
|
||||||
elseif($category === b8::SPAM)
|
|
||||||
$count_spam += $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($action == b8::UNLEARN) {
|
|
||||||
if($category === b8::HAM)
|
|
||||||
$count_ham -= $count;
|
|
||||||
elseif($category === b8::SPAM)
|
|
||||||
$count_spam -= $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
# We don't want to have negative values
|
|
||||||
|
|
||||||
if($count_ham < 0)
|
|
||||||
$count_ham = 0;
|
|
||||||
|
|
||||||
if($count_spam < 0)
|
|
||||||
$count_spam = 0;
|
|
||||||
|
|
||||||
# Now let's see if we have to update or delete the token
|
|
||||||
if($count_ham !== 0 or $count_spam !== 0)
|
|
||||||
$this->_update($token, "$count_ham $count_spam " . $this->b8_config['today']);
|
|
||||||
else
|
|
||||||
$this->_del($token);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
|
|
||||||
# We don't have the token. If we unlearn a text, we can't delete it
|
|
||||||
# as we don't have it anyway, so just do something if we learn a text
|
|
||||||
|
|
||||||
if($action === b8::LEARN) {
|
|
||||||
|
|
||||||
if($category === b8::HAM)
|
|
||||||
$data = '1 0 ';
|
|
||||||
elseif($category === b8::SPAM)
|
|
||||||
$data = '0 1 ';
|
|
||||||
|
|
||||||
$data .= $this->b8_config['today'];
|
|
||||||
|
|
||||||
$this->_put($token, $data);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# Now, all token have been processed, so let's update the right text
|
|
||||||
|
|
||||||
if($action === b8::LEARN) {
|
|
||||||
|
|
||||||
if($category === b8::HAM) {
|
|
||||||
$internals['texts_ham']++;
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_HAM, $internals['texts_ham']);
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($category === b8::SPAM) {
|
|
||||||
$internals['texts_spam']++;
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_SPAM, $internals['texts_spam']);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($action == b8::UNLEARN) {
|
|
||||||
|
|
||||||
if($category === b8::HAM) {
|
|
||||||
|
|
||||||
$internals['texts_ham']--;
|
|
||||||
|
|
||||||
if($internals['texts_ham'] < 0)
|
|
||||||
$internals['texts_ham'] = 0;
|
|
||||||
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_HAM, $internals['texts_ham']);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($category === b8::SPAM) {
|
|
||||||
|
|
||||||
$internals['texts_spam']--;
|
|
||||||
|
|
||||||
if($internals['texts_spam'] < 0)
|
|
||||||
$internals['texts_spam'] = 0;
|
|
||||||
|
|
||||||
$this->_update(self::INTERNALS_TEXTS_SPAM, $internals['texts_spam']);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
# We're done and can commit all changes to the database now
|
|
||||||
$this->_commit();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,198 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The DBA (Berkeley DB) abstraction layer for communicating with the database.
|
|
||||||
* Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Tobias Leupold
|
|
||||||
*/
|
|
||||||
|
|
||||||
class b8_storage_dba extends b8_storage_base
|
|
||||||
{
|
|
||||||
|
|
||||||
public $config = array(
|
|
||||||
'database' => 'wordlist.db',
|
|
||||||
'handler' => 'db4',
|
|
||||||
);
|
|
||||||
|
|
||||||
public $b8_config = array(
|
|
||||||
'degenerator' => NULL,
|
|
||||||
'today' => NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
private $_db = NULL;
|
|
||||||
|
|
||||||
const DATABASE_CONNECTION_FAIL = 'DATABASE_CONNECTION_FAIL';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the database layer.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $config
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __construct($config, $degenerator, $today)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Pass some variables of the main b8 config to this class
|
|
||||||
$this->b8_config['degenerator'] = $degenerator;
|
|
||||||
$this->b8_config['today'] = $today;
|
|
||||||
|
|
||||||
# Validate the config items
|
|
||||||
if(count($config) > 0) {
|
|
||||||
foreach ($config as $name => $value) {
|
|
||||||
$this->config[$name] = (string) $value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the database connection.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __destruct()
|
|
||||||
{
|
|
||||||
if($this->_db !== NULL) {
|
|
||||||
dba_close($this->_db);
|
|
||||||
$this->connected = FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to the database and do some checks.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return mixed Returns TRUE on a successful database connection, otherwise returns a constant from b8.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function connect()
|
|
||||||
{
|
|
||||||
|
|
||||||
# Have we already connected?
|
|
||||||
if($this->_db !== NULL)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
# Open the database connection
|
|
||||||
$this->_db = dba_open(dirname(__FILE__) . DIRECTORY_SEPARATOR . ".." . DIRECTORY_SEPARATOR . $this->config['database'], "w", $this->config['handler']);
|
|
||||||
|
|
||||||
if($this->_db === FALSE) {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
$this->_db = NULL;
|
|
||||||
return self::DATABASE_CONNECTION_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Everything is okay and connected
|
|
||||||
|
|
||||||
$this->connected = TRUE;
|
|
||||||
|
|
||||||
# Let's see if this is a b8 database and the version is okay
|
|
||||||
return $this->check_database();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the actual interaction with the database when fetching data.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param array $tokens
|
|
||||||
* @return mixed Returns an array of the returned data in the format array(token => data) or an empty array if there was no data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _get_query($tokens)
|
|
||||||
{
|
|
||||||
|
|
||||||
$data = array();
|
|
||||||
|
|
||||||
foreach ($tokens as $token) {
|
|
||||||
|
|
||||||
$count = dba_fetch($token, $this->_db);
|
|
||||||
|
|
||||||
if($count !== FALSE)
|
|
||||||
$data[$token] = $count;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a token to the database.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @param string $count
|
|
||||||
* @return bool TRUE on success or FALSE on failure
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _put($token, $count) {
|
|
||||||
return dba_insert($token, $count, $this->_db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update an existing token.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @param string $count
|
|
||||||
* @return bool TRUE on success or FALSE on failure
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _update($token, $count)
|
|
||||||
{
|
|
||||||
return dba_replace($token, $count, $this->_db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a token from the database.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @return bool TRUE on success or FALSE on failure
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _del($token)
|
|
||||||
{
|
|
||||||
return dba_delete($token, $this->_db);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does nothing :-D
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _commit()
|
|
||||||
{
|
|
||||||
# We just need this function because the (My)SQL backend(s) need it.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,313 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2011 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The MySQL abstraction layer for communicating with the database.
|
|
||||||
* Copyright (C) 2009 Oliver Lillie (aka buggedcom)
|
|
||||||
* Copyright (C) 2010-2011 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Oliver Lillie (aka buggedcom) (original PHP 5 port and optimizations)
|
|
||||||
* @author Tobias Leupold
|
|
||||||
*/
|
|
||||||
|
|
||||||
class b8_storage_frndc extends b8_storage_base
|
|
||||||
{
|
|
||||||
|
|
||||||
public $config = array(
|
|
||||||
'database' => 'b8_wordlist',
|
|
||||||
'table_name' => 'b8_wordlist',
|
|
||||||
'host' => 'localhost',
|
|
||||||
'user' => FALSE,
|
|
||||||
'pass' => FALSE,
|
|
||||||
'connection' => NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
public $b8_config = array(
|
|
||||||
'degenerator' => NULL,
|
|
||||||
'today' => NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
private $_connection = NULL;
|
|
||||||
private $_deletes = array();
|
|
||||||
private $_puts = array();
|
|
||||||
private $_updates = array();
|
|
||||||
private $uid = 0;
|
|
||||||
|
|
||||||
const DATABASE_CONNECTION_FAIL = 'DATABASE_CONNECTION_FAIL';
|
|
||||||
const DATABASE_CONNECTION_ERROR = 'DATABASE_CONNECTION_ERROR';
|
|
||||||
const DATABASE_CONNECTION_BAD_RESOURCE = 'DATABASE_CONNECTION_BAD_RESOURCE';
|
|
||||||
const DATABASE_SELECT_ERROR = 'DATABASE_SELECT_ERROR';
|
|
||||||
const DATABASE_TABLE_ACCESS_FAIL = 'DATABASE_TABLE_ACCESS_FAIL';
|
|
||||||
const DATABASE_WRONG_VERSION = 'DATABASE_WRONG_VERSION';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the database layer.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $config
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __construct($config, $degenerator, $today)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Pass some variables of the main b8 config to this class
|
|
||||||
$this->b8_config['degenerator'] = $degenerator;
|
|
||||||
$this->b8_config['today'] = $today;
|
|
||||||
|
|
||||||
# Validate the config items
|
|
||||||
|
|
||||||
if(count($config) > 0) {
|
|
||||||
|
|
||||||
foreach ($config as $name => $value) {
|
|
||||||
|
|
||||||
switch($name) {
|
|
||||||
|
|
||||||
case 'table_name':
|
|
||||||
case 'host':
|
|
||||||
case 'user':
|
|
||||||
case 'pass':
|
|
||||||
case 'database':
|
|
||||||
$this->config[$name] = (string) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'connection':
|
|
||||||
|
|
||||||
if($value !== NULL) {
|
|
||||||
|
|
||||||
if(is_resource($value) === TRUE) {
|
|
||||||
$resource_type = get_resource_type($value);
|
|
||||||
$this->config['connection'] = $resource_type !== 'mysql link' && $resource_type !== 'mysql link persistent' ? FALSE : $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
$this->config['connection'] = FALSE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the database connection.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __destruct()
|
|
||||||
{
|
|
||||||
|
|
||||||
if ($this->_connection === NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commit any changes before closing
|
|
||||||
$this->_commit();
|
|
||||||
|
|
||||||
// Just close the connection if no link-resource was passed and b8 created it's own connection
|
|
||||||
if ($this->config['connection'] === NULL) {
|
|
||||||
mysql_close($this->_connection);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->connected = FALSE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to the database and do some checks.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return mixed Returns TRUE on a successful database connection, otherwise returns a constant from b8.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function connect()
|
|
||||||
{
|
|
||||||
|
|
||||||
$this->connected = TRUE;
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the actual interaction with the database when fetching data.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param array $tokens
|
|
||||||
* @return mixed Returns an array of the returned data in the format array(token => data) or an empty array if there was no data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _get_query($tokens, $uid)
|
|
||||||
{
|
|
||||||
|
|
||||||
// Construct the query ...
|
|
||||||
if (count($tokens) > 0) {
|
|
||||||
|
|
||||||
$where = array();
|
|
||||||
|
|
||||||
foreach ($tokens as $token) {
|
|
||||||
$token = dbesc($token);
|
|
||||||
array_push($where, $token);
|
|
||||||
}
|
|
||||||
|
|
||||||
$where = 'term IN ("' . implode('", "', $where) . '")';
|
|
||||||
} else {
|
|
||||||
$token = dbesc($token);
|
|
||||||
$where = 'term = "' . $token . '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
// ... and fetch the data
|
|
||||||
|
|
||||||
$result = q('SELECT * FROM `spam` WHERE ' . $where . ' AND `uid` = ' . $uid );
|
|
||||||
|
|
||||||
|
|
||||||
$returned_tokens = array();
|
|
||||||
if (dbm::is_result($result)) {
|
|
||||||
foreach ($result as $rr) {
|
|
||||||
$returned_tokens[] = $rr['term'];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$to_create = array();
|
|
||||||
|
|
||||||
if (count($tokens) > 0) {
|
|
||||||
foreach($tokens as $token)
|
|
||||||
if(! in_array($token,$returned_tokens))
|
|
||||||
$to_create[] = str_tolower($token);
|
|
||||||
}
|
|
||||||
if (count($to_create)) {
|
|
||||||
$sql = '';
|
|
||||||
foreach ($to_create as $term) {
|
|
||||||
if (strlen($sql)) {
|
|
||||||
$sql .= ',';
|
|
||||||
}
|
|
||||||
$sql .= sprintf("(`term`,`datetime`,`uid`) VALUES('%s','%s',%d)",
|
|
||||||
dbesc(str_tolower($term)),
|
|
||||||
dbesc(datetime_convert()),
|
|
||||||
intval($uid)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
q("INSERT INTO `spam` " . $sql);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a token to the database.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @param string $count
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function _put($token, $count, $uid) {
|
|
||||||
$token = dbesc($token);
|
|
||||||
$count = dbesc($count);
|
|
||||||
$uid = dbesc($uid);
|
|
||||||
array_push($this->_puts, '("' . $token . '", "' . $count . '", "' . $uid .'")');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update an existing token.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @param string $count
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function _update($token, $count, $uid)
|
|
||||||
{
|
|
||||||
$token = dbesc($token);
|
|
||||||
$count = dbesc($count);
|
|
||||||
$uid = dbesc($uid);
|
|
||||||
array_push($this->_puts, '("' . $token . '", "' . $count . '", "' . $uid .'")');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a token from the database.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function _del($token, $uid)
|
|
||||||
{
|
|
||||||
$token = dbesc($token);
|
|
||||||
$uid = dbesc($uid);
|
|
||||||
$this->uid = $uid;
|
|
||||||
array_push($this->_deletes, $token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Commits any modification queries.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
protected function _commit($uid)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(count($this->_deletes) > 0) {
|
|
||||||
|
|
||||||
$result = q('
|
|
||||||
DELETE FROM ' . $this->config['table_name'] . '
|
|
||||||
WHERE token IN ("' . implode('", "', $this->_deletes) . '") AND uid = ' . $this->uid);
|
|
||||||
|
|
||||||
$this->_deletes = array();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($this->_puts) > 0) {
|
|
||||||
|
|
||||||
$result = q('
|
|
||||||
INSERT INTO ' . $this->config['table_name'] . '(token, count, uid)
|
|
||||||
VALUES ' . implode(', ', $this->_puts));
|
|
||||||
|
|
||||||
$this->_puts = array();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($this->_updates) > 0) {
|
|
||||||
|
|
||||||
// this still needs work
|
|
||||||
$result = q("select * from " . $this->config['table_name'] . ' where token = ');
|
|
||||||
|
|
||||||
|
|
||||||
$result = q('
|
|
||||||
INSERT INTO ' . $this->config['table_name'] . '(token, count, uid)
|
|
||||||
VALUES ' . implode(', ', $this->_updates) . ', ' . $uid . '
|
|
||||||
ON DUPLICATE KEY UPDATE ' . $this->config['table_name'] . '.count = VALUES(count);', $this->_connection);
|
|
||||||
|
|
||||||
$this->_updates = array();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,351 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2011 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The MySQL abstraction layer for communicating with the database.
|
|
||||||
* Copyright (C) 2009 Oliver Lillie (aka buggedcom)
|
|
||||||
* Copyright (C) 2010-2011 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
*
|
|
||||||
* @license LGPL
|
|
||||||
* @access public
|
|
||||||
* @package b8
|
|
||||||
* @author Oliver Lillie (aka buggedcom) (original PHP 5 port and optimizations)
|
|
||||||
* @author Tobias Leupold
|
|
||||||
*/
|
|
||||||
|
|
||||||
class b8_storage_mysql extends b8_storage_base
|
|
||||||
{
|
|
||||||
|
|
||||||
public $config = array(
|
|
||||||
'database' => 'b8_wordlist',
|
|
||||||
'table_name' => 'b8_wordlist',
|
|
||||||
'host' => 'localhost',
|
|
||||||
'user' => FALSE,
|
|
||||||
'pass' => FALSE,
|
|
||||||
'connection' => NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
public $b8_config = array(
|
|
||||||
'degenerator' => NULL,
|
|
||||||
'today' => NULL
|
|
||||||
);
|
|
||||||
|
|
||||||
private $_connection = NULL;
|
|
||||||
private $_deletes = array();
|
|
||||||
private $_puts = array();
|
|
||||||
private $_updates = array();
|
|
||||||
|
|
||||||
const DATABASE_CONNECTION_FAIL = 'DATABASE_CONNECTION_FAIL';
|
|
||||||
const DATABASE_CONNECTION_ERROR = 'DATABASE_CONNECTION_ERROR';
|
|
||||||
const DATABASE_CONNECTION_BAD_RESOURCE = 'DATABASE_CONNECTION_BAD_RESOURCE';
|
|
||||||
const DATABASE_SELECT_ERROR = 'DATABASE_SELECT_ERROR';
|
|
||||||
const DATABASE_TABLE_ACCESS_FAIL = 'DATABASE_TABLE_ACCESS_FAIL';
|
|
||||||
const DATABASE_WRONG_VERSION = 'DATABASE_WRONG_VERSION';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs the database layer.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @param string $config
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __construct($config, $degenerator, $today)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Pass some variables of the main b8 config to this class
|
|
||||||
$this->b8_config['degenerator'] = $degenerator;
|
|
||||||
$this->b8_config['today'] = $today;
|
|
||||||
|
|
||||||
# Validate the config items
|
|
||||||
|
|
||||||
if(count($config) > 0) {
|
|
||||||
|
|
||||||
foreach ($config as $name => $value) {
|
|
||||||
|
|
||||||
switch($name) {
|
|
||||||
|
|
||||||
case 'table_name':
|
|
||||||
case 'host':
|
|
||||||
case 'user':
|
|
||||||
case 'pass':
|
|
||||||
case 'database':
|
|
||||||
$this->config[$name] = (string) $value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'connection':
|
|
||||||
|
|
||||||
if($value !== NULL) {
|
|
||||||
|
|
||||||
if(is_resource($value) === TRUE) {
|
|
||||||
$resource_type = get_resource_type($value);
|
|
||||||
$this->config['connection'] = $resource_type !== 'mysql link' && $resource_type !== 'mysql link persistent' ? FALSE : $value;
|
|
||||||
}
|
|
||||||
|
|
||||||
else
|
|
||||||
$this->config['connection'] = FALSE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the database connection.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
function __destruct()
|
|
||||||
{
|
|
||||||
|
|
||||||
if($this->_connection === NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
# Commit any changes before closing
|
|
||||||
$this->_commit();
|
|
||||||
|
|
||||||
# Just close the connection if no link-resource was passed and b8 created it's own connection
|
|
||||||
if($this->config['connection'] === NULL)
|
|
||||||
mysql_close($this->_connection);
|
|
||||||
|
|
||||||
$this->connected = FALSE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to the database and do some checks.
|
|
||||||
*
|
|
||||||
* @access public
|
|
||||||
* @return mixed Returns TRUE on a successful database connection, otherwise returns a constant from b8.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public function connect()
|
|
||||||
{
|
|
||||||
|
|
||||||
# Are we already connected?
|
|
||||||
if($this->connected === TRUE)
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
# Are we using an existing passed resource?
|
|
||||||
if($this->config['connection'] === FALSE) {
|
|
||||||
# ... yes we are, but the connection is not a resource, so return an error
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_CONNECTION_BAD_RESOURCE;
|
|
||||||
}
|
|
||||||
|
|
||||||
elseif($this->config['connection'] === NULL) {
|
|
||||||
|
|
||||||
# ... no we aren't so we have to connect.
|
|
||||||
|
|
||||||
if($this->_connection = mysql_connect($this->config['host'], $this->config['user'], $this->config['pass'])) {
|
|
||||||
if(mysql_select_db($this->config['database'], $this->_connection) === FALSE) {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_SELECT_ERROR . ": " . mysql_error();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_CONNECTION_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
# ... yes we are
|
|
||||||
$this->_connection = $this->config['connection'];
|
|
||||||
}
|
|
||||||
|
|
||||||
# Just in case ...
|
|
||||||
if($this->_connection === NULL) {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_CONNECTION_FAIL;
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check to see if the wordlist table exists
|
|
||||||
if(mysql_query('DESCRIBE ' . $this->config['table_name'], $this->_connection) === FALSE) {
|
|
||||||
$this->connected = FALSE;
|
|
||||||
return self::DATABASE_TABLE_ACCESS_FAIL . ": " . mysql_error();
|
|
||||||
}
|
|
||||||
|
|
||||||
# Everything is okay and connected
|
|
||||||
$this->connected = TRUE;
|
|
||||||
|
|
||||||
# Let's see if this is a b8 database and the version is okay
|
|
||||||
return $this->check_database();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does the actual interaction with the database when fetching data.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param array $tokens
|
|
||||||
* @return mixed Returns an array of the returned data in the format array(token => data) or an empty array if there was no data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _get_query($tokens)
|
|
||||||
{
|
|
||||||
|
|
||||||
# Construct the query ...
|
|
||||||
|
|
||||||
if(count($tokens) > 0) {
|
|
||||||
|
|
||||||
$where = array();
|
|
||||||
|
|
||||||
foreach ($tokens as $token) {
|
|
||||||
$token = mysql_real_escape_string($token, $this->_connection);
|
|
||||||
array_push($where, $token);
|
|
||||||
}
|
|
||||||
|
|
||||||
$where = 'token IN ("' . implode('", "', $where) . '")';
|
|
||||||
}
|
|
||||||
|
|
||||||
else {
|
|
||||||
$token = mysql_real_escape_string($token, $this->_connection);
|
|
||||||
$where = 'token = "' . $token . '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
# ... and fetch the data
|
|
||||||
|
|
||||||
$result = mysql_query('
|
|
||||||
SELECT token, count
|
|
||||||
FROM ' . $this->config['table_name'] . '
|
|
||||||
WHERE ' . $where . ';
|
|
||||||
', $this->_connection);
|
|
||||||
|
|
||||||
$data = array();
|
|
||||||
|
|
||||||
while ($row = mysql_fetch_array($result, MYSQL_ASSOC))
|
|
||||||
$data[$row['token']] = $row['count'];
|
|
||||||
|
|
||||||
mysql_free_result($result);
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store a token to the database.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @param string $count
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _put($token, $count) {
|
|
||||||
$token = mysql_real_escape_string($token, $this->_connection);
|
|
||||||
$count = mysql_real_escape_string($count, $this->_connection);;
|
|
||||||
array_push($this->_puts, '("' . $token . '", "' . $count . '")');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update an existing token.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @param string $count
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _update($token, $count)
|
|
||||||
{
|
|
||||||
$token = mysql_real_escape_string($token, $this->_connection);
|
|
||||||
$count = mysql_real_escape_string($count, $this->_connection);
|
|
||||||
array_push($this->_updates, '("' . $token . '", "' . $count . '")');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove a token from the database.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @param string $token
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _del($token)
|
|
||||||
{
|
|
||||||
$token = mysql_real_escape_string($token, $this->_connection);
|
|
||||||
array_push($this->_deletes, $token);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Commits any modification queries.
|
|
||||||
*
|
|
||||||
* @access protected
|
|
||||||
* @return void
|
|
||||||
*/
|
|
||||||
|
|
||||||
protected function _commit()
|
|
||||||
{
|
|
||||||
|
|
||||||
if(count($this->_deletes) > 0) {
|
|
||||||
|
|
||||||
$result = mysql_query('
|
|
||||||
DELETE FROM ' . $this->config['table_name'] . '
|
|
||||||
WHERE token IN ("' . implode('", "', $this->_deletes) . '");
|
|
||||||
', $this->_connection);
|
|
||||||
|
|
||||||
if(is_resource($result) === TRUE)
|
|
||||||
mysql_free_result($result);
|
|
||||||
|
|
||||||
$this->_deletes = array();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($this->_puts) > 0) {
|
|
||||||
|
|
||||||
$result = mysql_query('
|
|
||||||
INSERT INTO ' . $this->config['table_name'] . '(token, count)
|
|
||||||
VALUES ' . implode(', ', $this->_puts) . ';', $this->_connection);
|
|
||||||
|
|
||||||
if(is_resource($result) === TRUE)
|
|
||||||
mysql_free_result($result);
|
|
||||||
|
|
||||||
$this->_puts = array();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(count($this->_updates) > 0) {
|
|
||||||
|
|
||||||
$result = mysql_query('
|
|
||||||
INSERT INTO ' . $this->config['table_name'] . '(token, count)
|
|
||||||
VALUES ' . implode(', ', $this->_updates) . '
|
|
||||||
ON DUPLICATE KEY UPDATE ' . $this->config['table_name'] . '.count = VALUES(count);', $this->_connection);
|
|
||||||
|
|
||||||
if(is_resource($result) === TRUE)
|
|
||||||
mysql_free_result($result);
|
|
||||||
|
|
||||||
$this->_updates = array();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
|
@ -1,504 +0,0 @@
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
Version 2.1, February 1999
|
|
||||||
|
|
||||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
|
||||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
Everyone is permitted to copy and distribute verbatim copies
|
|
||||||
of this license document, but changing it is not allowed.
|
|
||||||
|
|
||||||
[This is the first released version of the Lesser GPL. It also counts
|
|
||||||
as the successor of the GNU Library Public License, version 2, hence
|
|
||||||
the version number 2.1.]
|
|
||||||
|
|
||||||
Preamble
|
|
||||||
|
|
||||||
The licenses for most software are designed to take away your
|
|
||||||
freedom to share and change it. By contrast, the GNU General Public
|
|
||||||
Licenses are intended to guarantee your freedom to share and change
|
|
||||||
free software--to make sure the software is free for all its users.
|
|
||||||
|
|
||||||
This license, the Lesser General Public License, applies to some
|
|
||||||
specially designated software packages--typically libraries--of the
|
|
||||||
Free Software Foundation and other authors who decide to use it. You
|
|
||||||
can use it too, but we suggest you first think carefully about whether
|
|
||||||
this license or the ordinary General Public License is the better
|
|
||||||
strategy to use in any particular case, based on the explanations below.
|
|
||||||
|
|
||||||
When we speak of free software, we are referring to freedom of use,
|
|
||||||
not price. Our General Public Licenses are designed to make sure that
|
|
||||||
you have the freedom to distribute copies of free software (and charge
|
|
||||||
for this service if you wish); that you receive source code or can get
|
|
||||||
it if you want it; that you can change the software and use pieces of
|
|
||||||
it in new free programs; and that you are informed that you can do
|
|
||||||
these things.
|
|
||||||
|
|
||||||
To protect your rights, we need to make restrictions that forbid
|
|
||||||
distributors to deny you these rights or to ask you to surrender these
|
|
||||||
rights. These restrictions translate to certain responsibilities for
|
|
||||||
you if you distribute copies of the library or if you modify it.
|
|
||||||
|
|
||||||
For example, if you distribute copies of the library, whether gratis
|
|
||||||
or for a fee, you must give the recipients all the rights that we gave
|
|
||||||
you. You must make sure that they, too, receive or can get the source
|
|
||||||
code. If you link other code with the library, you must provide
|
|
||||||
complete object files to the recipients, so that they can relink them
|
|
||||||
with the library after making changes to the library and recompiling
|
|
||||||
it. And you must show them these terms so they know their rights.
|
|
||||||
|
|
||||||
We protect your rights with a two-step method: (1) we copyright the
|
|
||||||
library, and (2) we offer you this license, which gives you legal
|
|
||||||
permission to copy, distribute and/or modify the library.
|
|
||||||
|
|
||||||
To protect each distributor, we want to make it very clear that
|
|
||||||
there is no warranty for the free library. Also, if the library is
|
|
||||||
modified by someone else and passed on, the recipients should know
|
|
||||||
that what they have is not the original version, so that the original
|
|
||||||
author's reputation will not be affected by problems that might be
|
|
||||||
introduced by others.
|
|
||||||
|
|
||||||
Finally, software patents pose a constant threat to the existence of
|
|
||||||
any free program. We wish to make sure that a company cannot
|
|
||||||
effectively restrict the users of a free program by obtaining a
|
|
||||||
restrictive license from a patent holder. Therefore, we insist that
|
|
||||||
any patent license obtained for a version of the library must be
|
|
||||||
consistent with the full freedom of use specified in this license.
|
|
||||||
|
|
||||||
Most GNU software, including some libraries, is covered by the
|
|
||||||
ordinary GNU General Public License. This license, the GNU Lesser
|
|
||||||
General Public License, applies to certain designated libraries, and
|
|
||||||
is quite different from the ordinary General Public License. We use
|
|
||||||
this license for certain libraries in order to permit linking those
|
|
||||||
libraries into non-free programs.
|
|
||||||
|
|
||||||
When a program is linked with a library, whether statically or using
|
|
||||||
a shared library, the combination of the two is legally speaking a
|
|
||||||
combined work, a derivative of the original library. The ordinary
|
|
||||||
General Public License therefore permits such linking only if the
|
|
||||||
entire combination fits its criteria of freedom. The Lesser General
|
|
||||||
Public License permits more lax criteria for linking other code with
|
|
||||||
the library.
|
|
||||||
|
|
||||||
We call this license the "Lesser" General Public License because it
|
|
||||||
does Less to protect the user's freedom than the ordinary General
|
|
||||||
Public License. It also provides other free software developers Less
|
|
||||||
of an advantage over competing non-free programs. These disadvantages
|
|
||||||
are the reason we use the ordinary General Public License for many
|
|
||||||
libraries. However, the Lesser license provides advantages in certain
|
|
||||||
special circumstances.
|
|
||||||
|
|
||||||
For example, on rare occasions, there may be a special need to
|
|
||||||
encourage the widest possible use of a certain library, so that it becomes
|
|
||||||
a de-facto standard. To achieve this, non-free programs must be
|
|
||||||
allowed to use the library. A more frequent case is that a free
|
|
||||||
library does the same job as widely used non-free libraries. In this
|
|
||||||
case, there is little to gain by limiting the free library to free
|
|
||||||
software only, so we use the Lesser General Public License.
|
|
||||||
|
|
||||||
In other cases, permission to use a particular library in non-free
|
|
||||||
programs enables a greater number of people to use a large body of
|
|
||||||
free software. For example, permission to use the GNU C Library in
|
|
||||||
non-free programs enables many more people to use the whole GNU
|
|
||||||
operating system, as well as its variant, the GNU/Linux operating
|
|
||||||
system.
|
|
||||||
|
|
||||||
Although the Lesser General Public License is Less protective of the
|
|
||||||
users' freedom, it does ensure that the user of a program that is
|
|
||||||
linked with the Library has the freedom and the wherewithal to run
|
|
||||||
that program using a modified version of the Library.
|
|
||||||
|
|
||||||
The precise terms and conditions for copying, distribution and
|
|
||||||
modification follow. Pay close attention to the difference between a
|
|
||||||
"work based on the library" and a "work that uses the library". The
|
|
||||||
former contains code derived from the library, whereas the latter must
|
|
||||||
be combined with the library in order to run.
|
|
||||||
|
|
||||||
GNU LESSER GENERAL PUBLIC LICENSE
|
|
||||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
|
||||||
|
|
||||||
0. This License Agreement applies to any software library or other
|
|
||||||
program which contains a notice placed by the copyright holder or
|
|
||||||
other authorized party saying it may be distributed under the terms of
|
|
||||||
this Lesser General Public License (also called "this License").
|
|
||||||
Each licensee is addressed as "you".
|
|
||||||
|
|
||||||
A "library" means a collection of software functions and/or data
|
|
||||||
prepared so as to be conveniently linked with application programs
|
|
||||||
(which use some of those functions and data) to form executables.
|
|
||||||
|
|
||||||
The "Library", below, refers to any such software library or work
|
|
||||||
which has been distributed under these terms. A "work based on the
|
|
||||||
Library" means either the Library or any derivative work under
|
|
||||||
copyright law: that is to say, a work containing the Library or a
|
|
||||||
portion of it, either verbatim or with modifications and/or translated
|
|
||||||
straightforwardly into another language. (Hereinafter, translation is
|
|
||||||
included without limitation in the term "modification".)
|
|
||||||
|
|
||||||
"Source code" for a work means the preferred form of the work for
|
|
||||||
making modifications to it. For a library, complete source code means
|
|
||||||
all the source code for all modules it contains, plus any associated
|
|
||||||
interface definition files, plus the scripts used to control compilation
|
|
||||||
and installation of the library.
|
|
||||||
|
|
||||||
Activities other than copying, distribution and modification are not
|
|
||||||
covered by this License; they are outside its scope. The act of
|
|
||||||
running a program using the Library is not restricted, and output from
|
|
||||||
such a program is covered only if its contents constitute a work based
|
|
||||||
on the Library (independent of the use of the Library in a tool for
|
|
||||||
writing it). Whether that is true depends on what the Library does
|
|
||||||
and what the program that uses the Library does.
|
|
||||||
|
|
||||||
1. You may copy and distribute verbatim copies of the Library's
|
|
||||||
complete source code as you receive it, in any medium, provided that
|
|
||||||
you conspicuously and appropriately publish on each copy an
|
|
||||||
appropriate copyright notice and disclaimer of warranty; keep intact
|
|
||||||
all the notices that refer to this License and to the absence of any
|
|
||||||
warranty; and distribute a copy of this License along with the
|
|
||||||
Library.
|
|
||||||
|
|
||||||
You may charge a fee for the physical act of transferring a copy,
|
|
||||||
and you may at your option offer warranty protection in exchange for a
|
|
||||||
fee.
|
|
||||||
|
|
||||||
2. You may modify your copy or copies of the Library or any portion
|
|
||||||
of it, thus forming a work based on the Library, and copy and
|
|
||||||
distribute such modifications or work under the terms of Section 1
|
|
||||||
above, provided that you also meet all of these conditions:
|
|
||||||
|
|
||||||
a) The modified work must itself be a software library.
|
|
||||||
|
|
||||||
b) You must cause the files modified to carry prominent notices
|
|
||||||
stating that you changed the files and the date of any change.
|
|
||||||
|
|
||||||
c) You must cause the whole of the work to be licensed at no
|
|
||||||
charge to all third parties under the terms of this License.
|
|
||||||
|
|
||||||
d) If a facility in the modified Library refers to a function or a
|
|
||||||
table of data to be supplied by an application program that uses
|
|
||||||
the facility, other than as an argument passed when the facility
|
|
||||||
is invoked, then you must make a good faith effort to ensure that,
|
|
||||||
in the event an application does not supply such function or
|
|
||||||
table, the facility still operates, and performs whatever part of
|
|
||||||
its purpose remains meaningful.
|
|
||||||
|
|
||||||
(For example, a function in a library to compute square roots has
|
|
||||||
a purpose that is entirely well-defined independent of the
|
|
||||||
application. Therefore, Subsection 2d requires that any
|
|
||||||
application-supplied function or table used by this function must
|
|
||||||
be optional: if the application does not supply it, the square
|
|
||||||
root function must still compute square roots.)
|
|
||||||
|
|
||||||
These requirements apply to the modified work as a whole. If
|
|
||||||
identifiable sections of that work are not derived from the Library,
|
|
||||||
and can be reasonably considered independent and separate works in
|
|
||||||
themselves, then this License, and its terms, do not apply to those
|
|
||||||
sections when you distribute them as separate works. But when you
|
|
||||||
distribute the same sections as part of a whole which is a work based
|
|
||||||
on the Library, the distribution of the whole must be on the terms of
|
|
||||||
this License, whose permissions for other licensees extend to the
|
|
||||||
entire whole, and thus to each and every part regardless of who wrote
|
|
||||||
it.
|
|
||||||
|
|
||||||
Thus, it is not the intent of this section to claim rights or contest
|
|
||||||
your rights to work written entirely by you; rather, the intent is to
|
|
||||||
exercise the right to control the distribution of derivative or
|
|
||||||
collective works based on the Library.
|
|
||||||
|
|
||||||
In addition, mere aggregation of another work not based on the Library
|
|
||||||
with the Library (or with a work based on the Library) on a volume of
|
|
||||||
a storage or distribution medium does not bring the other work under
|
|
||||||
the scope of this License.
|
|
||||||
|
|
||||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
|
||||||
License instead of this License to a given copy of the Library. To do
|
|
||||||
this, you must alter all the notices that refer to this License, so
|
|
||||||
that they refer to the ordinary GNU General Public License, version 2,
|
|
||||||
instead of to this License. (If a newer version than version 2 of the
|
|
||||||
ordinary GNU General Public License has appeared, then you can specify
|
|
||||||
that version instead if you wish.) Do not make any other change in
|
|
||||||
these notices.
|
|
||||||
|
|
||||||
Once this change is made in a given copy, it is irreversible for
|
|
||||||
that copy, so the ordinary GNU General Public License applies to all
|
|
||||||
subsequent copies and derivative works made from that copy.
|
|
||||||
|
|
||||||
This option is useful when you wish to copy part of the code of
|
|
||||||
the Library into a program that is not a library.
|
|
||||||
|
|
||||||
4. You may copy and distribute the Library (or a portion or
|
|
||||||
derivative of it, under Section 2) in object code or executable form
|
|
||||||
under the terms of Sections 1 and 2 above provided that you accompany
|
|
||||||
it with the complete corresponding machine-readable source code, which
|
|
||||||
must be distributed under the terms of Sections 1 and 2 above on a
|
|
||||||
medium customarily used for software interchange.
|
|
||||||
|
|
||||||
If distribution of object code is made by offering access to copy
|
|
||||||
from a designated place, then offering equivalent access to copy the
|
|
||||||
source code from the same place satisfies the requirement to
|
|
||||||
distribute the source code, even though third parties are not
|
|
||||||
compelled to copy the source along with the object code.
|
|
||||||
|
|
||||||
5. A program that contains no derivative of any portion of the
|
|
||||||
Library, but is designed to work with the Library by being compiled or
|
|
||||||
linked with it, is called a "work that uses the Library". Such a
|
|
||||||
work, in isolation, is not a derivative work of the Library, and
|
|
||||||
therefore falls outside the scope of this License.
|
|
||||||
|
|
||||||
However, linking a "work that uses the Library" with the Library
|
|
||||||
creates an executable that is a derivative of the Library (because it
|
|
||||||
contains portions of the Library), rather than a "work that uses the
|
|
||||||
library". The executable is therefore covered by this License.
|
|
||||||
Section 6 states terms for distribution of such executables.
|
|
||||||
|
|
||||||
When a "work that uses the Library" uses material from a header file
|
|
||||||
that is part of the Library, the object code for the work may be a
|
|
||||||
derivative work of the Library even though the source code is not.
|
|
||||||
Whether this is true is especially significant if the work can be
|
|
||||||
linked without the Library, or if the work is itself a library. The
|
|
||||||
threshold for this to be true is not precisely defined by law.
|
|
||||||
|
|
||||||
If such an object file uses only numerical parameters, data
|
|
||||||
structure layouts and accessors, and small macros and small inline
|
|
||||||
functions (ten lines or less in length), then the use of the object
|
|
||||||
file is unrestricted, regardless of whether it is legally a derivative
|
|
||||||
work. (Executables containing this object code plus portions of the
|
|
||||||
Library will still fall under Section 6.)
|
|
||||||
|
|
||||||
Otherwise, if the work is a derivative of the Library, you may
|
|
||||||
distribute the object code for the work under the terms of Section 6.
|
|
||||||
Any executables containing that work also fall under Section 6,
|
|
||||||
whether or not they are linked directly with the Library itself.
|
|
||||||
|
|
||||||
6. As an exception to the Sections above, you may also combine or
|
|
||||||
link a "work that uses the Library" with the Library to produce a
|
|
||||||
work containing portions of the Library, and distribute that work
|
|
||||||
under terms of your choice, provided that the terms permit
|
|
||||||
modification of the work for the customer's own use and reverse
|
|
||||||
engineering for debugging such modifications.
|
|
||||||
|
|
||||||
You must give prominent notice with each copy of the work that the
|
|
||||||
Library is used in it and that the Library and its use are covered by
|
|
||||||
this License. You must supply a copy of this License. If the work
|
|
||||||
during execution displays copyright notices, you must include the
|
|
||||||
copyright notice for the Library among them, as well as a reference
|
|
||||||
directing the user to the copy of this License. Also, you must do one
|
|
||||||
of these things:
|
|
||||||
|
|
||||||
a) Accompany the work with the complete corresponding
|
|
||||||
machine-readable source code for the Library including whatever
|
|
||||||
changes were used in the work (which must be distributed under
|
|
||||||
Sections 1 and 2 above); and, if the work is an executable linked
|
|
||||||
with the Library, with the complete machine-readable "work that
|
|
||||||
uses the Library", as object code and/or source code, so that the
|
|
||||||
user can modify the Library and then relink to produce a modified
|
|
||||||
executable containing the modified Library. (It is understood
|
|
||||||
that the user who changes the contents of definitions files in the
|
|
||||||
Library will not necessarily be able to recompile the application
|
|
||||||
to use the modified definitions.)
|
|
||||||
|
|
||||||
b) Use a suitable shared library mechanism for linking with the
|
|
||||||
Library. A suitable mechanism is one that (1) uses at run time a
|
|
||||||
copy of the library already present on the user's computer system,
|
|
||||||
rather than copying library functions into the executable, and (2)
|
|
||||||
will operate properly with a modified version of the library, if
|
|
||||||
the user installs one, as long as the modified version is
|
|
||||||
interface-compatible with the version that the work was made with.
|
|
||||||
|
|
||||||
c) Accompany the work with a written offer, valid for at
|
|
||||||
least three years, to give the same user the materials
|
|
||||||
specified in Subsection 6a, above, for a charge no more
|
|
||||||
than the cost of performing this distribution.
|
|
||||||
|
|
||||||
d) If distribution of the work is made by offering access to copy
|
|
||||||
from a designated place, offer equivalent access to copy the above
|
|
||||||
specified materials from the same place.
|
|
||||||
|
|
||||||
e) Verify that the user has already received a copy of these
|
|
||||||
materials or that you have already sent this user a copy.
|
|
||||||
|
|
||||||
For an executable, the required form of the "work that uses the
|
|
||||||
Library" must include any data and utility programs needed for
|
|
||||||
reproducing the executable from it. However, as a special exception,
|
|
||||||
the materials to be distributed need not include anything that is
|
|
||||||
normally distributed (in either source or binary form) with the major
|
|
||||||
components (compiler, kernel, and so on) of the operating system on
|
|
||||||
which the executable runs, unless that component itself accompanies
|
|
||||||
the executable.
|
|
||||||
|
|
||||||
It may happen that this requirement contradicts the license
|
|
||||||
restrictions of other proprietary libraries that do not normally
|
|
||||||
accompany the operating system. Such a contradiction means you cannot
|
|
||||||
use both them and the Library together in an executable that you
|
|
||||||
distribute.
|
|
||||||
|
|
||||||
7. You may place library facilities that are a work based on the
|
|
||||||
Library side-by-side in a single library together with other library
|
|
||||||
facilities not covered by this License, and distribute such a combined
|
|
||||||
library, provided that the separate distribution of the work based on
|
|
||||||
the Library and of the other library facilities is otherwise
|
|
||||||
permitted, and provided that you do these two things:
|
|
||||||
|
|
||||||
a) Accompany the combined library with a copy of the same work
|
|
||||||
based on the Library, uncombined with any other library
|
|
||||||
facilities. This must be distributed under the terms of the
|
|
||||||
Sections above.
|
|
||||||
|
|
||||||
b) Give prominent notice with the combined library of the fact
|
|
||||||
that part of it is a work based on the Library, and explaining
|
|
||||||
where to find the accompanying uncombined form of the same work.
|
|
||||||
|
|
||||||
8. You may not copy, modify, sublicense, link with, or distribute
|
|
||||||
the Library except as expressly provided under this License. Any
|
|
||||||
attempt otherwise to copy, modify, sublicense, link with, or
|
|
||||||
distribute the Library is void, and will automatically terminate your
|
|
||||||
rights under this License. However, parties who have received copies,
|
|
||||||
or rights, from you under this License will not have their licenses
|
|
||||||
terminated so long as such parties remain in full compliance.
|
|
||||||
|
|
||||||
9. You are not required to accept this License, since you have not
|
|
||||||
signed it. However, nothing else grants you permission to modify or
|
|
||||||
distribute the Library or its derivative works. These actions are
|
|
||||||
prohibited by law if you do not accept this License. Therefore, by
|
|
||||||
modifying or distributing the Library (or any work based on the
|
|
||||||
Library), you indicate your acceptance of this License to do so, and
|
|
||||||
all its terms and conditions for copying, distributing or modifying
|
|
||||||
the Library or works based on it.
|
|
||||||
|
|
||||||
10. Each time you redistribute the Library (or any work based on the
|
|
||||||
Library), the recipient automatically receives a license from the
|
|
||||||
original licensor to copy, distribute, link with or modify the Library
|
|
||||||
subject to these terms and conditions. You may not impose any further
|
|
||||||
restrictions on the recipients' exercise of the rights granted herein.
|
|
||||||
You are not responsible for enforcing compliance by third parties with
|
|
||||||
this License.
|
|
||||||
|
|
||||||
11. If, as a consequence of a court judgment or allegation of patent
|
|
||||||
infringement or for any other reason (not limited to patent issues),
|
|
||||||
conditions are imposed on you (whether by court order, agreement or
|
|
||||||
otherwise) that contradict the conditions of this License, they do not
|
|
||||||
excuse you from the conditions of this License. If you cannot
|
|
||||||
distribute so as to satisfy simultaneously your obligations under this
|
|
||||||
License and any other pertinent obligations, then as a consequence you
|
|
||||||
may not distribute the Library at all. For example, if a patent
|
|
||||||
license would not permit royalty-free redistribution of the Library by
|
|
||||||
all those who receive copies directly or indirectly through you, then
|
|
||||||
the only way you could satisfy both it and this License would be to
|
|
||||||
refrain entirely from distribution of the Library.
|
|
||||||
|
|
||||||
If any portion of this section is held invalid or unenforceable under any
|
|
||||||
particular circumstance, the balance of the section is intended to apply,
|
|
||||||
and the section as a whole is intended to apply in other circumstances.
|
|
||||||
|
|
||||||
It is not the purpose of this section to induce you to infringe any
|
|
||||||
patents or other property right claims or to contest validity of any
|
|
||||||
such claims; this section has the sole purpose of protecting the
|
|
||||||
integrity of the free software distribution system which is
|
|
||||||
implemented by public license practices. Many people have made
|
|
||||||
generous contributions to the wide range of software distributed
|
|
||||||
through that system in reliance on consistent application of that
|
|
||||||
system; it is up to the author/donor to decide if he or she is willing
|
|
||||||
to distribute software through any other system and a licensee cannot
|
|
||||||
impose that choice.
|
|
||||||
|
|
||||||
This section is intended to make thoroughly clear what is believed to
|
|
||||||
be a consequence of the rest of this License.
|
|
||||||
|
|
||||||
12. If the distribution and/or use of the Library is restricted in
|
|
||||||
certain countries either by patents or by copyrighted interfaces, the
|
|
||||||
original copyright holder who places the Library under this License may add
|
|
||||||
an explicit geographical distribution limitation excluding those countries,
|
|
||||||
so that distribution is permitted only in or among countries not thus
|
|
||||||
excluded. In such case, this License incorporates the limitation as if
|
|
||||||
written in the body of this License.
|
|
||||||
|
|
||||||
13. The Free Software Foundation may publish revised and/or new
|
|
||||||
versions of the Lesser General Public License from time to time.
|
|
||||||
Such new versions will be similar in spirit to the present version,
|
|
||||||
but may differ in detail to address new problems or concerns.
|
|
||||||
|
|
||||||
Each version is given a distinguishing version number. If the Library
|
|
||||||
specifies a version number of this License which applies to it and
|
|
||||||
"any later version", you have the option of following the terms and
|
|
||||||
conditions either of that version or of any later version published by
|
|
||||||
the Free Software Foundation. If the Library does not specify a
|
|
||||||
license version number, you may choose any version ever published by
|
|
||||||
the Free Software Foundation.
|
|
||||||
|
|
||||||
14. If you wish to incorporate parts of the Library into other free
|
|
||||||
programs whose distribution conditions are incompatible with these,
|
|
||||||
write to the author to ask for permission. For software which is
|
|
||||||
copyrighted by the Free Software Foundation, write to the Free
|
|
||||||
Software Foundation; we sometimes make exceptions for this. Our
|
|
||||||
decision will be guided by the two goals of preserving the free status
|
|
||||||
of all derivatives of our free software and of promoting the sharing
|
|
||||||
and reuse of software generally.
|
|
||||||
|
|
||||||
NO WARRANTY
|
|
||||||
|
|
||||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
|
||||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
|
||||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
|
||||||
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
|
|
||||||
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
||||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
|
|
||||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
|
||||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
|
||||||
|
|
||||||
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
|
|
||||||
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
|
|
||||||
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
|
|
||||||
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
|
|
||||||
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
|
|
||||||
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
|
|
||||||
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
|
|
||||||
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
|
||||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
|
||||||
DAMAGES.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
How to Apply These Terms to Your New Libraries
|
|
||||||
|
|
||||||
If you develop a new library, and you want it to be of the greatest
|
|
||||||
possible use to the public, we recommend making it free software that
|
|
||||||
everyone can redistribute and change. You can do so by permitting
|
|
||||||
redistribution under these terms (or, alternatively, under the terms of the
|
|
||||||
ordinary General Public License).
|
|
||||||
|
|
||||||
To apply these terms, attach the following notices to the library. It is
|
|
||||||
safest to attach them to the start of each source file to most effectively
|
|
||||||
convey the exclusion of warranty; and each file should have at least the
|
|
||||||
"copyright" line and a pointer to where the full notice is found.
|
|
||||||
|
|
||||||
<one line to give the library's name and a brief idea of what it does.>
|
|
||||||
Copyright (C) <year> <name of author>
|
|
||||||
|
|
||||||
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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
||||||
|
|
||||||
Also add information on how to contact you by electronic and paper mail.
|
|
||||||
|
|
||||||
You should also get your employer (if you work as a programmer) or your
|
|
||||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
|
||||||
necessary. Here is a sample; alter the names:
|
|
||||||
|
|
||||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
|
||||||
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
|
|
||||||
|
|
||||||
<signature of Ty Coon>, 1 April 1990
|
|
||||||
Ty Coon, President of Vice
|
|
||||||
|
|
||||||
That's all there is to it!
|
|
||||||
|
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
2010-12-30 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.5.1
|
|
||||||
|
|
||||||
* Bigger changes:
|
|
||||||
- Fixed some issues with the scope of variables leading to problems when multiple instances of b8 are created. Thanks to Mike Creuzer for the bug report :-)
|
|
||||||
- Centralized the loading of class definition files in the b8 constructor and created a function to handle the inclusion.
|
|
||||||
|
|
||||||
* b8.php: Return a lexer error code instead of a rating if the lexer failed. The lexer never returned FALSE but b8 checked only for this value to validate the lexer didn't fail. Thanks to Matt Friedman for the bug report :-)
|
|
||||||
|
|
||||||
* lexer/lexer_default.php: A bit of code cleanup: less useless nesting.
|
|
||||||
|
|
||||||
* doc/readme.*: Updated the documentation, added a FAQ.
|
|
||||||
|
|
||||||
2010-06-27 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.5-r1
|
|
||||||
|
|
||||||
* doc/readme.*: Updated the documentation; forgot the newly introduced b8::HAM and b8::SPAM variables. Added some additional information about the storage model.
|
|
||||||
|
|
||||||
2010-06-02 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.5
|
|
||||||
|
|
||||||
* 100.000 Changes (new major release!), at a glance:
|
|
||||||
- No PHP 4 compatibility anymore. Much cleaner code base with less hacks.
|
|
||||||
- Completely reworked storage model. The SQL performance increased dramatically, the Berkeley DB performance remains as fast as it always has been.
|
|
||||||
- Better lexer which can also handle non-latin1 texts in a nice way, so that e. g. Cyrillic or Chinese texts can be classified more performant.
|
|
||||||
- No config files anymore, multiple instances of b8 can be now created in the same script with different configuration, databases and no problems.
|
|
||||||
- No spooky administration interface anymore that needs an SQL database, even if Berkeley DB is used (anybody who actually used this?! I never did ;-).
|
|
||||||
- No "install" scripts and routines and a less end-user compatible documentation. Anybody integrating b8 in his homepage won't be an end-user, will he?
|
|
||||||
|
|
||||||
2009-02-03 Oliver Lillie (aka buggedcom)
|
|
||||||
|
|
||||||
* Revision: 221 (the original PHP 5 port)
|
|
||||||
|
|
||||||
* Rewrote Tobias' original class for optimisation and PHP 5 functionality.
|
|
||||||
|
|
||||||
* Improved database mysql query useage by over ~820%
|
|
||||||
|
|
||||||
* Class is faster, ~20%.
|
|
||||||
|
|
||||||
* Slight increase in memory usage, but it's small and given the advantages of the speed increase and query reduction it's worth it.
|
|
||||||
|
|
||||||
* Removed install code from mysql class and added a sql file. Anyone who wants to use this is generally going to be more advanced anyway and see the sql to install.
|
|
||||||
|
|
||||||
2009-02-03 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.4.4 -- changed the license type from GPL to LGPL
|
|
||||||
|
|
||||||
2008-06-27 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.4.3 -- no bugs found ... so let's make a release with only small changes ;-)
|
|
||||||
|
|
||||||
* b8.php: Removed debugging messages that were commented out anyway
|
|
||||||
|
|
||||||
* storage/storage_mysql.php: Made it possible to pass both a MySQL-link resource and a table name to b8. This makes b8 useable in the Redaxo CMS (and probably others)
|
|
||||||
|
|
||||||
* doc/readme.htm: Updated documentation accordingly
|
|
||||||
|
|
||||||
2008-02-17 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.4.2
|
|
||||||
|
|
||||||
* interface/backup.php: the bayes*dbversion tag is now written to a database emptied by drop(), so that it will be useable without an error message even if no backup is recovered afterwards.
|
|
||||||
|
|
||||||
* doc/readme.htm: added a security note to the configuration section (htaccess should be used to avoid everybody to be able to see the configuration)
|
|
||||||
|
|
||||||
2007-09-17 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.4.1
|
|
||||||
|
|
||||||
* storage/storage_mysql.php: fixed b8 crashing when getting passed a persistent MySQL resource link. Thanks to Paul Chapman for the bug report :-)
|
|
||||||
|
|
||||||
2007-06-08 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.4
|
|
||||||
|
|
||||||
* Let's go the whole hog. b8's class is now "b8" and no more "bayes", and all internal variables have now according names.
|
|
||||||
|
|
||||||
* Reworked the whole (surprisingly crappy) implementation of b8. No more global() calls, everything happens inside the classes now. Made that whole stuff really object oriented (as good as possible with PHP's poor OOP model ;-).
|
|
||||||
|
|
||||||
* No more PHP code in the configuration files.
|
|
||||||
|
|
||||||
* Created an extra lexer class. This is now also configurable.
|
|
||||||
|
|
||||||
* Storage classes now can create their own databases when this is requested by the configuration.
|
|
||||||
|
|
||||||
* MySQL calls are no random shots anymore: either, a MySQL-link resource is passed to b8 on startup which will be used for the queries, or the class sets up it's own link. Same for SQLite.
|
|
||||||
|
|
||||||
* The interface now uses a separate storage backend capable of SQL. In this way, we _really_ can query the database for e. g. an ordered list of tokens. After doing what we wanted with this work database, the b8 database can be synced with it.
|
|
||||||
|
|
||||||
* Added a lot of verbose error handling.
|
|
||||||
|
|
||||||
* Fixed a dumb error: all tokens from a text were used for the spamminess calculation, because two for() loops both used $i as their counter. D'oh!!! Now, the filter's performance is way better.
|
|
||||||
|
|
||||||
* Catched on the way how that whole math stuff works a little more ;-) Now, the calculation of the single probabilities proposed by Mr. Robinson does a little more the stuff it was intended to do, because ...
|
|
||||||
|
|
||||||
* Made some calculation constants parameters: the number of tokens to use, the default rating for unknown tokens and Gary Robinson's s constant.
|
|
||||||
|
|
||||||
* Introduced an optional minimum deviation that a token's rating must have to be considered in the spamminess calculation.
|
|
||||||
|
|
||||||
* The default extreme ratings for tokens only in ham or spam are now optional. One can also choose to calculate all ratings by Mr. Robinson's method.
|
|
||||||
|
|
||||||
* Noticed that text primary keys are not case sensitive by default in MySQL, which has a noticeable impact on the filter's performance. Informed the MySQL users about that.
|
|
||||||
|
|
||||||
* The whole code sucks much less ;-) b8 should be way more user friendly now.
|
|
||||||
|
|
||||||
* Re-wrote the whole documentation.
|
|
||||||
|
|
||||||
* Fixed the ChangeLog :-)
|
|
||||||
|
|
||||||
2007-02-08 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.3.3 again ;-)
|
|
||||||
|
|
||||||
* bayes-php is now b8. See http://www.nasauber.de/blog/text.php?text=58 for details :-) Thanks to Tobias Lang (http://langt.net/) for this cool new name!
|
|
||||||
|
|
||||||
2007-01-05 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.3.3
|
|
||||||
|
|
||||||
* Renamed the internal BerkeleyDB handle from "$db" to the less general name "$bayes_php_db" due to an collision with phpwcms's (http://www.phpwcms.de/) global $db variable and potentially other php programs.
|
|
||||||
|
|
||||||
* Commented out Laurent Goussard's SQLite storage class by default, as it's try { } catch { } calls break PHP 4
|
|
||||||
|
|
||||||
2006-09-03 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.3.2
|
|
||||||
|
|
||||||
* Laurent Goussard (loranger@free.fr) contributed an SQLite storage class(which needs PHP 5).
|
|
||||||
|
|
||||||
* I finally added my eMail address to the sources ;-)
|
|
||||||
|
|
||||||
2006-07-24 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.3.1
|
|
||||||
|
|
||||||
* Fixed a problem in the unlearn() function: If a text was unlearned that wasn't learned before (accidentaly), it could happen that the count parameter for this text was smaller than 0, breaking the spamminess calulation
|
|
||||||
|
|
||||||
2006-07-02 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.3
|
|
||||||
|
|
||||||
* Improved the get_tokens() function; the filter should now be a lot more performant, especially with short texts
|
|
||||||
|
|
||||||
* Added the "lastseen" parameter for each token to make the database maintainable (outdated tokens can be deleted)
|
|
||||||
|
|
||||||
* Added a real database maintainance interface
|
|
||||||
|
|
||||||
2006-06-12 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.2.1
|
|
||||||
|
|
||||||
* Fixed a problem in get_tokens() (if it was called more than once, tokens were counted more often than they appeared in the text)
|
|
||||||
|
|
||||||
* Slightly enhanced the default index.php interface: after learning a text as Ham or Spam, the rating before and after it is displayed to inform the user about it
|
|
||||||
|
|
||||||
2006-05-21 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.2
|
|
||||||
|
|
||||||
* Comments now in English (to pretend international success of bayes-php ;-)
|
|
||||||
|
|
||||||
* Recommendations of Paul Graham's article "Better Bayesian Filtering" ( http://www.paulgraham.com/better.html ) are now considered: Tokens that only appear in Ham or Spam and not in the other category are rated with 0.9998 or 0.0002 if they were less than 10 times in Ham or Spam and with 0.9999 or 0.0001 if they appeared more that 10 times. This should allow the filter to differentiate spam texts more sharp from ham texts. Also, token "degeneration" as described in the article is performed for unknown tokens to estimate their spamminess.
|
|
||||||
|
|
||||||
* The database connect is now swapped in a separate configuration file, so only this file has to be preserved if bayes-php is updated and only this file has to be changed to configure the script.
|
|
||||||
|
|
||||||
2006-03-29 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Release: Version 0.1.1
|
|
||||||
|
|
||||||
* get_tokens() beachtet jetzt auch HTML-Tags und Wörter mit Akzenten und Apostrophen
|
|
||||||
|
|
||||||
* Verschiedene Kleinigkeiten "sauber" gemacht :-)
|
|
||||||
|
|
||||||
2006-03-05 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
|
|
||||||
* Added 2007-06-08: Initial release (Version 0.1)
|
|
|
@ -1,707 +0,0 @@
|
||||||
<?xml version="1.0" encoding="utf-8" ?>
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
|
||||||
<head>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
|
||||||
<meta name="generator" content="Docutils 0.7: http://docutils.sourceforge.net/" />
|
|
||||||
<title>b8: readme</title>
|
|
||||||
<meta name="author" content="Tobias Leupold" />
|
|
||||||
<meta name="date" content="2010-12-23" />
|
|
||||||
<style type="text/css">
|
|
||||||
|
|
||||||
/*
|
|
||||||
:Author: David Goodger (goodger@python.org)
|
|
||||||
:Id: $Id: html4css1.css 6253 2010-03-02 00:24:53Z milde $
|
|
||||||
:Copyright: This stylesheet has been placed in the public domain.
|
|
||||||
|
|
||||||
Default cascading style sheet for the HTML output of Docutils.
|
|
||||||
|
|
||||||
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
|
|
||||||
customize this style sheet.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* used to remove borders from tables and images */
|
|
||||||
.borderless, table.borderless td, table.borderless th {
|
|
||||||
border: 0 }
|
|
||||||
|
|
||||||
table.borderless td, table.borderless th {
|
|
||||||
/* Override padding for "table.docutils td" with "! important".
|
|
||||||
The right padding separates the table cells. */
|
|
||||||
padding: 0 0.5em 0 0 ! important }
|
|
||||||
|
|
||||||
.first {
|
|
||||||
/* Override more specific margin styles with "! important". */
|
|
||||||
margin-top: 0 ! important }
|
|
||||||
|
|
||||||
.last, .with-subtitle {
|
|
||||||
margin-bottom: 0 ! important }
|
|
||||||
|
|
||||||
.hidden {
|
|
||||||
display: none }
|
|
||||||
|
|
||||||
a.toc-backref {
|
|
||||||
text-decoration: none ;
|
|
||||||
color: black }
|
|
||||||
|
|
||||||
blockquote.epigraph {
|
|
||||||
margin: 2em 5em ; }
|
|
||||||
|
|
||||||
dl.docutils dd {
|
|
||||||
margin-bottom: 0.5em }
|
|
||||||
|
|
||||||
/* Uncomment (and remove this text!) to get bold-faced definition list terms
|
|
||||||
dl.docutils dt {
|
|
||||||
font-weight: bold }
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.abstract {
|
|
||||||
margin: 2em 5em }
|
|
||||||
|
|
||||||
div.abstract p.topic-title {
|
|
||||||
font-weight: bold ;
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
div.admonition, div.attention, div.caution, div.danger, div.error,
|
|
||||||
div.hint, div.important, div.note, div.tip, div.warning {
|
|
||||||
margin: 2em ;
|
|
||||||
border: medium outset ;
|
|
||||||
padding: 1em }
|
|
||||||
|
|
||||||
div.admonition p.admonition-title, div.hint p.admonition-title,
|
|
||||||
div.important p.admonition-title, div.note p.admonition-title,
|
|
||||||
div.tip p.admonition-title {
|
|
||||||
font-weight: bold ;
|
|
||||||
font-family: sans-serif }
|
|
||||||
|
|
||||||
div.attention p.admonition-title, div.caution p.admonition-title,
|
|
||||||
div.danger p.admonition-title, div.error p.admonition-title,
|
|
||||||
div.warning p.admonition-title {
|
|
||||||
color: red ;
|
|
||||||
font-weight: bold ;
|
|
||||||
font-family: sans-serif }
|
|
||||||
|
|
||||||
/* Uncomment (and remove this text!) to get reduced vertical space in
|
|
||||||
compound paragraphs.
|
|
||||||
div.compound .compound-first, div.compound .compound-middle {
|
|
||||||
margin-bottom: 0.5em }
|
|
||||||
|
|
||||||
div.compound .compound-last, div.compound .compound-middle {
|
|
||||||
margin-top: 0.5em }
|
|
||||||
*/
|
|
||||||
|
|
||||||
div.dedication {
|
|
||||||
margin: 2em 5em ;
|
|
||||||
text-align: center ;
|
|
||||||
font-style: italic }
|
|
||||||
|
|
||||||
div.dedication p.topic-title {
|
|
||||||
font-weight: bold ;
|
|
||||||
font-style: normal }
|
|
||||||
|
|
||||||
div.figure {
|
|
||||||
margin-left: 2em ;
|
|
||||||
margin-right: 2em }
|
|
||||||
|
|
||||||
div.footer, div.header {
|
|
||||||
clear: both;
|
|
||||||
font-size: smaller }
|
|
||||||
|
|
||||||
div.line-block {
|
|
||||||
display: block ;
|
|
||||||
margin-top: 1em ;
|
|
||||||
margin-bottom: 1em }
|
|
||||||
|
|
||||||
div.line-block div.line-block {
|
|
||||||
margin-top: 0 ;
|
|
||||||
margin-bottom: 0 ;
|
|
||||||
margin-left: 1.5em }
|
|
||||||
|
|
||||||
div.sidebar {
|
|
||||||
margin: 0 0 0.5em 1em ;
|
|
||||||
border: medium outset ;
|
|
||||||
padding: 1em ;
|
|
||||||
background-color: #ffffee ;
|
|
||||||
width: 40% ;
|
|
||||||
float: right ;
|
|
||||||
clear: right }
|
|
||||||
|
|
||||||
div.sidebar p.rubric {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-size: medium }
|
|
||||||
|
|
||||||
div.system-messages {
|
|
||||||
margin: 5em }
|
|
||||||
|
|
||||||
div.system-messages h1 {
|
|
||||||
color: red }
|
|
||||||
|
|
||||||
div.system-message {
|
|
||||||
border: medium outset ;
|
|
||||||
padding: 1em }
|
|
||||||
|
|
||||||
div.system-message p.system-message-title {
|
|
||||||
color: red ;
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
div.topic {
|
|
||||||
margin: 2em }
|
|
||||||
|
|
||||||
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
|
|
||||||
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
|
|
||||||
margin-top: 0.4em }
|
|
||||||
|
|
||||||
h1.title {
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
h2.subtitle {
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
hr.docutils {
|
|
||||||
width: 75% }
|
|
||||||
|
|
||||||
img.align-left, .figure.align-left, object.align-left {
|
|
||||||
clear: left ;
|
|
||||||
float: left ;
|
|
||||||
margin-right: 1em }
|
|
||||||
|
|
||||||
img.align-right, .figure.align-right, object.align-right {
|
|
||||||
clear: right ;
|
|
||||||
float: right ;
|
|
||||||
margin-left: 1em }
|
|
||||||
|
|
||||||
img.align-center, .figure.align-center, object.align-center {
|
|
||||||
display: block;
|
|
||||||
margin-left: auto;
|
|
||||||
margin-right: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.align-left {
|
|
||||||
text-align: left }
|
|
||||||
|
|
||||||
.align-center {
|
|
||||||
clear: both ;
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
.align-right {
|
|
||||||
text-align: right }
|
|
||||||
|
|
||||||
/* reset inner alignment in figures */
|
|
||||||
div.align-right {
|
|
||||||
text-align: left }
|
|
||||||
|
|
||||||
/* div.align-center * { */
|
|
||||||
/* text-align: left } */
|
|
||||||
|
|
||||||
ol.simple, ul.simple {
|
|
||||||
margin-bottom: 1em }
|
|
||||||
|
|
||||||
ol.arabic {
|
|
||||||
list-style: decimal }
|
|
||||||
|
|
||||||
ol.loweralpha {
|
|
||||||
list-style: lower-alpha }
|
|
||||||
|
|
||||||
ol.upperalpha {
|
|
||||||
list-style: upper-alpha }
|
|
||||||
|
|
||||||
ol.lowerroman {
|
|
||||||
list-style: lower-roman }
|
|
||||||
|
|
||||||
ol.upperroman {
|
|
||||||
list-style: upper-roman }
|
|
||||||
|
|
||||||
p.attribution {
|
|
||||||
text-align: right ;
|
|
||||||
margin-left: 50% }
|
|
||||||
|
|
||||||
p.caption {
|
|
||||||
font-style: italic }
|
|
||||||
|
|
||||||
p.credits {
|
|
||||||
font-style: italic ;
|
|
||||||
font-size: smaller }
|
|
||||||
|
|
||||||
p.label {
|
|
||||||
white-space: nowrap }
|
|
||||||
|
|
||||||
p.rubric {
|
|
||||||
font-weight: bold ;
|
|
||||||
font-size: larger ;
|
|
||||||
color: maroon ;
|
|
||||||
text-align: center }
|
|
||||||
|
|
||||||
p.sidebar-title {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-weight: bold ;
|
|
||||||
font-size: larger }
|
|
||||||
|
|
||||||
p.sidebar-subtitle {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
p.topic-title {
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
pre.address {
|
|
||||||
margin-bottom: 0 ;
|
|
||||||
margin-top: 0 ;
|
|
||||||
font: inherit }
|
|
||||||
|
|
||||||
pre.literal-block, pre.doctest-block {
|
|
||||||
margin-left: 2em ;
|
|
||||||
margin-right: 2em }
|
|
||||||
|
|
||||||
span.classifier {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-style: oblique }
|
|
||||||
|
|
||||||
span.classifier-delimiter {
|
|
||||||
font-family: sans-serif ;
|
|
||||||
font-weight: bold }
|
|
||||||
|
|
||||||
span.interpreted {
|
|
||||||
font-family: sans-serif }
|
|
||||||
|
|
||||||
span.option {
|
|
||||||
white-space: nowrap }
|
|
||||||
|
|
||||||
span.pre {
|
|
||||||
white-space: pre }
|
|
||||||
|
|
||||||
span.problematic {
|
|
||||||
color: red }
|
|
||||||
|
|
||||||
span.section-subtitle {
|
|
||||||
/* font-size relative to parent (h1..h6 element) */
|
|
||||||
font-size: 80% }
|
|
||||||
|
|
||||||
table.citation {
|
|
||||||
border-left: solid 1px gray;
|
|
||||||
margin-left: 1px }
|
|
||||||
|
|
||||||
table.docinfo {
|
|
||||||
margin: 2em 4em }
|
|
||||||
|
|
||||||
table.docutils {
|
|
||||||
margin-top: 0.5em ;
|
|
||||||
margin-bottom: 0.5em }
|
|
||||||
|
|
||||||
table.footnote {
|
|
||||||
border-left: solid 1px black;
|
|
||||||
margin-left: 1px }
|
|
||||||
|
|
||||||
table.docutils td, table.docutils th,
|
|
||||||
table.docinfo td, table.docinfo th {
|
|
||||||
padding-left: 0.5em ;
|
|
||||||
padding-right: 0.5em ;
|
|
||||||
vertical-align: top }
|
|
||||||
|
|
||||||
table.docutils th.field-name, table.docinfo th.docinfo-name {
|
|
||||||
font-weight: bold ;
|
|
||||||
text-align: left ;
|
|
||||||
white-space: nowrap ;
|
|
||||||
padding-left: 0 }
|
|
||||||
|
|
||||||
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
|
|
||||||
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
|
|
||||||
font-size: 100% }
|
|
||||||
|
|
||||||
ul.auto-toc {
|
|
||||||
list-style-type: none }
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="document" id="b8-readme">
|
|
||||||
<h1 class="title">b8: readme</h1>
|
|
||||||
<table class="docinfo" frame="void" rules="none">
|
|
||||||
<col class="docinfo-name" />
|
|
||||||
<col class="docinfo-content" />
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr><th class="docinfo-name">Author:</th>
|
|
||||||
<td>Tobias Leupold</td></tr>
|
|
||||||
<tr class="field"><th class="docinfo-name">Homepage:</th><td class="field-body"><a class="reference external" href="http://nasauber.de/">http://nasauber.de/</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr><th class="docinfo-name">Contact:</th>
|
|
||||||
<td><a class="first last reference external" href="mailto:tobias.leupold@web.de">tobias.leupold@web.de</a></td></tr>
|
|
||||||
<tr><th class="docinfo-name">Date:</th>
|
|
||||||
<td>2010-12-23</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<div class="contents topic" id="table-of-contents">
|
|
||||||
<p class="topic-title first">Table of Contents</p>
|
|
||||||
<ul class="auto-toc simple">
|
|
||||||
<li><a class="reference internal" href="#description-of-b8" id="id18">1 Description of b8</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#what-is-b8" id="id19">1.1 What is b8?</a></li>
|
|
||||||
<li><a class="reference internal" href="#how-does-it-work" id="id20">1.2 How does it work?</a></li>
|
|
||||||
<li><a class="reference internal" href="#what-do-i-need-for-it" id="id21">1.3 What do I need for it?</a></li>
|
|
||||||
<li><a class="reference internal" href="#what-s-different" id="id22">1.4 What's different?</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#update-from-prior-versions" id="id23">2 Update from prior versions</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#update-from-bayes-php-version-0-2-1-or-earlier" id="id24">2.1 Update from bayes-php version 0.2.1 or earlier</a></li>
|
|
||||||
<li><a class="reference internal" href="#update-from-bayes-php-version-0-3-or-later" id="id25">2.2 Update from bayes-php version 0.3 or later</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#installation" id="id26">3 Installation</a></li>
|
|
||||||
<li><a class="reference internal" href="#configuration" id="id27">4 Configuration</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#b8-s-base-configuration" id="id28">4.1 b8's base configuration</a></li>
|
|
||||||
<li><a class="reference internal" href="#configuration-of-the-storage-backend" id="id29">4.2 Configuration of the storage backend</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#settings-for-the-berkeley-db-dba-backend" id="id30">4.2.1 Settings for the Berkeley DB (DBA) backend</a></li>
|
|
||||||
<li><a class="reference internal" href="#settings-for-the-mysql-backend" id="id31">4.2.2 Settings for the MySQL backend</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#using-b8" id="id32">5 Using b8</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#setting-up-a-new-database" id="id33">5.1 Setting up a new database</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#setting-up-a-new-berkeley-db" id="id34">5.1.1 Setting up a new Berkeley DB</a></li>
|
|
||||||
<li><a class="reference internal" href="#setting-up-a-new-mysql-table" id="id35">5.1.2 Setting up a new MySQL table</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#using-b8-in-your-scripts" id="id36">5.2 Using b8 in your scripts</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#tips-on-operation" id="id37">6 Tips on operation</a></li>
|
|
||||||
<li><a class="reference internal" href="#closing" id="id38">7 Closing</a></li>
|
|
||||||
<li><a class="reference internal" href="#references" id="id39">8 References</a></li>
|
|
||||||
<li><a class="reference internal" href="#appendix" id="id40">9 Appendix</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#faq" id="id41">9.1 FAQ</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#what-about-more-than-two-categories" id="id42">9.1.1 What about more than two categories?</a></li>
|
|
||||||
<li><a class="reference internal" href="#what-about-a-list-with-words-to-ignore" id="id43">9.1.2 What about a list with words to ignore?</a></li>
|
|
||||||
<li><a class="reference internal" href="#why-is-it-called-b8" id="id44">9.1.3 Why is it called "b8"?</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
<li><a class="reference internal" href="#about-the-database" id="id45">9.2 About the database</a><ul class="auto-toc">
|
|
||||||
<li><a class="reference internal" href="#the-database-layout" id="id46">9.2.1 The database layout</a></li>
|
|
||||||
<li><a class="reference internal" href="#the-lastseen-parameter" id="id47">9.2.2 The "lastseen" parameter</a></li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="description-of-b8">
|
|
||||||
<h1><a class="toc-backref" href="#id18">1 Description of b8</a></h1>
|
|
||||||
<div class="section" id="what-is-b8">
|
|
||||||
<h2><a class="toc-backref" href="#id19">1.1 What is b8?</a></h2>
|
|
||||||
<p>b8 is a spam filter implemented in <a class="reference external" href="http://www.php.net/">PHP</a>. It is intended to keep your weblog or guestbook spam-free. The filter can be used anywhere in your PHP code and tells you whether a text is spam or not, using statistical text analysis. See <a class="reference internal" href="#how-does-it-work">How does it work?</a> for details about this. To be able to do this, b8 first has to learn some spam and some ham example texts to decide what's good and what's not. If it makes mistakes classifying unknown texts, they can be corrected and b8 learns from the corrections, getting better with each learned text.</p>
|
|
||||||
<p>At the moment of this writing, b8 has classified 14411 guestbook entries and weblog comments on my homepage since december 2006. 131 were ham. 39 spam texts (0.27 %) have been rated as ham (false negatives), with not even one false positive (ham message classified as spam). This results in a sensitivity of 99.73 % (the probability that a spam text will actually be rated as spam) and a specifity of 100 % (the probability that a ham text will actually be rated as ham) for me. I hope, you'll get the same good results :-)</p>
|
|
||||||
<p>Basically, b8 is a statistical ("Bayesian"<a class="footnote-reference" href="#id2" id="id1">[1]</a>) spam filter like <a class="reference external" href="http://bogofilter.sourceforge.net/">Bogofilter</a> or <a class="reference external" href="http://spambayes.sourceforge.net/">SpamBayes</a>, but it is not intended to classify e-mails. When I started to write b8, I didn't find a good PHP spam filter (or any spam filter that wasn't just some example code how one <em>could</em> implement a Bayesian spam filter in PHP) that was intended to filter weblog or guestbook entries. That's why I had to write my own ;-) <br />
|
|
||||||
Caused by it's purpose, the way b8 works is slightly different from most of the Bayesian email spam filters out there. See <a class="reference internal" href="#what-s-different">What's different?</a> if you're interested in the details.</p>
|
|
||||||
<table class="docutils footnote" frame="void" id="id2" rules="none">
|
|
||||||
<colgroup><col class="label" /><col /></colgroup>
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr><td class="label"><a class="fn-backref" href="#id1">[1]</a></td><td>A mathematician told me that the math in b8 actually does not use Bayes' theorem but some derived algorithms that are just related to it. So … let's simply believe that and stop claiming b8 was a <em>Bayesian</em> spam filter ;-)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="how-does-it-work">
|
|
||||||
<h2><a class="toc-backref" href="#id20">1.2 How does it work?</a></h2>
|
|
||||||
<p>b8 basically uses the math and technique described in Paul Graham's article "A Plan For Spam" <a class="footnote-reference" href="#planforspam" id="id3">[2]</a> to distinguish ham and spam. The improvements proposed in Graham's article "Better Bayesian Filtering" <a class="footnote-reference" href="#betterbayesian" id="id4">[3]</a> and Gary Robinson's article "Spam Detection" <a class="footnote-reference" href="#spamdetection" id="id5">[4]</a> have also been considered. See also the article "A Statistical Approach to the Spam Problem" <a class="footnote-reference" href="#statisticalapproach" id="id6">[5]</a>.</p>
|
|
||||||
<p>b8 cuts the text to classify to pieces, extracting stuff like e-mail addresses, links and HTML tags. For each such token, it calculates a single probability for a text containing it being spam, based on what the filter has learned so far. When the token was not seen before, b8 tries to find similar ones using the "degeneration" described in <a class="footnote-reference" href="#betterbayesian" id="id7">[3]</a> and uses the most relevant value found. If really nothing is found, b8 assumes a default rating for this token for the further calculations. <br />
|
|
||||||
Then, b8 takes the most relevant values (which have a rating far from 0.5, which would mean we don't know what it is) and calculates the probability that the whole text is spam by the inverse chi-square function described in <a class="footnote-reference" href="#spamdetection" id="id8">[4]</a>.
|
|
||||||
There are some parameters that can be set which influence the filter's behaviour (see below).</p>
|
|
||||||
<p>In short words: you give b8 a text and it returns a value between 0 and 1, saying it's ham when it's near 0 and saying it's spam when it's near 1.</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="what-do-i-need-for-it">
|
|
||||||
<h2><a class="toc-backref" href="#id21">1.3 What do I need for it?</a></h2>
|
|
||||||
<p>Not much! You just need PHP 5 on the server where b8 will be used (b8 version 0.5 finally dropped PHP 4 compatibility – thankfully ;-) and a proper storage possibility for the wordlists. I strongly recommend using <a class="reference external" href="http://www.oracle.com/database/berkeley-db/index.html">Berkeley DB</a>. See below how you can check if you can use it and why you should use it. If the server's PHP wasn't compiled with Berkeley DB support, a <a class="reference external" href="http://mysql.com/">MySQL</a> table can be used alternatively.</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="what-s-different">
|
|
||||||
<h2><a class="toc-backref" href="#id22">1.4 What's different?</a></h2>
|
|
||||||
<p>b8 is designed to classify weblog or guestbook entries, not e-mails. For this reason, it uses a slightly different technique than most of the other statistical spam filters out there use.</p>
|
|
||||||
<p>My experience was that spam entries on my weblog or guestbook were often quite short, sometimes just something like "123abc" as text and a link to a suspect homepage. Some spam bots don't even made a difference between e. g. the "name" and "text" fields and posted their text as email address, for example. Considering this, b8 just takes one string to classify, making no difference between "headers" and "text". <br />
|
|
||||||
The other thing is that most statistical spam filters count one token one time, no matter how often it appears in the text (as Graham describes it in <a class="footnote-reference" href="#planforspam" id="id9">[2]</a>). b8 does count how often a token was seen and learns or considers this. Additionally, the number of learned ham and spam texts are saved and used as the calculation base for the single probabilities. Why this? Because a text containing one link (no matter where it points to, just indicated by a "http://" or a "www.") might not be spam, but a text containing 20 links might be.</p>
|
|
||||||
<p>This means that b8 might be good for classifying weblog or guestbook entries (I really think it is ;-) – but very likely, it will work quite poor when being used for something else (like classifying e-mails). But as said above, for this task, there are a lot of very good filters out there to choose from.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="update-from-prior-versions">
|
|
||||||
<h1><a class="toc-backref" href="#id23">2 Update from prior versions</a></h1>
|
|
||||||
<p>If this is a new b8 installation, read on at the <a class="reference internal" href="#installation">Installation</a> section!</p>
|
|
||||||
<div class="section" id="update-from-bayes-php-version-0-2-1-or-earlier">
|
|
||||||
<h2><a class="toc-backref" href="#id24">2.1 Update from bayes-php version 0.2.1 or earlier</a></h2>
|
|
||||||
<p>Please first follow the database update instructions of the bayes-php-0.3 release if you update from a version prior to bayes-php-0.3 and then read the following paragraph about updating from a version <0.3.3.</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="update-from-bayes-php-version-0-3-or-later">
|
|
||||||
<h2><a class="toc-backref" href="#id25">2.2 Update from bayes-php version 0.3 or later</a></h2>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>You use Berkeley DB?</strong></dt>
|
|
||||||
<dd>Everything's fine, you can simply continue using your database.</dd>
|
|
||||||
<dt><strong>You use MySQL?</strong></dt>
|
|
||||||
<dd>The <tt class="docutils literal">CREATE</tt> statement of b8's wordlist has changed. The best is probably to create a dump via your favorite administration tool or script, create the new table and re-insert all data. The layout is still the same: there's one "token" column and one "data" column. Having done that, you can keep using your data.</dd>
|
|
||||||
<dt><strong>You use SQLite?</strong></dt>
|
|
||||||
<dd>Sorry, at the moment, there's no SQLite backend for b8. But we're working on it :-)</dd>
|
|
||||||
</dl>
|
|
||||||
<p>The configuration model of b8 has changed. Please read through the <a class="reference internal" href="#configuration">Configuration</a> section and update your configuration accordingly.</p>
|
|
||||||
<p>b8's lexer has been partially re-written. It should now be able to handle all kind of non-latin-1 input, like cyrillic, chinese or japanese texts. Caused by this fact, much more tokens will be recognized when classifying such texts. Therefore, you could get different results in b8's ratings, even if the same database is used and although the math is still the same.</p>
|
|
||||||
<p>b8 0.5 introduced two constants that can be used in the <tt class="docutils literal">learn()</tt> and <tt class="docutils literal">unlearn()</tt> functions: <tt class="docutils literal"><span class="pre">b8::HAM</span></tt> and <tt class="docutils literal"><span class="pre">b8::SPAM</span></tt>. The literal values "ham" and "spam" can still be used anyway.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="installation">
|
|
||||||
<h1><a class="toc-backref" href="#id26">3 Installation</a></h1>
|
|
||||||
<p>Installing b8 on your server is quite easy. You just have to provide the needed files. To do this, you could just upload the whole <tt class="docutils literal">b8</tt> subdirectory to the base directory of your homepage. It contains the filter itself and all needed backend classes. The other directories (<tt class="docutils literal">doc</tt>, <tt class="docutils literal">example</tt> and <tt class="docutils literal">install</tt>) are not used by b8.</p>
|
|
||||||
<p>That's it ;-)</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="configuration">
|
|
||||||
<h1><a class="toc-backref" href="#id27">4 Configuration</a></h1>
|
|
||||||
<p>The configuration is passed as arrays when instantiating a new b8 object. Two arrays can be passed to b8, one containing b8's base configuration and some settings for the lexer (which should be common for all lexer classes, in case some other lexer than the default one will be written one day) and one for the storage backend. <br />
|
|
||||||
You can have a look at <tt class="docutils literal">example/index.php</tt> to see how this can be done. <a class="reference internal" href="#using-b8-in-your-scripts">Using b8 in your scripts</a> also shows example code showing how b8 can be included in a PHP script.</p>
|
|
||||||
<p>Not all values have to be set. When some values are missing, the default ones will be used. If you do use the default settings, you don't have to pass them to b8.</p>
|
|
||||||
<div class="section" id="b8-s-base-configuration">
|
|
||||||
<h2><a class="toc-backref" href="#id28">4.1 b8's base configuration</a></h2>
|
|
||||||
<p>All these values can be set in the "config_b8" array (the first parameter) passed to b8. The name of the array doesn't matter (of course), it just has to be the first argument.</p>
|
|
||||||
<p>These are some basic settings telling b8 which backend classes to use:</p>
|
|
||||||
<blockquote>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>storage</strong></dt>
|
|
||||||
<dd><p class="first">This defines which storage backend will be used to save b8's wordlist. Currently, two backends are available: <a class="reference external" href="http://www.oracle.com/database/berkeley-db/index.html">Berkeley DB</a> (<tt class="docutils literal">dba</tt>) and <a class="reference external" href="http://mysql.com/">MySQL</a> (<tt class="docutils literal">mysql</tt>). At the moment, b8 does not support <a class="reference external" href="http://sqlite.org/">SQLite</a> (as the previous version did), but it will be (hopefully) re-added in one of the next releases. The default is <tt class="docutils literal">dba</tt> (string).</p>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><em>Berkeley DB</em></dt>
|
|
||||||
<dd>This is the preferred storage backend. It was the original backend for the filter and remains the most performant. b8's storage model is optimized for this database, as it is really fast and fits perfectly to what the filter needs to do the job. All content is saved in a single file, you don't need special user rights or a database server. <br />
|
|
||||||
If you don't know whether your server's PHP can use a Berkeley DB, simply run the script <tt class="docutils literal">install/setup_berkeleydb.php</tt>. If it shows a Berkeley DB handler, please use this backend.</dd>
|
|
||||||
<dt><em>MySQL</em></dt>
|
|
||||||
<dd>As some webspace hosters don't allow using a Berkeley DB (but please be sure to check if you can use it!), but most do provide a MySQL server, using a MySQL table for the wordlist is provided as an alternative storage method. As said above, b8 was always intended to use a Berkeley DB. It doesn't use or need SQL to query the database. So, very likely, this will work less performant, produce a lot of unnecessary overhead and waste computing power. But it will do fine anyway!</dd>
|
|
||||||
</dl>
|
|
||||||
<p class="last">See <a class="reference internal" href="#configuration-of-the-storage-backend">Configuration of the storage backend</a> for the settings of the chosen backend.</p>
|
|
||||||
</dd>
|
|
||||||
<dt><strong>degenerator</strong></dt>
|
|
||||||
<dd>The degenerator class to be used. See <a class="reference internal" href="#how-does-it-work">How does it work?</a> and <a class="footnote-reference" href="#betterbayesian" id="id12">[3]</a> if you're interested in what "degeneration" is. Defaults to <tt class="docutils literal">default</tt> (string). At the moment, only one degenerator exists, so you probably don't want to change this unless you have written your own degenerator.</dd>
|
|
||||||
<dt><strong>lexer</strong></dt>
|
|
||||||
<dd><p class="first">The lexer class to be used. Defaults to <tt class="docutils literal">default</tt> (string). At the moment, only one lexer exists, so you probably don't want to change this unless you have written your own lexer.</p>
|
|
||||||
<p>The behaviour of the lexer can be additionally configured with the following variables:</p>
|
|
||||||
<blockquote class="last">
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>min_size</strong></dt>
|
|
||||||
<dd>The minimal length for a token to be considered when calculating the rating of a text. Defaults to <tt class="docutils literal">3</tt> (integer).</dd>
|
|
||||||
<dt><strong>max_size</strong></dt>
|
|
||||||
<dd>The maximal length for a token to be considered when calculating the rating of a text. Defaults to <tt class="docutils literal">30</tt> (integer).</dd>
|
|
||||||
<dt><strong>allow_numbers</strong></dt>
|
|
||||||
<dd>Should pure numbers also be considered? Defaults to <tt class="docutils literal">FALSE</tt> (boolean).</dd>
|
|
||||||
</dl>
|
|
||||||
</blockquote>
|
|
||||||
</dd>
|
|
||||||
</dl>
|
|
||||||
</blockquote>
|
|
||||||
<p>The following settings influence the mathematical internals of b8. If you want to experiment, feel free to play around with them; but be warned: wrong settings of these values will result in poor performance or could even "short-circuit" the filter. <br />
|
|
||||||
Leave these values as they are unless you know what you are doing!</p>
|
|
||||||
<p>The "Statistical discussion about b8" <a class="footnote-reference" href="#b8statistic" id="id13">[6]</a> shows why the default values are the default ones.</p>
|
|
||||||
<blockquote>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>use_relevant</strong></dt>
|
|
||||||
<dd>This tells b8 how many tokens should be used when calculating the spamminess of a text. The default setting is <tt class="docutils literal">15</tt> (integer). This seems to be a quite reasonable value. When using to many tokens, the filter will fail on texts filled with useless stuff or with passages from a newspaper, etc. not being very spammish. <br />
|
|
||||||
The tokens counted multiple times (see above) are added in addition to this value. They don't replace other ratings.</dd>
|
|
||||||
<dt><strong>min_dev</strong></dt>
|
|
||||||
<dd>This defines a minimum deviation from 0.5 that a token's rating must have to be considered when calculating the spamminess. Tokens with a rating closer to 0.5 than this value will simply be skipped. <br />
|
|
||||||
If you don't want to use this feature, set this to <tt class="docutils literal">0</tt>. Defaults to <tt class="docutils literal">0.2</tt> (float). Read <a class="footnote-reference" href="#b8statistic" id="id14">[6]</a> before increasing this.</dd>
|
|
||||||
<dt><strong>rob_x</strong></dt>
|
|
||||||
<dd>This is Gary Robinson's <em>x</em> constant (cf. <a class="footnote-reference" href="#spamdetection" id="id15">[4]</a>). A completely unknown token will be rated with the value of <tt class="docutils literal">rob_x</tt>. The default <tt class="docutils literal">0.5</tt> (float) seems to be quite reasonable, as we can't say if a token that also can't be rated by degeneration is good or bad. <br />
|
|
||||||
If you receive much more spam than ham or vice versa, you could change this setting accordingly.</dd>
|
|
||||||
<dt><strong>rob_s</strong></dt>
|
|
||||||
<dd>This is Gary Robinson's <em>s</em> constant. This is essentially the probability that the <em>rob_x</em> value is correct for a completely unknown token. It will also shift the probability of rarely seen tokens towards this value. The default is <tt class="docutils literal">0.3</tt> (float) <br />
|
|
||||||
See <a class="footnote-reference" href="#spamdetection" id="id16">[4]</a> for a closer description of the <em>s</em> constant and read <a class="footnote-reference" href="#b8statistic" id="id17">[6]</a> for specific information about this constant in b8's algorithms.</dd>
|
|
||||||
</dl>
|
|
||||||
</blockquote>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="configuration-of-the-storage-backend">
|
|
||||||
<h2><a class="toc-backref" href="#id29">4.2 Configuration of the storage backend</a></h2>
|
|
||||||
<p>All the following values can be set in the "config_database" array (the second parameter) passed to b8. The name of the array doesn't matter (of course), it just has to be the second argument.</p>
|
|
||||||
<div class="section" id="settings-for-the-berkeley-db-dba-backend">
|
|
||||||
<h3><a class="toc-backref" href="#id30">4.2.1 Settings for the Berkeley DB (DBA) backend</a></h3>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>database</strong></dt>
|
|
||||||
<dd>The filename of the database file, relative to the location of <tt class="docutils literal">b8.php</tt>. Defaults to <tt class="docutils literal">wordlist.db</tt> (string).</dd>
|
|
||||||
<dt><strong>handler</strong></dt>
|
|
||||||
<dd>The DBA handler to use (cf. <a class="reference external" href="http://php.net/manual/en/dba.requirements.php">the PHP documentation</a> and <a class="reference internal" href="#setting-up-a-new-berkeley-db">Setting up a new Berkeley DB</a>). Defaults to <tt class="docutils literal">db4</tt> (string).</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="settings-for-the-mysql-backend">
|
|
||||||
<h3><a class="toc-backref" href="#id31">4.2.2 Settings for the MySQL backend</a></h3>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>database</strong></dt>
|
|
||||||
<dd>The database containing b8's wordlist table. Defaults to <tt class="docutils literal">b8_wordlist</tt> (string).</dd>
|
|
||||||
<dt><strong>table_name</strong></dt>
|
|
||||||
<dd>The table containing b8's wordlist. Defaults to <tt class="docutils literal">b8_wordlist</tt> (string).</dd>
|
|
||||||
<dt><strong>host</strong></dt>
|
|
||||||
<dd>The host of the MySQL server. Defaults to <tt class="docutils literal">localhost</tt> (string).</dd>
|
|
||||||
<dt><strong>user</strong></dt>
|
|
||||||
<dd>The user name used to open the database connection. Defaults to <tt class="docutils literal">FALSE</tt> (boolean).</dd>
|
|
||||||
<dt><strong>pass</strong></dt>
|
|
||||||
<dd>The password required to open the database connection. Defaults to <tt class="docutils literal">FALSE</tt> (boolean).</dd>
|
|
||||||
<dt><strong>connection</strong></dt>
|
|
||||||
<dd>An existing MySQL link-resource that can be used by b8. Defaults to <tt class="docutils literal">NULL</tt> (NULL).</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="using-b8">
|
|
||||||
<h1><a class="toc-backref" href="#id32">5 Using b8</a></h1>
|
|
||||||
<p>Now, that everything is configured, you can start to use b8. A sample script that shows what can be done with the filter exists in <tt class="docutils literal">example/index.php</tt>. The best thing for testing how all this works is to use this script before using b8 in your own scripts.</p>
|
|
||||||
<p>Before you can start, you have to setup a database so that b8 can store a wordlist.</p>
|
|
||||||
<div class="section" id="setting-up-a-new-database">
|
|
||||||
<h2><a class="toc-backref" href="#id33">5.1 Setting up a new database</a></h2>
|
|
||||||
<div class="section" id="setting-up-a-new-berkeley-db">
|
|
||||||
<h3><a class="toc-backref" href="#id34">5.1.1 Setting up a new Berkeley DB</a></h3>
|
|
||||||
<p>I wrote a script to setup a new Berkeley DB for b8. It is located in <tt class="docutils literal">install/setup_berkeleydb.php</tt>. Just run this script on your server and be sure that the directory containing it has the proper access rights set so that the server's HTTP server user or PHP user can create a new file in it (probably <tt class="docutils literal">0777</tt>). The script is quite self-explaining, just run it.</p>
|
|
||||||
<p>Of course, you can also create a Berkeley DB by hand. In this case, you just have to insert three keys:</p>
|
|
||||||
<pre class="literal-block">
|
|
||||||
bayes*dbversion => 2
|
|
||||||
bayes*texts.ham => 0
|
|
||||||
bayes*texts.spam => 0
|
|
||||||
</pre>
|
|
||||||
<p>Be sure to set the right DBA handler in the storage backend configuration if it's not <tt class="docutils literal">db4</tt>.</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="setting-up-a-new-mysql-table">
|
|
||||||
<h3><a class="toc-backref" href="#id35">5.1.2 Setting up a new MySQL table</a></h3>
|
|
||||||
<p>The SQL file <tt class="docutils literal">install/setup_mysql.sql</tt> contains both the create statement for the wordlist table of b8 and the <tt class="docutils literal">INSERT</tt> statements for adding the necessary internal variables.</p>
|
|
||||||
<p>Simply change the table name according to your needs (or leave it as it is ;-) and run the SQL to setup a b8 wordlist MySQL table.</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="using-b8-in-your-scripts">
|
|
||||||
<h2><a class="toc-backref" href="#id36">5.2 Using b8 in your scripts</a></h2>
|
|
||||||
<p>Just have a look at the example script located in <tt class="docutils literal">example/index.php</tt> to see how you can include b8 in your scripts. Essentially, this strips down to:</p>
|
|
||||||
<pre class="literal-block">
|
|
||||||
# Include the b8 code
|
|
||||||
require "{$_SERVER['DOCUMENT_ROOT']}/b8/b8.php";
|
|
||||||
|
|
||||||
# Do some configuration
|
|
||||||
|
|
||||||
$config_b8 = array(
|
|
||||||
'some_key' => 'some_value',
|
|
||||||
'foo' => 'bar'
|
|
||||||
);
|
|
||||||
|
|
||||||
$config_database = array(
|
|
||||||
'some_key' => 'some_value',
|
|
||||||
'foo' => 'bar'
|
|
||||||
);
|
|
||||||
|
|
||||||
# Create a new b8 instance
|
|
||||||
$b8 = new b8($config_b8, $config_database);
|
|
||||||
</pre>
|
|
||||||
<p>b8 provides three functions in an object oriented way (called e. g. via <tt class="docutils literal"><span class="pre">$b8->classify($text)</span></tt>):</p>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>learn($text, $category)</strong></dt>
|
|
||||||
<dd>This saves the reference text <tt class="docutils literal">$text</tt> (string) in the category <tt class="docutils literal">$category</tt> (b8 constant). <br />
|
|
||||||
b8 0.5 introduced two constants that can be used as <tt class="docutils literal">$category</tt>: <tt class="docutils literal"><span class="pre">b8::HAM</span></tt> and <tt class="docutils literal"><span class="pre">b8::SPAM</span></tt>. To be downward compatible with older versions of b8, the literal values "ham" and "spam" (case-sensitive strings) can still be used here.</dd>
|
|
||||||
<dt><strong>unlearn($text, $category)</strong></dt>
|
|
||||||
<dd>This function just exists to delete a text from a category in which is has been stored accidentally before. It deletes the reference text <tt class="docutils literal">$text</tt> (string) from the category <tt class="docutils literal">$category</tt> (either the constants <tt class="docutils literal"><span class="pre">b8::HAM</span></tt> or <tt class="docutils literal"><span class="pre">b8::SPAM</span></tt> or the literal case-sensitive strings "ham" or "spam" – cf. above). <br />
|
|
||||||
<strong>Don't delete a spam text from ham after saving it in spam or vice versa, as long you don't have stored it accidentally in the wrong category before!</strong> This will not improve performance, quite the opposite: it will actually break the filter after a time, as the counter for saved ham or spam texts will reach 0, although you have ham or spam tokens stored: the filter will try to remove texts from the ham or spam data which have never been stored there, decrease the counter for tokens which are found just skip the non-existing words.</dd>
|
|
||||||
<dt><strong>classify($text)</strong></dt>
|
|
||||||
<dd>This function takes the text <tt class="docutils literal">$text</tt> (string), calculates it's probability for being spam it and returns a value between 0 and 1 (float). <br />
|
|
||||||
A value close to 0 says the text is more likely ham and a value close to 1 says the text is more likely spam. What to do with this value is <em>your</em> business ;-) See also <a class="reference internal" href="#tips-on-operation">Tips on operation</a> below.</dd>
|
|
||||||
</dl>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="tips-on-operation">
|
|
||||||
<h1><a class="toc-backref" href="#id37">6 Tips on operation</a></h1>
|
|
||||||
<p>Before b8 can decide whether a text is spam or ham, you have to tell it what you consider as spam or ham. At least one learned spam or one learned ham text is needed to calculate anything. To get good ratings, you need both learned ham and learned spam texts, the more the better. <br />
|
|
||||||
What's considered as "ham" or "spam" can be very different, depending on the operation site. On my homepage, practically each and every text posted in English or using cyrillic letters is spam. On an English or Russian homepage, this will be not the case. So I think it's not really meaningful to provide some "spam data" to start. Just train b8 with "your" spam and ham.</p>
|
|
||||||
<p>For the practical use, I advise to give the filter all data availible. E. g. name, email address, homepage, IP address und of course the text itself should be stored in a variable (e. g. separated with an <tt class="docutils literal">\n</tt> or just a space or tab after each block) and then be classified. The learning should also be done with all data availible. <br />
|
|
||||||
Saving the IP address is probably only meaningful for spam entries, because spammers often use the same IP address multiple times. In principle, you can leave out the IP of ham entries.</p>
|
|
||||||
<p>You can use b8 e. g. in a guestbook script and let it classify the text before saving it. Everyone has to decide which rating is necessary to classify a text as "spam", but a rating of >= 0.8 seems to be reasonable for me. If one expects the spam to be in another language that the ham entries or the spams are very short normally, one could also think about a limit of 0.7. <br />
|
|
||||||
The email filters out there mostly use > 0.9 or even > 0.99; but keep in mind that they have way more data to analyze in most of the cases. A guestbook entry may be quite short, especially when it's spam.</p>
|
|
||||||
<p>In my opinion, a autolearn function is very handy. I save spam messages with a rating higher than 0.7 but less than 0.9 automatically as spam. I don't do this with ham messages in an automated way to prevent the filter from saving a false negative as ham and then classifying and learning all the spam as ham when I'm on holidays ;-)</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="closing">
|
|
||||||
<h1><a class="toc-backref" href="#id38">7 Closing</a></h1>
|
|
||||||
<p>So … that's it. Thanks for using b8! If you find a bug or have an idea how to make b8 better, let me know. I'm also always looking forward to get e-mails from people using b8 on their homepages :-)</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="references">
|
|
||||||
<h1><a class="toc-backref" href="#id39">8 References</a></h1>
|
|
||||||
<table class="docutils footnote" frame="void" id="planforspam" rules="none">
|
|
||||||
<colgroup><col class="label" /><col /></colgroup>
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr><td class="label">[2]</td><td><em>(<a class="fn-backref" href="#id3">1</a>, <a class="fn-backref" href="#id9">2</a>)</em> Paul Graham, <em>A Plan For Spam</em> (<a class="reference external" href="http://paulgraham.com/spam.html">http://paulgraham.com/spam.html</a>)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table class="docutils footnote" frame="void" id="betterbayesian" rules="none">
|
|
||||||
<colgroup><col class="label" /><col /></colgroup>
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr><td class="label">[3]</td><td><em>(<a class="fn-backref" href="#id4">1</a>, <a class="fn-backref" href="#id7">2</a>, <a class="fn-backref" href="#id12">3</a>)</em> Paul Graham, <em>Better Bayesian Filtering</em> (<a class="reference external" href="http://paulgraham.com/better.html">http://paulgraham.com/better.html</a>)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table class="docutils footnote" frame="void" id="spamdetection" rules="none">
|
|
||||||
<colgroup><col class="label" /><col /></colgroup>
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr><td class="label">[4]</td><td><em>(<a class="fn-backref" href="#id5">1</a>, <a class="fn-backref" href="#id8">2</a>, <a class="fn-backref" href="#id15">3</a>, <a class="fn-backref" href="#id16">4</a>)</em> Gary Robinson, <em>Spam Detection</em> (<a class="reference external" href="http://radio.weblogs.com/0101454/stories/2002/09/16/spamDetection.html">http://radio.weblogs.com/0101454/stories/2002/09/16/spamDetection.html</a>)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table class="docutils footnote" frame="void" id="statisticalapproach" rules="none">
|
|
||||||
<colgroup><col class="label" /><col /></colgroup>
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr><td class="label"><a class="fn-backref" href="#id6">[5]</a></td><td><em>A Statistical Approach to the Spam Problem</em> (<a class="reference external" href="http://linuxjournal.com/article/6467">http://linuxjournal.com/article/6467</a>)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<table class="docutils footnote" frame="void" id="b8statistic" rules="none">
|
|
||||||
<colgroup><col class="label" /><col /></colgroup>
|
|
||||||
<tbody valign="top">
|
|
||||||
<tr><td class="label">[6]</td><td><em>(<a class="fn-backref" href="#id13">1</a>, <a class="fn-backref" href="#id14">2</a>, <a class="fn-backref" href="#id17">3</a>)</em> Tobias Leupold, <em>Statistical discussion about b8</em> (<a class="reference external" href="http://nasauber.de/opensource/b8/discussion/">http://nasauber.de/opensource/b8/discussion/</a>)</td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="appendix">
|
|
||||||
<h1><a class="toc-backref" href="#id40">9 Appendix</a></h1>
|
|
||||||
<div class="section" id="faq">
|
|
||||||
<h2><a class="toc-backref" href="#id41">9.1 FAQ</a></h2>
|
|
||||||
<div class="section" id="what-about-more-than-two-categories">
|
|
||||||
<h3><a class="toc-backref" href="#id42">9.1.1 What about more than two categories?</a></h3>
|
|
||||||
<p>I wrote b8 with the <a class="reference external" href="http://en.wikipedia.org/wiki/KISS_principle">KISS principle</a> in mind. For the "end-user", we have a class with almost no setup to do that can do three things: classify a text, learn a text and un-learn a text. Normally, there's no need to un-learn a text, so essentially, there are only two functions we need. <br />
|
|
||||||
This simplicity is only possible because b8 only knows two categories (normally "Ham" and "Spam" or some other category pair) and tells you, in one float number between 0 and 1, if a given texts rather fits in the first or the second category. If we would support multiple categories, more work would have to be done and things would become more complicated. One would have to setup the categories, have another database layout (perhaps making it mandatory to have SQL) and one float number would not be sufficient to describe b8's output, so more code would be needed – even outside of b8.</p>
|
|
||||||
<p>All the code, the database layout and particularly the math is intended to do exactly one thing: distinguish between two categories. I think it would be a lot of work to change b8 so that it would support more than two categories. Probably, this is possible to do, but don't ask me in which way we would have to change the math to get multiple-category support – I'm a dentist, not a mathematician ;-) <br />
|
|
||||||
Apart from this I do believe that most people using b8 don't want or need multiple categories. They just want to know if a text is spam or not, don't they? I do, at least ;-)</p>
|
|
||||||
<p>But let's think about the multiple-category thing. How would we calculate a rating for more than two categories? If we had a third one, let's call it "<a class="reference external" href="http://en.wikipedia.org/wiki/Treet">Treet</a>", how would we calculate a rating? We could calculate three different ratings. One for "Ham", one for "Spam" and one for "Treet" and choose the highest one to tell the user what category fits best for the text. This could be done by using a small wrapper script using three instances of b8 as-is and three different databases, each containing texts being "Ham", "Spam", "Treet" and the respective counterparts. <br />
|
|
||||||
But here's the problem: if we have "Ham" and "Spam", "Spam" is the counterpart of "Ham". But what's the counterpart of "Spam" if we have more than one additional category? Where do the "Non-Ham", "Non-Spam" and "Non-Treet" texts come from?</p>
|
|
||||||
<p>Another approach, a direct calculation of more than two probabilities (the "Ham" probability is simply 1 minus the "Spam" probability, so we actually get two probabilities with the return value of b8) out of one database would require big changes in b8's structure and math.</p>
|
|
||||||
<p>There's a project called <a class="reference external" href="http://xhtml.net/scripts/PHPNaiveBayesianFilter">PHPNaiveBayesianFilter</a> which supports multiple categories by default. The author calls his software "Version 1.0", but I think this is the very first release, not a stable or mature one. The most recent change of that release dates back to 2003 according to the "changed" date of the files inside the zip archive, so probably, this project is dead or has never been alive and under active development at all. <br />
|
|
||||||
Actually, I played around with that code but the results weren't really good, so I decided to write my own spam filter from scratch back in early 2006 ;-)</p>
|
|
||||||
<p>All in all, there seems to be no easy way to implement multiple (meaning more than two) categories using b8's current code base and probably, b8 will never support more than two categories. Perhaps, a fork or a complete re-write would be better than implementing such a feature. Anyway, I don't close my mind to multiple categories in b8. Feel free to tell me how multiple categories could be implementented in b8 or how a multiple-category version using the same code base (sharing a common abstract class?) could be written.</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="what-about-a-list-with-words-to-ignore">
|
|
||||||
<h3><a class="toc-backref" href="#id43">9.1.2 What about a list with words to ignore?</a></h3>
|
|
||||||
<p>Some people suggested to introduce a list with words that b8 will simply ignore. Like "and", "or", "the", and so on. I don't think this is very meaningful.</p>
|
|
||||||
<p>First, it would just work for the particular language that has been stored in the list. Speaking of my homepage, most of my spam is English, almost all my ham is German. So I would have to maintain a list with the probably less interesting words for at least two languages. Additionally, I get spam in Chinese, Japanese and Cyrillic writing or something else I can't read as well. What word should be ignored in those texts? <br />
|
|
||||||
Second, why should we ever exclude words? Who tells us those words are <em>actually</em> meaningless? If a word appears both in ham and spam, it's rating will be near 0.5 and so, it won't be used for the final calculation if a appropriate minimum deviation was set. So b8 will exclude it anyway without any blacklist. And think of this: if we excluded a word of which we only <em>think</em> it doesn't mean anything but it actually does appear more often in ham or spam, the results will get even worse.</p>
|
|
||||||
<p>So why should we care about things we do not have to care about? ;-)</p>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="why-is-it-called-b8">
|
|
||||||
<h3><a class="toc-backref" href="#id44">9.1.3 Why is it called "b8"?</a></h3>
|
|
||||||
<p>The initial name for the filter was (damn creative!) "bayes-php". There were two main reasons for searching another name: 1. "bayes-php" sucks. 2. the <a class="reference external" href="http://php.net/license/3_01.txt">PHP License</a> says the PHP guys do not like when the name of a script written in PHP contains the word "PHP". Read the <a class="reference external" href="http://www.php.net/license/index.php#faq-lic">License FAQ</a> for a reasonable argumentation about this.</p>
|
|
||||||
<p>Luckily, <a class="reference external" href="http://langt.net/">Tobias Lang</a> proposed the new name "b8". And these are the reasons why I chose this name:</p>
|
|
||||||
<ul class="simple">
|
|
||||||
<li>"bayes-php" is a "b" followed by 8 letters.</li>
|
|
||||||
<li>"b8" is short and handy. Additionally, there was no program with the name "b8" or "bate"</li>
|
|
||||||
<li>The English verb "to bate" means "to decrease" – and that's what b8 does: it decreases the number of spam entries in your weblog or guestbook!</li>
|
|
||||||
<li>"b8" just sounds way cooler than "bayes-php" ;-)</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="about-the-database">
|
|
||||||
<h2><a class="toc-backref" href="#id45">9.2 About the database</a></h2>
|
|
||||||
<div class="section" id="the-database-layout">
|
|
||||||
<h3><a class="toc-backref" href="#id46">9.2.1 The database layout</a></h3>
|
|
||||||
<p>The database layout is quite simple. It's just key:value for everything stored. There are three "internal" variables stored as normal tokens (but all containing a <tt class="docutils literal">*</tt> which is always used as a split character by the lexer, so we can't get collisions):</p>
|
|
||||||
<dl class="docutils">
|
|
||||||
<dt><strong>bayes*dbversion</strong></dt>
|
|
||||||
<dd>This indicates the database's "version". The first versions of b8 did not set this. Version "2" indicates that we have a database created by a b8 version already storing <a class="reference internal" href="#the-lastseen-parameter">the "lastseen" parameter</a>.</dd>
|
|
||||||
<dt><strong>bayes*texts.ham</strong></dt>
|
|
||||||
<dd>The number of ham texts learned.</dd>
|
|
||||||
<dt><strong>bayes*texts.spam</strong></dt>
|
|
||||||
<dd>The number of spam texts learned.</dd>
|
|
||||||
</dl>
|
|
||||||
<p>Each "normal" token is stored with it's literal name as the key and it's data as the value. The data consists of the count of the token in all ham and spam texts and the date when the token was used the last time, all in one string and separated by spaces. So we have the following scheme:</p>
|
|
||||||
<pre class="literal-block">
|
|
||||||
"token" => "count_ham count_spam lastseen"
|
|
||||||
</pre>
|
|
||||||
</div>
|
|
||||||
<div class="section" id="the-lastseen-parameter">
|
|
||||||
<h3><a class="toc-backref" href="#id47">9.2.2 The "lastseen" parameter</a></h3>
|
|
||||||
<p>Somebody looking at the code might be wondering why b8 stores this "lastseen" parameter. This value is not used for any calculation at the moment. Initially, it was intended to keep the database maintainable in a way that "old" data could be removed. When e. g. a token only appeared once in ham or spam and has not been seen for a year, one could simply delete it from the database. <br />
|
|
||||||
I actually never used this feature (does anybody?). So probably, some changes will be done to this one day. Perhaps, I find a way to include this data in the spamminess calculation in a meaningful way, or at least for some statistics. One could also make this optional to keep the calculation effort small if this is needed.</p>
|
|
||||||
<p>Feel free to send me any suggestions about this!</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
|
@ -1,371 +0,0 @@
|
||||||
==========
|
|
||||||
b8: readme
|
|
||||||
==========
|
|
||||||
|
|
||||||
:Author: Tobias Leupold
|
|
||||||
:Homepage: http://nasauber.de/
|
|
||||||
:Contact: tobias.leupold@web.de
|
|
||||||
:Date: |date|
|
|
||||||
|
|
||||||
.. contents:: Table of Contents
|
|
||||||
|
|
||||||
Description of b8
|
|
||||||
=================
|
|
||||||
|
|
||||||
What is b8?
|
|
||||||
-----------
|
|
||||||
|
|
||||||
b8 is a spam filter implemented in `PHP <http://www.php.net/>`__. It is intended to keep your weblog or guestbook spam-free. The filter can be used anywhere in your PHP code and tells you whether a text is spam or not, using statistical text analysis. See `How does it work?`_ for details about this. To be able to do this, b8 first has to learn some spam and some ham example texts to decide what's good and what's not. If it makes mistakes classifying unknown texts, they can be corrected and b8 learns from the corrections, getting better with each learned text.
|
|
||||||
|
|
||||||
At the moment of this writing, b8 has classified 14411 guestbook entries and weblog comments on my homepage since december 2006. 131 were ham. 39 spam texts (0.27 %) have been rated as ham (false negatives), with not even one false positive (ham message classified as spam). This results in a sensitivity of 99.73 % (the probability that a spam text will actually be rated as spam) and a specifity of 100 % (the probability that a ham text will actually be rated as ham) for me. I hope, you'll get the same good results :-)
|
|
||||||
|
|
||||||
Basically, b8 is a statistical ("Bayesian"[#]_) spam filter like `Bogofilter <http://bogofilter.sourceforge.net/>`__ or `SpamBayes <http://spambayes.sourceforge.net/>`__, but it is not intended to classify e-mails. When I started to write b8, I didn't find a good PHP spam filter (or any spam filter that wasn't just some example code how one *could* implement a Bayesian spam filter in PHP) that was intended to filter weblog or guestbook entries. That's why I had to write my own ;-) |br|
|
|
||||||
Caused by it's purpose, the way b8 works is slightly different from most of the Bayesian email spam filters out there. See `What's different?`_ if you're interested in the details.
|
|
||||||
|
|
||||||
.. [#] A mathematician told me that the math in b8 actually does not use Bayes' theorem but some derived algorithms that are just related to it. So … let's simply believe that and stop claiming b8 was a *Bayesian* spam filter ;-)
|
|
||||||
|
|
||||||
How does it work?
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
b8 basically uses the math and technique described in Paul Graham's article "A Plan For Spam" [#planforspam]_ to distinguish ham and spam. The improvements proposed in Graham's article "Better Bayesian Filtering" [#betterbayesian]_ and Gary Robinson's article "Spam Detection" [#spamdetection]_ have also been considered. See also the article "A Statistical Approach to the Spam Problem" [#statisticalapproach]_.
|
|
||||||
|
|
||||||
b8 cuts the text to classify to pieces, extracting stuff like e-mail addresses, links and HTML tags. For each such token, it calculates a single probability for a text containing it being spam, based on what the filter has learned so far. When the token was not seen before, b8 tries to find similar ones using the "degeneration" described in [#betterbayesian]_ and uses the most relevant value found. If really nothing is found, b8 assumes a default rating for this token for the further calculations. |br|
|
|
||||||
Then, b8 takes the most relevant values (which have a rating far from 0.5, which would mean we don't know what it is) and calculates the probability that the whole text is spam by the inverse chi-square function described in [#spamdetection]_.
|
|
||||||
There are some parameters that can be set which influence the filter's behaviour (see below).
|
|
||||||
|
|
||||||
In short words: you give b8 a text and it returns a value between 0 and 1, saying it's ham when it's near 0 and saying it's spam when it's near 1.
|
|
||||||
|
|
||||||
What do I need for it?
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Not much! You just need PHP 5 on the server where b8 will be used (b8 version 0.5 finally dropped PHP 4 compatibility – thankfully ;-) and a proper storage possibility for the wordlists. I strongly recommend using `Berkeley DB <http://www.oracle.com/database/berkeley-db/index.html>`_. See below how you can check if you can use it and why you should use it. If the server's PHP wasn't compiled with Berkeley DB support, a `MySQL <http://mysql.com/>`_ table can be used alternatively.
|
|
||||||
|
|
||||||
What's different?
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
b8 is designed to classify weblog or guestbook entries, not e-mails. For this reason, it uses a slightly different technique than most of the other statistical spam filters out there use.
|
|
||||||
|
|
||||||
My experience was that spam entries on my weblog or guestbook were often quite short, sometimes just something like "123abc" as text and a link to a suspect homepage. Some spam bots don't even made a difference between e. g. the "name" and "text" fields and posted their text as email address, for example. Considering this, b8 just takes one string to classify, making no difference between "headers" and "text". |br|
|
|
||||||
The other thing is that most statistical spam filters count one token one time, no matter how often it appears in the text (as Graham describes it in [#planforspam]_). b8 does count how often a token was seen and learns or considers this. Additionally, the number of learned ham and spam texts are saved and used as the calculation base for the single probabilities. Why this? Because a text containing one link (no matter where it points to, just indicated by a "\h\t\t\p\:\/\/" or a "www.") might not be spam, but a text containing 20 links might be.
|
|
||||||
|
|
||||||
This means that b8 might be good for classifying weblog or guestbook entries (I really think it is ;-) – but very likely, it will work quite poor when being used for something else (like classifying e-mails). But as said above, for this task, there are a lot of very good filters out there to choose from.
|
|
||||||
|
|
||||||
Update from prior versions
|
|
||||||
==========================
|
|
||||||
|
|
||||||
If this is a new b8 installation, read on at the `Installation`_ section!
|
|
||||||
|
|
||||||
Update from bayes-php version 0.2.1 or earlier
|
|
||||||
----------------------------------------------
|
|
||||||
|
|
||||||
Please first follow the database update instructions of the bayes-php-0.3 release if you update from a version prior to bayes-php-0.3 and then read the following paragraph about updating from a version <0.3.3.
|
|
||||||
|
|
||||||
Update from bayes-php version 0.3 or later
|
|
||||||
------------------------------------------
|
|
||||||
|
|
||||||
**You use Berkeley DB?**
|
|
||||||
Everything's fine, you can simply continue using your database.
|
|
||||||
|
|
||||||
**You use MySQL?**
|
|
||||||
The ``CREATE`` statement of b8's wordlist has changed. The best is probably to create a dump via your favorite administration tool or script, create the new table and re-insert all data. The layout is still the same: there's one "token" column and one "data" column. Having done that, you can keep using your data.
|
|
||||||
|
|
||||||
**You use SQLite?**
|
|
||||||
Sorry, at the moment, there's no SQLite backend for b8. But we're working on it :-)
|
|
||||||
|
|
||||||
The configuration model of b8 has changed. Please read through the `Configuration`_ section and update your configuration accordingly.
|
|
||||||
|
|
||||||
b8's lexer has been partially re-written. It should now be able to handle all kind of non-latin-1 input, like cyrillic, chinese or japanese texts. Caused by this fact, much more tokens will be recognized when classifying such texts. Therefore, you could get different results in b8's ratings, even if the same database is used and although the math is still the same.
|
|
||||||
|
|
||||||
b8 0.5 introduced two constants that can be used in the ``learn()`` and ``unlearn()`` functions: ``b8::HAM`` and ``b8::SPAM``. The literal values "ham" and "spam" can still be used anyway.
|
|
||||||
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
Installing b8 on your server is quite easy. You just have to provide the needed files. To do this, you could just upload the whole ``b8`` subdirectory to the base directory of your homepage. It contains the filter itself and all needed backend classes. The other directories (``doc``, ``example`` and ``install``) are not used by b8.
|
|
||||||
|
|
||||||
That's it ;-)
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
The configuration is passed as arrays when instantiating a new b8 object. Two arrays can be passed to b8, one containing b8's base configuration and some settings for the lexer (which should be common for all lexer classes, in case some other lexer than the default one will be written one day) and one for the storage backend. |br|
|
|
||||||
You can have a look at ``example/index.php`` to see how this can be done. `Using b8 in your scripts`_ also shows example code showing how b8 can be included in a PHP script.
|
|
||||||
|
|
||||||
Not all values have to be set. When some values are missing, the default ones will be used. If you do use the default settings, you don't have to pass them to b8.
|
|
||||||
|
|
||||||
b8's base configuration
|
|
||||||
-----------------------
|
|
||||||
|
|
||||||
All these values can be set in the "config_b8" array (the first parameter) passed to b8. The name of the array doesn't matter (of course), it just has to be the first argument.
|
|
||||||
|
|
||||||
These are some basic settings telling b8 which backend classes to use:
|
|
||||||
|
|
||||||
**storage**
|
|
||||||
This defines which storage backend will be used to save b8's wordlist. Currently, two backends are available: `Berkeley DB <http://www.oracle.com/database/berkeley-db/index.html>`_ (``dba``) and `MySQL <http://mysql.com/>`_ (``mysql``). At the moment, b8 does not support `SQLite <http://sqlite.org/>`_ (as the previous version did), but it will be (hopefully) re-added in one of the next releases. The default is ``dba`` (string).
|
|
||||||
|
|
||||||
*Berkeley DB*
|
|
||||||
This is the preferred storage backend. It was the original backend for the filter and remains the most performant. b8's storage model is optimized for this database, as it is really fast and fits perfectly to what the filter needs to do the job. All content is saved in a single file, you don't need special user rights or a database server. |br|
|
|
||||||
If you don't know whether your server's PHP can use a Berkeley DB, simply run the script ``install/setup_berkeleydb.php``. If it shows a Berkeley DB handler, please use this backend.
|
|
||||||
|
|
||||||
*MySQL*
|
|
||||||
As some webspace hosters don't allow using a Berkeley DB (but please be sure to check if you can use it!), but most do provide a MySQL server, using a MySQL table for the wordlist is provided as an alternative storage method. As said above, b8 was always intended to use a Berkeley DB. It doesn't use or need SQL to query the database. So, very likely, this will work less performant, produce a lot of unnecessary overhead and waste computing power. But it will do fine anyway!
|
|
||||||
|
|
||||||
See `Configuration of the storage backend`_ for the settings of the chosen backend.
|
|
||||||
|
|
||||||
**degenerator**
|
|
||||||
The degenerator class to be used. See `How does it work?`_ and [#betterbayesian]_ if you're interested in what "degeneration" is. Defaults to ``default`` (string). At the moment, only one degenerator exists, so you probably don't want to change this unless you have written your own degenerator.
|
|
||||||
|
|
||||||
**lexer**
|
|
||||||
The lexer class to be used. Defaults to ``default`` (string). At the moment, only one lexer exists, so you probably don't want to change this unless you have written your own lexer.
|
|
||||||
|
|
||||||
The behaviour of the lexer can be additionally configured with the following variables:
|
|
||||||
|
|
||||||
**min_size**
|
|
||||||
The minimal length for a token to be considered when calculating the rating of a text. Defaults to ``3`` (integer).
|
|
||||||
|
|
||||||
**max_size**
|
|
||||||
The maximal length for a token to be considered when calculating the rating of a text. Defaults to ``30`` (integer).
|
|
||||||
|
|
||||||
**allow_numbers**
|
|
||||||
Should pure numbers also be considered? Defaults to ``FALSE`` (boolean).
|
|
||||||
|
|
||||||
The following settings influence the mathematical internals of b8. If you want to experiment, feel free to play around with them; but be warned: wrong settings of these values will result in poor performance or could even "short-circuit" the filter. |br|
|
|
||||||
Leave these values as they are unless you know what you are doing!
|
|
||||||
|
|
||||||
The "Statistical discussion about b8" [#b8statistic]_ shows why the default values are the default ones.
|
|
||||||
|
|
||||||
**use_relevant**
|
|
||||||
This tells b8 how many tokens should be used when calculating the spamminess of a text. The default setting is ``15`` (integer). This seems to be a quite reasonable value. When using to many tokens, the filter will fail on texts filled with useless stuff or with passages from a newspaper, etc. not being very spammish. |br|
|
|
||||||
The tokens counted multiple times (see above) are added in addition to this value. They don't replace other ratings.
|
|
||||||
|
|
||||||
**min_dev**
|
|
||||||
This defines a minimum deviation from 0.5 that a token's rating must have to be considered when calculating the spamminess. Tokens with a rating closer to 0.5 than this value will simply be skipped. |br|
|
|
||||||
If you don't want to use this feature, set this to ``0``. Defaults to ``0.2`` (float). Read [#b8statistic]_ before increasing this.
|
|
||||||
|
|
||||||
**rob_x**
|
|
||||||
This is Gary Robinson's *x* constant (cf. [#spamdetection]_). A completely unknown token will be rated with the value of ``rob_x``. The default ``0.5`` (float) seems to be quite reasonable, as we can't say if a token that also can't be rated by degeneration is good or bad. |br|
|
|
||||||
If you receive much more spam than ham or vice versa, you could change this setting accordingly.
|
|
||||||
|
|
||||||
**rob_s**
|
|
||||||
This is Gary Robinson's *s* constant. This is essentially the probability that the *rob_x* value is correct for a completely unknown token. It will also shift the probability of rarely seen tokens towards this value. The default is ``0.3`` (float) |br|
|
|
||||||
See [#spamdetection]_ for a closer description of the *s* constant and read [#b8statistic]_ for specific information about this constant in b8's algorithms.
|
|
||||||
|
|
||||||
Configuration of the storage backend
|
|
||||||
------------------------------------
|
|
||||||
|
|
||||||
All the following values can be set in the "config_database" array (the second parameter) passed to b8. The name of the array doesn't matter (of course), it just has to be the second argument.
|
|
||||||
|
|
||||||
Settings for the Berkeley DB (DBA) backend
|
|
||||||
``````````````````````````````````````````
|
|
||||||
**database**
|
|
||||||
The filename of the database file, relative to the location of ``b8.php``. Defaults to ``wordlist.db`` (string).
|
|
||||||
|
|
||||||
**handler**
|
|
||||||
The DBA handler to use (cf. `the PHP documentation <http://php.net/manual/en/dba.requirements.php>`_ and `Setting up a new Berkeley DB`_). Defaults to ``db4`` (string).
|
|
||||||
|
|
||||||
Settings for the MySQL backend
|
|
||||||
``````````````````````````````
|
|
||||||
|
|
||||||
**database**
|
|
||||||
The database containing b8's wordlist table. Defaults to ``b8_wordlist`` (string).
|
|
||||||
|
|
||||||
**table_name**
|
|
||||||
The table containing b8's wordlist. Defaults to ``b8_wordlist`` (string).
|
|
||||||
|
|
||||||
**host**
|
|
||||||
The host of the MySQL server. Defaults to ``localhost`` (string).
|
|
||||||
|
|
||||||
**user**
|
|
||||||
The user name used to open the database connection. Defaults to ``FALSE`` (boolean).
|
|
||||||
|
|
||||||
**pass**
|
|
||||||
The password required to open the database connection. Defaults to ``FALSE`` (boolean).
|
|
||||||
|
|
||||||
**connection**
|
|
||||||
An existing MySQL link-resource that can be used by b8. Defaults to ``NULL`` (NULL).
|
|
||||||
|
|
||||||
Using b8
|
|
||||||
========
|
|
||||||
|
|
||||||
Now, that everything is configured, you can start to use b8. A sample script that shows what can be done with the filter exists in ``example/index.php``. The best thing for testing how all this works is to use this script before using b8 in your own scripts.
|
|
||||||
|
|
||||||
Before you can start, you have to setup a database so that b8 can store a wordlist.
|
|
||||||
|
|
||||||
Setting up a new database
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
Setting up a new Berkeley DB
|
|
||||||
````````````````````````````
|
|
||||||
|
|
||||||
I wrote a script to setup a new Berkeley DB for b8. It is located in ``install/setup_berkeleydb.php``. Just run this script on your server and be sure that the directory containing it has the proper access rights set so that the server's HTTP server user or PHP user can create a new file in it (probably ``0777``). The script is quite self-explaining, just run it.
|
|
||||||
|
|
||||||
Of course, you can also create a Berkeley DB by hand. In this case, you just have to insert three keys:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
bayes*dbversion => 2
|
|
||||||
bayes*texts.ham => 0
|
|
||||||
bayes*texts.spam => 0
|
|
||||||
|
|
||||||
Be sure to set the right DBA handler in the storage backend configuration if it's not ``db4``.
|
|
||||||
|
|
||||||
Setting up a new MySQL table
|
|
||||||
````````````````````````````
|
|
||||||
|
|
||||||
The SQL file ``install/setup_mysql.sql`` contains both the create statement for the wordlist table of b8 and the ``INSERT`` statements for adding the necessary internal variables.
|
|
||||||
|
|
||||||
Simply change the table name according to your needs (or leave it as it is ;-) and run the SQL to setup a b8 wordlist MySQL table.
|
|
||||||
|
|
||||||
Using b8 in your scripts
|
|
||||||
------------------------
|
|
||||||
|
|
||||||
Just have a look at the example script located in ``example/index.php`` to see how you can include b8 in your scripts. Essentially, this strips down to:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
# Include the b8 code
|
|
||||||
require "{$_SERVER['DOCUMENT_ROOT']}/b8/b8.php";
|
|
||||||
|
|
||||||
# Do some configuration
|
|
||||||
|
|
||||||
$config_b8 = array(
|
|
||||||
'some_key' => 'some_value',
|
|
||||||
'foo' => 'bar'
|
|
||||||
);
|
|
||||||
|
|
||||||
$config_database = array(
|
|
||||||
'some_key' => 'some_value',
|
|
||||||
'foo' => 'bar'
|
|
||||||
);
|
|
||||||
|
|
||||||
# Create a new b8 instance
|
|
||||||
$b8 = new b8($config_b8, $config_database);
|
|
||||||
|
|
||||||
b8 provides three functions in an object oriented way (called e. g. via ``$b8->classify($text)``):
|
|
||||||
|
|
||||||
**learn($text, $category)**
|
|
||||||
This saves the reference text ``$text`` (string) in the category ``$category`` (b8 constant). |br|
|
|
||||||
b8 0.5 introduced two constants that can be used as ``$category``: ``b8::HAM`` and ``b8::SPAM``. To be downward compatible with older versions of b8, the literal values "ham" and "spam" (case-sensitive strings) can still be used here.
|
|
||||||
|
|
||||||
**unlearn($text, $category)**
|
|
||||||
This function just exists to delete a text from a category in which is has been stored accidentally before. It deletes the reference text ``$text`` (string) from the category ``$category`` (either the constants ``b8::HAM`` or ``b8::SPAM`` or the literal case-sensitive strings "ham" or "spam" – cf. above). |br|
|
|
||||||
**Don't delete a spam text from ham after saving it in spam or vice versa, as long you don't have stored it accidentally in the wrong category before!** This will not improve performance, quite the opposite: it will actually break the filter after a time, as the counter for saved ham or spam texts will reach 0, although you have ham or spam tokens stored: the filter will try to remove texts from the ham or spam data which have never been stored there, decrease the counter for tokens which are found just skip the non-existing words.
|
|
||||||
|
|
||||||
**classify($text)**
|
|
||||||
This function takes the text ``$text`` (string), calculates it's probability for being spam it and returns a value between 0 and 1 (float). |br|
|
|
||||||
A value close to 0 says the text is more likely ham and a value close to 1 says the text is more likely spam. What to do with this value is *your* business ;-) See also `Tips on operation`_ below.
|
|
||||||
|
|
||||||
Tips on operation
|
|
||||||
=================
|
|
||||||
|
|
||||||
Before b8 can decide whether a text is spam or ham, you have to tell it what you consider as spam or ham. At least one learned spam or one learned ham text is needed to calculate anything. To get good ratings, you need both learned ham and learned spam texts, the more the better. |br|
|
|
||||||
What's considered as "ham" or "spam" can be very different, depending on the operation site. On my homepage, practically each and every text posted in English or using cyrillic letters is spam. On an English or Russian homepage, this will be not the case. So I think it's not really meaningful to provide some "spam data" to start. Just train b8 with "your" spam and ham.
|
|
||||||
|
|
||||||
For the practical use, I advise to give the filter all data availible. E. g. name, email address, homepage, IP address und of course the text itself should be stored in a variable (e. g. separated with an ``\n`` or just a space or tab after each block) and then be classified. The learning should also be done with all data availible. |br|
|
|
||||||
Saving the IP address is probably only meaningful for spam entries, because spammers often use the same IP address multiple times. In principle, you can leave out the IP of ham entries.
|
|
||||||
|
|
||||||
You can use b8 e. g. in a guestbook script and let it classify the text before saving it. Everyone has to decide which rating is necessary to classify a text as "spam", but a rating of >= 0.8 seems to be reasonable for me. If one expects the spam to be in another language that the ham entries or the spams are very short normally, one could also think about a limit of 0.7. |br|
|
|
||||||
The email filters out there mostly use > 0.9 or even > 0.99; but keep in mind that they have way more data to analyze in most of the cases. A guestbook entry may be quite short, especially when it's spam.
|
|
||||||
|
|
||||||
In my opinion, a autolearn function is very handy. I save spam messages with a rating higher than 0.7 but less than 0.9 automatically as spam. I don't do this with ham messages in an automated way to prevent the filter from saving a false negative as ham and then classifying and learning all the spam as ham when I'm on holidays ;-)
|
|
||||||
|
|
||||||
Closing
|
|
||||||
=======
|
|
||||||
|
|
||||||
So … that's it. Thanks for using b8! If you find a bug or have an idea how to make b8 better, let me know. I'm also always looking forward to get e-mails from people using b8 on their homepages :-)
|
|
||||||
|
|
||||||
References
|
|
||||||
==========
|
|
||||||
|
|
||||||
.. [#planforspam] Paul Graham, *A Plan For Spam* (http://paulgraham.com/spam.html)
|
|
||||||
.. [#betterbayesian] Paul Graham, *Better Bayesian Filtering* (http://paulgraham.com/better.html)
|
|
||||||
.. [#spamdetection] Gary Robinson, *Spam Detection* (http://radio.weblogs.com/0101454/stories/2002/09/16/spamDetection.html)
|
|
||||||
.. [#statisticalapproach] *A Statistical Approach to the Spam Problem* (http://linuxjournal.com/article/6467)
|
|
||||||
.. [#b8statistic] Tobias Leupold, *Statistical discussion about b8* (http://nasauber.de/opensource/b8/discussion/)
|
|
||||||
|
|
||||||
Appendix
|
|
||||||
========
|
|
||||||
|
|
||||||
FAQ
|
|
||||||
---
|
|
||||||
|
|
||||||
What about more than two categories?
|
|
||||||
````````````````````````````````````
|
|
||||||
|
|
||||||
I wrote b8 with the `KISS principle <http://en.wikipedia.org/wiki/KISS_principle>`__ in mind. For the "end-user", we have a class with almost no setup to do that can do three things: classify a text, learn a text and un-learn a text. Normally, there's no need to un-learn a text, so essentially, there are only two functions we need. |br|
|
|
||||||
This simplicity is only possible because b8 only knows two categories (normally "Ham" and "Spam" or some other category pair) and tells you, in one float number between 0 and 1, if a given texts rather fits in the first or the second category. If we would support multiple categories, more work would have to be done and things would become more complicated. One would have to setup the categories, have another database layout (perhaps making it mandatory to have SQL) and one float number would not be sufficient to describe b8's output, so more code would be needed – even outside of b8.
|
|
||||||
|
|
||||||
All the code, the database layout and particularly the math is intended to do exactly one thing: distinguish between two categories. I think it would be a lot of work to change b8 so that it would support more than two categories. Probably, this is possible to do, but don't ask me in which way we would have to change the math to get multiple-category support – I'm a dentist, not a mathematician ;-) |br|
|
|
||||||
Apart from this I do believe that most people using b8 don't want or need multiple categories. They just want to know if a text is spam or not, don't they? I do, at least ;-)
|
|
||||||
|
|
||||||
But let's think about the multiple-category thing. How would we calculate a rating for more than two categories? If we had a third one, let's call it "`Treet <http://en.wikipedia.org/wiki/Treet>`__", how would we calculate a rating? We could calculate three different ratings. One for "Ham", one for "Spam" and one for "Treet" and choose the highest one to tell the user what category fits best for the text. This could be done by using a small wrapper script using three instances of b8 as-is and three different databases, each containing texts being "Ham", "Spam", "Treet" and the respective counterparts. |br|
|
|
||||||
But here's the problem: if we have "Ham" and "Spam", "Spam" is the counterpart of "Ham". But what's the counterpart of "Spam" if we have more than one additional category? Where do the "Non-Ham", "Non-Spam" and "Non-Treet" texts come from?
|
|
||||||
|
|
||||||
Another approach, a direct calculation of more than two probabilities (the "Ham" probability is simply 1 minus the "Spam" probability, so we actually get two probabilities with the return value of b8) out of one database would require big changes in b8's structure and math.
|
|
||||||
|
|
||||||
There's a project called `PHPNaiveBayesianFilter <http://xhtml.net/scripts/PHPNaiveBayesianFilter>`__ which supports multiple categories by default. The author calls his software "Version 1.0", but I think this is the very first release, not a stable or mature one. The most recent change of that release dates back to 2003 according to the "changed" date of the files inside the zip archive, so probably, this project is dead or has never been alive and under active development at all. |br|
|
|
||||||
Actually, I played around with that code but the results weren't really good, so I decided to write my own spam filter from scratch back in early 2006 ;-)
|
|
||||||
|
|
||||||
All in all, there seems to be no easy way to implement multiple (meaning more than two) categories using b8's current code base and probably, b8 will never support more than two categories. Perhaps, a fork or a complete re-write would be better than implementing such a feature. Anyway, I don't close my mind to multiple categories in b8. Feel free to tell me how multiple categories could be implementented in b8 or how a multiple-category version using the same code base (sharing a common abstract class?) could be written.
|
|
||||||
|
|
||||||
What about a list with words to ignore?
|
|
||||||
```````````````````````````````````````
|
|
||||||
|
|
||||||
Some people suggested to introduce a list with words that b8 will simply ignore. Like "and", "or", "the", and so on. I don't think this is very meaningful.
|
|
||||||
|
|
||||||
First, it would just work for the particular language that has been stored in the list. Speaking of my homepage, most of my spam is English, almost all my ham is German. So I would have to maintain a list with the probably less interesting words for at least two languages. Additionally, I get spam in Chinese, Japanese and Cyrillic writing or something else I can't read as well. What word should be ignored in those texts? |br|
|
|
||||||
Second, why should we ever exclude words? Who tells us those words are *actually* meaningless? If a word appears both in ham and spam, it's rating will be near 0.5 and so, it won't be used for the final calculation if a appropriate minimum deviation was set. So b8 will exclude it anyway without any blacklist. And think of this: if we excluded a word of which we only *think* it doesn't mean anything but it actually does appear more often in ham or spam, the results will get even worse.
|
|
||||||
|
|
||||||
So why should we care about things we do not have to care about? ;-)
|
|
||||||
|
|
||||||
|
|
||||||
Why is it called "b8"?
|
|
||||||
``````````````````````
|
|
||||||
|
|
||||||
The initial name for the filter was (damn creative!) "bayes-php". There were two main reasons for searching another name: 1. "bayes-php" sucks. 2. the `PHP License <http://php.net/license/3_01.txt>`_ says the PHP guys do not like when the name of a script written in PHP contains the word "PHP". Read the `License FAQ <http://www.php.net/license/index.php#faq-lic>`_ for a reasonable argumentation about this.
|
|
||||||
|
|
||||||
Luckily, `Tobias Lang <http://langt.net/>`_ proposed the new name "b8". And these are the reasons why I chose this name:
|
|
||||||
|
|
||||||
- "bayes-php" is a "b" followed by 8 letters.
|
|
||||||
- "b8" is short and handy. Additionally, there was no program with the name "b8" or "bate"
|
|
||||||
- The English verb "to bate" means "to decrease" – and that's what b8 does: it decreases the number of spam entries in your weblog or guestbook!
|
|
||||||
- "b8" just sounds way cooler than "bayes-php" ;-)
|
|
||||||
|
|
||||||
About the database
|
|
||||||
------------------
|
|
||||||
|
|
||||||
The database layout
|
|
||||||
```````````````````
|
|
||||||
|
|
||||||
The database layout is quite simple. It's just key:value for everything stored. There are three "internal" variables stored as normal tokens (but all containing a ``*`` which is always used as a split character by the lexer, so we can't get collisions):
|
|
||||||
|
|
||||||
**bayes*dbversion**
|
|
||||||
This indicates the database's "version". The first versions of b8 did not set this. Version "2" indicates that we have a database created by a b8 version already storing `the "lastseen" parameter`_.
|
|
||||||
|
|
||||||
**bayes*texts.ham**
|
|
||||||
The number of ham texts learned.
|
|
||||||
|
|
||||||
**bayes*texts.spam**
|
|
||||||
The number of spam texts learned.
|
|
||||||
|
|
||||||
Each "normal" token is stored with it's literal name as the key and it's data as the value. The data consists of the count of the token in all ham and spam texts and the date when the token was used the last time, all in one string and separated by spaces. So we have the following scheme:
|
|
||||||
|
|
||||||
::
|
|
||||||
|
|
||||||
"token" => "count_ham count_spam lastseen"
|
|
||||||
|
|
||||||
The "lastseen" parameter
|
|
||||||
````````````````````````
|
|
||||||
|
|
||||||
Somebody looking at the code might be wondering why b8 stores this "lastseen" parameter. This value is not used for any calculation at the moment. Initially, it was intended to keep the database maintainable in a way that "old" data could be removed. When e. g. a token only appeared once in ham or spam and has not been seen for a year, one could simply delete it from the database. |br|
|
|
||||||
I actually never used this feature (does anybody?). So probably, some changes will be done to this one day. Perhaps, I find a way to include this data in the spamminess calculation in a meaningful way, or at least for some statistics. One could also make this optional to keep the calculation effort small if this is needed.
|
|
||||||
|
|
||||||
Feel free to send me any suggestions about this!
|
|
||||||
|
|
||||||
.. |br| raw:: html
|
|
||||||
|
|
||||||
<br />
|
|
||||||
|
|
||||||
.. section-numbering::
|
|
||||||
|
|
||||||
.. |date| date::
|
|
|
@ -1,241 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2006-2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
|
|
||||||
### This is an example script demonstrating how b8 can be used. ###
|
|
||||||
|
|
||||||
#/*
|
|
||||||
|
|
||||||
# Use this code block if you want to use Berkeley DB.
|
|
||||||
|
|
||||||
# The database filename is interpreted relative to the b8.php script location.
|
|
||||||
|
|
||||||
$config_b8 = array(
|
|
||||||
'storage' => 'dba'
|
|
||||||
);
|
|
||||||
|
|
||||||
$config_database = array(
|
|
||||||
'database' => 'wordlist.db',
|
|
||||||
'handler' => 'db4'
|
|
||||||
);
|
|
||||||
|
|
||||||
#*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
# Use this code block if you want to use MySQL.
|
|
||||||
|
|
||||||
# An existing link resource can be passed to b8 by setting
|
|
||||||
# $config_database['connection'] to this link resource.
|
|
||||||
# Be sure to set your database access data otherwise!
|
|
||||||
|
|
||||||
$config_b8 = array(
|
|
||||||
'storage' => 'mysql'
|
|
||||||
);
|
|
||||||
|
|
||||||
$config_database = array(
|
|
||||||
'database' => 'test',
|
|
||||||
'table_name' => 'b8_wordlist',
|
|
||||||
'host' => 'localhost',
|
|
||||||
'user' => '',
|
|
||||||
'pass' => ''
|
|
||||||
);
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
# To be able to calculate the time the classification took
|
|
||||||
|
|
||||||
$time_start = NULL;
|
|
||||||
|
|
||||||
function microtimeFloat()
|
|
||||||
{
|
|
||||||
list($usec, $sec) = explode(" ", microtime());
|
|
||||||
return ((float) $usec + (float) $sec);
|
|
||||||
}
|
|
||||||
|
|
||||||
# Output a nicely colored rating
|
|
||||||
|
|
||||||
function formatRating($rating)
|
|
||||||
{
|
|
||||||
|
|
||||||
if($rating === FALSE)
|
|
||||||
return "<span style=\"color:red\">could not calculate spaminess</span>";
|
|
||||||
|
|
||||||
$red = floor(255 * $rating);
|
|
||||||
$green = floor(255 * (1 - $rating));
|
|
||||||
|
|
||||||
return "<span style=\"color:rgb($red, $green, 0);\"><b>" . sprintf("%5f", $rating) . "</b></span>";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
echo <<<END
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
||||||
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<title>example b8 interface</title>
|
|
||||||
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
|
||||||
|
|
||||||
<meta name="dc.creator" content="Tobias Leupold" />
|
|
||||||
<meta name="dc.rights" content="Copyright (c) by Tobias Leupold" />
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<h1>example b8 interface</h1>
|
|
||||||
|
|
||||||
|
|
||||||
END;
|
|
||||||
|
|
||||||
$postedText = "";
|
|
||||||
|
|
||||||
if(isset($_POST['action']) and $_POST['text'] == "")
|
|
||||||
echo "<p style=\"color:red;\"><b>Please type in a text!</b></p>\n\n";
|
|
||||||
|
|
||||||
elseif(isset($_POST['action']) and $_POST['text'] != "") {
|
|
||||||
|
|
||||||
$time_start = microtimeFloat();
|
|
||||||
|
|
||||||
# Include the b8 code
|
|
||||||
require dirname(__FILE__) . "/../b8/b8.php";
|
|
||||||
|
|
||||||
# Create a new b8 instance
|
|
||||||
$b8 = new b8($config_b8, $config_database);
|
|
||||||
|
|
||||||
# Check if everything worked smoothly
|
|
||||||
|
|
||||||
$started_up = $b8->validate();
|
|
||||||
|
|
||||||
if($started_up !== TRUE) {
|
|
||||||
echo "<b>example:</b> Could not initialize b8. error code: $started_up";
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$text = stripslashes($_POST['text']);
|
|
||||||
$postedText = htmlentities($text, ENT_QUOTES, 'UTF-8');
|
|
||||||
|
|
||||||
switch($_POST['action']) {
|
|
||||||
|
|
||||||
case "Classify":
|
|
||||||
echo "<p><b>Spaminess: " . formatRating($b8->classify($text)) . "</b></p>\n";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Save as Spam":
|
|
||||||
|
|
||||||
$ratingBefore = $b8->classify($text);
|
|
||||||
$b8->learn($text, b8::SPAM);
|
|
||||||
$ratingAfter = $b8->classify($text);
|
|
||||||
|
|
||||||
echo "<p>Saved the text as Spam</p>\n\n";
|
|
||||||
|
|
||||||
echo "<div><table>\n";
|
|
||||||
echo "<tr><td>Classification before learning:</td><td>" . formatRating($ratingBefore) . "</td></tr>\n";
|
|
||||||
echo "<tr><td>Classification after learning:</td><td>" . formatRating($ratingAfter) . "</td></tr>\n";
|
|
||||||
echo "</table></div>\n\n";
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Save as Ham":
|
|
||||||
|
|
||||||
$ratingBefore = $b8->classify($text);
|
|
||||||
$b8->learn($text, b8::HAM);
|
|
||||||
$ratingAfter = $b8->classify($text);
|
|
||||||
|
|
||||||
echo "<p>Saved the text as Ham</p>\n\n";
|
|
||||||
|
|
||||||
echo "<div><table>\n";
|
|
||||||
echo "<tr><td>Classification before learning:</td><td>" . formatRating($ratingBefore) . "</td></tr>\n";
|
|
||||||
echo "<tr><td>Classification after learning:</td><td>" . formatRating($ratingAfter) . "</td></tr>\n";
|
|
||||||
echo "</table></div>\n\n";
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Delete from Spam":
|
|
||||||
$b8->unlearn($text, b8::SPAM);
|
|
||||||
echo "<p style=\"color:green\">Deleted the text from Spam</p>\n\n";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "Delete from Ham":
|
|
||||||
$b8->unlearn($text, b8::HAM);
|
|
||||||
echo "<p style=\"color:green\">Deleted the text from Ham</p>\n\n";
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$mem_used = round(memory_get_usage() / 1048576, 5);
|
|
||||||
$peak_mem_used = round(memory_get_peak_usage() / 1048576, 5);
|
|
||||||
$time_taken = round(microtimeFloat() - $time_start, 5);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
echo <<<END
|
|
||||||
<div>
|
|
||||||
<form action="{$_SERVER['PHP_SELF']}" method="post">
|
|
||||||
<div>
|
|
||||||
<textarea name="text" cols="50" rows="16">$postedText</textarea>
|
|
||||||
</div>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td><input type="submit" name="action" value="Classify" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><input type="submit" name="action" value="Save as Spam" /></td>
|
|
||||||
<td><input type="submit" name="action" value="Save as Ham" /></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><input type="submit" name="action" value="Delete from Spam" /></td>
|
|
||||||
<td><input type="submit" name="action" value="Delete from Ham" /></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
END;
|
|
||||||
|
|
||||||
if($time_start !== NULL) {
|
|
||||||
|
|
||||||
echo <<<END
|
|
||||||
<div>
|
|
||||||
<table border="0">
|
|
||||||
<tr><td>Memory used: </td><td>$mem_used MB</td></tr>
|
|
||||||
<tr><td>Peak memory used:</td><td>$peak_mem_used MB</td></tr>
|
|
||||||
<tr><td>Time taken: </td><td>$time_taken sec</td></tr>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
END;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,240 +0,0 @@
|
||||||
<?php
|
|
||||||
|
|
||||||
# Copyright (C) 2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
#
|
|
||||||
# This file is part of the b8 package
|
|
||||||
#
|
|
||||||
# This program 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 in version 2.1 of the License.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
# License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License
|
|
||||||
# along with this program; if not, write to the Free Software Foundation,
|
|
||||||
# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
echo <<<END
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
|
|
||||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
|
||||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
|
||||||
|
|
||||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
|
|
||||||
<title>b8 Berkeley DB setup</title>
|
|
||||||
|
|
||||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
|
|
||||||
|
|
||||||
<meta name="dc.creator" content="Tobias Leupold" />
|
|
||||||
<meta name="dc.rights" content="Copyright (c) by Tobias Leupold" />
|
|
||||||
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div>
|
|
||||||
|
|
||||||
<h1>b8 Berkeley DB setup</h1>
|
|
||||||
|
|
||||||
|
|
||||||
END;
|
|
||||||
|
|
||||||
$failed = FALSE;
|
|
||||||
|
|
||||||
if(isset($_POST['handler'])) {
|
|
||||||
|
|
||||||
$dbfile = $_POST['dbfile'];
|
|
||||||
$dbfile_directory = $_SERVER['DOCUMENT_ROOT'] . dirname($_SERVER['PHP_SELF']);
|
|
||||||
|
|
||||||
echo "<h2>Creating database</h2>\n\n";
|
|
||||||
|
|
||||||
echo "<p>\n";
|
|
||||||
|
|
||||||
echo "Checking database file name … ";
|
|
||||||
|
|
||||||
if($dbfile == "") {
|
|
||||||
echo "<span style=\"color:red;\">Please provide the name of the database file!</span><br />\n";
|
|
||||||
$failed = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo "$dbfile<br />\n";
|
|
||||||
|
|
||||||
if(!$failed) {
|
|
||||||
|
|
||||||
echo "Touching/Creating " . htmlentities($dbfile) . " … ";
|
|
||||||
|
|
||||||
if(touch($dbfile) === FALSE) {
|
|
||||||
echo "<span style=\"color:red;\">Failed to touch the database file. Please check the given filename and/or fix the permissions of $dbfile_directory.</span><br />\n";
|
|
||||||
$failed = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo "done<br />\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$failed) {
|
|
||||||
|
|
||||||
echo "Setting file permissions to 0666 &hellip ";
|
|
||||||
|
|
||||||
if(chmod($dbfile, 0666) === FALSE) {
|
|
||||||
echo "<span style=\"color:red;\">Failed to change the permissions of $dbfile_directory/$dbfile. Please adjust them manually.</span><br />\n";
|
|
||||||
$failed = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo "done<br />\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$failed) {
|
|
||||||
|
|
||||||
echo "Checking if the given file is empty &hellip ";
|
|
||||||
|
|
||||||
if(filesize($dbfile) > 0) {
|
|
||||||
echo "<span style=\"color:red;\">$dbfile_directory/$dbfile is not empty. Can't create a new database. Please delete/empty this file or give another filename.</span><br />\n";
|
|
||||||
$failed = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo "it is<br />\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$failed) {
|
|
||||||
|
|
||||||
echo "Connecting to $dbfile … ";
|
|
||||||
|
|
||||||
$db = dba_open($dbfile, "c", $_POST['handler']);
|
|
||||||
|
|
||||||
if($db === FALSE) {
|
|
||||||
echo "<span style=\"color:red;\">Could not connect to the database!</span><br />\n";
|
|
||||||
$failed = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo "done<br />\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$failed) {
|
|
||||||
|
|
||||||
echo "Storing necessary internal variables &hellip ";
|
|
||||||
|
|
||||||
$internals = array(
|
|
||||||
"bayes*dbversion" => "2",
|
|
||||||
"bayes*texts.ham" => "0",
|
|
||||||
"bayes*texts.spam" => "0"
|
|
||||||
);
|
|
||||||
|
|
||||||
foreach($internals as $key => $value) {
|
|
||||||
if(dba_insert($key, $value, $db) === FALSE) {
|
|
||||||
echo "<span style=\"color:red;\">Failed to insert data!</span><br />\n";
|
|
||||||
$failed = TRUE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$failed)
|
|
||||||
echo "done<br />\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$failed) {
|
|
||||||
|
|
||||||
echo "Trying to read data from the database &hellip ";
|
|
||||||
|
|
||||||
$dbversion = dba_fetch("bayes*dbversion", $db);
|
|
||||||
|
|
||||||
if($dbversion != "2") {
|
|
||||||
echo "<span style=\"color:red;\">Failed to read data!</span><br />\n";
|
|
||||||
$failed = TRUE;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
echo "success<br />\n";
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$failed) {
|
|
||||||
|
|
||||||
dba_close($db);
|
|
||||||
|
|
||||||
echo "</p>\n\n";
|
|
||||||
echo "<p style=\"color:green;\">Successfully created a new b8 database!</p>\n\n";
|
|
||||||
echo "<table>\n";
|
|
||||||
echo "<tr><td>Filename:</td><td>$dbfile_directory/$dbfile</td></tr>\n";
|
|
||||||
echo "<tr><td>DBA handler:</td><td>{$_POST['handler']}</td><tr>\n";
|
|
||||||
echo "</table>\n\n";
|
|
||||||
echo "<p>Move this file to it's destination directory (default: the base directory of b8) to use it with b8. Be sure to use the right DBA handler in b8's configuration.";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "</p>\n\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if($failed === TRUE or !isset($_POST['handler'])) {
|
|
||||||
|
|
||||||
echo <<<END
|
|
||||||
<form action="{$_SERVER['PHP_SELF']}" method="post">
|
|
||||||
|
|
||||||
<h2>DBA Handler</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The following table shows all available DBA handlers. Please choose the "Berkeley DB" one.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<table>
|
|
||||||
<tr><td></td><td><b>Handler</b></td><td><b>Description</b></td></tr>
|
|
||||||
|
|
||||||
END;
|
|
||||||
|
|
||||||
foreach(dba_handlers(TRUE) as $name => $version) {
|
|
||||||
|
|
||||||
$checked = "";
|
|
||||||
|
|
||||||
if(!isset($_POST['handler'])) {
|
|
||||||
if(strpos($version, "Berkeley") !== FALSE )
|
|
||||||
$checked = " checked=\"checked\"";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if($_POST['handler'] == $name)
|
|
||||||
$checked = " checked=\"checked\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "<tr><td><input type=\"radio\" name=\"handler\" value=\"$name\"$checked /></td><td>$name</td><td>$version</td></tr>\n";
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
echo <<<END
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<h2>Database file</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Please the name of the desired database file. It will be created in the directory where this script is located.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<input type="text" name="dbfile" value="wordlist.db" />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
<input type="submit" value="Create the database" />
|
|
||||||
</p>
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
END;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
|
@ -1,27 +0,0 @@
|
||||||
|
|
||||||
-- Copyright (C) 2010 Tobias Leupold <tobias.leupold@web.de>
|
|
||||||
--
|
|
||||||
-- This file is part of the b8 package
|
|
||||||
--
|
|
||||||
-- This program 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 in version 2.1 of the License.
|
|
||||||
--
|
|
||||||
-- This program is distributed in the hope that it will be useful, but
|
|
||||||
-- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
-- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
|
|
||||||
-- License for more details.
|
|
||||||
--
|
|
||||||
-- You should have received a copy of the GNU Lesser General Public License
|
|
||||||
-- along with this program; if not, write to the Free Software Foundation,
|
|
||||||
-- Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
|
|
||||||
|
|
||||||
CREATE TABLE `b8_wordlist` (
|
|
||||||
`token` varchar(255) character set utf8 collate utf8_bin NOT NULL,
|
|
||||||
`count` varchar(255) default NULL,
|
|
||||||
PRIMARY KEY (`token`)
|
|
||||||
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
|
|
||||||
|
|
||||||
INSERT INTO `b8_wordlist` VALUES ('bayes*dbversion', '2');
|
|
||||||
INSERT INTO `b8_wordlist` VALUES ('bayes*texts.ham', '0');
|
|
||||||
INSERT INTO `b8_wordlist` VALUES ('bayes*texts.spam', '0');
|
|
|
@ -629,7 +629,6 @@ function admin_page_site_post(App $a) {
|
||||||
$no_multi_reg = ((x($_POST,'no_multi_reg')) ? True : False);
|
$no_multi_reg = ((x($_POST,'no_multi_reg')) ? True : False);
|
||||||
$no_openid = !((x($_POST,'no_openid')) ? True : False);
|
$no_openid = !((x($_POST,'no_openid')) ? True : False);
|
||||||
$no_regfullname = !((x($_POST,'no_regfullname')) ? True : False);
|
$no_regfullname = !((x($_POST,'no_regfullname')) ? True : False);
|
||||||
$no_utf = !((x($_POST,'no_utf')) ? True : False);
|
|
||||||
$community_page_style = ((x($_POST,'community_page_style')) ? intval(trim($_POST['community_page_style'])) : 0);
|
$community_page_style = ((x($_POST,'community_page_style')) ? intval(trim($_POST['community_page_style'])) : 0);
|
||||||
$max_author_posts_community_page = ((x($_POST,'max_author_posts_community_page')) ? intval(trim($_POST['max_author_posts_community_page'])) : 0);
|
$max_author_posts_community_page = ((x($_POST,'max_author_posts_community_page')) ? intval(trim($_POST['max_author_posts_community_page'])) : 0);
|
||||||
|
|
||||||
|
@ -666,7 +665,6 @@ function admin_page_site_post(App $a) {
|
||||||
$proxy_disabled = ((x($_POST,'proxy_disabled')) ? True : False);
|
$proxy_disabled = ((x($_POST,'proxy_disabled')) ? True : False);
|
||||||
$only_tag_search = ((x($_POST,'only_tag_search')) ? True : False);
|
$only_tag_search = ((x($_POST,'only_tag_search')) ? True : False);
|
||||||
$rino = ((x($_POST,'rino')) ? intval($_POST['rino']) : 0);
|
$rino = ((x($_POST,'rino')) ? intval($_POST['rino']) : 0);
|
||||||
$embedly = ((x($_POST,'embedly')) ? notags(trim($_POST['embedly'])) : '');
|
|
||||||
$worker_queues = ((x($_POST,'worker_queues')) ? intval($_POST['worker_queues']) : 4);
|
$worker_queues = ((x($_POST,'worker_queues')) ? intval($_POST['worker_queues']) : 4);
|
||||||
$worker_dont_fork = ((x($_POST,'worker_dont_fork')) ? True : False);
|
$worker_dont_fork = ((x($_POST,'worker_dont_fork')) ? True : False);
|
||||||
$worker_fastlane = ((x($_POST,'worker_fastlane')) ? True : False);
|
$worker_fastlane = ((x($_POST,'worker_fastlane')) ? True : False);
|
||||||
|
@ -787,7 +785,6 @@ function admin_page_site_post(App $a) {
|
||||||
set_config('system', 'no_regfullname', $no_regfullname);
|
set_config('system', 'no_regfullname', $no_regfullname);
|
||||||
set_config('system', 'community_page_style', $community_page_style);
|
set_config('system', 'community_page_style', $community_page_style);
|
||||||
set_config('system', 'max_author_posts_community_page', $max_author_posts_community_page);
|
set_config('system', 'max_author_posts_community_page', $max_author_posts_community_page);
|
||||||
set_config('system','no_utf', $no_utf);
|
|
||||||
set_config('system', 'verifyssl', $verifyssl);
|
set_config('system', 'verifyssl', $verifyssl);
|
||||||
set_config('system', 'proxyuser', $proxyuser);
|
set_config('system', 'proxyuser', $proxyuser);
|
||||||
set_config('system', 'proxy', $proxy);
|
set_config('system', 'proxy', $proxy);
|
||||||
|
@ -814,15 +811,7 @@ function admin_page_site_post(App $a) {
|
||||||
set_config('system', 'worker_dont_fork', $worker_dont_fork);
|
set_config('system', 'worker_dont_fork', $worker_dont_fork);
|
||||||
set_config('system', 'worker_fastlane', $worker_fastlane);
|
set_config('system', 'worker_fastlane', $worker_fastlane);
|
||||||
set_config('system', 'frontend_worker', $worker_frontend);
|
set_config('system', 'frontend_worker', $worker_frontend);
|
||||||
|
|
||||||
if (($rino == 2) and !function_exists('mcrypt_create_iv')) {
|
|
||||||
notice(t("RINO2 needs mcrypt php extension to work."));
|
|
||||||
} else {
|
|
||||||
set_config('system', 'rino_encrypt', $rino);
|
set_config('system', 'rino_encrypt', $rino);
|
||||||
}
|
|
||||||
|
|
||||||
set_config('system','embedly', $embedly);
|
|
||||||
|
|
||||||
|
|
||||||
info(t('Site settings updated.').EOL);
|
info(t('Site settings updated.').EOL);
|
||||||
goaway('admin/site');
|
goaway('admin/site');
|
||||||
|
@ -1012,7 +1001,6 @@ function admin_page_site(App $a) {
|
||||||
'$no_multi_reg' => array('no_multi_reg', t("Block multiple registrations"), get_config('system','block_extended_register'), t("Disallow users to register additional accounts for use as pages.")),
|
'$no_multi_reg' => array('no_multi_reg', t("Block multiple registrations"), get_config('system','block_extended_register'), t("Disallow users to register additional accounts for use as pages.")),
|
||||||
'$no_openid' => array('no_openid', t("OpenID support"), !get_config('system','no_openid'), t("OpenID support for registration and logins.")),
|
'$no_openid' => array('no_openid', t("OpenID support"), !get_config('system','no_openid'), t("OpenID support for registration and logins.")),
|
||||||
'$no_regfullname' => array('no_regfullname', t("Fullname check"), !get_config('system','no_regfullname'), t("Force users to register with a space between firstname and lastname in Full name, as an antispam measure")),
|
'$no_regfullname' => array('no_regfullname', t("Fullname check"), !get_config('system','no_regfullname'), t("Force users to register with a space between firstname and lastname in Full name, as an antispam measure")),
|
||||||
'$no_utf' => array('no_utf', t("UTF-8 Regular expressions"), !get_config('system','no_utf'), t("Use PHP UTF8 regular expressions")),
|
|
||||||
'$community_page_style' => array('community_page_style', t("Community Page Style"), get_config('system','community_page_style'), t("Type of community page to show. 'Global community' shows every public posting from an open distributed network that arrived on this server."), $community_page_style_choices),
|
'$community_page_style' => array('community_page_style', t("Community Page Style"), get_config('system','community_page_style'), t("Type of community page to show. 'Global community' shows every public posting from an open distributed network that arrived on this server."), $community_page_style_choices),
|
||||||
'$max_author_posts_community_page' => array('max_author_posts_community_page', t("Posts per user on community page"), get_config('system','max_author_posts_community_page'), t("The maximum number of posts per user on the community page. (Not valid for 'Global Community')")),
|
'$max_author_posts_community_page' => array('max_author_posts_community_page', t("Posts per user on community page"), get_config('system','max_author_posts_community_page'), t("The maximum number of posts per user on the community page. (Not valid for 'Global Community')")),
|
||||||
'$ostatus_disabled' => array('ostatus_disabled', t("Enable OStatus support"), !get_config('system','ostatus_disabled'), t("Provide built-in OStatus \x28StatusNet, GNU Social etc.\x29 compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed.")),
|
'$ostatus_disabled' => array('ostatus_disabled', t("Enable OStatus support"), !get_config('system','ostatus_disabled'), t("Provide built-in OStatus \x28StatusNet, GNU Social etc.\x29 compatibility. All communications in OStatus are public, so privacy warnings will be occasionally displayed.")),
|
||||||
|
@ -1053,7 +1041,6 @@ function admin_page_site(App $a) {
|
||||||
'$relocate_url' => array('relocate_url', t("New base url"), App::get_baseurl(), t("Change base url for this server. Sends relocate message to all DFRN contacts of all users.")),
|
'$relocate_url' => array('relocate_url', t("New base url"), App::get_baseurl(), t("Change base url for this server. Sends relocate message to all DFRN contacts of all users.")),
|
||||||
|
|
||||||
'$rino' => array('rino', t("RINO Encryption"), intval(get_config('system','rino_encrypt')), t("Encryption layer between nodes."), array("Disabled", "RINO1 (deprecated)", "RINO2")),
|
'$rino' => array('rino', t("RINO Encryption"), intval(get_config('system','rino_encrypt')), t("Encryption layer between nodes."), array("Disabled", "RINO1 (deprecated)", "RINO2")),
|
||||||
'$embedly' => array('embedly', t("Embedly API key"), get_config('system','embedly'), t("<a href='http://embed.ly'>Embedly</a> is used to fetch additional data for web pages. This is an optional parameter.")),
|
|
||||||
|
|
||||||
'$worker_queues' => array('worker_queues', t("Maximum number of parallel workers"), get_config('system','worker_queues'), t("On shared hosters set this to 2. On larger systems, values of 10 are great. Default value is 4.")),
|
'$worker_queues' => array('worker_queues', t("Maximum number of parallel workers"), get_config('system','worker_queues'), t("On shared hosters set this to 2. On larger systems, values of 10 are great. Default value is 4.")),
|
||||||
'$worker_dont_fork' => array('worker_dont_fork', t("Don't use 'proc_open' with the worker"), get_config('system','worker_dont_fork'), t("Enable this if your system doesn't allow the use of 'proc_open'. This can happen on shared hosters. If this is enabled you should increase the frequency of poller calls in your crontab.")),
|
'$worker_dont_fork' => array('worker_dont_fork', t("Don't use 'proc_open' with the worker"), get_config('system','worker_dont_fork'), t("Enable this if your system doesn't allow the use of 'proc_open'. This can happen on shared hosters. If this is enabled you should increase the frequency of poller calls in your crontab.")),
|
||||||
|
|
|
@ -1,78 +1,73 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require_once('include/bbcode.php');
|
require_once 'include/bbcode.php';
|
||||||
require_once('library/markdown.php');
|
require_once 'library/markdown.php';
|
||||||
require_once('include/bb2diaspora.php');
|
require_once 'include/bb2diaspora.php';
|
||||||
require_once('include/html2bbcode.php');
|
require_once 'include/html2bbcode.php';
|
||||||
|
|
||||||
function visible_lf($s) {
|
function visible_lf($s) {
|
||||||
return str_replace("\n", '<br />', $s);
|
return str_replace("\n", '<br />', $s);
|
||||||
}
|
}
|
||||||
|
|
||||||
function babel_content(App $a) {
|
function babel_content(App $a) {
|
||||||
|
|
||||||
$o .= '<h1>Babel Diagnostic</h1>';
|
$o .= '<h1>Babel Diagnostic</h1>';
|
||||||
|
|
||||||
$o .= '<form action="babel" method="post">';
|
$o .= '<form action="babel" method="post">';
|
||||||
$o .= t('Source (bbcode) text:') . EOL . '<textarea name="text" >' . htmlspecialchars($_REQUEST['text']) .'</textarea>' . EOL;
|
$o .= t('Source (bbcode) text:') . EOL;
|
||||||
|
$o .= '<textarea name="text" cols="80" rows="10">' . htmlspecialchars($_REQUEST['text']) .'</textarea>' . EOL;
|
||||||
$o .= '<input type="submit" name="submit" value="Submit" /></form>';
|
$o .= '<input type="submit" name="submit" value="Submit" /></form>';
|
||||||
|
|
||||||
$o .= '<br /><br />';
|
$o .= '<br /><br />';
|
||||||
|
|
||||||
$o .= '<form action="babel" method="post">';
|
$o .= '<form action="babel" method="post">';
|
||||||
$o .= t('Source (Diaspora) text to convert to BBcode:') . EOL . '<textarea name="d2bbtext" >' . htmlspecialchars($_REQUEST['d2bbtext']) .'</textarea>' . EOL;
|
$o .= t('Source (Diaspora) text to convert to BBcode:') . EOL;
|
||||||
|
$o .= '<textarea name="d2bbtext" cols="80" rows="10">' . htmlspecialchars($_REQUEST['d2bbtext']) .'</textarea>' . EOL;
|
||||||
$o .= '<input type="submit" name="submit" value="Submit" /></form>';
|
$o .= '<input type="submit" name="submit" value="Submit" /></form>';
|
||||||
|
|
||||||
$o .= '<br /><br />';
|
$o .= '<br /><br />';
|
||||||
|
|
||||||
if (x($_REQUEST, 'text')) {
|
if (x($_REQUEST, 'text')) {
|
||||||
|
|
||||||
$text = trim($_REQUEST['text']);
|
$text = trim($_REQUEST['text']);
|
||||||
$o .= "<h2>" . t("Source input: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('Source input: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= visible_lf($text) . EOL. EOL;
|
$o .= visible_lf($text) . EOL. EOL;
|
||||||
|
|
||||||
$html = bbcode($text);
|
$html = bbcode($text);
|
||||||
$o .= "<h2>" . t("bb2html (raw HTML): ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('bb2html (raw HTML): ') . '</h2>' . EOL. EOL;
|
||||||
$o .= htmlspecialchars($html). EOL. EOL;
|
$o .= htmlspecialchars($html). EOL. EOL;
|
||||||
|
|
||||||
//$html = bbcode($text);
|
//$html = bbcode($text);
|
||||||
$o .= "<h2>" . t("bb2html: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('bb2html: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= $html. EOL. EOL;
|
$o .= $html. EOL. EOL;
|
||||||
|
|
||||||
$bbcode = html2bbcode($html);
|
$bbcode = html2bbcode($html);
|
||||||
$o .= "<h2>" . t("bb2html2bb: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('bb2html2bb: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= visible_lf($bbcode) . EOL. EOL;
|
$o .= visible_lf($bbcode) . EOL. EOL;
|
||||||
|
|
||||||
$diaspora = bb2diaspora($text);
|
$diaspora = bb2diaspora($text);
|
||||||
$o .= "<h2>" . t("bb2md: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('bb2md: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= visible_lf($diaspora) . EOL. EOL;
|
$o .= visible_lf($diaspora) . EOL. EOL;
|
||||||
|
|
||||||
$html = Markdown($diaspora);
|
$html = Markdown($diaspora);
|
||||||
$o .= "<h2>" . t("bb2md2html: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('bb2md2html: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= $html. EOL. EOL;
|
$o .= $html. EOL. EOL;
|
||||||
|
|
||||||
$bbcode = diaspora2bb($diaspora);
|
$bbcode = diaspora2bb($diaspora);
|
||||||
$o .= "<h2>" . t("bb2dia2bb: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('bb2dia2bb: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= visible_lf($bbcode) . EOL. EOL;
|
$o .= visible_lf($bbcode) . EOL. EOL;
|
||||||
|
|
||||||
$bbcode = html2bbcode($html);
|
$bbcode = html2bbcode($html);
|
||||||
$o .= "<h2>" . t("bb2md2html2bb: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('bb2md2html2bb: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= visible_lf($bbcode) . EOL. EOL;
|
$o .= visible_lf($bbcode) . EOL. EOL;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (x($_REQUEST, 'd2bbtext')) {
|
if (x($_REQUEST, 'd2bbtext')) {
|
||||||
|
|
||||||
$d2bbtext = trim($_REQUEST['d2bbtext']);
|
$d2bbtext = trim($_REQUEST['d2bbtext']);
|
||||||
$o .= "<h2>" . t("Source input (Diaspora format): ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('Source input (Diaspora format): ') . '</h2>' . EOL. EOL;
|
||||||
$o .= visible_lf($d2bbtext) . EOL. EOL;
|
$o .= '<pre>' . $d2bbtext . '</pre>' . EOL. EOL;
|
||||||
|
|
||||||
|
|
||||||
$bb = diaspora2bb($d2bbtext);
|
$bb = diaspora2bb($d2bbtext);
|
||||||
$o .= "<h2>" . t("diaspora2bb: ") . "</h2>" . EOL. EOL;
|
$o .= '<h2>' . t('diaspora2bb: ') . '</h2>' . EOL. EOL;
|
||||||
$o .= visible_lf($bb) . EOL. EOL;
|
$o .= '<pre>' . $bb . '</pre>' . EOL. EOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $o;
|
return $o;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use \Friendica\Core\Config;
|
||||||
|
|
||||||
function community_init(App $a) {
|
function community_init(App $a) {
|
||||||
if (! local_user()) {
|
if (! local_user()) {
|
||||||
unset($_SESSION['theme']);
|
unset($_SESSION['theme']);
|
||||||
unset($_SESSION['mobile-theme']);
|
unset($_SESSION['mobile-theme']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,16 +14,12 @@ function community_content(App $a, $update = 0) {
|
||||||
|
|
||||||
$o = '';
|
$o = '';
|
||||||
|
|
||||||
// Currently the community page isn't able to handle update requests
|
if ((Config::get('system','block_public')) && (! local_user()) && (! remote_user())) {
|
||||||
if ($update)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if((get_config('system','block_public')) && (! local_user()) && (! remote_user())) {
|
|
||||||
notice( t('Public access denied.') . EOL);
|
notice( t('Public access denied.') . EOL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(get_config('system','community_page_style') == CP_NO_COMMUNITY_PAGE) {
|
if (Config::get('system','community_page_style') == CP_NO_COMMUNITY_PAGE) {
|
||||||
notice( t('Not available.') . EOL);
|
notice( t('Not available.') . EOL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -38,11 +34,11 @@ function community_content(App $a, $update = 0) {
|
||||||
nav_set_selected('community');
|
nav_set_selected('community');
|
||||||
}
|
}
|
||||||
|
|
||||||
if(x($a->data,'search'))
|
if (x($a->data,'search')) {
|
||||||
$search = notags(trim($a->data['search']));
|
$search = notags(trim($a->data['search']));
|
||||||
else
|
} else {
|
||||||
$search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
|
$search = ((x($_GET,'search')) ? notags(trim(rawurldecode($_GET['search']))) : '');
|
||||||
|
}
|
||||||
|
|
||||||
// Here is the way permissions work in this module...
|
// Here is the way permissions work in this module...
|
||||||
// Only public posts can be shown
|
// Only public posts can be shown
|
||||||
|
@ -55,7 +51,7 @@ function community_content(App $a, $update = 0) {
|
||||||
return $o;
|
return $o;
|
||||||
}
|
}
|
||||||
|
|
||||||
$maxpostperauthor = get_config('system','max_author_posts_community_page');
|
$maxpostperauthor = Config::get('system','max_author_posts_community_page');
|
||||||
|
|
||||||
if ($maxpostperauthor != 0) {
|
if ($maxpostperauthor != 0) {
|
||||||
$count = 1;
|
$count = 1;
|
||||||
|
@ -65,23 +61,24 @@ function community_content(App $a, $update = 0) {
|
||||||
|
|
||||||
do {
|
do {
|
||||||
foreach ($r AS $row=>$item) {
|
foreach ($r AS $row=>$item) {
|
||||||
if ($previousauthor == $item["author-link"])
|
if ($previousauthor == $item["author-link"]) {
|
||||||
++$numposts;
|
++$numposts;
|
||||||
else
|
} else {
|
||||||
$numposts = 0;
|
$numposts = 0;
|
||||||
|
}
|
||||||
$previousauthor = $item["author-link"];
|
$previousauthor = $item["author-link"];
|
||||||
|
|
||||||
if (($numposts < $maxpostperauthor) AND (sizeof($s) < $a->pager['itemspage']))
|
if (($numposts < $maxpostperauthor) AND (sizeof($s) < $a->pager['itemspage'])) {
|
||||||
$s[] = $item;
|
$s[] = $item;
|
||||||
}
|
}
|
||||||
if ((sizeof($s) < $a->pager['itemspage']))
|
}
|
||||||
|
if ((sizeof($s) < $a->pager['itemspage'])) {
|
||||||
$r = community_getitems($a->pager['start'] + ($count * $a->pager['itemspage']), $a->pager['itemspage']);
|
$r = community_getitems($a->pager['start'] + ($count * $a->pager['itemspage']), $a->pager['itemspage']);
|
||||||
|
}
|
||||||
} while ((sizeof($s) < $a->pager['itemspage']) AND (++$count < 50) AND (sizeof($r) > 0));
|
} while ((sizeof($s) < $a->pager['itemspage']) AND (++$count < 50) AND (sizeof($r) > 0));
|
||||||
} else
|
} else {
|
||||||
$s = $r;
|
$s = $r;
|
||||||
|
}
|
||||||
// we behave the same in message lists as the search module
|
// we behave the same in message lists as the search module
|
||||||
|
|
||||||
$o .= conversation($a, $s, 'community', $update);
|
$o .= conversation($a, $s, 'community', $update);
|
||||||
|
@ -92,9 +89,9 @@ function community_content(App $a, $update = 0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function community_getitems($start, $itemspage) {
|
function community_getitems($start, $itemspage) {
|
||||||
if (get_config('system','community_page_style') == CP_GLOBAL_COMMUNITY)
|
if (Config::get('system','community_page_style') == CP_GLOBAL_COMMUNITY) {
|
||||||
return(community_getpublicitems($start, $itemspage));
|
return(community_getpublicitems($start, $itemspage));
|
||||||
|
}
|
||||||
$r = qu("SELECT %s
|
$r = qu("SELECT %s
|
||||||
FROM `thread`
|
FROM `thread`
|
||||||
INNER JOIN `user` ON `user`.`uid` = `thread`.`uid` AND NOT `user`.`hidewall`
|
INNER JOIN `user` ON `user`.`uid` = `thread`.`uid` AND NOT `user`.`hidewall`
|
||||||
|
|
|
@ -185,7 +185,7 @@ function dfrn_confirm_post(App $a, $handsfree = null) {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
$src_aes_key = random_string();
|
$src_aes_key = openssl_random_pseudo_bytes(64);
|
||||||
|
|
||||||
$result = '';
|
$result = '';
|
||||||
openssl_private_encrypt($dfrn_id, $result, $user[0]['prvkey']);
|
openssl_private_encrypt($dfrn_id, $result, $user[0]['prvkey']);
|
||||||
|
|
|
@ -142,8 +142,6 @@ function dfrn_notify_post(App $a) {
|
||||||
|
|
||||||
$rino = get_config('system','rino_encrypt');
|
$rino = get_config('system','rino_encrypt');
|
||||||
$rino = intval($rino);
|
$rino = intval($rino);
|
||||||
// use RINO1 if mcrypt isn't installed and RINO2 was selected
|
|
||||||
if ($rino==2 and !function_exists('mcrypt_create_iv')) $rino=1;
|
|
||||||
|
|
||||||
logger("Local rino version: ". $rino, LOGGER_DEBUG);
|
logger("Local rino version: ". $rino, LOGGER_DEBUG);
|
||||||
|
|
||||||
|
@ -184,7 +182,7 @@ function dfrn_notify_post(App $a) {
|
||||||
case 1:
|
case 1:
|
||||||
// we got a key. old code send only the key, without RINO version.
|
// we got a key. old code send only the key, without RINO version.
|
||||||
// we assume RINO 1 if key and no RINO version
|
// we assume RINO 1 if key and no RINO version
|
||||||
$data = aes_decrypt(hex2bin($data),$final_key);
|
$data = dfrn::aes_decrypt(hex2bin($data),$final_key);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
try {
|
try {
|
||||||
|
@ -315,8 +313,6 @@ function dfrn_notify_content(App $a) {
|
||||||
|
|
||||||
$rino = get_config('system','rino_encrypt');
|
$rino = get_config('system','rino_encrypt');
|
||||||
$rino = intval($rino);
|
$rino = intval($rino);
|
||||||
// use RINO1 if mcrypt isn't installed and RINO2 was selected
|
|
||||||
if ($rino==2 and !function_exists('mcrypt_create_iv')) $rino=1;
|
|
||||||
|
|
||||||
logger("Local rino version: ". $rino, LOGGER_DEBUG);
|
logger("Local rino version: ". $rino, LOGGER_DEBUG);
|
||||||
|
|
||||||
|
|
|
@ -73,9 +73,9 @@ function dirfind_content(App $a, $prefix = "") {
|
||||||
$j->results[] = $objresult;
|
$j->results[] = $objresult;
|
||||||
|
|
||||||
// Add the contact to the global contacts if it isn't already in our system
|
// Add the contact to the global contacts if it isn't already in our system
|
||||||
if (($contact["cid"] == 0) AND ($contact["zid"] == 0) AND ($contact["gid"] == 0))
|
if (($contact["cid"] == 0) AND ($contact["zid"] == 0) AND ($contact["gid"] == 0)) {
|
||||||
poco_check($user_data["url"], $user_data["name"], $user_data["network"], $user_data["photo"],
|
update_gcontact($user_data);
|
||||||
"", "", "", "", "", datetime_convert(), 0);
|
}
|
||||||
} elseif ($local) {
|
} elseif ($local) {
|
||||||
|
|
||||||
if ($community)
|
if ($community)
|
||||||
|
|
|
@ -78,14 +78,7 @@ function install_post(App $a) {
|
||||||
$timezone = notags(trim($_POST['timezone']));
|
$timezone = notags(trim($_POST['timezone']));
|
||||||
$language = notags(trim($_POST['language']));
|
$language = notags(trim($_POST['language']));
|
||||||
$adminmail = notags(trim($_POST['adminmail']));
|
$adminmail = notags(trim($_POST['adminmail']));
|
||||||
// In step 4 of the installer, we passed the check for mcrypt
|
|
||||||
// already, so we can activate RINO, make RINO2 the default
|
|
||||||
// and only fall back if the mcrypt_create_iv function is
|
|
||||||
// not available on the system.
|
|
||||||
$rino = 2;
|
$rino = 2;
|
||||||
if (! function_exists('mcrypt_create_iv')) {
|
|
||||||
$rino = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// connect to db
|
// connect to db
|
||||||
$db = new dba($dbhost, $dbuser, $dbpass, $dbdata, true);
|
$db = new dba($dbhost, $dbuser, $dbpass, $dbdata, true);
|
||||||
|
@ -422,7 +415,6 @@ function check_funcs(&$checks) {
|
||||||
check_add($ck_funcs, t('OpenSSL PHP module'), true, true, "");
|
check_add($ck_funcs, t('OpenSSL PHP module'), true, true, "");
|
||||||
check_add($ck_funcs, t('mysqli PHP module'), true, true, "");
|
check_add($ck_funcs, t('mysqli PHP module'), true, true, "");
|
||||||
check_add($ck_funcs, t('mb_string PHP module'), true, true, "");
|
check_add($ck_funcs, t('mb_string PHP module'), true, true, "");
|
||||||
check_add($ck_funcs, t('mcrypt PHP module'), true, true, "");
|
|
||||||
check_add($ck_funcs, t('XML PHP module'), true, true, "");
|
check_add($ck_funcs, t('XML PHP module'), true, true, "");
|
||||||
check_add($ck_funcs, t('iconv module'), true, true, "");
|
check_add($ck_funcs, t('iconv module'), true, true, "");
|
||||||
|
|
||||||
|
@ -454,10 +446,6 @@ function check_funcs(&$checks) {
|
||||||
$ck_funcs[4]['status']= false;
|
$ck_funcs[4]['status']= false;
|
||||||
$ck_funcs[4]['help']= t('Error: mb_string PHP module required but not installed.');
|
$ck_funcs[4]['help']= t('Error: mb_string PHP module required but not installed.');
|
||||||
}
|
}
|
||||||
if (! function_exists('mcrypt_create_iv')){
|
|
||||||
$ck_funcs[5]['status']= false;
|
|
||||||
$ck_funcs[5]['help']= t('Error: mcrypt PHP module required but not installed.');
|
|
||||||
}
|
|
||||||
if (! function_exists('iconv_strlen')){
|
if (! function_exists('iconv_strlen')){
|
||||||
$ck_funcs[7]['status']= false;
|
$ck_funcs[7]['status']= false;
|
||||||
$ck_funcs[7]['help']= t('Error: iconv PHP module required but not installed.');
|
$ck_funcs[7]['help']= t('Error: iconv PHP module required but not installed.');
|
||||||
|
@ -465,18 +453,6 @@ function check_funcs(&$checks) {
|
||||||
|
|
||||||
$checks = array_merge($checks, $ck_funcs);
|
$checks = array_merge($checks, $ck_funcs);
|
||||||
|
|
||||||
// check for 'mcrypt_create_iv()', needed for RINO2
|
|
||||||
if ($ck_funcs[5]['status']) {
|
|
||||||
if (function_exists('mcrypt_create_iv')) {
|
|
||||||
$__status = true;
|
|
||||||
$__help = t("If you are using php_cli, please make sure that mcrypt module is enabled in its config file");
|
|
||||||
} else {
|
|
||||||
$__status = false;
|
|
||||||
$__help = t('Function mcrypt_create_iv() is not defined. This is needed to enable RINO2 encryption layer.');
|
|
||||||
}
|
|
||||||
check_add($checks, t('mcrypt_create_iv() function'), $__status, false, $__help);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for XML DOM Documents being able to be generated
|
// check for XML DOM Documents being able to be generated
|
||||||
try {
|
try {
|
||||||
$xml = new DOMDocument();
|
$xml = new DOMDocument();
|
||||||
|
|
263
mod/nodeinfo.php
263
mod/nodeinfo.php
|
@ -5,15 +5,13 @@
|
||||||
* Documentation: http://nodeinfo.diaspora.software/schema.html
|
* Documentation: http://nodeinfo.diaspora.software/schema.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
require_once("include/plugin.php");
|
use \Friendica\Core\Config;
|
||||||
|
|
||||||
|
require_once 'include/plugin.php';
|
||||||
|
|
||||||
function nodeinfo_wellknown(App $a) {
|
function nodeinfo_wellknown(App $a) {
|
||||||
if (!get_config("system", "nodeinfo")) {
|
$nodeinfo = array('links' => array(array('rel' => 'http://nodeinfo.diaspora.software/ns/schema/1.0',
|
||||||
http_status_exit(404);
|
'href' => App::get_baseurl().'/nodeinfo/1.0')));
|
||||||
killme();
|
|
||||||
}
|
|
||||||
$nodeinfo = array("links" => array(array("rel" => "http://nodeinfo.diaspora.software/ns/schema/1.0",
|
|
||||||
"href" => App::get_baseurl()."/nodeinfo/1.0")));
|
|
||||||
|
|
||||||
header('Content-type: application/json; charset=utf-8');
|
header('Content-type: application/json; charset=utf-8');
|
||||||
echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||||
|
@ -21,123 +19,126 @@ function nodeinfo_wellknown(App $a) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function nodeinfo_init(App $a) {
|
function nodeinfo_init(App $a) {
|
||||||
if (!get_config("system", "nodeinfo")) {
|
if (!Config::get('system', 'nodeinfo')) {
|
||||||
http_status_exit(404);
|
http_status_exit(404);
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (($a->argc != 2) OR ($a->argv[1] != "1.0")) {
|
if (($a->argc != 2) OR ($a->argv[1] != '1.0')) {
|
||||||
http_status_exit(404);
|
http_status_exit(404);
|
||||||
killme();
|
killme();
|
||||||
}
|
}
|
||||||
|
|
||||||
$smtp = (function_exists("imap_open") AND !get_config("system","imap_disabled") AND !get_config("system","dfrn_only"));
|
$smtp = (function_exists('imap_open') AND !Config::get('system', 'imap_disabled') AND !Config::get('system', 'dfrn_only'));
|
||||||
|
|
||||||
$nodeinfo = array();
|
$nodeinfo = array();
|
||||||
$nodeinfo["version"] = "1.0";
|
$nodeinfo['version'] = '1.0';
|
||||||
$nodeinfo["software"] = array("name" => "friendica", "version" => FRIENDICA_VERSION."-".DB_UPDATE_VERSION);
|
$nodeinfo['software'] = array('name' => 'friendica', 'version' => FRIENDICA_VERSION.'-'.DB_UPDATE_VERSION);
|
||||||
|
|
||||||
$nodeinfo["protocols"] = array();
|
$nodeinfo['protocols'] = array();
|
||||||
$nodeinfo["protocols"]["inbound"] = array();
|
$nodeinfo['protocols']['inbound'] = array();
|
||||||
$nodeinfo["protocols"]["outbound"] = array();
|
$nodeinfo['protocols']['outbound'] = array();
|
||||||
|
|
||||||
if (get_config("system","diaspora_enabled")) {
|
if (Config::get('system', 'diaspora_enabled')) {
|
||||||
$nodeinfo["protocols"]["inbound"][] = "diaspora";
|
$nodeinfo['protocols']['inbound'][] = 'diaspora';
|
||||||
$nodeinfo["protocols"]["outbound"][] = "diaspora";
|
$nodeinfo['protocols']['outbound'][] = 'diaspora';
|
||||||
}
|
}
|
||||||
|
|
||||||
$nodeinfo["protocols"]["inbound"][] = "friendica";
|
$nodeinfo['protocols']['inbound'][] = 'friendica';
|
||||||
$nodeinfo["protocols"]["outbound"][] = "friendica";
|
$nodeinfo['protocols']['outbound'][] = 'friendica';
|
||||||
|
|
||||||
if (!get_config("system","ostatus_disabled")) {
|
if (!Config::get('system', 'ostatus_disabled')) {
|
||||||
$nodeinfo["protocols"]["inbound"][] = "gnusocial";
|
$nodeinfo['protocols']['inbound'][] = 'gnusocial';
|
||||||
$nodeinfo["protocols"]["outbound"][] = "gnusocial";
|
$nodeinfo['protocols']['outbound'][] = 'gnusocial';
|
||||||
}
|
}
|
||||||
|
|
||||||
$nodeinfo["services"] = array();
|
$nodeinfo['services'] = array();
|
||||||
$nodeinfo["services"]["inbound"] = array();
|
$nodeinfo['services']['inbound'] = array();
|
||||||
$nodeinfo["services"]["outbound"] = array();
|
$nodeinfo['services']['outbound'] = array();
|
||||||
|
|
||||||
$nodeinfo["openRegistrations"] = ($a->config['register_policy'] != 0);
|
$nodeinfo['usage'] = array();
|
||||||
|
|
||||||
$nodeinfo["usage"] = array();
|
$nodeinfo['openRegistrations'] = ($a->config['register_policy'] != 0);
|
||||||
$nodeinfo["usage"]["users"] = array("total" => (int)get_config("nodeinfo","total_users"),
|
|
||||||
"activeHalfyear" => (int)get_config("nodeinfo","active_users_halfyear"),
|
|
||||||
"activeMonth" => (int)get_config("nodeinfo","active_users_monthly"));
|
|
||||||
$nodeinfo["usage"]["localPosts"] = (int)get_config("nodeinfo","local_posts");
|
|
||||||
$nodeinfo["usage"]["localComments"] = (int)get_config("nodeinfo","local_comments");
|
|
||||||
|
|
||||||
$nodeinfo["metadata"] = array("nodeName" => $a->config["sitename"]);
|
$nodeinfo['metadata'] = array('nodeName' => $a->config['sitename']);
|
||||||
|
|
||||||
if (plugin_enabled("appnet"))
|
if (Config::get('system', 'nodeinfo')) {
|
||||||
$nodeinfo["services"]["inbound"][] = "appnet";
|
|
||||||
|
|
||||||
if (plugin_enabled("appnet") OR plugin_enabled("buffer"))
|
$nodeinfo['usage']['users'] = array('total' => (int)Config::get('nodeinfo', 'total_users'),
|
||||||
$nodeinfo["services"]["outbound"][] = "appnet";
|
'activeHalfyear' => (int)Config::get('nodeinfo', 'active_users_halfyear'),
|
||||||
|
'activeMonth' => (int)Config::get('nodeinfo', 'active_users_monthly'));
|
||||||
|
$nodeinfo['usage']['localPosts'] = (int)Config::get('nodeinfo', 'local_posts');
|
||||||
|
$nodeinfo['usage']['localComments'] = (int)Config::get('nodeinfo', 'local_comments');
|
||||||
|
|
||||||
if (plugin_enabled("blogger"))
|
if (plugin_enabled('appnet')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "blogger";
|
$nodeinfo['services']['inbound'][] = 'appnet';
|
||||||
|
}
|
||||||
if (plugin_enabled("dwpost"))
|
if (plugin_enabled('appnet') OR plugin_enabled('buffer')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "dreamwidth";
|
$nodeinfo['services']['outbound'][] = 'appnet';
|
||||||
|
}
|
||||||
if (plugin_enabled("fbpost") OR plugin_enabled("buffer"))
|
if (plugin_enabled('blogger')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "facebook";
|
$nodeinfo['services']['outbound'][] = 'blogger';
|
||||||
|
}
|
||||||
if (plugin_enabled("statusnet")) {
|
if (plugin_enabled('dwpost')) {
|
||||||
$nodeinfo["services"]["inbound"][] = "gnusocial";
|
$nodeinfo['services']['outbound'][] = 'dreamwidth';
|
||||||
$nodeinfo["services"]["outbound"][] = "gnusocial";
|
}
|
||||||
|
if (plugin_enabled('fbpost') OR plugin_enabled('buffer')) {
|
||||||
|
$nodeinfo['services']['outbound'][] = 'facebook';
|
||||||
|
}
|
||||||
|
if (plugin_enabled('statusnet')) {
|
||||||
|
$nodeinfo['services']['inbound'][] = 'gnusocial';
|
||||||
|
$nodeinfo['services']['outbound'][] = 'gnusocial';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin_enabled("gpluspost") OR plugin_enabled("buffer"))
|
if (plugin_enabled('gpluspost') OR plugin_enabled('buffer')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "google";
|
$nodeinfo['services']['outbound'][] = 'google';
|
||||||
|
}
|
||||||
if (plugin_enabled("ijpost"))
|
if (plugin_enabled('ijpost')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "insanejournal";
|
$nodeinfo['services']['outbound'][] = 'insanejournal';
|
||||||
|
}
|
||||||
if (plugin_enabled("libertree"))
|
if (plugin_enabled('libertree')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "libertree";
|
$nodeinfo['services']['outbound'][] = 'libertree';
|
||||||
|
}
|
||||||
if (plugin_enabled("buffer"))
|
if (plugin_enabled('buffer')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "linkedin";
|
$nodeinfo['services']['outbound'][] = 'linkedin';
|
||||||
|
}
|
||||||
if (plugin_enabled("ljpost"))
|
if (plugin_enabled('ljpost')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "livejournal";
|
$nodeinfo['services']['outbound'][] = 'livejournal';
|
||||||
|
}
|
||||||
if (plugin_enabled("buffer"))
|
if (plugin_enabled('buffer')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "pinterest";
|
$nodeinfo['services']['outbound'][] = 'pinterest';
|
||||||
|
}
|
||||||
if (plugin_enabled("posterous"))
|
if (plugin_enabled('posterous')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "posterous";
|
$nodeinfo['services']['outbound'][] = 'posterous';
|
||||||
|
}
|
||||||
if (plugin_enabled("pumpio")) {
|
if (plugin_enabled('pumpio')) {
|
||||||
$nodeinfo["services"]["inbound"][] = "pumpio";
|
$nodeinfo['services']['inbound'][] = 'pumpio';
|
||||||
$nodeinfo["services"]["outbound"][] = "pumpio";
|
$nodeinfo['services']['outbound'][] = 'pumpio';
|
||||||
}
|
}
|
||||||
|
|
||||||
// redmatrix
|
if ($smtp) {
|
||||||
|
$nodeinfo['services']['outbound'][] = 'smtp';
|
||||||
|
}
|
||||||
|
if (plugin_enabled('tumblr')) {
|
||||||
|
$nodeinfo['services']['outbound'][] = 'tumblr';
|
||||||
|
}
|
||||||
|
if (plugin_enabled('twitter') OR plugin_enabled('buffer')) {
|
||||||
|
$nodeinfo['services']['outbound'][] = 'twitter';
|
||||||
|
}
|
||||||
|
if (plugin_enabled('wppost')) {
|
||||||
|
$nodeinfo['services']['outbound'][] = 'wordpress';
|
||||||
|
}
|
||||||
|
$nodeinfo['metadata']['protocols'] = $nodeinfo['protocols'];
|
||||||
|
$nodeinfo['metadata']['protocols']['outbound'][] = 'atom1.0';
|
||||||
|
$nodeinfo['metadata']['protocols']['inbound'][] = 'atom1.0';
|
||||||
|
$nodeinfo['metadata']['protocols']['inbound'][] = 'rss2.0';
|
||||||
|
|
||||||
if ($smtp)
|
$nodeinfo['metadata']['services'] = $nodeinfo['services'];
|
||||||
$nodeinfo["services"]["outbound"][] = "smtp";
|
|
||||||
|
|
||||||
if (plugin_enabled("tumblr"))
|
if (plugin_enabled('twitter')) {
|
||||||
$nodeinfo["services"]["outbound"][] = "tumblr";
|
$nodeinfo['metadata']['services']['inbound'][] = 'twitter';
|
||||||
|
}
|
||||||
if (plugin_enabled("twitter") OR plugin_enabled("buffer"))
|
}
|
||||||
$nodeinfo["services"]["outbound"][] = "twitter";
|
|
||||||
|
|
||||||
if (plugin_enabled("wppost"))
|
|
||||||
$nodeinfo["services"]["outbound"][] = "wordpress";
|
|
||||||
|
|
||||||
$nodeinfo["metadata"]["protocols"] = $nodeinfo["protocols"];
|
|
||||||
$nodeinfo["metadata"]["protocols"]["outbound"][] = "atom1.0";
|
|
||||||
$nodeinfo["metadata"]["protocols"]["inbound"][] = "atom1.0";
|
|
||||||
$nodeinfo["metadata"]["protocols"]["inbound"][] = "rss2.0";
|
|
||||||
|
|
||||||
$nodeinfo["metadata"]["services"] = $nodeinfo["services"];
|
|
||||||
|
|
||||||
if (plugin_enabled("twitter"))
|
|
||||||
$nodeinfo["metadata"]["services"]["inbound"][] = "twitter";
|
|
||||||
|
|
||||||
header('Content-type: application/json; charset=utf-8');
|
header('Content-type: application/json; charset=utf-8');
|
||||||
echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
echo json_encode($nodeinfo, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES);
|
||||||
|
@ -150,40 +151,40 @@ function nodeinfo_cron() {
|
||||||
|
|
||||||
$a = get_app();
|
$a = get_app();
|
||||||
|
|
||||||
// If the plugin "statistics_json" is enabled then disable it and actrivate nodeinfo.
|
// If the plugin 'statistics_json' is enabled then disable it and actrivate nodeinfo.
|
||||||
if (plugin_enabled("statistics_json")) {
|
if (plugin_enabled('statistics_json')) {
|
||||||
set_config("system", "nodeinfo", true);
|
Config::set('system', 'nodeinfo', true);
|
||||||
|
|
||||||
$plugin = "statistics_json";
|
$plugin = 'statistics_json';
|
||||||
$plugins = get_config("system","addon");
|
$plugins = Config::get('system', 'addon');
|
||||||
$plugins_arr = array();
|
$plugins_arr = array();
|
||||||
|
|
||||||
if ($plugins) {
|
if ($plugins) {
|
||||||
$plugins_arr = explode(",",str_replace(" ", "",$plugins));
|
$plugins_arr = explode(',',str_replace(' ', '',$plugins));
|
||||||
|
|
||||||
$idx = array_search($plugin, $plugins_arr);
|
$idx = array_search($plugin, $plugins_arr);
|
||||||
if ($idx !== false) {
|
if ($idx !== false) {
|
||||||
unset($plugins_arr[$idx]);
|
unset($plugins_arr[$idx]);
|
||||||
uninstall_plugin($plugin);
|
uninstall_plugin($plugin);
|
||||||
set_config("system","addon", implode(", ",$plugins_arr));
|
Config::set('system', 'addon', implode(', ',$plugins_arr));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!get_config("system", "nodeinfo"))
|
if (!Config::get('system', 'nodeinfo')) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
$last = get_config('nodeinfo','last_calucation');
|
$last = Config::get('nodeinfo', 'last_calucation');
|
||||||
|
|
||||||
if ($last) {
|
if ($last) {
|
||||||
// Calculate every 24 hours
|
// Calculate every 24 hours
|
||||||
$next = $last + (24 * 60 * 60);
|
$next = $last + (24 * 60 * 60);
|
||||||
if ($next > time()) {
|
if ($next > time()) {
|
||||||
logger("calculation intervall not reached");
|
logger('calculation intervall not reached');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger("cron_start");
|
logger('cron_start');
|
||||||
|
|
||||||
$users = qu("SELECT `user`.`uid`, `user`.`login_date`, `contact`.`last-item`
|
$users = qu("SELECT `user`.`uid`, `user`.`login_date`, `contact`.`last-item`
|
||||||
FROM `user`
|
FROM `user`
|
||||||
|
@ -202,31 +203,31 @@ function nodeinfo_cron() {
|
||||||
|
|
||||||
foreach ($users AS $user) {
|
foreach ($users AS $user) {
|
||||||
if ((strtotime($user['login_date']) > $halfyear) OR
|
if ((strtotime($user['login_date']) > $halfyear) OR
|
||||||
(strtotime($user['last-item']) > $halfyear))
|
(strtotime($user['last-item']) > $halfyear)) {
|
||||||
++$active_users_halfyear;
|
++$active_users_halfyear;
|
||||||
|
|
||||||
if ((strtotime($user['login_date']) > $month) OR
|
|
||||||
(strtotime($user['last-item']) > $month))
|
|
||||||
++$active_users_monthly;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
set_config('nodeinfo','total_users', $total_users);
|
if ((strtotime($user['login_date']) > $month) OR
|
||||||
logger("total_users: ".$total_users, LOGGER_DEBUG);
|
(strtotime($user['last-item']) > $month)) {
|
||||||
|
++$active_users_monthly;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Config::set('nodeinfo', 'total_users', $total_users);
|
||||||
|
logger('total_users: '.$total_users, LOGGER_DEBUG);
|
||||||
|
|
||||||
set_config('nodeinfo','active_users_halfyear', $active_users_halfyear);
|
Config::set('nodeinfo', 'active_users_halfyear', $active_users_halfyear);
|
||||||
set_config('nodeinfo','active_users_monthly', $active_users_monthly);
|
Config::set('nodeinfo', 'active_users_monthly', $active_users_monthly);
|
||||||
}
|
}
|
||||||
|
|
||||||
$posts = qu("SELECT COUNT(*) AS local_posts FROM `thread` WHERE `thread`.`wall` AND `thread`.`uid` != 0");
|
$posts = qu("SELECT COUNT(*) AS local_posts FROM `thread` WHERE `thread`.`wall` AND `thread`.`uid` != 0");
|
||||||
|
|
||||||
if (!is_array($posts))
|
if (!is_array($posts)) {
|
||||||
$local_posts = -1;
|
$local_posts = -1;
|
||||||
else
|
} else {
|
||||||
$local_posts = $posts[0]["local_posts"];
|
$local_posts = $posts[0]['local_posts'];
|
||||||
|
}
|
||||||
|
Config::set('nodeinfo', 'local_posts', $local_posts);
|
||||||
|
|
||||||
set_config('nodeinfo','local_posts', $local_posts);
|
logger('local_posts: '.$local_posts, LOGGER_DEBUG);
|
||||||
|
|
||||||
logger("local_posts: ".$local_posts, LOGGER_DEBUG);
|
|
||||||
|
|
||||||
$posts = qu("SELECT COUNT(*) FROM `contact`
|
$posts = qu("SELECT COUNT(*) FROM `contact`
|
||||||
INNER JOIN `item` ON `item`.`contact-id` = `contact`.`id` AND `item`.`uid` = `contact`.`uid` AND
|
INNER JOIN `item` ON `item`.`contact-id` = `contact`.`id` AND `item`.`uid` = `contact`.`uid` AND
|
||||||
|
@ -234,21 +235,21 @@ function nodeinfo_cron() {
|
||||||
WHERE `contact`.`self`",
|
WHERE `contact`.`self`",
|
||||||
dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_DFRN));
|
dbesc(NETWORK_OSTATUS), dbesc(NETWORK_DIASPORA), dbesc(NETWORK_DFRN));
|
||||||
|
|
||||||
if (!is_array($posts))
|
if (!is_array($posts)) {
|
||||||
$local_comments = -1;
|
$local_comments = -1;
|
||||||
else
|
} else {
|
||||||
$local_comments = $posts[0]["local_comments"];
|
$local_comments = $posts[0]['local_comments'];
|
||||||
|
}
|
||||||
set_config('nodeinfo','local_comments', $local_comments);
|
Config::set('nodeinfo', 'local_comments', $local_comments);
|
||||||
|
|
||||||
// Now trying to register
|
// Now trying to register
|
||||||
$url = "http://the-federation.info/register/".$a->get_hostname();
|
$url = 'http://the-federation.info/register/'.$a->get_hostname();
|
||||||
logger('registering url: '.$url, LOGGER_DEBUG);
|
logger('registering url: '.$url, LOGGER_DEBUG);
|
||||||
$ret = fetch_url($url);
|
$ret = fetch_url($url);
|
||||||
logger('registering answer: '.$ret, LOGGER_DEBUG);
|
logger('registering answer: '.$ret, LOGGER_DEBUG);
|
||||||
|
|
||||||
logger("cron_end");
|
logger('cron_end');
|
||||||
set_config('nodeinfo','last_calucation', time());
|
Config::set('nodeinfo', 'last_calucation', time());
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue