Merge remote-tracking branch 'upstream/nightly' into nightly
This commit is contained in:
commit
250221ff7f
16
.gitignore
vendored
16
.gitignore
vendored
@ -1,11 +1,11 @@
|
|||||||
avatar/*
|
avatar/
|
||||||
files/*
|
files/
|
||||||
file/*
|
file/
|
||||||
local/*
|
local/
|
||||||
_darcs/*
|
_darcs/
|
||||||
logs/*
|
logs/
|
||||||
log/*
|
log/
|
||||||
run/*
|
run/
|
||||||
config.php
|
config.php
|
||||||
.htaccess
|
.htaccess
|
||||||
httpd.conf
|
httpd.conf
|
||||||
|
@ -497,9 +497,9 @@ Profile management.
|
|||||||
|
|
||||||
biolimit: max character length of bio; 0 means no limit; null means to use
|
biolimit: max character length of bio; 0 means no limit; null means to use
|
||||||
the site text limit default.
|
the site text limit default.
|
||||||
backup: whether users can backup their own profiles. Defaults to true.
|
backup: whether users can backup their own profiles. Defaults to false.
|
||||||
restore: whether users can restore their profiles from backup files. Defaults
|
restore: whether users can restore their profiles from backup files. Defaults
|
||||||
to true.
|
to false.
|
||||||
delete: whether users can delete their own accounts. Defaults to false.
|
delete: whether users can delete their own accounts. Defaults to false.
|
||||||
move: whether users can move their accounts to another server. Defaults
|
move: whether users can move their accounts to another server. Defaults
|
||||||
to true.
|
to true.
|
||||||
|
37
INSTALL
37
INSTALL
@ -26,16 +26,12 @@ PHP modules
|
|||||||
The following software packages are *required* for this software to
|
The following software packages are *required* for this software to
|
||||||
run correctly.
|
run correctly.
|
||||||
|
|
||||||
- PHP 5.5+ For newer versions, some functions that are used may be
|
- PHP 5.6+ PHP7.x is also supported.
|
||||||
disabled by default, such as the pcntl_* family. See the
|
- MariaDB 5+ MariaDB 10.x is also supported.
|
||||||
section on 'Queues and daemons' for more information.
|
- Web server Apache, lighttpd and nginx will all work, see sample
|
||||||
- MariaDB 5+ GNU Social uses, by default, a MariaDB server for data
|
configuration files in the web root. Please use PHP-FPM
|
||||||
storage. Versions 5.x and 10.x have both reportedly
|
and configure mod_rewrite (or equivalent) for an optimal
|
||||||
worked well. It is also possible to run MySQL 5.5+.
|
experience.
|
||||||
- Web server Apache, lighttpd and nginx will all work. CGI mode is
|
|
||||||
recommended and also some variant of 'suexec' (or a
|
|
||||||
proper setup php-fpm pool)
|
|
||||||
NOTE: mod_rewrite or its equivalent is extremely useful.
|
|
||||||
|
|
||||||
Your PHP installation must include the following PHP extensions for a
|
Your PHP installation must include the following PHP extensions for a
|
||||||
functional setup of GNU Social:
|
functional setup of GNU Social:
|
||||||
@ -49,22 +45,22 @@ functional setup of GNU Social:
|
|||||||
- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
|
- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
|
||||||
use MySQL, 'php5-mysql' or 'php5-mysqli' may be enough.
|
use MySQL, 'php5-mysql' or 'php5-mysqli' may be enough.
|
||||||
|
|
||||||
Or, for PHP7, some or all of these will be necessary. PHP7 support is still
|
Or, for PHP7, some or all of these will be necessary. PHP7 works and on
|
||||||
experimental and not necessarily working:
|
the development servers we are successful running PHP7.2. This is a good
|
||||||
|
list of PHP modules you will want installed with PHP7:
|
||||||
php7.0-bcmath
|
php7.0-bcmath
|
||||||
php7.0-curl
|
php7.0-curl
|
||||||
php7.0-exif
|
php7.0-exif
|
||||||
php7.0-gd
|
php7.0-gd
|
||||||
php7.0-intl
|
php7.0-intl
|
||||||
php7.0-mbstring
|
php7.0-mbstring
|
||||||
php7.0-mysqlnd
|
php7.0-mysql
|
||||||
php7.0-opcache
|
php7.0-opcache
|
||||||
php7.0-readline
|
php7.0-readline
|
||||||
php7.0-xmlwriter
|
php7.0-xmlwriter
|
||||||
|
|
||||||
The above package names are for Debian based systems. In the case of
|
NOTE: In Arch Linux, at least PHP5 requires manual enabling in the
|
||||||
Arch Linux, PHP is compiled with support for most extensions but they
|
relevant php.ini for some modules, most notably 'gmp'.
|
||||||
require manual enabling in the relevant php.ini file (mostly php5-gmp).
|
|
||||||
|
|
||||||
Better performance
|
Better performance
|
||||||
------------------
|
------------------
|
||||||
@ -74,19 +70,10 @@ For some functionality, you will also need the following extensions:
|
|||||||
- opcache Improves performance a _lot_. Included in PHP, must be
|
- opcache Improves performance a _lot_. Included in PHP, must be
|
||||||
enabled manually in php.ini for most distributions. Find
|
enabled manually in php.ini for most distributions. Find
|
||||||
and set at least: opcache.enable=1
|
and set at least: opcache.enable=1
|
||||||
- mailparse Efficient parsing of email requires this extension.
|
|
||||||
Submission by email or SMS-over-email uses this.
|
|
||||||
- sphinx A client for the sphinx server, an alternative to MySQL
|
|
||||||
or Postgresql fulltext search. You will also need a
|
|
||||||
Sphinx server to serve the search queries.
|
|
||||||
- gettext For multiple languages. Default on many PHP installs;
|
- gettext For multiple languages. Default on many PHP installs;
|
||||||
will be emulated if not present.
|
will be emulated if not present.
|
||||||
- exif For thumbnails to be properly oriented.
|
- exif For thumbnails to be properly oriented.
|
||||||
|
|
||||||
You may also experience better performance from your site if you configure
|
|
||||||
a PHP cache/accelerator. Most distributions come with "opcache" support.
|
|
||||||
Enable it in your php.ini where it is documented together with its settings.
|
|
||||||
|
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
|
|
||||||
|
@ -107,6 +107,7 @@ So far it includes the following changes:
|
|||||||
|
|
||||||
- Backing up a user's account is more and more complete.
|
- Backing up a user's account is more and more complete.
|
||||||
- Emojis 😸 (utf8mb4 support)
|
- Emojis 😸 (utf8mb4 support)
|
||||||
|
- Fully qualified group mentions (!group@example.com)
|
||||||
|
|
||||||
The last release, 1.1.3, gave us these improvements:
|
The last release, 1.1.3, gave us these improvements:
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
/api/statuses/update.:format
|
/api/statuses/update.:format
|
||||||
|
|
||||||
@par Formats (:format)
|
@par Formats (:format)
|
||||||
xml, json
|
xml, json, atom
|
||||||
|
|
||||||
@par HTTP Method(s)
|
@par HTTP Method(s)
|
||||||
POST
|
POST
|
||||||
@ -339,6 +339,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction
|
|||||||
$this->showSingleXmlStatus($this->notice);
|
$this->showSingleXmlStatus($this->notice);
|
||||||
} elseif ($this->format == 'json') {
|
} elseif ($this->format == 'json') {
|
||||||
$this->show_single_json_status($this->notice);
|
$this->show_single_json_status($this->notice);
|
||||||
|
} elseif ($this->format == 'atom') {
|
||||||
|
$this->showSingleAtomStatus($this->notice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ class GroupBlockList extends ProfileList
|
|||||||
$this->group = $group;
|
$this->group = $group;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newListItem($profile)
|
function newListItem(Profile $profile)
|
||||||
{
|
{
|
||||||
return new GroupBlockListItem($profile, $this->group, $this->action);
|
return new GroupBlockListItem($profile, $this->group, $this->action);
|
||||||
}
|
}
|
||||||
|
@ -153,7 +153,7 @@ class GroupqueueAction extends GroupAction
|
|||||||
// @todo FIXME: documentation missing.
|
// @todo FIXME: documentation missing.
|
||||||
class GroupQueueList extends GroupMemberList
|
class GroupQueueList extends GroupMemberList
|
||||||
{
|
{
|
||||||
function newListItem($profile)
|
function newListItem(Profile $profile)
|
||||||
{
|
{
|
||||||
return new GroupQueueListItem($profile, $this->group, $this->action);
|
return new GroupQueueListItem($profile, $this->group, $this->action);
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,8 @@ class NewnoticeAction extends FormAction
|
|||||||
{
|
{
|
||||||
protected $form = 'Notice';
|
protected $form = 'Notice';
|
||||||
|
|
||||||
|
protected $inreplyto = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Title of the page
|
* Title of the page
|
||||||
*
|
*
|
||||||
@ -75,6 +77,11 @@ class NewnoticeAction extends FormAction
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->int('inreplyto')) {
|
||||||
|
// Throws exception if the inreplyto Notice is given but not found.
|
||||||
|
$this->inreplyto = Notice::getByID($this->int('inreplyto'));
|
||||||
|
}
|
||||||
|
|
||||||
// Backwards compatibility for "share this" widget things.
|
// Backwards compatibility for "share this" widget things.
|
||||||
// If no 'content', use 'status_textarea'
|
// If no 'content', use 'status_textarea'
|
||||||
$this->formOpts['content'] = $this->trimmed('content') ?: $this->trimmed('status_textarea');
|
$this->formOpts['content'] = $this->trimmed('content') ?: $this->trimmed('status_textarea');
|
||||||
@ -132,13 +139,6 @@ class NewnoticeAction extends FormAction
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->int('inreplyto')) {
|
|
||||||
// Throws exception if the inreplyto Notice is given but not found.
|
|
||||||
$parent = Notice::getByID($this->int('inreplyto'));
|
|
||||||
} else {
|
|
||||||
$parent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$act = new Activity();
|
$act = new Activity();
|
||||||
$act->verb = ActivityVerb::POST;
|
$act->verb = ActivityVerb::POST;
|
||||||
$act->time = time();
|
$act->time = time();
|
||||||
@ -157,9 +157,9 @@ class NewnoticeAction extends FormAction
|
|||||||
|
|
||||||
$act->context = new ActivityContext();
|
$act->context = new ActivityContext();
|
||||||
|
|
||||||
if ($parent instanceof Notice) {
|
if ($this->inreplyto instanceof Notice) {
|
||||||
$act->context->replyToID = $parent->getUri();
|
$act->context->replyToID = $this->inreplyto->getUri();
|
||||||
$act->context->replyToUrl = $parent->getUrl(true); // maybe we don't have to send true here to force a URL?
|
$act->context->replyToUrl = $this->inreplyto->getUrl(true); // maybe we don't have to send true here to force a URL?
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->scoped->shareLocation()) {
|
if ($this->scoped->shareLocation()) {
|
||||||
@ -188,14 +188,14 @@ class NewnoticeAction extends FormAction
|
|||||||
|
|
||||||
// FIXME: We should be able to get the attentions from common_render_content!
|
// FIXME: We should be able to get the attentions from common_render_content!
|
||||||
// and maybe even directly save whether they're local or not!
|
// and maybe even directly save whether they're local or not!
|
||||||
$act->context->attention = common_get_attentions($content, $this->scoped, $parent);
|
$act->context->attention = common_get_attentions($content, $this->scoped, $this->inreplyto);
|
||||||
|
|
||||||
// $options gets filled with possible scoping settings
|
// $options gets filled with possible scoping settings
|
||||||
ToSelector::fillActivity($this, $act, $options);
|
ToSelector::fillActivity($this, $act, $options);
|
||||||
|
|
||||||
$actobj = new ActivityObject();
|
$actobj = new ActivityObject();
|
||||||
$actobj->type = ActivityObject::NOTE;
|
$actobj->type = ActivityObject::NOTE;
|
||||||
$actobj->content = common_render_content($content, $this->scoped, $parent);
|
$actobj->content = common_render_content($content, $this->scoped, $this->inreplyto);
|
||||||
|
|
||||||
// Finally add the activity object to our activity
|
// Finally add the activity object to our activity
|
||||||
$act->objects[] = $actobj;
|
$act->objects[] = $actobj;
|
||||||
@ -224,6 +224,9 @@ class NewnoticeAction extends FormAction
|
|||||||
if ($this->getInfo() && $this->stored instanceof Notice) {
|
if ($this->getInfo() && $this->stored instanceof Notice) {
|
||||||
$this->showNotice($this->stored);
|
$this->showNotice($this->stored);
|
||||||
} elseif (!$this->getError()) {
|
} elseif (!$this->getError()) {
|
||||||
|
if (!GNUsocial::isAjax() && $this->inreplyto instanceof Notice) {
|
||||||
|
$this->showNotice($this->inreplyto);
|
||||||
|
}
|
||||||
parent::showContent();
|
parent::showContent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ class SearchNoticeList extends NoticeList {
|
|||||||
$this->terms = $terms;
|
$this->terms = $terms;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newListItem($notice)
|
function newListItem(Notice $notice)
|
||||||
{
|
{
|
||||||
return new SearchNoticeListItem($notice, $this->out, $this->terms);
|
return new SearchNoticeListItem($notice, $this->out, $this->terms);
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ class PeopletagMemberList extends ProfileList
|
|||||||
$this->peopletag = $peopletag;
|
$this->peopletag = $peopletag;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newListItem($profile)
|
function newListItem(Profile $profile)
|
||||||
{
|
{
|
||||||
return new PeopletagMemberListItem($profile, $this->peopletag, $this->action);
|
return new PeopletagMemberListItem($profile, $this->peopletag, $this->action);
|
||||||
}
|
}
|
||||||
|
@ -167,7 +167,7 @@ class PeopletagSubscriberList extends ProfileList
|
|||||||
$this->peopletag = $peopletag;
|
$this->peopletag = $peopletag;
|
||||||
}
|
}
|
||||||
|
|
||||||
function newListItem($profile)
|
function newListItem(Profile $profile)
|
||||||
{
|
{
|
||||||
return new PeopletagSubscriberListItem($profile, $this->peopletag, $this->action);
|
return new PeopletagSubscriberListItem($profile, $this->peopletag, $this->action);
|
||||||
}
|
}
|
||||||
|
@ -113,6 +113,18 @@ class ShowstreamAction extends NoticestreamAction
|
|||||||
$this->target->getNickname(), $this->tag)));
|
$this->target->getNickname(), $this->tag)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$this->target->isLocal()) {
|
||||||
|
// remote profiles at least have Atom, but we can't guarantee anything else
|
||||||
|
return array(
|
||||||
|
new Feed(Feed::ATOM,
|
||||||
|
$this->target->getAtomFeed(),
|
||||||
|
// TRANS: Title for link to notice feed.
|
||||||
|
// TRANS: %s is a user nickname.
|
||||||
|
sprintf(_('Notice feed for %s (Atom)'),
|
||||||
|
$this->target->getNickname()))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return array(new Feed(Feed::JSON,
|
return array(new Feed(Feed::JSON,
|
||||||
common_local_url('ApiTimelineUser',
|
common_local_url('ApiTimelineUser',
|
||||||
array(
|
array(
|
||||||
@ -139,10 +151,7 @@ class ShowstreamAction extends NoticestreamAction
|
|||||||
sprintf(_('Notice feed for %s (RSS 2.0)'),
|
sprintf(_('Notice feed for %s (RSS 2.0)'),
|
||||||
$this->target->getNickname())),
|
$this->target->getNickname())),
|
||||||
new Feed(Feed::ATOM,
|
new Feed(Feed::ATOM,
|
||||||
common_local_url('ApiTimelineUser',
|
$this->target->getAtomFeed(),
|
||||||
array(
|
|
||||||
'id' => $this->target->getID(),
|
|
||||||
'format' => 'atom')),
|
|
||||||
// TRANS: Title for link to notice feed.
|
// TRANS: Title for link to notice feed.
|
||||||
// TRANS: %s is a user nickname.
|
// TRANS: %s is a user nickname.
|
||||||
sprintf(_('Notice feed for %s (Atom)'),
|
sprintf(_('Notice feed for %s (Atom)'),
|
||||||
@ -221,13 +230,14 @@ class ShowstreamAction extends NoticestreamAction
|
|||||||
$this->showEmptyListMessage();
|
$this->showEmptyListMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
$args = array('nickname' => $this->target->getNickname());
|
// either nickname or id will be used, depending on which action (showstream, userbyid...)
|
||||||
|
$args = array('nickname' => $this->target->getNickname(), 'id' => $this->target->getID());
|
||||||
if (!empty($this->tag))
|
if (!empty($this->tag))
|
||||||
{
|
{
|
||||||
$args['tag'] = $this->tag;
|
$args['tag'] = $this->tag;
|
||||||
}
|
}
|
||||||
$this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
|
$this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
|
||||||
'showstream', $args);
|
$this->getActionName(), $args);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showAnonymousMessage()
|
function showAnonymousMessage()
|
||||||
|
@ -36,6 +36,7 @@ class Conversation extends Managed_DataObject
|
|||||||
public $__table = 'conversation'; // table name
|
public $__table = 'conversation'; // table name
|
||||||
public $id; // int(4) primary_key not_null auto_increment
|
public $id; // int(4) primary_key not_null auto_increment
|
||||||
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||||
|
public $url; // varchar(191) unique_key not 255 because utf8mb4 takes more space
|
||||||
public $created; // datetime not_null
|
public $created; // datetime not_null
|
||||||
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||||
|
|
||||||
@ -45,6 +46,7 @@ class Conversation extends Managed_DataObject
|
|||||||
'fields' => array(
|
'fields' => array(
|
||||||
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique identifier, (again) unrelated to notice id since 2016-01-06'),
|
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique identifier, (again) unrelated to notice id since 2016-01-06'),
|
||||||
'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 191, 'description' => 'URI of the conversation'),
|
'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 191, 'description' => 'URI of the conversation'),
|
||||||
|
'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'Resolvable URL, preferrably remote (local can be generated on the fly)'),
|
||||||
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||||
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||||
),
|
),
|
||||||
@ -89,15 +91,21 @@ class Conversation extends Managed_DataObject
|
|||||||
*
|
*
|
||||||
* @return Conversation the new conversation DO
|
* @return Conversation the new conversation DO
|
||||||
*/
|
*/
|
||||||
static function create($uri=null, $created=null)
|
static function create(ActivityContext $ctx=null, $created=null)
|
||||||
{
|
{
|
||||||
// Be aware that the Notice does not have an id yet since it's not inserted!
|
// Be aware that the Notice does not have an id yet since it's not inserted!
|
||||||
$conv = new Conversation();
|
$conv = new Conversation();
|
||||||
$conv->created = $created ?: common_sql_now();
|
$conv->created = $created ?: common_sql_now();
|
||||||
$conv->uri = $uri ?: sprintf('%s%s=%s:%s=%s',
|
if ($ctx instanceof ActivityContext) {
|
||||||
|
$conv->uri = $ctx->conversation;
|
||||||
|
$conv->url = $ctx->conversation_url;
|
||||||
|
} else {
|
||||||
|
$conv->uri = sprintf('%s%s=%s:%s=%s',
|
||||||
TagURI::mint(),
|
TagURI::mint(),
|
||||||
'objectType', 'thread',
|
'objectType', 'thread',
|
||||||
'nonce', common_random_hexstr(8));
|
'nonce', common_random_hexstr(8));
|
||||||
|
$conv->url = null; // locally generated Conversation objects don't get static URLs stored
|
||||||
|
}
|
||||||
// This insert throws exceptions on failure
|
// This insert throws exceptions on failure
|
||||||
$conv->insert();
|
$conv->insert();
|
||||||
|
|
||||||
|
@ -194,10 +194,14 @@ class File extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
$redir = File_redirection::where($given_url);
|
$redir = File_redirection::where($given_url);
|
||||||
$file = $redir->getFile();
|
try {
|
||||||
|
$file = $redir->getFile();
|
||||||
if (!$file instanceof File || empty($file->id)) {
|
} catch (EmptyPkeyValueException $e) {
|
||||||
|
common_log(LOG_ERR, 'File_redirection::where gave object with empty file_id for given_url '._ve($given_url));
|
||||||
|
throw new ServerException('URL processing failed without new File object');
|
||||||
|
} catch (NoResultException $e) {
|
||||||
// This should not happen
|
// This should not happen
|
||||||
|
common_log(LOG_ERR, 'File_redirection after discovery could still not return a File object.');
|
||||||
throw new ServerException('URL processing failed without new File object');
|
throw new ServerException('URL processing failed without new File object');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -731,16 +735,18 @@ class File extends Managed_DataObject
|
|||||||
$dupfile = new File();
|
$dupfile = new File();
|
||||||
// First we find file entries that would be duplicates of this when shortened
|
// First we find file entries that would be duplicates of this when shortened
|
||||||
// ... and we'll just throw the dupes out the window for now! It's already so borken.
|
// ... and we'll just throw the dupes out the window for now! It's already so borken.
|
||||||
$dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = "%1$s"', $file->shortenedurl));
|
$dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = %1$s', $dupfile->_quote($file->shortenedurl)));
|
||||||
// Leave one of the URLs in the database by using ->find(true) (fetches first entry)
|
// Leave one of the URLs in the database by using ->find(true) (fetches first entry)
|
||||||
if ($dupfile->find(true)) {
|
if ($dupfile->find(true)) {
|
||||||
print "\nShortening url entry for $table id: {$file->id} [";
|
print "\nShortening url entry for $table id: {$file->id} [";
|
||||||
$orig = clone($dupfile);
|
$orig = clone($dupfile);
|
||||||
|
$origurl = $dupfile->url; // save for logging purposes
|
||||||
$dupfile->url = $file->shortenedurl; // make sure it's only 191 chars from now on
|
$dupfile->url = $file->shortenedurl; // make sure it's only 191 chars from now on
|
||||||
$dupfile->update($orig);
|
$dupfile->update($orig);
|
||||||
print "\nDeleting duplicate entries of too long URL on $table id: {$file->id} [";
|
print "\nDeleting duplicate entries of too long URL on $table id: {$file->id} [";
|
||||||
// only start deleting with this fetch.
|
// only start deleting with this fetch.
|
||||||
while($dupfile->fetch()) {
|
while($dupfile->fetch()) {
|
||||||
|
common_log(LOG_INFO, sprintf('Deleting duplicate File entry of %1$d: %2$d (original URL: %3$s collides with these first 191 characters: %4$s', $dupfile->id, $file->id, $origurl, $file->shortenedurl));
|
||||||
print ".";
|
print ".";
|
||||||
$dupfile->delete();
|
$dupfile->delete();
|
||||||
}
|
}
|
||||||
|
@ -445,8 +445,8 @@ class File_redirection extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function getFile() {
|
public function getFile() {
|
||||||
if(empty($this->file) && $this->file_id) {
|
if (!$this->file instanceof File) {
|
||||||
$this->file = File::getKV('id', $this->file_id);
|
$this->file = File::getByID($this->file_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->file;
|
return $this->file;
|
||||||
|
@ -89,7 +89,7 @@ class Foreign_link extends Managed_DataObject
|
|||||||
return $flink;
|
return $flink;
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_flags($noticesend, $noticerecv, $replysync, $friendsync)
|
function set_flags($noticesend, $noticerecv, $replysync, $repeatsync, $friendsync)
|
||||||
{
|
{
|
||||||
if ($noticesend) {
|
if ($noticesend) {
|
||||||
$this->noticesync |= FOREIGN_NOTICE_SEND;
|
$this->noticesync |= FOREIGN_NOTICE_SEND;
|
||||||
@ -109,6 +109,12 @@ class Foreign_link extends Managed_DataObject
|
|||||||
$this->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY;
|
$this->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($repeatsync) {
|
||||||
|
$this->noticesync |= FOREIGN_NOTICE_SEND_REPEAT;
|
||||||
|
} else {
|
||||||
|
$this->noticesync &= ~FOREIGN_NOTICE_SEND_REPEAT;
|
||||||
|
}
|
||||||
|
|
||||||
if ($friendsync) {
|
if ($friendsync) {
|
||||||
$this->friendsync |= FOREIGN_FRIEND_RECV;
|
$this->friendsync |= FOREIGN_FRIEND_RECV;
|
||||||
} else {
|
} else {
|
||||||
|
@ -320,6 +320,21 @@ class Notice extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getSelfLink()
|
||||||
|
{
|
||||||
|
if ($this->isLocal()) {
|
||||||
|
return common_local_url('ApiStatusesShow', array('id' => $this->getID(), 'format' => 'atom'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$selfLink = $this->getPref('ostatus', 'self');
|
||||||
|
|
||||||
|
if (!common_valid_http_url($selfLink)) {
|
||||||
|
throw new InvalidUrlException($selfLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $selfLink;
|
||||||
|
}
|
||||||
|
|
||||||
public function getObjectType($canonical=false) {
|
public function getObjectType($canonical=false) {
|
||||||
if (is_null($this->object_type) || $this->object_type==='') {
|
if (is_null($this->object_type) || $this->object_type==='') {
|
||||||
throw new NoObjectTypeException($this);
|
throw new NoObjectTypeException($this);
|
||||||
@ -442,6 +457,7 @@ class Notice extends Managed_DataObject
|
|||||||
static function saveNew($profile_id, $content, $source, array $options=null) {
|
static function saveNew($profile_id, $content, $source, array $options=null) {
|
||||||
$defaults = array('uri' => null,
|
$defaults = array('uri' => null,
|
||||||
'url' => null,
|
'url' => null,
|
||||||
|
'self' => null,
|
||||||
'conversation' => null, // URI of conversation
|
'conversation' => null, // URI of conversation
|
||||||
'reply_to' => null, // This will override convo URI if the parent is known
|
'reply_to' => null, // This will override convo URI if the parent is known
|
||||||
'repeat_of' => null, // This will override convo URI if the repeated notice is known
|
'repeat_of' => null, // This will override convo URI if the repeated notice is known
|
||||||
@ -624,8 +640,13 @@ class Notice extends Managed_DataObject
|
|||||||
} else {
|
} else {
|
||||||
// Conversation entry with specified URI was not found, so we must create it.
|
// Conversation entry with specified URI was not found, so we must create it.
|
||||||
common_debug('Conversation URI not found, so we will create it with the URI given in the options to Notice::saveNew: '.$options['conversation']);
|
common_debug('Conversation URI not found, so we will create it with the URI given in the options to Notice::saveNew: '.$options['conversation']);
|
||||||
|
$convctx = new ActivityContext();
|
||||||
|
$convctx->conversation = $options['conversation'];
|
||||||
|
if (array_key_exists('conversation_url', $options)) {
|
||||||
|
$convctx->conversation_url = $options['conversation_url'];
|
||||||
|
}
|
||||||
// The insert in Conversation::create throws exception on failure
|
// The insert in Conversation::create throws exception on failure
|
||||||
$conv = Conversation::create($options['conversation'], $notice->created);
|
$conv = Conversation::create($convctx, $notice->created);
|
||||||
}
|
}
|
||||||
$notice->conversation = $conv->getID();
|
$notice->conversation = $conv->getID();
|
||||||
unset($conv);
|
unset($conv);
|
||||||
@ -706,6 +727,10 @@ class Notice extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($self && common_valid_http_url($self)) {
|
||||||
|
$notice->setPref('ostatus', 'self', $self);
|
||||||
|
}
|
||||||
|
|
||||||
// Only save 'attention' and metadata stuff (URLs, tags...) stuff if
|
// Only save 'attention' and metadata stuff (URLs, tags...) stuff if
|
||||||
// the activityverb is a POST (since stuff like repeat, favorite etc.
|
// the activityverb is a POST (since stuff like repeat, favorite etc.
|
||||||
// reasonably handle notifications themselves.
|
// reasonably handle notifications themselves.
|
||||||
@ -765,6 +790,9 @@ class Notice extends Managed_DataObject
|
|||||||
// implied object
|
// implied object
|
||||||
$options['uri'] = $act->id;
|
$options['uri'] = $act->id;
|
||||||
$options['url'] = $act->link;
|
$options['url'] = $act->link;
|
||||||
|
if ($act->selfLink) {
|
||||||
|
$options['self'] = $act->selfLink;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$actobj = count($act->objects)===1 ? $act->objects[0] : null;
|
$actobj = count($act->objects)===1 ? $act->objects[0] : null;
|
||||||
if (!is_null($actobj) && !empty($actobj->id)) {
|
if (!is_null($actobj) && !empty($actobj->id)) {
|
||||||
@ -775,6 +803,9 @@ class Notice extends Managed_DataObject
|
|||||||
$options['url'] = $actobj->id;
|
$options['url'] = $actobj->id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if ($actobj->selfLink) {
|
||||||
|
$options['self'] = $actobj->selfLink;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$defaults = array(
|
$defaults = array(
|
||||||
@ -784,6 +815,7 @@ class Notice extends Managed_DataObject
|
|||||||
'reply_to' => null,
|
'reply_to' => null,
|
||||||
'repeat_of' => null,
|
'repeat_of' => null,
|
||||||
'scope' => null,
|
'scope' => null,
|
||||||
|
'self' => null,
|
||||||
'source' => 'unknown',
|
'source' => 'unknown',
|
||||||
'tags' => array(),
|
'tags' => array(),
|
||||||
'uri' => null,
|
'uri' => null,
|
||||||
@ -921,7 +953,7 @@ class Notice extends Managed_DataObject
|
|||||||
// Conversation entry with specified URI was not found, so we must create it.
|
// Conversation entry with specified URI was not found, so we must create it.
|
||||||
common_debug('Conversation URI not found, so we will create it with the URI given in the context of the activity: '.$act->context->conversation);
|
common_debug('Conversation URI not found, so we will create it with the URI given in the context of the activity: '.$act->context->conversation);
|
||||||
// The insert in Conversation::create throws exception on failure
|
// The insert in Conversation::create throws exception on failure
|
||||||
$conv = Conversation::create($act->context->conversation, $stored->created);
|
$conv = Conversation::create($act->context, $stored->created);
|
||||||
}
|
}
|
||||||
$stored->conversation = $conv->getID();
|
$stored->conversation = $conv->getID();
|
||||||
unset($conv);
|
unset($conv);
|
||||||
@ -1020,6 +1052,14 @@ class Notice extends Managed_DataObject
|
|||||||
throw new ServerException('Supposedly saved Notice has no ID.');
|
throw new ServerException('Supposedly saved Notice has no ID.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($self && common_valid_http_url($self)) {
|
||||||
|
$stored->setPref('ostatus', 'self', $self);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($self && common_valid_http_url($self)) {
|
||||||
|
$stored->setPref('ostatus', 'self', $self);
|
||||||
|
}
|
||||||
|
|
||||||
// Only save 'attention' and metadata stuff (URLs, tags...) stuff if
|
// Only save 'attention' and metadata stuff (URLs, tags...) stuff if
|
||||||
// the activityverb is a POST (since stuff like repeat, favorite etc.
|
// the activityverb is a POST (since stuff like repeat, favorite etc.
|
||||||
// reasonably handle notifications themselves.
|
// reasonably handle notifications themselves.
|
||||||
@ -1578,12 +1618,12 @@ class Notice extends Managed_DataObject
|
|||||||
|
|
||||||
if (common_config('group', 'addtag')) {
|
if (common_config('group', 'addtag')) {
|
||||||
// we automatically add a tag for every group name, too
|
// we automatically add a tag for every group name, too
|
||||||
|
common_debug('Adding hashtag matching group nickname: '._ve($group->getNickname()));
|
||||||
$tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->nickname),
|
$tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->getNickname()),
|
||||||
'notice_id' => $this->id));
|
'notice_id' => $this->getID()));
|
||||||
|
|
||||||
if (is_null($tag)) {
|
if (is_null($tag)) {
|
||||||
$this->saveTag($group->nickname);
|
$this->saveTag($group->getNickname());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2008,6 +2048,7 @@ class Notice extends Managed_DataObject
|
|||||||
$conv = Conversation::getKV('id', $this->conversation);
|
$conv = Conversation::getKV('id', $this->conversation);
|
||||||
if ($conv instanceof Conversation) {
|
if ($conv instanceof Conversation) {
|
||||||
$ctx->conversation = $conv->uri;
|
$ctx->conversation = $conv->uri;
|
||||||
|
$ctx->conversation_url = $conv->url;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2070,9 +2111,12 @@ class Notice extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$act->selfLink = $this->getSelfLink();
|
||||||
|
} catch (InvalidUrlException $e) {
|
||||||
|
$act->selfLink = null;
|
||||||
|
}
|
||||||
if ($this->isLocal()) {
|
if ($this->isLocal()) {
|
||||||
$act->selfLink = common_local_url('ApiStatusesShow', array('id' => $this->id,
|
|
||||||
'format' => 'atom'));
|
|
||||||
$act->editLink = $act->selfLink;
|
$act->editLink = $act->selfLink;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2170,8 +2214,13 @@ class Notice extends Managed_DataObject
|
|||||||
$object->title = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $this->getProfile()->getNickname());
|
$object->title = sprintf('New %1$s by %2$s', ActivityObject::canonicalType($object->type), $this->getProfile()->getNickname());
|
||||||
$object->content = $this->getRendered();
|
$object->content = $this->getRendered();
|
||||||
$object->link = $this->getUrl();
|
$object->link = $this->getUrl();
|
||||||
|
try {
|
||||||
|
$object->selfLink = $this->getSelfLink();
|
||||||
|
} catch (InvalidUrlException $e) {
|
||||||
|
$object->selfLink = null;
|
||||||
|
}
|
||||||
|
|
||||||
$object->extra[] = array('status_net', array('notice_id' => $this->id));
|
$object->extra[] = array('statusnet:notice_id', null, $this->id);
|
||||||
|
|
||||||
Event::handle('EndActivityObjectFromNotice', array($this, &$object));
|
Event::handle('EndActivityObjectFromNotice', array($this, &$object));
|
||||||
}
|
}
|
||||||
@ -3195,4 +3244,27 @@ class Notice extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function delPref($namespace, $topic) {
|
||||||
|
return Notice_prefs::setData($this, $namespace, $topic, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPref($namespace, $topic, $default=null) {
|
||||||
|
// If you want an exception to be thrown, call Notice_prefs::getData directly
|
||||||
|
try {
|
||||||
|
return Notice_prefs::getData($this, $namespace, $topic, $default);
|
||||||
|
} catch (NoResultException $e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The same as getPref but will fall back to common_config value for the same namespace/topic
|
||||||
|
public function getConfigPref($namespace, $topic)
|
||||||
|
{
|
||||||
|
return Notice_prefs::getConfigData($this, $namespace, $topic);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPref($namespace, $topic, $data) {
|
||||||
|
return Notice_prefs::setData($this, $namespace, $topic, $data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
172
classes/Notice_prefs.php
Normal file
172
classes/Notice_prefs.php
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* GNU social
|
||||||
|
*
|
||||||
|
* Data class for Notice preferences
|
||||||
|
*
|
||||||
|
* PHP version 5
|
||||||
|
*
|
||||||
|
* LICENCE: This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
* @category Data
|
||||||
|
* @package GNUsocial
|
||||||
|
* @author Mikael Nordfeldth <mmn@hethane.se>
|
||||||
|
* @copyright 2013 Free Software Foundation, Inc.
|
||||||
|
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
|
||||||
|
* @link http://www.gnu.org/software/social/
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Notice_prefs extends Managed_DataObject
|
||||||
|
{
|
||||||
|
public $__table = 'notice_prefs'; // table name
|
||||||
|
public $notice_id; // int(4) primary_key not_null
|
||||||
|
public $namespace; // varchar(191) not_null
|
||||||
|
public $topic; // varchar(191) not_null
|
||||||
|
public $data; // text
|
||||||
|
public $created; // datetime not_null default_0000-00-00%2000%3A00%3A00
|
||||||
|
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
|
||||||
|
|
||||||
|
public static function schemaDef()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
'fields' => array(
|
||||||
|
'notice_id' => array('type' => 'int', 'not null' => true, 'description' => 'user'),
|
||||||
|
'namespace' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'namespace, like pluginname or category'),
|
||||||
|
'topic' => array('type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'preference key, i.e. description, age...'),
|
||||||
|
'data' => array('type' => 'blob', 'description' => 'topic data, may be anything'),
|
||||||
|
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
|
||||||
|
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
|
||||||
|
),
|
||||||
|
'primary key' => array('notice_id', 'namespace', 'topic'),
|
||||||
|
'foreign keys' => array(
|
||||||
|
'notice_prefs_notice_id_fkey' => array('notice', array('notice_id' => 'id')),
|
||||||
|
),
|
||||||
|
'indexes' => array(
|
||||||
|
'notice_prefs_notice_id_idx' => array('notice_id'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getNamespacePrefs(Notice $notice, $namespace, array $topic=array())
|
||||||
|
{
|
||||||
|
if (empty($topic)) {
|
||||||
|
$prefs = new Notice_prefs();
|
||||||
|
$prefs->notice_id = $notice->getID();
|
||||||
|
$prefs->namespace = $namespace;
|
||||||
|
$prefs->find();
|
||||||
|
} else {
|
||||||
|
$prefs = self::pivotGet('notice_id', $notice->getID(), array('namespace'=>$namespace, 'topic'=>$topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (empty($prefs->N)) {
|
||||||
|
throw new NoResultException($prefs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getNamespace(Notice $notice, $namespace, array $topic=array())
|
||||||
|
{
|
||||||
|
$prefs = self::getNamespacePrefs($notice, $namespace, $topic);
|
||||||
|
return $prefs->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getAll(Notice $notice)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$prefs = self::listFind('notice_id', array($notice->getID()));
|
||||||
|
} catch (NoResultException $e) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = array();
|
||||||
|
while ($prefs->fetch()) {
|
||||||
|
if (!isset($list[$prefs->namespace])) {
|
||||||
|
$list[$prefs->namespace] = array();
|
||||||
|
}
|
||||||
|
$list[$prefs->namespace][$prefs->topic] = $prefs->data;
|
||||||
|
}
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getTopic(Notice $notice, $namespace, $topic) {
|
||||||
|
return self::getByPK(array('notice_id' => $notice->getID(),
|
||||||
|
'namespace' => $namespace,
|
||||||
|
'topic' => $topic));
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getData(Notice $notice, $namespace, $topic, $def=null) {
|
||||||
|
try {
|
||||||
|
$pref = self::getTopic($notice, $namespace, $topic);
|
||||||
|
} catch (NoResultException $e) {
|
||||||
|
if ($def === null) {
|
||||||
|
// If no default value was set, continue the exception.
|
||||||
|
throw $e;
|
||||||
|
}
|
||||||
|
// If there was a default value, return that.
|
||||||
|
return $def;
|
||||||
|
}
|
||||||
|
return $pref->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function getConfigData(Notice $notice, $namespace, $topic) {
|
||||||
|
try {
|
||||||
|
$data = self::getData($notice, $namespace, $topic);
|
||||||
|
} catch (NoResultException $e) {
|
||||||
|
$data = common_config($namespace, $topic);
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sets a notice preference based on Notice, namespace and topic
|
||||||
|
*
|
||||||
|
* @param Notice $notice Which notice this is for
|
||||||
|
* @param string $namespace Under which namespace (pluginname etc.)
|
||||||
|
* @param string $topic Preference name (think key in key-val store)
|
||||||
|
* @param string $data Data to be put into preference storage, null means delete
|
||||||
|
*
|
||||||
|
* @return true if changes are made, false if no action taken
|
||||||
|
* @throws ServerException if preference could not be saved
|
||||||
|
*/
|
||||||
|
static function setData(Notice $notice, $namespace, $topic, $data=null) {
|
||||||
|
try {
|
||||||
|
$pref = self::getTopic($notice, $namespace, $topic);
|
||||||
|
if (is_null($data)) {
|
||||||
|
$pref->delete();
|
||||||
|
} else {
|
||||||
|
$orig = clone($pref);
|
||||||
|
$pref->data = $data;
|
||||||
|
$pref->update($orig);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (NoResultException $e) {
|
||||||
|
if (is_null($data)) {
|
||||||
|
return false; // No action taken
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pref = new Notice_prefs();
|
||||||
|
$pref->notice_id = $notice->getID();
|
||||||
|
$pref->namespace = $namespace;
|
||||||
|
$pref->topic = $topic;
|
||||||
|
$pref->data = $data;
|
||||||
|
$pref->created = common_sql_now();
|
||||||
|
|
||||||
|
if ($pref->insert() === false) {
|
||||||
|
throw new ServerException('Could not save notice preference.');
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -89,9 +89,14 @@ class Profile extends Managed_DataObject
|
|||||||
public function getUser()
|
public function getUser()
|
||||||
{
|
{
|
||||||
if (!isset($this->_user[$this->id])) {
|
if (!isset($this->_user[$this->id])) {
|
||||||
$user = User::getKV('id', $this->id);
|
$cur_user = common_current_user();
|
||||||
if (!$user instanceof User) {
|
if (($cur_user instanceof User) && $cur_user->sameAs($this)) {
|
||||||
throw new NoSuchUserException(array('id'=>$this->id));
|
$user = $cur_user;
|
||||||
|
} else {
|
||||||
|
$user = User::getKV('id', $this->id);
|
||||||
|
if (!$user instanceof User) {
|
||||||
|
throw new NoSuchUserException(array('id'=>$this->id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$this->_user[$this->id] = $user;
|
$this->_user[$this->id] = $user;
|
||||||
}
|
}
|
||||||
@ -941,11 +946,6 @@ class Profile extends Managed_DataObject
|
|||||||
|
|
||||||
function delete($useWhere=false)
|
function delete($useWhere=false)
|
||||||
{
|
{
|
||||||
// just in case it hadn't been done before... (usually set before adding deluser to queue handling!)
|
|
||||||
if (!$this->hasRole(Profile_role::DELETED)) {
|
|
||||||
$this->grantRole(Profile_role::DELETED);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->_deleteNotices();
|
$this->_deleteNotices();
|
||||||
$this->_deleteSubscriptions();
|
$this->_deleteSubscriptions();
|
||||||
$this->_deleteTags();
|
$this->_deleteTags();
|
||||||
@ -957,6 +957,7 @@ class Profile extends Managed_DataObject
|
|||||||
// not on individual objects.
|
// not on individual objects.
|
||||||
$related = array('Reply',
|
$related = array('Reply',
|
||||||
'Group_member',
|
'Group_member',
|
||||||
|
'Profile_role'
|
||||||
);
|
);
|
||||||
Event::handle('ProfileDeleteRelated', array($this, &$related));
|
Event::handle('ProfileDeleteRelated', array($this, &$related));
|
||||||
|
|
||||||
@ -965,6 +966,8 @@ class Profile extends Managed_DataObject
|
|||||||
$inst->profile_id = $this->id;
|
$inst->profile_id = $this->id;
|
||||||
$inst->delete();
|
$inst->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->grantRole(Profile_role::DELETED);
|
||||||
|
|
||||||
$localuser = User::getKV('id', $this->id);
|
$localuser = User::getKV('id', $this->id);
|
||||||
if ($localuser instanceof User) {
|
if ($localuser instanceof User) {
|
||||||
@ -1532,6 +1535,14 @@ class Profile extends Managed_DataObject
|
|||||||
}
|
}
|
||||||
return $url;
|
return $url;
|
||||||
}
|
}
|
||||||
|
public function getHtmlTitle()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return $this->getAcctUri(false);
|
||||||
|
} catch (ProfileNoAcctUriException $e) {
|
||||||
|
return $this->getNickname();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getNickname()
|
public function getNickname()
|
||||||
{
|
{
|
||||||
@ -1612,14 +1623,13 @@ class Profile extends Managed_DataObject
|
|||||||
return !empty($block);
|
return !empty($block);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAtomFeed()
|
public function getAtomFeed()
|
||||||
{
|
{
|
||||||
$feed = null;
|
$feed = null;
|
||||||
|
|
||||||
if (Event::handle('StartProfileGetAtomFeed', array($this, &$feed))) {
|
if (Event::handle('StartProfileGetAtomFeed', array($this, &$feed))) {
|
||||||
$user = User::getKV('id', $this->id);
|
if ($this->isLocal()) {
|
||||||
if (!empty($user)) {
|
$feed = common_local_url('ApiTimelineUser', array('id' => $this->getID(),
|
||||||
$feed = common_local_url('ApiTimelineUser', array('id' => $user->id,
|
|
||||||
'format' => 'atom'));
|
'format' => 'atom'));
|
||||||
}
|
}
|
||||||
Event::handle('EndProfileGetAtomFeed', array($this, $feed));
|
Event::handle('EndProfileGetAtomFeed', array($this, $feed));
|
||||||
|
@ -289,6 +289,11 @@ class User extends Managed_DataObject
|
|||||||
// TRANS: Profile data could not be inserted for some reason.
|
// TRANS: Profile data could not be inserted for some reason.
|
||||||
throw new ServerException(_m('Could not insert profile data for new user.'));
|
throw new ServerException(_m('Could not insert profile data for new user.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Necessary because id has been known to be reissued.
|
||||||
|
if ($profile->hasRole(Profile_role::DELETED)) {
|
||||||
|
$profile->revokeRole(Profile_role::DELETED);
|
||||||
|
}
|
||||||
|
|
||||||
$user->id = $id;
|
$user->id = $id;
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ $classes = array('Schema_version',
|
|||||||
'Notice',
|
'Notice',
|
||||||
'Notice_location',
|
'Notice_location',
|
||||||
'Notice_source',
|
'Notice_source',
|
||||||
|
'Notice_prefs',
|
||||||
'Reply',
|
'Reply',
|
||||||
'Consumer',
|
'Consumer',
|
||||||
'Token',
|
'Token',
|
||||||
|
@ -14,7 +14,7 @@ check out [Beginner’s Guide to OAuth](http://hueniverse.com/oauth/)).
|
|||||||
|
|
||||||
To use OAuth, you'll need to register your client application via the web interface
|
To use OAuth, you'll need to register your client application via the web interface
|
||||||
and obtain a consumer key and secret. You can find the interface for application
|
and obtain a consumer key and secret. You can find the interface for application
|
||||||
registration at [http://%%site.server%%/%%site.path%%settings/oauthapps](http://%%site.server%%/%%site.path%%settings/oauthapps).
|
registration at [%%action.oauthappssettings%%](%%action.oauthappssettings%%).
|
||||||
|
|
||||||
## JSONP callbacks
|
## JSONP callbacks
|
||||||
|
|
||||||
|
@ -218,21 +218,11 @@ class Auth_OpenID_Parse {
|
|||||||
|
|
||||||
function match($regexp, $text, &$match)
|
function match($regexp, $text, &$match)
|
||||||
{
|
{
|
||||||
if (!is_callable('mb_ereg_search_init')) {
|
if (preg_match($regexp, $text, $match)) {
|
||||||
if (!preg_match($regexp, $text, $match)) {
|
return true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$match = $match[0];
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$regexp = substr($regexp, 1, strlen($regexp) - 2 - strlen($this->_re_flags));
|
return false;
|
||||||
mb_ereg_search_init($text);
|
|
||||||
if (!mb_ereg_search($regexp)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
$match = mb_ereg_search_getregs();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -426,7 +426,7 @@ define('DB_PORTABILITY_ALL', 63);
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB
|
class DB
|
||||||
@ -577,7 +577,7 @@ class DB
|
|||||||
*/
|
*/
|
||||||
function apiVersion()
|
function apiVersion()
|
||||||
{
|
{
|
||||||
return '1.8.2';
|
return '1.9.2';
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
@ -772,7 +772,7 @@ class DB
|
|||||||
$parsed['dbsyntax'] = $str;
|
$parsed['dbsyntax'] = $str;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!count($dsn)) {
|
if (!strlen($dsn)) {
|
||||||
return $parsed;
|
return $parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -941,7 +941,7 @@ class DB
|
|||||||
* @author Stig Bakken <ssb@php.net>
|
* @author Stig Bakken <ssb@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_Error extends PEAR_Error
|
class DB_Error extends PEAR_Error
|
||||||
@ -959,18 +959,32 @@ class DB_Error extends PEAR_Error
|
|||||||
*
|
*
|
||||||
* @see PEAR_Error
|
* @see PEAR_Error
|
||||||
*/
|
*/
|
||||||
function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
|
function __construct($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
|
||||||
$level = E_USER_NOTICE, $debuginfo = null)
|
$level = E_USER_NOTICE, $debuginfo = null)
|
||||||
{
|
{
|
||||||
if (is_int($code)) {
|
if (is_int($code)) {
|
||||||
$this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code,
|
parent::__construct('DB Error: ' . DB::errorMessage($code), $code,
|
||||||
$mode, $level, $debuginfo, common_log(LOG_ERR, var_export($debuginfo,true)));
|
$mode, $level, $debuginfo);
|
||||||
} else {
|
} else {
|
||||||
$this->PEAR_Error("DB Error: $code", DB_ERROR,
|
parent::__construct("DB Error: $code", DB_ERROR,
|
||||||
$mode, $level, $debuginfo);
|
$mode, $level, $debuginfo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Workaround to both avoid the "Redefining already defined constructor"
|
||||||
|
* PHP error and provide backward compatibility in case someone is calling
|
||||||
|
* DB_Error() dynamically
|
||||||
|
*/
|
||||||
|
public function __call($method, $arguments)
|
||||||
|
{
|
||||||
|
if ($method == 'DB_Error') {
|
||||||
|
return call_user_func_array(array($this, '__construct'), $arguments);
|
||||||
|
}
|
||||||
|
trigger_error(
|
||||||
|
'Call to undefined method DB_Error::' . $method . '()', E_USER_ERROR
|
||||||
|
);
|
||||||
|
}
|
||||||
// }}}
|
// }}}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -988,7 +1002,7 @@ class DB_Error extends PEAR_Error
|
|||||||
* @author Stig Bakken <ssb@php.net>
|
* @author Stig Bakken <ssb@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_result
|
class DB_result
|
||||||
@ -1095,7 +1109,7 @@ class DB_result
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_result(&$dbh, $result, $options = array())
|
function __construct(&$dbh, $result, $options = array())
|
||||||
{
|
{
|
||||||
$this->autofree = $dbh->options['autofree'];
|
$this->autofree = $dbh->options['autofree'];
|
||||||
$this->dbh = &$dbh;
|
$this->dbh = &$dbh;
|
||||||
@ -1453,7 +1467,7 @@ class DB_result
|
|||||||
* @author Stig Bakken <ssb@php.net>
|
* @author Stig Bakken <ssb@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
* @see DB_common::setFetchMode()
|
* @see DB_common::setFetchMode()
|
||||||
*/
|
*/
|
||||||
@ -1468,7 +1482,7 @@ class DB_row
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_row(&$arr)
|
function __construct(&$arr)
|
||||||
{
|
{
|
||||||
foreach ($arr as $key => $value) {
|
foreach ($arr as $key => $value) {
|
||||||
$this->$key = &$arr[$key];
|
$this->$key = &$arr[$key];
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* @author Alan Knowles <alan@akbkhome.com>
|
* @author Alan Knowles <alan@akbkhome.com>
|
||||||
* @copyright 1997-2006 The PHP Group
|
* @copyright 1997-2006 The PHP Group
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
||||||
* @version CVS: $Id: DataObject.php 320069 2011-11-28 04:34:08Z alan_k $
|
* @version CVS: $Id: DataObject.php 336751 2015-05-12 04:39:50Z alan_k $
|
||||||
* @link http://pear.php.net/package/DB_DataObject
|
* @link http://pear.php.net/package/DB_DataObject
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -410,7 +410,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
$this->debug($n, "find",1);
|
$this->debug($n, "find",1);
|
||||||
}
|
}
|
||||||
if (!$this->__table) {
|
if (!strlen($this->tableName())) {
|
||||||
// xdebug can backtrace this!
|
// xdebug can backtrace this!
|
||||||
trigger_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR);
|
trigger_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR);
|
||||||
}
|
}
|
||||||
@ -2073,6 +2073,9 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
if (count($args)) {
|
if (count($args)) {
|
||||||
$this->__table = $args[0];
|
$this->__table = $args[0];
|
||||||
}
|
}
|
||||||
|
if (empty($this->__table)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
|
if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
|
||||||
return strtolower($this->__table);
|
return strtolower($this->__table);
|
||||||
}
|
}
|
||||||
@ -2421,7 +2424,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
$dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
|
$dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
|
||||||
|
|
||||||
if (!$dsn) {
|
if (!$dsn) {
|
||||||
if (!$this->_database && !empty($this->__table)) {
|
if (!$this->_database && !strlen($this->tableName())) {
|
||||||
$this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
|
$this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
|
||||||
}
|
}
|
||||||
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
|
||||||
@ -3522,7 +3525,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
if ($joinCol !== false) {
|
if ($joinCol !== false) {
|
||||||
$this->raiseError(
|
$this->raiseError(
|
||||||
"joinAdd: You cannot target a join column in the " .
|
"joinAdd: You cannot target a join column in the " .
|
||||||
"'link from' table ({$obj->__table}). " .
|
"'link from' table ({$obj->tableName()}). " .
|
||||||
"Either remove the fourth argument to joinAdd() ".
|
"Either remove the fourth argument to joinAdd() ".
|
||||||
"({$joinCol}), or alter your links.ini file.",
|
"({$joinCol}), or alter your links.ini file.",
|
||||||
DB_DATAOBJECT_ERROR_NODATA);
|
DB_DATAOBJECT_ERROR_NODATA);
|
||||||
@ -3605,7 +3608,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
|
|
||||||
if (!$items) {
|
if (!$items) {
|
||||||
$this->raiseError(
|
$this->raiseError(
|
||||||
"joinAdd: No table definition for {$obj->__table}",
|
"joinAdd: No table definition for {$obj->tableName()}",
|
||||||
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -3800,6 +3803,7 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
*/
|
*/
|
||||||
function autoJoin($cfg = array())
|
function autoJoin($cfg = array())
|
||||||
{
|
{
|
||||||
|
global $_DB_DATAOBJECT;
|
||||||
//var_Dump($cfg);exit;
|
//var_Dump($cfg);exit;
|
||||||
$pre_links = $this->links();
|
$pre_links = $this->links();
|
||||||
if (!empty($cfg['links'])) {
|
if (!empty($cfg['links'])) {
|
||||||
@ -3807,7 +3811,8 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
}
|
}
|
||||||
$map = $this->links( );
|
$map = $this->links( );
|
||||||
|
|
||||||
|
$this->databaseStructure();
|
||||||
|
$dbstructure = $_DB_DATAOBJECT['INI'][$this->_database];
|
||||||
//print_r($map);
|
//print_r($map);
|
||||||
$tabdef = $this->table();
|
$tabdef = $this->table();
|
||||||
|
|
||||||
@ -3874,6 +3879,12 @@ class DB_DataObject extends DB_DataObject_Overload
|
|||||||
|
|
||||||
list($tab,$col) = explode(':', $info);
|
list($tab,$col) = explode(':', $info);
|
||||||
// what about multiple joins on the same table!!!
|
// what about multiple joins on the same table!!!
|
||||||
|
|
||||||
|
// if links point to a table that does not exist - ignore.
|
||||||
|
if (!isset($dbstructure[$tab])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
$xx = DB_DataObject::factory($tab);
|
$xx = DB_DataObject::factory($tab);
|
||||||
if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
|
if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
* @author Alan Knowles <alan@akbkhome.com>
|
* @author Alan Knowles <alan@akbkhome.com>
|
||||||
* @copyright 1997-2006 The PHP Group
|
* @copyright 1997-2006 The PHP Group
|
||||||
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
* @license http://www.php.net/license/3_01.txt PHP License 3.01
|
||||||
* @version CVS: $Id: Generator.php 315531 2011-08-26 02:21:29Z alan_k $
|
* @version CVS: $Id: Generator.php 336719 2015-05-05 10:37:33Z alan_k $
|
||||||
* @link http://pear.php.net/package/DB_DataObject
|
* @link http://pear.php.net/package/DB_DataObject
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -406,7 +406,7 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
* Currenly only works with mysql / mysqli / posgtreas
|
* Currenly only works with mysql / mysqli / posgtreas
|
||||||
* to use, you must set option: generate_links=true
|
* to use, you must set option: generate_links=true
|
||||||
*
|
*
|
||||||
* @author Pascal Schöni
|
* @author Pascal Sch<EFBFBD>ni
|
||||||
*/
|
*/
|
||||||
|
|
||||||
function _createForiegnKeys()
|
function _createForiegnKeys()
|
||||||
@ -507,7 +507,7 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
* Currenly only works with mysql / mysqli
|
* Currenly only works with mysql / mysqli
|
||||||
* to use, you must set option: generate_links=true
|
* to use, you must set option: generate_links=true
|
||||||
*
|
*
|
||||||
* @author Pascal Schöni
|
* @author Pascal Sch<EFBFBD>ni
|
||||||
*/
|
*/
|
||||||
function generateForeignKeys()
|
function generateForeignKeys()
|
||||||
{
|
{
|
||||||
@ -895,7 +895,7 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
$options = &PEAR::getStaticProperty('DB_DataObject','options');
|
||||||
|
|
||||||
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
||||||
$this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
$this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
||||||
|
|
||||||
|
|
||||||
foreach($this->tables as $this->table) {
|
foreach($this->tables as $this->table) {
|
||||||
@ -976,8 +976,12 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
$head .= $this->derivedHookExtendsDocBlock();
|
$head .= $this->derivedHookExtendsDocBlock();
|
||||||
|
|
||||||
|
|
||||||
// requires
|
// requires - if you set extends_location = (blank) then no require line will be set
|
||||||
$head .= "require_once '{$this->_extendsFile}';\n\n";
|
// this can be used if you have an autoloader
|
||||||
|
|
||||||
|
if (!empty($this->_extendsFile)) {
|
||||||
|
$head .= "require_once '{$this->_extendsFile}';\n\n";
|
||||||
|
}
|
||||||
// add dummy class header in...
|
// add dummy class header in...
|
||||||
// class
|
// class
|
||||||
$head .= $this->derivedHookClassDocBlock();
|
$head .= $this->derivedHookClassDocBlock();
|
||||||
@ -1039,10 +1043,11 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$p = str_repeat(' ',max(2, (30 - strlen($t->name))));
|
$pad = str_repeat(' ',max(2, (30 - strlen($t->name))));
|
||||||
|
|
||||||
$length = empty($t->len) ? '' : '('.$t->len.')';
|
$length = empty($t->len) ? '' : '('.$t->len.')';
|
||||||
$body .=" {$var} \${$t->name}; {$p}// {$t->type}$length {$t->flags}\n";
|
$flags = strlen($t->flags) ? (' '. trim($t->flags)) : '';
|
||||||
|
$body .=" {$var} \${$t->name}; {$pad}// {$t->type}{$length}{$flags}\n";
|
||||||
|
|
||||||
// can not do set as PEAR::DB table info doesnt support it.
|
// can not do set as PEAR::DB table info doesnt support it.
|
||||||
//if (substr($t->Type,0,3) == "set")
|
//if (substr($t->Type,0,3) == "set")
|
||||||
@ -1283,7 +1288,7 @@ class DB_DataObject_Generator extends DB_DataObject
|
|||||||
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
|
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
|
||||||
|
|
||||||
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
|
||||||
$this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
$this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
|
||||||
|
|
||||||
$classname = $this->classname = $this->getClassNameFromTableName($this->table);
|
$classname = $this->classname = $this->getClassNameFromTableName($this->table);
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ require_once 'PEAR.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_common extends PEAR
|
class DB_common extends PEAR
|
||||||
@ -145,7 +145,7 @@ class DB_common extends PEAR
|
|||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_common()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->PEAR('DB_Error');
|
$this->PEAR('DB_Error');
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_dbase extends DB_common
|
class DB_dbase extends DB_common
|
||||||
@ -140,13 +140,13 @@ class DB_dbase extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_dbase()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -41,7 +41,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
* @since Class functional since Release 1.7.0
|
* @since Class functional since Release 1.7.0
|
||||||
*/
|
*/
|
||||||
@ -124,13 +124,13 @@ class DB_fbsql extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_fbsql()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -49,7 +49,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
* @since Class became stable in Release 1.7.0
|
* @since Class became stable in Release 1.7.0
|
||||||
*/
|
*/
|
||||||
@ -180,13 +180,13 @@ class DB_ibase extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_ibase()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -48,7 +48,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_ifx extends DB_common
|
class DB_ifx extends DB_common
|
||||||
@ -167,13 +167,13 @@ class DB_ifx extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_ifx()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -47,7 +47,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
* @since Class not functional until Release 1.7.0
|
* @since Class not functional until Release 1.7.0
|
||||||
*/
|
*/
|
||||||
@ -126,13 +126,13 @@ class DB_msql extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_msql()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -49,7 +49,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_mssql extends DB_common
|
class DB_mssql extends DB_common
|
||||||
@ -179,13 +179,13 @@ class DB_mssql extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_mssql()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -41,7 +41,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_mysql extends DB_common
|
class DB_mysql extends DB_common
|
||||||
@ -162,13 +162,13 @@ class DB_mysql extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_mysql()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -43,7 +43,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
* @since Class functional since Release 1.6.3
|
* @since Class functional since Release 1.6.3
|
||||||
*/
|
*/
|
||||||
@ -224,13 +224,13 @@ class DB_mysqli extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_mysqli()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
@ -497,7 +497,11 @@ class DB_mysqli extends DB_common
|
|||||||
*/
|
*/
|
||||||
function freeResult($result)
|
function freeResult($result)
|
||||||
{
|
{
|
||||||
return is_resource($result) ? mysqli_free_result($result) : false;
|
if (! $result instanceof mysqli_result) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
mysqli_free_result($result);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
@ -1031,6 +1035,10 @@ class DB_mysqli extends DB_common
|
|||||||
? $this->mysqli_types[$tmp->type]
|
? $this->mysqli_types[$tmp->type]
|
||||||
: 'unknown',
|
: 'unknown',
|
||||||
// http://bugs.php.net/?id=36579
|
// http://bugs.php.net/?id=36579
|
||||||
|
// Doc Bug #36579: mysqli_fetch_field length handling
|
||||||
|
// https://bugs.php.net/bug.php?id=62426
|
||||||
|
// Bug #62426: mysqli_fetch_field_direct returns incorrect
|
||||||
|
// length on UTF8 fields
|
||||||
'len' => $tmp->length,
|
'len' => $tmp->length,
|
||||||
'flags' => $flags,
|
'flags' => $flags,
|
||||||
);
|
);
|
||||||
|
@ -47,7 +47,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_oci8 extends DB_common
|
class DB_oci8 extends DB_common
|
||||||
@ -173,13 +173,13 @@ class DB_oci8 extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_oci8()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -44,7 +44,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_odbc extends DB_common
|
class DB_odbc extends DB_common
|
||||||
@ -153,13 +153,13 @@ class DB_odbc extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_odbc()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -43,7 +43,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_pgsql extends DB_common
|
class DB_pgsql extends DB_common
|
||||||
@ -148,13 +148,13 @@ class DB_pgsql extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_pgsql()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -47,7 +47,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_sqlite extends DB_common
|
class DB_sqlite extends DB_common
|
||||||
@ -152,13 +152,13 @@ class DB_sqlite extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_sqlite()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -38,7 +38,7 @@ require_once 'DB.php';
|
|||||||
* @author Stig Bakken <stig@php.net>
|
* @author Stig Bakken <stig@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_storage extends PEAR
|
class DB_storage extends PEAR
|
||||||
@ -94,7 +94,7 @@ class DB_storage extends PEAR
|
|||||||
* a reference to this object
|
* a reference to this object
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function DB_storage($table, $keycolumn, &$dbh, $validator = null)
|
function __construct($table, $keycolumn, &$dbh, $validator = null)
|
||||||
{
|
{
|
||||||
$this->PEAR('DB_Error');
|
$this->PEAR('DB_Error');
|
||||||
$this->_table = $table;
|
$this->_table = $table;
|
||||||
|
@ -46,7 +46,7 @@ require_once 'DB/common.php';
|
|||||||
* @author Daniel Convissor <danielc@php.net>
|
* @author Daniel Convissor <danielc@php.net>
|
||||||
* @copyright 1997-2007 The PHP Group
|
* @copyright 1997-2007 The PHP Group
|
||||||
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
* @license http://www.php.net/license/3_0.txt PHP License 3.0
|
||||||
* @version Release: 1.8.2
|
* @version Release: 1.9.2
|
||||||
* @link http://pear.php.net/package/DB
|
* @link http://pear.php.net/package/DB
|
||||||
*/
|
*/
|
||||||
class DB_sybase extends DB_common
|
class DB_sybase extends DB_common
|
||||||
@ -141,13 +141,13 @@ class DB_sybase extends DB_common
|
|||||||
// {{{ constructor
|
// {{{ constructor
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This constructor calls <kbd>$this->DB_common()</kbd>
|
* This constructor calls <kbd>parent::__construct()</kbd>
|
||||||
*
|
*
|
||||||
* @return void
|
* @return void
|
||||||
*/
|
*/
|
||||||
function DB_sybase()
|
function __construct()
|
||||||
{
|
{
|
||||||
$this->DB_common();
|
parent::__construct();
|
||||||
}
|
}
|
||||||
|
|
||||||
// }}}
|
// }}}
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
|
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
|
||||||
* FILE, changes will be overwritten the next time the script is run.
|
* FILE, changes will be overwritten the next time the script is run.
|
||||||
*
|
*
|
||||||
* @version 4.7.0
|
* @version 4.9.3
|
||||||
*
|
*
|
||||||
* @warning
|
* @warning
|
||||||
* You must *not* include any other HTML Purifier files before this file,
|
* You must *not* include any other HTML Purifier files before this file,
|
||||||
@ -137,6 +137,8 @@ require 'HTMLPurifier/AttrTransform/SafeObject.php';
|
|||||||
require 'HTMLPurifier/AttrTransform/SafeParam.php';
|
require 'HTMLPurifier/AttrTransform/SafeParam.php';
|
||||||
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
|
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||||
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
|
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
|
||||||
|
require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
|
||||||
|
require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
|
||||||
require 'HTMLPurifier/AttrTransform/Textarea.php';
|
require 'HTMLPurifier/AttrTransform/Textarea.php';
|
||||||
require 'HTMLPurifier/ChildDef/Chameleon.php';
|
require 'HTMLPurifier/ChildDef/Chameleon.php';
|
||||||
require 'HTMLPurifier/ChildDef/Custom.php';
|
require 'HTMLPurifier/ChildDef/Custom.php';
|
||||||
@ -175,6 +177,8 @@ require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
|
|||||||
require 'HTMLPurifier/HTMLModule/Tables.php';
|
require 'HTMLPurifier/HTMLModule/Tables.php';
|
||||||
require 'HTMLPurifier/HTMLModule/Target.php';
|
require 'HTMLPurifier/HTMLModule/Target.php';
|
||||||
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
|
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
|
||||||
|
require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
|
||||||
|
require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
|
||||||
require 'HTMLPurifier/HTMLModule/Text.php';
|
require 'HTMLPurifier/HTMLModule/Text.php';
|
||||||
require 'HTMLPurifier/HTMLModule/Tidy.php';
|
require 'HTMLPurifier/HTMLModule/Tidy.php';
|
||||||
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
|
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
|
||||||
@ -225,5 +229,6 @@ require 'HTMLPurifier/URIScheme/https.php';
|
|||||||
require 'HTMLPurifier/URIScheme/mailto.php';
|
require 'HTMLPurifier/URIScheme/mailto.php';
|
||||||
require 'HTMLPurifier/URIScheme/news.php';
|
require 'HTMLPurifier/URIScheme/news.php';
|
||||||
require 'HTMLPurifier/URIScheme/nntp.php';
|
require 'HTMLPurifier/URIScheme/nntp.php';
|
||||||
|
require 'HTMLPurifier/URIScheme/tel.php';
|
||||||
require 'HTMLPurifier/VarParser/Flexible.php';
|
require 'HTMLPurifier/VarParser/Flexible.php';
|
||||||
require 'HTMLPurifier/VarParser/Native.php';
|
require 'HTMLPurifier/VarParser/Native.php';
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
HTML Purifier 4.7.0 - Standards Compliant HTML Filtering
|
HTML Purifier 4.9.3 - Standards Compliant HTML Filtering
|
||||||
Copyright (C) 2006-2008 Edward Z. Yang
|
Copyright (C) 2006-2008 Edward Z. Yang
|
||||||
|
|
||||||
This library is free software; you can redistribute it and/or
|
This library is free software; you can redistribute it and/or
|
||||||
@ -58,12 +58,12 @@ class HTMLPurifier
|
|||||||
* Version of HTML Purifier.
|
* Version of HTML Purifier.
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
public $version = '4.7.0';
|
public $version = '4.9.3';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constant with version of HTML Purifier.
|
* Constant with version of HTML Purifier.
|
||||||
*/
|
*/
|
||||||
const VERSION = '4.7.0';
|
const VERSION = '4.9.3';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Global configuration object.
|
* Global configuration object.
|
||||||
@ -104,7 +104,7 @@ class HTMLPurifier
|
|||||||
/**
|
/**
|
||||||
* Initializes the purifier.
|
* Initializes the purifier.
|
||||||
*
|
*
|
||||||
* @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object
|
* @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object
|
||||||
* for all instances of the purifier, if omitted, a default
|
* for all instances of the purifier, if omitted, a default
|
||||||
* configuration is supplied (which can be overridden on a
|
* configuration is supplied (which can be overridden on a
|
||||||
* per-use basis).
|
* per-use basis).
|
||||||
|
@ -131,6 +131,8 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
|
|||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
|
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
|
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
|
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
|
||||||
|
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php';
|
||||||
|
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php';
|
||||||
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
|
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
|
||||||
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
|
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
|
||||||
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
|
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
|
||||||
@ -169,6 +171,8 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
|
|||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
|
||||||
|
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php';
|
||||||
|
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
|
||||||
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
|
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
|
||||||
@ -219,5 +223,6 @@ require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
|
|||||||
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
|
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
|
||||||
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
|
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
|
||||||
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
|
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
|
||||||
|
require_once $__dir . '/HTMLPurifier/URIScheme/tel.php';
|
||||||
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
|
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
|
||||||
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
|
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
|
||||||
|
@ -19,8 +19,8 @@ class HTMLPurifier_Arborize
|
|||||||
if ($token instanceof HTMLPurifier_Token_End) {
|
if ($token instanceof HTMLPurifier_Token_End) {
|
||||||
$token->start = null; // [MUT]
|
$token->start = null; // [MUT]
|
||||||
$r = array_pop($stack);
|
$r = array_pop($stack);
|
||||||
assert($r->name === $token->name);
|
//assert($r->name === $token->name);
|
||||||
assert(empty($token->attr));
|
//assert(empty($token->attr));
|
||||||
$r->endCol = $token->col;
|
$r->endCol = $token->col;
|
||||||
$r->endLine = $token->line;
|
$r->endLine = $token->line;
|
||||||
$r->endArmor = $token->armor;
|
$r->endArmor = $token->armor;
|
||||||
@ -32,7 +32,7 @@ class HTMLPurifier_Arborize
|
|||||||
$stack[] = $node;
|
$stack[] = $node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(count($stack) == 1);
|
//assert(count($stack) == 1);
|
||||||
return $stack[0];
|
return $stack[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,11 @@ class HTMLPurifier_AttrCollections
|
|||||||
* @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
|
* @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
|
||||||
*/
|
*/
|
||||||
public function __construct($attr_types, $modules)
|
public function __construct($attr_types, $modules)
|
||||||
|
{
|
||||||
|
$this->doConstruct($attr_types, $modules);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function doConstruct($attr_types, $modules)
|
||||||
{
|
{
|
||||||
// load extensions from the modules
|
// load extensions from the modules
|
||||||
foreach ($modules as $module) {
|
foreach ($modules as $module) {
|
||||||
|
@ -86,7 +86,13 @@ abstract class HTMLPurifier_AttrDef
|
|||||||
*/
|
*/
|
||||||
protected function mungeRgb($string)
|
protected function mungeRgb($string)
|
||||||
{
|
{
|
||||||
return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
|
$p = '\s*(\d+(\.\d+)?([%]?))\s*';
|
||||||
|
|
||||||
|
if (preg_match('/(rgba|hsla)\(/', $string)) {
|
||||||
|
return preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8,\11)', $string);
|
||||||
|
}
|
||||||
|
|
||||||
|
return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8)', $string);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,15 +25,42 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
|
|||||||
$css = $this->parseCDATA($css);
|
$css = $this->parseCDATA($css);
|
||||||
|
|
||||||
$definition = $config->getCSSDefinition();
|
$definition = $config->getCSSDefinition();
|
||||||
|
$allow_duplicates = $config->get("CSS.AllowDuplicates");
|
||||||
|
|
||||||
// we're going to break the spec and explode by semicolons.
|
|
||||||
// This is because semicolon rarely appears in escaped form
|
|
||||||
// Doing this is generally flaky but fast
|
|
||||||
// IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
|
|
||||||
// for details
|
|
||||||
|
|
||||||
$declarations = explode(';', $css);
|
// According to the CSS2.1 spec, the places where a
|
||||||
|
// non-delimiting semicolon can appear are in strings
|
||||||
|
// escape sequences. So here is some dumb hack to
|
||||||
|
// handle quotes.
|
||||||
|
$len = strlen($css);
|
||||||
|
$accum = "";
|
||||||
|
$declarations = array();
|
||||||
|
$quoted = false;
|
||||||
|
for ($i = 0; $i < $len; $i++) {
|
||||||
|
$c = strcspn($css, ";'\"", $i);
|
||||||
|
$accum .= substr($css, $i, $c);
|
||||||
|
$i += $c;
|
||||||
|
if ($i == $len) break;
|
||||||
|
$d = $css[$i];
|
||||||
|
if ($quoted) {
|
||||||
|
$accum .= $d;
|
||||||
|
if ($d == $quoted) {
|
||||||
|
$quoted = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($d == ";") {
|
||||||
|
$declarations[] = $accum;
|
||||||
|
$accum = "";
|
||||||
|
} else {
|
||||||
|
$accum .= $d;
|
||||||
|
$quoted = $d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ($accum != "") $declarations[] = $accum;
|
||||||
|
|
||||||
$propvalues = array();
|
$propvalues = array();
|
||||||
|
$new_declarations = '';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Name of the current CSS property being validated.
|
* Name of the current CSS property being validated.
|
||||||
@ -83,7 +110,11 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
|
|||||||
if ($result === false) {
|
if ($result === false) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$propvalues[$property] = $result;
|
if ($allow_duplicates) {
|
||||||
|
$new_declarations .= "$property:$result;";
|
||||||
|
} else {
|
||||||
|
$propvalues[$property] = $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$context->destroy('CurrentCSSProperty');
|
$context->destroy('CurrentCSSProperty');
|
||||||
@ -92,7 +123,6 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
|
|||||||
// slightly inefficient, but it's the only way of getting rid of
|
// slightly inefficient, but it's the only way of getting rid of
|
||||||
// duplicates. Perhaps config to optimize it, but not now.
|
// duplicates. Perhaps config to optimize it, but not now.
|
||||||
|
|
||||||
$new_declarations = '';
|
|
||||||
foreach ($propvalues as $prop => $value) {
|
foreach ($propvalues as $prop => $value) {
|
||||||
$new_declarations .= "$prop:$value;";
|
$new_declarations .= "$prop:$value;";
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,16 @@
|
|||||||
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
|
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type HTMLPurifier_AttrDef_CSS_AlphaValue
|
||||||
|
*/
|
||||||
|
protected $alpha;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $color
|
* @param string $color
|
||||||
* @param HTMLPurifier_Config $config
|
* @param HTMLPurifier_Config $config
|
||||||
@ -29,59 +39,104 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
|
|||||||
return $colors[$lower];
|
return $colors[$lower];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strpos($color, 'rgb(') !== false) {
|
if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) {
|
||||||
// rgb literal handling
|
|
||||||
$length = strlen($color);
|
$length = strlen($color);
|
||||||
if (strpos($color, ')') !== $length - 1) {
|
if (strpos($color, ')') !== $length - 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$triad = substr($color, 4, $length - 4 - 1);
|
|
||||||
$parts = explode(',', $triad);
|
// get used function : rgb, rgba, hsl or hsla
|
||||||
if (count($parts) !== 3) {
|
$function = $matches[1];
|
||||||
|
|
||||||
|
$parameters_size = 3;
|
||||||
|
$alpha_channel = false;
|
||||||
|
if (substr($function, -1) === 'a') {
|
||||||
|
$parameters_size = 4;
|
||||||
|
$alpha_channel = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Allowed types for values :
|
||||||
|
* parameter_position => [type => max_value]
|
||||||
|
*/
|
||||||
|
$allowed_types = array(
|
||||||
|
1 => array('percentage' => 100, 'integer' => 255),
|
||||||
|
2 => array('percentage' => 100, 'integer' => 255),
|
||||||
|
3 => array('percentage' => 100, 'integer' => 255),
|
||||||
|
);
|
||||||
|
$allow_different_types = false;
|
||||||
|
|
||||||
|
if (strpos($function, 'hsl') !== false) {
|
||||||
|
$allowed_types = array(
|
||||||
|
1 => array('integer' => 360),
|
||||||
|
2 => array('percentage' => 100),
|
||||||
|
3 => array('percentage' => 100),
|
||||||
|
);
|
||||||
|
$allow_different_types = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
$values = trim(str_replace($function, '', $color), ' ()');
|
||||||
|
|
||||||
|
$parts = explode(',', $values);
|
||||||
|
if (count($parts) !== $parameters_size) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$type = false; // to ensure that they're all the same type
|
|
||||||
|
$type = false;
|
||||||
$new_parts = array();
|
$new_parts = array();
|
||||||
|
$i = 0;
|
||||||
|
|
||||||
foreach ($parts as $part) {
|
foreach ($parts as $part) {
|
||||||
|
$i++;
|
||||||
$part = trim($part);
|
$part = trim($part);
|
||||||
|
|
||||||
if ($part === '') {
|
if ($part === '') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$length = strlen($part);
|
|
||||||
if ($part[$length - 1] === '%') {
|
// different check for alpha channel
|
||||||
// handle percents
|
if ($alpha_channel === true && $i === count($parts)) {
|
||||||
if (!$type) {
|
$result = $this->alpha->validate($part, $config, $context);
|
||||||
$type = 'percentage';
|
|
||||||
} elseif ($type !== 'percentage') {
|
if ($result === false) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$num = (float)substr($part, 0, $length - 1);
|
|
||||||
if ($num < 0) {
|
$new_parts[] = (string)$result;
|
||||||
$num = 0;
|
continue;
|
||||||
}
|
}
|
||||||
if ($num > 100) {
|
|
||||||
$num = 100;
|
if (substr($part, -1) === '%') {
|
||||||
}
|
$current_type = 'percentage';
|
||||||
$new_parts[] = "$num%";
|
|
||||||
} else {
|
} else {
|
||||||
// handle integers
|
$current_type = 'integer';
|
||||||
if (!$type) {
|
}
|
||||||
$type = 'integer';
|
|
||||||
} elseif ($type !== 'integer') {
|
if (!array_key_exists($current_type, $allowed_types[$i])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$num = (int)$part;
|
|
||||||
if ($num < 0) {
|
if (!$type) {
|
||||||
$num = 0;
|
$type = $current_type;
|
||||||
}
|
}
|
||||||
if ($num > 255) {
|
|
||||||
$num = 255;
|
if ($allow_different_types === false && $type != $current_type) {
|
||||||
}
|
return false;
|
||||||
$new_parts[] = (string)$num;
|
}
|
||||||
|
|
||||||
|
$max_value = $allowed_types[$i][$current_type];
|
||||||
|
|
||||||
|
if ($current_type == 'integer') {
|
||||||
|
// Return value between range 0 -> $max_value
|
||||||
|
$new_parts[] = (int)max(min($part, $max_value), 0);
|
||||||
|
} elseif ($current_type == 'percentage') {
|
||||||
|
$new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$new_triad = implode(',', $new_parts);
|
|
||||||
$color = "rgb($new_triad)";
|
$new_values = implode(',', $new_parts);
|
||||||
|
|
||||||
|
$color = $function . '(' . $new_values . ')';
|
||||||
} else {
|
} else {
|
||||||
// hexadecimal handling
|
// hexadecimal handling
|
||||||
if ($color[0] === '#') {
|
if ($color[0] === '#') {
|
||||||
@ -100,6 +155,7 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
|
|||||||
}
|
}
|
||||||
return $color;
|
return $color;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// vim: et sw=4 sts=4
|
// vim: et sw=4 sts=4
|
||||||
|
@ -130,6 +130,8 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
|
|||||||
// <http://ja.wikipedia.org/wiki/MS_明朝>. See
|
// <http://ja.wikipedia.org/wiki/MS_明朝>. See
|
||||||
// the CSS3 spec for more examples:
|
// the CSS3 spec for more examples:
|
||||||
// <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
|
// <http://www.w3.org/TR/2011/WD-css3-fonts-20110324/localizedfamilynames.png>
|
||||||
|
// You can see live samples of these on the Internet:
|
||||||
|
// <http://www.google.co.jp/search?q=font-family+MS+明朝|ゴシック>
|
||||||
// However, most of these fonts have ASCII equivalents:
|
// However, most of these fonts have ASCII equivalents:
|
||||||
// for example, 'MS Mincho', and it's considered
|
// for example, 'MS Mincho', and it's considered
|
||||||
// professional to use ASCII font names instead of
|
// professional to use ASCII font names instead of
|
||||||
|
@ -33,6 +33,9 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
$uri_string = substr($uri_string, 4);
|
$uri_string = substr($uri_string, 4);
|
||||||
|
if (strlen($uri_string) == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
$new_length = strlen($uri_string) - 1;
|
$new_length = strlen($uri_string) - 1;
|
||||||
if ($uri_string[$new_length] != ')') {
|
if ($uri_string[$new_length] != ')') {
|
||||||
return false;
|
return false;
|
||||||
|
@ -72,18 +72,26 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
|
|||||||
|
|
||||||
// we purposely avoid using regex, hopefully this is faster
|
// we purposely avoid using regex, hopefully this is faster
|
||||||
|
|
||||||
if (ctype_alpha($id)) {
|
if ($config->get('Attr.ID.HTML5') === true) {
|
||||||
$result = true;
|
if (preg_match('/[\t\n\x0b\x0c ]/', $id)) {
|
||||||
} else {
|
|
||||||
if (!ctype_alpha(@$id[0])) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// primitive style of regexps, I suppose
|
} else {
|
||||||
$trim = trim(
|
if (ctype_alpha($id)) {
|
||||||
$id,
|
// OK
|
||||||
'A..Za..z0..9:-._'
|
} else {
|
||||||
);
|
if (!ctype_alpha(@$id[0])) {
|
||||||
$result = ($trim === '');
|
return false;
|
||||||
|
}
|
||||||
|
// primitive style of regexps, I suppose
|
||||||
|
$trim = trim(
|
||||||
|
$id,
|
||||||
|
'A..Za..z0..9:-._'
|
||||||
|
);
|
||||||
|
if ($trim !== '') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$regexp = $config->get('Attr.IDBlacklistRegexp');
|
$regexp = $config->get('Attr.IDBlacklistRegexp');
|
||||||
@ -91,14 +99,14 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->selector && $result) {
|
if (!$this->selector) {
|
||||||
$id_accumulator->add($id);
|
$id_accumulator->add($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no change was made to the ID, return the result
|
// if no change was made to the ID, return the result
|
||||||
// else, return the new id if stripping whitespace made it
|
// else, return the new id if stripping whitespace made it
|
||||||
// valid, or return false.
|
// valid, or return false.
|
||||||
return $result ? $id : false;
|
return $id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,24 +76,33 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
|
|||||||
// fairly well supported.
|
// fairly well supported.
|
||||||
$underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
|
$underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
|
||||||
|
|
||||||
|
// Based off of RFC 1738, but amended so that
|
||||||
|
// as per RFC 3696, the top label need only not be all numeric.
|
||||||
// The productions describing this are:
|
// The productions describing this are:
|
||||||
$a = '[a-z]'; // alpha
|
$a = '[a-z]'; // alpha
|
||||||
$an = '[a-z0-9]'; // alphanum
|
$an = '[a-z0-9]'; // alphanum
|
||||||
$and = "[a-z0-9-$underscore]"; // alphanum | "-"
|
$and = "[a-z0-9-$underscore]"; // alphanum | "-"
|
||||||
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
||||||
$domainlabel = "$an($and*$an)?";
|
$domainlabel = "$an(?:$and*$an)?";
|
||||||
// toplabel = alpha | alpha *( alphanum | "-" ) alphanum
|
// AMENDED as per RFC 3696
|
||||||
$toplabel = "$a($and*$an)?";
|
// toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum
|
||||||
|
// side condition: not all numeric
|
||||||
|
$toplabel = "$an(?:$and*$an)?";
|
||||||
// hostname = *( domainlabel "." ) toplabel [ "." ]
|
// hostname = *( domainlabel "." ) toplabel [ "." ]
|
||||||
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
|
if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) {
|
||||||
return $string;
|
if (!ctype_digit($matches[1])) {
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PHP 5.3 and later support this functionality natively
|
||||||
|
if (function_exists('idn_to_ascii')) {
|
||||||
|
$string = idn_to_ascii($string);
|
||||||
|
|
||||||
// If we have Net_IDNA2 support, we can support IRIs by
|
// If we have Net_IDNA2 support, we can support IRIs by
|
||||||
// punycoding them. (This is the most portable thing to do,
|
// punycoding them. (This is the most portable thing to do,
|
||||||
// since otherwise we have to assume browsers support
|
// since otherwise we have to assume browsers support
|
||||||
|
} elseif ($config->get('Core.EnableIDNA')) {
|
||||||
if ($config->get('Core.EnableIDNA')) {
|
|
||||||
$idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
|
$idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
|
||||||
// we need to encode each period separately
|
// we need to encode each period separately
|
||||||
$parts = explode('.', $string);
|
$parts = explode('.', $string);
|
||||||
@ -114,13 +123,14 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$string = implode('.', $new_parts);
|
$string = implode('.', $new_parts);
|
||||||
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
|
|
||||||
return $string;
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
// XXX error reporting
|
// XXX error reporting
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Try again
|
||||||
|
if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
|
||||||
|
return $string;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,8 +32,7 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
|
|||||||
if ($src) {
|
if ($src) {
|
||||||
$alt = $config->get('Attr.DefaultImageAlt');
|
$alt = $config->get('Attr.DefaultImageAlt');
|
||||||
if ($alt === null) {
|
if ($alt === null) {
|
||||||
// truncate if the alt is too long
|
$attr['alt'] = basename($attr['src']);
|
||||||
$attr['alt'] = substr(basename($attr['src']), 0, 40);
|
|
||||||
} else {
|
} else {
|
||||||
$attr['alt'] = $alt;
|
$attr['alt'] = $alt;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// must be called POST validation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds rel="noopener" to any links which target a different window
|
||||||
|
* than the current one. This is used to prevent malicious websites
|
||||||
|
* from silently replacing the original window, which could be used
|
||||||
|
* to do phishing.
|
||||||
|
* This transform is controlled by %HTML.TargetNoopener.
|
||||||
|
*/
|
||||||
|
class HTMLPurifier_AttrTransform_TargetNoopener extends HTMLPurifier_AttrTransform
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $attr
|
||||||
|
* @param HTMLPurifier_Config $config
|
||||||
|
* @param HTMLPurifier_Context $context
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function transform($attr, $config, $context)
|
||||||
|
{
|
||||||
|
if (isset($attr['rel'])) {
|
||||||
|
$rels = explode(' ', $attr['rel']);
|
||||||
|
} else {
|
||||||
|
$rels = array();
|
||||||
|
}
|
||||||
|
if (isset($attr['target']) && !in_array('noopener', $rels)) {
|
||||||
|
$rels[] = 'noopener';
|
||||||
|
}
|
||||||
|
if (!empty($rels) || isset($attr['rel'])) {
|
||||||
|
$attr['rel'] = implode(' ', $rels);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,37 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// must be called POST validation
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds rel="noreferrer" to any links which target a different window
|
||||||
|
* than the current one. This is used to prevent malicious websites
|
||||||
|
* from silently replacing the original window, which could be used
|
||||||
|
* to do phishing.
|
||||||
|
* This transform is controlled by %HTML.TargetNoreferrer.
|
||||||
|
*/
|
||||||
|
class HTMLPurifier_AttrTransform_TargetNoreferrer extends HTMLPurifier_AttrTransform
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @param array $attr
|
||||||
|
* @param HTMLPurifier_Config $config
|
||||||
|
* @param HTMLPurifier_Context $context
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function transform($attr, $config, $context)
|
||||||
|
{
|
||||||
|
if (isset($attr['rel'])) {
|
||||||
|
$rels = explode(' ', $attr['rel']);
|
||||||
|
} else {
|
||||||
|
$rels = array();
|
||||||
|
}
|
||||||
|
if (isset($attr['target']) && !in_array('noreferrer', $rels)) {
|
||||||
|
$rels[] = 'noreferrer';
|
||||||
|
}
|
||||||
|
if (!empty($rels) || isset($attr['rel'])) {
|
||||||
|
$attr['rel'] = implode(' ', $rels);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $attr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -225,6 +225,10 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
|||||||
);
|
);
|
||||||
$max = $config->get('CSS.MaxImgLength');
|
$max = $config->get('CSS.MaxImgLength');
|
||||||
|
|
||||||
|
$this->info['min-width'] =
|
||||||
|
$this->info['max-width'] =
|
||||||
|
$this->info['min-height'] =
|
||||||
|
$this->info['max-height'] =
|
||||||
$this->info['width'] =
|
$this->info['width'] =
|
||||||
$this->info['height'] =
|
$this->info['height'] =
|
||||||
$max === null ?
|
$max === null ?
|
||||||
@ -370,6 +374,19 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
|
|||||||
);
|
);
|
||||||
$this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
|
$this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
|
||||||
|
|
||||||
|
$border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
|
||||||
|
array(
|
||||||
|
new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
|
||||||
|
new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
|
||||||
|
));
|
||||||
|
|
||||||
|
$this->info['border-top-left-radius'] =
|
||||||
|
$this->info['border-top-right-radius'] =
|
||||||
|
$this->info['border-bottom-right-radius'] =
|
||||||
|
$this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
|
||||||
|
// TODO: support SLASH syntax
|
||||||
|
$this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,13 +38,19 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if li is not allowed, delete parent node
|
||||||
|
if (!isset($config->getHTMLDefinition()->info['li'])) {
|
||||||
|
trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// the new set of children
|
// the new set of children
|
||||||
$result = array();
|
$result = array();
|
||||||
|
|
||||||
// a little sanity check to make sure it's not ALL whitespace
|
// a little sanity check to make sure it's not ALL whitespace
|
||||||
$all_whitespace = true;
|
$all_whitespace = true;
|
||||||
|
|
||||||
$current_li = false;
|
$current_li = null;
|
||||||
|
|
||||||
foreach ($children as $node) {
|
foreach ($children as $node) {
|
||||||
if (!empty($node->is_whitespace)) {
|
if (!empty($node->is_whitespace)) {
|
||||||
@ -65,7 +71,7 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
|
|||||||
// to handle non-list elements; non-list elements should
|
// to handle non-list elements; non-list elements should
|
||||||
// not be appended to an existing li; only li created
|
// not be appended to an existing li; only li created
|
||||||
// for non-list. This distinction is not currently made.
|
// for non-list. This distinction is not currently made.
|
||||||
if ($current_li === false) {
|
if ($current_li === null) {
|
||||||
$current_li = new HTMLPurifier_Node_Element('li');
|
$current_li = new HTMLPurifier_Node_Element('li');
|
||||||
$result[] = $current_li;
|
$result[] = $current_li;
|
||||||
}
|
}
|
||||||
|
@ -203,7 +203,7 @@ class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
|
|||||||
$current_tr_tbody->children[] = $node;
|
$current_tr_tbody->children[] = $node;
|
||||||
break;
|
break;
|
||||||
case '#PCDATA':
|
case '#PCDATA':
|
||||||
assert($node->is_whitespace);
|
//assert($node->is_whitespace);
|
||||||
if ($current_tr_tbody === null) {
|
if ($current_tr_tbody === null) {
|
||||||
$ret[] = $node;
|
$ret[] = $node;
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,7 +21,7 @@ class HTMLPurifier_Config
|
|||||||
* HTML Purifier's version
|
* HTML Purifier's version
|
||||||
* @type string
|
* @type string
|
||||||
*/
|
*/
|
||||||
public $version = '4.7.0';
|
public $version = '4.9.3';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether or not to automatically finalize
|
* Whether or not to automatically finalize
|
||||||
@ -333,7 +333,7 @@ class HTMLPurifier_Config
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Raw type might be negative when using the fully optimized form
|
// Raw type might be negative when using the fully optimized form
|
||||||
// of stdclass, which indicates allow_null == true
|
// of stdClass, which indicates allow_null == true
|
||||||
$rtype = is_int($def) ? $def : $def->type;
|
$rtype = is_int($def) ? $def : $def->type;
|
||||||
if ($rtype < 0) {
|
if ($rtype < 0) {
|
||||||
$type = -$rtype;
|
$type = -$rtype;
|
||||||
|
@ -24,11 +24,11 @@ class HTMLPurifier_ConfigSchema
|
|||||||
*
|
*
|
||||||
* array(
|
* array(
|
||||||
* 'Namespace' => array(
|
* 'Namespace' => array(
|
||||||
* 'Directive' => new stdclass(),
|
* 'Directive' => new stdClass(),
|
||||||
* )
|
* )
|
||||||
* )
|
* )
|
||||||
*
|
*
|
||||||
* The stdclass may have the following properties:
|
* The stdClass may have the following properties:
|
||||||
*
|
*
|
||||||
* - If isAlias isn't set:
|
* - If isAlias isn't set:
|
||||||
* - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
|
* - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
|
||||||
@ -39,8 +39,8 @@ class HTMLPurifier_ConfigSchema
|
|||||||
* - namespace: Namespace this directive aliases to
|
* - namespace: Namespace this directive aliases to
|
||||||
* - name: Directive name this directive aliases to
|
* - name: Directive name this directive aliases to
|
||||||
*
|
*
|
||||||
* In certain degenerate cases, stdclass will actually be an integer. In
|
* In certain degenerate cases, stdClass will actually be an integer. In
|
||||||
* that case, the value is equivalent to an stdclass with the type
|
* that case, the value is equivalent to an stdClass with the type
|
||||||
* property set to the integer. If the integer is negative, type is
|
* property set to the integer. If the integer is negative, type is
|
||||||
* equal to the absolute value of integer, and allow_null is true.
|
* equal to the absolute value of integer, and allow_null is true.
|
||||||
*
|
*
|
||||||
@ -105,7 +105,7 @@ class HTMLPurifier_ConfigSchema
|
|||||||
*/
|
*/
|
||||||
public function add($key, $default, $type, $allow_null)
|
public function add($key, $default, $type, $allow_null)
|
||||||
{
|
{
|
||||||
$obj = new stdclass();
|
$obj = new stdClass();
|
||||||
$obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
|
$obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
|
||||||
if ($allow_null) {
|
if ($allow_null) {
|
||||||
$obj->allow_null = true;
|
$obj->allow_null = true;
|
||||||
@ -152,14 +152,14 @@ class HTMLPurifier_ConfigSchema
|
|||||||
*/
|
*/
|
||||||
public function addAlias($key, $new_key)
|
public function addAlias($key, $new_key)
|
||||||
{
|
{
|
||||||
$obj = new stdclass;
|
$obj = new stdClass;
|
||||||
$obj->key = $new_key;
|
$obj->key = $new_key;
|
||||||
$obj->isAlias = true;
|
$obj->isAlias = true;
|
||||||
$this->info[$key] = $obj;
|
$this->info[$key] = $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replaces any stdclass that only has the type property with type integer.
|
* Replaces any stdClass that only has the type property with type integer.
|
||||||
*/
|
*/
|
||||||
public function postProcess()
|
public function postProcess()
|
||||||
{
|
{
|
||||||
|
Binary file not shown.
@ -0,0 +1,10 @@
|
|||||||
|
Attr.ID.HTML5
|
||||||
|
TYPE: bool/null
|
||||||
|
DEFAULT: null
|
||||||
|
VERSION: 4.8.0
|
||||||
|
--DESCRIPTION--
|
||||||
|
In HTML5, restrictions on the format of the id attribute have been significantly
|
||||||
|
relaxed, such that any string is valid so long as it contains no spaces and
|
||||||
|
is at least one character. In lieu of a general HTML5 compatibility flag,
|
||||||
|
set this configuration directive to true to use the relaxed rules.
|
||||||
|
--# vim: et sw=4 sts=4
|
@ -0,0 +1,11 @@
|
|||||||
|
CSS.AllowDuplicates
|
||||||
|
TYPE: bool
|
||||||
|
DEFAULT: false
|
||||||
|
VERSION: 4.8.0
|
||||||
|
--DESCRIPTION--
|
||||||
|
<p>
|
||||||
|
By default, HTML Purifier removes duplicate CSS properties,
|
||||||
|
like <code>color:red; color:blue</code>. If this is set to
|
||||||
|
true, duplicate properties are allowed.
|
||||||
|
</p>
|
||||||
|
--# vim: et sw=4 sts=4
|
@ -1,5 +1,5 @@
|
|||||||
Cache.SerializerPermissions
|
Cache.SerializerPermissions
|
||||||
TYPE: int
|
TYPE: int/null
|
||||||
VERSION: 4.3.0
|
VERSION: 4.3.0
|
||||||
DEFAULT: 0755
|
DEFAULT: 0755
|
||||||
--DESCRIPTION--
|
--DESCRIPTION--
|
||||||
@ -8,4 +8,9 @@ DEFAULT: 0755
|
|||||||
Directory permissions of the files and directories created inside
|
Directory permissions of the files and directories created inside
|
||||||
the DefinitionCache/Serializer or other custom serializer path.
|
the DefinitionCache/Serializer or other custom serializer path.
|
||||||
</p>
|
</p>
|
||||||
|
<p>
|
||||||
|
In HTML Purifier 4.8.0, this also supports <code>NULL</code>,
|
||||||
|
which means that no chmod'ing or directory creation shall
|
||||||
|
occur.
|
||||||
|
</p>
|
||||||
--# vim: et sw=4 sts=4
|
--# vim: et sw=4 sts=4
|
||||||
|
@ -0,0 +1,16 @@
|
|||||||
|
Core.AggressivelyRemoveScript
|
||||||
|
TYPE: bool
|
||||||
|
VERSION: 4.9.0
|
||||||
|
DEFAULT: true
|
||||||
|
--DESCRIPTION--
|
||||||
|
<p>
|
||||||
|
This directive enables aggressive pre-filter removal of
|
||||||
|
script tags. This is not necessary for security,
|
||||||
|
but it can help work around a bug in libxml where embedded
|
||||||
|
HTML elements inside script sections cause the parser to
|
||||||
|
choke. To revert to pre-4.9.0 behavior, set this to false.
|
||||||
|
This directive has no effect if %Core.Trusted is true,
|
||||||
|
%Core.RemoveScriptContents is false, or %Core.HiddenElements
|
||||||
|
does not contain script.
|
||||||
|
</p>
|
||||||
|
--# vim: et sw=4 sts=4
|
@ -0,0 +1,36 @@
|
|||||||
|
Core.LegacyEntityDecoder
|
||||||
|
TYPE: bool
|
||||||
|
VERSION: 4.9.0
|
||||||
|
DEFAULT: false
|
||||||
|
--DESCRIPTION--
|
||||||
|
<p>
|
||||||
|
Prior to HTML Purifier 4.9.0, entities were decoded by performing
|
||||||
|
a global search replace for all entities whose decoded versions
|
||||||
|
did not have special meanings under HTML, and replaced them with
|
||||||
|
their decoded versions. We would match all entities, even if they did
|
||||||
|
not have a trailing semicolon, but only if there weren't any trailing
|
||||||
|
alphanumeric characters.
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
|
||||||
|
<tr><td>&yen;</td><td>¥</td><td>¥</td></tr>
|
||||||
|
<tr><td>&yen</td><td>¥</td><td>¥</td></tr>
|
||||||
|
<tr><td>&yena</td><td>&yena</td><td>&yena</td></tr>
|
||||||
|
<tr><td>&yen=</td><td>¥=</td><td>¥=</td></tr>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
In HTML Purifier 4.9.0, we changed the behavior of entity parsing
|
||||||
|
to match entities that had missing trailing semicolons in less
|
||||||
|
cases, to more closely match HTML5 parsing behavior:
|
||||||
|
</p>
|
||||||
|
<table>
|
||||||
|
<tr><th>Original</th><th>Text</th><th>Attribute</th></tr>
|
||||||
|
<tr><td>&yen;</td><td>¥</td><td>¥</td></tr>
|
||||||
|
<tr><td>&yen</td><td>¥</td><td>¥</td></tr>
|
||||||
|
<tr><td>&yena</td><td>¥a</td><td>&yena</td></tr>
|
||||||
|
<tr><td>&yen=</td><td>¥=</td><td>&yen=</td></tr>
|
||||||
|
</table>
|
||||||
|
<p>
|
||||||
|
This flag reverts back to pre-HTML Purifier 4.9.0 behavior.
|
||||||
|
</p>
|
||||||
|
--# vim: et sw=4 sts=4
|
@ -0,0 +1,10 @@
|
|||||||
|
--# vim: et sw=4 sts=4
|
||||||
|
HTML.TargetNoopener
|
||||||
|
TYPE: bool
|
||||||
|
VERSION: 4.8.0
|
||||||
|
DEFAULT: TRUE
|
||||||
|
--DESCRIPTION--
|
||||||
|
If enabled, noopener rel attributes are added to links which have
|
||||||
|
a target attribute associated with them. This prevents malicious
|
||||||
|
destinations from overwriting the original window.
|
||||||
|
--# vim: et sw=4 sts=4
|
@ -0,0 +1,9 @@
|
|||||||
|
HTML.TargetNoreferrer
|
||||||
|
TYPE: bool
|
||||||
|
VERSION: 4.8.0
|
||||||
|
DEFAULT: TRUE
|
||||||
|
--DESCRIPTION--
|
||||||
|
If enabled, noreferrer rel attributes are added to links which have
|
||||||
|
a target attribute associated with them. This prevents malicious
|
||||||
|
destinations from overwriting the original window.
|
||||||
|
--# vim: et sw=4 sts=4
|
@ -8,6 +8,7 @@ array (
|
|||||||
'ftp' => true,
|
'ftp' => true,
|
||||||
'nntp' => true,
|
'nntp' => true,
|
||||||
'news' => true,
|
'news' => true,
|
||||||
|
'tel' => true,
|
||||||
)
|
)
|
||||||
--DESCRIPTION--
|
--DESCRIPTION--
|
||||||
Whitelist that defines the schemes that a URI is allowed to have. This
|
Whitelist that defines the schemes that a URI is allowed to have. This
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
URI.DefaultScheme
|
URI.DefaultScheme
|
||||||
TYPE: string
|
TYPE: string/null
|
||||||
DEFAULT: 'http'
|
DEFAULT: 'http'
|
||||||
--DESCRIPTION--
|
--DESCRIPTION--
|
||||||
|
|
||||||
@ -7,4 +7,9 @@ DEFAULT: 'http'
|
|||||||
Defines through what scheme the output will be served, in order to
|
Defines through what scheme the output will be served, in order to
|
||||||
select the proper object validator when no scheme information is present.
|
select the proper object validator when no scheme information is present.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Starting with HTML Purifier 4.9.0, the default scheme can be null, in
|
||||||
|
which case we reject all URIs which do not have explicit schemes.
|
||||||
|
</p>
|
||||||
--# vim: et sw=4 sts=4
|
--# vim: et sw=4 sts=4
|
||||||
|
@ -9,75 +9,75 @@ DEFAULT: NULL
|
|||||||
absolute URIs into another URI, usually a URI redirection service.
|
absolute URIs into another URI, usually a URI redirection service.
|
||||||
This directive accepts a URI, formatted with a <code>%s</code> where
|
This directive accepts a URI, formatted with a <code>%s</code> where
|
||||||
the url-encoded original URI should be inserted (sample:
|
the url-encoded original URI should be inserted (sample:
|
||||||
<code>https://searx.laquadrature.net/?q=%s</code>).
|
<code>http://www.google.com/url?q=%s</code>).
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Uses for this directive:
|
Uses for this directive:
|
||||||
</p>
|
</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li>
|
||||||
Prevent PageRank leaks, while being fairly transparent
|
Prevent PageRank leaks, while being fairly transparent
|
||||||
to users (you may also want to add some client side JavaScript to
|
to users (you may also want to add some client side JavaScript to
|
||||||
override the text in the statusbar). <strong>Notice</strong>:
|
override the text in the statusbar). <strong>Notice</strong>:
|
||||||
Many security experts believe that this form of protection does not deter spam-bots.
|
Many security experts believe that this form of protection does not deter spam-bots.
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
Redirect users to a splash page telling them they are leaving your
|
Redirect users to a splash page telling them they are leaving your
|
||||||
website. While this is poor usability practice, it is often mandated
|
website. While this is poor usability practice, it is often mandated
|
||||||
in corporate environments.
|
in corporate environments.
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>
|
<p>
|
||||||
Prior to HTML Purifier 3.1.1, this directive also enabled the munging
|
Prior to HTML Purifier 3.1.1, this directive also enabled the munging
|
||||||
of browsable external resources, which could break things if your redirection
|
of browsable external resources, which could break things if your redirection
|
||||||
script was a splash page or used <code>meta</code> tags. To revert to
|
script was a splash page or used <code>meta</code> tags. To revert to
|
||||||
previous behavior, please use %URI.MungeResources.
|
previous behavior, please use %URI.MungeResources.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
You may want to also use %URI.MungeSecretKey along with this directive
|
You may want to also use %URI.MungeSecretKey along with this directive
|
||||||
in order to enforce what URIs your redirector script allows. Open
|
in order to enforce what URIs your redirector script allows. Open
|
||||||
redirector scripts can be a security risk and negatively affect the
|
redirector scripts can be a security risk and negatively affect the
|
||||||
reputation of your domain name.
|
reputation of your domain name.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Starting with HTML Purifier 3.1.1, there is also these substitutions:
|
Starting with HTML Purifier 3.1.1, there is also these substitutions:
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Key</th>
|
<th>Key</th>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
<th>Example <code><a href=""></code></th>
|
<th>Example <code><a href=""></code></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr>
|
||||||
<td>%r</td>
|
<td>%r</td>
|
||||||
<td>1 - The URI embeds a resource<br />(blank) - The URI is merely a link</td>
|
<td>1 - The URI embeds a resource<br />(blank) - The URI is merely a link</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>%n</td>
|
<td>%n</td>
|
||||||
<td>The name of the tag this URI came from</td>
|
<td>The name of the tag this URI came from</td>
|
||||||
<td>a</td>
|
<td>a</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>%m</td>
|
<td>%m</td>
|
||||||
<td>The name of the attribute this URI came from</td>
|
<td>The name of the attribute this URI came from</td>
|
||||||
<td>href</td>
|
<td>href</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>%p</td>
|
<td>%p</td>
|
||||||
<td>The name of the CSS property this URI came from, or blank if irrelevant</td>
|
<td>The name of the CSS property this URI came from, or blank if irrelevant</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<p>
|
<p>
|
||||||
Admittedly, these letters are somewhat arbitrary; the only stipulation
|
Admittedly, these letters are somewhat arbitrary; the only stipulation
|
||||||
was that they couldn't be a through f. r is for resource (I would have preferred
|
was that they couldn't be a through f. r is for resource (I would have preferred
|
||||||
e, but you take what you can get), n is for name, m
|
e, but you take what you can get), n is for name, m
|
||||||
was picked because it came after n (and I couldn't use a), p is for
|
was picked because it came after n (and I couldn't use a), p is for
|
||||||
property.
|
property.
|
||||||
</p>
|
</p>
|
||||||
--# vim: et sw=4 sts=4
|
--# vim: et sw=4 sts=4
|
||||||
|
@ -118,7 +118,7 @@ abstract class HTMLPurifier_DefinitionCache
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears all expired (older version or revision) objects from cache
|
* Clears all expired (older version or revision) objects from cache
|
||||||
* @note Be carefuly implementing this method as flush. Flush must
|
* @note Be careful implementing this method as flush. Flush must
|
||||||
* not interfere with other Definition types, and cleanup()
|
* not interfere with other Definition types, and cleanup()
|
||||||
* should not be repeatedly called by userland code.
|
* should not be repeatedly called by userland code.
|
||||||
* @param HTMLPurifier_Config $config
|
* @param HTMLPurifier_Config $config
|
||||||
|
@ -97,6 +97,12 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
}
|
}
|
||||||
$dir = $this->generateDirectoryPath($config);
|
$dir = $this->generateDirectoryPath($config);
|
||||||
$dh = opendir($dir);
|
$dh = opendir($dir);
|
||||||
|
// Apparently, on some versions of PHP, readdir will return
|
||||||
|
// an empty string if you pass an invalid argument to readdir.
|
||||||
|
// So you need this test. See #49.
|
||||||
|
if (false === $dh) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
while (false !== ($filename = readdir($dh))) {
|
while (false !== ($filename = readdir($dh))) {
|
||||||
if (empty($filename)) {
|
if (empty($filename)) {
|
||||||
continue;
|
continue;
|
||||||
@ -106,6 +112,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
}
|
}
|
||||||
unlink($dir . '/' . $filename);
|
unlink($dir . '/' . $filename);
|
||||||
}
|
}
|
||||||
|
closedir($dh);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -119,6 +127,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
}
|
}
|
||||||
$dir = $this->generateDirectoryPath($config);
|
$dir = $this->generateDirectoryPath($config);
|
||||||
$dh = opendir($dir);
|
$dh = opendir($dir);
|
||||||
|
// See #49 (and above).
|
||||||
|
if (false === $dh) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
while (false !== ($filename = readdir($dh))) {
|
while (false !== ($filename = readdir($dh))) {
|
||||||
if (empty($filename)) {
|
if (empty($filename)) {
|
||||||
continue;
|
continue;
|
||||||
@ -131,6 +143,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
unlink($dir . '/' . $filename);
|
unlink($dir . '/' . $filename);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
closedir($dh);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -186,11 +200,9 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
if ($result !== false) {
|
if ($result !== false) {
|
||||||
// set permissions of the new file (no execute)
|
// set permissions of the new file (no execute)
|
||||||
$chmod = $config->get('Cache.SerializerPermissions');
|
$chmod = $config->get('Cache.SerializerPermissions');
|
||||||
if (!$chmod) {
|
if ($chmod !== null) {
|
||||||
$chmod = 0644; // invalid config or simpletest
|
chmod($file, $chmod & 0666);
|
||||||
}
|
}
|
||||||
$chmod = $chmod & 0666;
|
|
||||||
chmod($file, $chmod);
|
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
@ -204,8 +216,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
{
|
{
|
||||||
$directory = $this->generateDirectoryPath($config);
|
$directory = $this->generateDirectoryPath($config);
|
||||||
$chmod = $config->get('Cache.SerializerPermissions');
|
$chmod = $config->get('Cache.SerializerPermissions');
|
||||||
if (!$chmod) {
|
if ($chmod === null) {
|
||||||
$chmod = 0755; // invalid config or simpletest
|
// TODO: This races
|
||||||
|
if (is_dir($directory)) return true;
|
||||||
|
return mkdir($directory);
|
||||||
}
|
}
|
||||||
if (!is_dir($directory)) {
|
if (!is_dir($directory)) {
|
||||||
$base = $this->generateBaseDirectoryPath($config);
|
$base = $this->generateBaseDirectoryPath($config);
|
||||||
@ -219,15 +233,16 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
} elseif (!$this->_testPermissions($base, $chmod)) {
|
} elseif (!$this->_testPermissions($base, $chmod)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mkdir($directory, $chmod);
|
if (!mkdir($directory, $chmod)) {
|
||||||
if (!$this->_testPermissions($directory, $chmod)) {
|
|
||||||
trigger_error(
|
trigger_error(
|
||||||
'Base directory ' . $base . ' does not exist,
|
'Could not create directory ' . $directory . '',
|
||||||
please create or change using %Cache.SerializerPath',
|
|
||||||
E_USER_WARNING
|
E_USER_WARNING
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!$this->_testPermissions($directory, $chmod)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
} elseif (!$this->_testPermissions($directory, $chmod)) {
|
} elseif (!$this->_testPermissions($directory, $chmod)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -256,7 +271,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
|
|||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (function_exists('posix_getuid')) {
|
if (function_exists('posix_getuid') && $chmod !== null) {
|
||||||
// POSIX system, we can give more specific advice
|
// POSIX system, we can give more specific advice
|
||||||
if (fileowner($dir) === posix_getuid()) {
|
if (fileowner($dir) === posix_getuid()) {
|
||||||
// we can chmod it ourselves
|
// we can chmod it ourselves
|
||||||
|
@ -101,6 +101,14 @@ class HTMLPurifier_Encoder
|
|||||||
* It will parse according to UTF-8 and return a valid UTF8 string, with
|
* It will parse according to UTF-8 and return a valid UTF8 string, with
|
||||||
* non-SGML codepoints excluded.
|
* non-SGML codepoints excluded.
|
||||||
*
|
*
|
||||||
|
* Specifically, it will permit:
|
||||||
|
* \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}
|
||||||
|
* Source: https://www.w3.org/TR/REC-xml/#NT-Char
|
||||||
|
* Arguably this function should be modernized to the HTML5 set
|
||||||
|
* of allowed characters:
|
||||||
|
* https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream
|
||||||
|
* which simultaneously expand and restrict the set of allowed characters.
|
||||||
|
*
|
||||||
* @param string $str The string to clean
|
* @param string $str The string to clean
|
||||||
* @param bool $force_php
|
* @param bool $force_php
|
||||||
* @return string
|
* @return string
|
||||||
@ -122,15 +130,12 @@ class HTMLPurifier_Encoder
|
|||||||
* function that needs to be able to understand UTF-8 characters.
|
* function that needs to be able to understand UTF-8 characters.
|
||||||
* As of right now, only smart lossless character encoding converters
|
* As of right now, only smart lossless character encoding converters
|
||||||
* would need that, and I'm probably not going to implement them.
|
* would need that, and I'm probably not going to implement them.
|
||||||
* Once again, PHP 6 should solve all our problems.
|
|
||||||
*/
|
*/
|
||||||
public static function cleanUTF8($str, $force_php = false)
|
public static function cleanUTF8($str, $force_php = false)
|
||||||
{
|
{
|
||||||
// UTF-8 validity is checked since PHP 4.3.5
|
// UTF-8 validity is checked since PHP 4.3.5
|
||||||
// This is an optimization: if the string is already valid UTF-8, no
|
// This is an optimization: if the string is already valid UTF-8, no
|
||||||
// need to do PHP stuff. 99% of the time, this will be the case.
|
// need to do PHP stuff. 99% of the time, this will be the case.
|
||||||
// The regexp matches the XML char production, as well as well as excluding
|
|
||||||
// non-SGML codepoints U+007F to U+009F
|
|
||||||
if (preg_match(
|
if (preg_match(
|
||||||
'/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
|
'/^[\x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}]*$/Du',
|
||||||
$str
|
$str
|
||||||
@ -255,6 +260,7 @@ class HTMLPurifier_Encoder
|
|||||||
// 7F-9F is not strictly prohibited by XML,
|
// 7F-9F is not strictly prohibited by XML,
|
||||||
// but it is non-SGML, and thus we don't allow it
|
// but it is non-SGML, and thus we don't allow it
|
||||||
(0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
|
(0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
|
||||||
|
(0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) ||
|
||||||
(0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
|
(0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
@ -16,6 +16,138 @@ class HTMLPurifier_EntityParser
|
|||||||
*/
|
*/
|
||||||
protected $_entity_lookup;
|
protected $_entity_lookup;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback regex string for entities in text.
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
protected $_textEntitiesRegex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback regex string for entities in attributes.
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
protected $_attrEntitiesRegex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests if the beginning of a string is a semi-optional regex
|
||||||
|
*/
|
||||||
|
protected $_semiOptionalPrefixRegex;
|
||||||
|
|
||||||
|
public function __construct() {
|
||||||
|
// From
|
||||||
|
// http://stackoverflow.com/questions/15532252/why-is-reg-being-rendered-as-without-the-bounding-semicolon
|
||||||
|
$semi_optional = "quot|QUOT|lt|LT|gt|GT|amp|AMP|AElig|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|Iacute|Icirc|Igrave|Iuml|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml";
|
||||||
|
|
||||||
|
// NB: three empty captures to put the fourth match in the right
|
||||||
|
// place
|
||||||
|
$this->_semiOptionalPrefixRegex = "/&()()()($semi_optional)/";
|
||||||
|
|
||||||
|
$this->_textEntitiesRegex =
|
||||||
|
'/&(?:'.
|
||||||
|
// hex
|
||||||
|
'[#]x([a-fA-F0-9]+);?|'.
|
||||||
|
// dec
|
||||||
|
'[#]0*(\d+);?|'.
|
||||||
|
// string (mandatory semicolon)
|
||||||
|
// NB: order matters: match semicolon preferentially
|
||||||
|
'([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
|
||||||
|
// string (optional semicolon)
|
||||||
|
"($semi_optional)".
|
||||||
|
')/';
|
||||||
|
|
||||||
|
$this->_attrEntitiesRegex =
|
||||||
|
'/&(?:'.
|
||||||
|
// hex
|
||||||
|
'[#]x([a-fA-F0-9]+);?|'.
|
||||||
|
// dec
|
||||||
|
'[#]0*(\d+);?|'.
|
||||||
|
// string (mandatory semicolon)
|
||||||
|
// NB: order matters: match semicolon preferentially
|
||||||
|
'([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
|
||||||
|
// string (optional semicolon)
|
||||||
|
// don't match if trailing is equals or alphanumeric (URL
|
||||||
|
// like)
|
||||||
|
"($semi_optional)(?![=;A-Za-z0-9])".
|
||||||
|
')/';
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Substitute entities with the parsed equivalents. Use this on
|
||||||
|
* textual data in an HTML document (as opposed to attributes.)
|
||||||
|
*
|
||||||
|
* @param string $string String to have entities parsed.
|
||||||
|
* @return string Parsed string.
|
||||||
|
*/
|
||||||
|
public function substituteTextEntities($string)
|
||||||
|
{
|
||||||
|
return preg_replace_callback(
|
||||||
|
$this->_textEntitiesRegex,
|
||||||
|
array($this, 'entityCallback'),
|
||||||
|
$string
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Substitute entities with the parsed equivalents. Use this on
|
||||||
|
* attribute contents in documents.
|
||||||
|
*
|
||||||
|
* @param string $string String to have entities parsed.
|
||||||
|
* @return string Parsed string.
|
||||||
|
*/
|
||||||
|
public function substituteAttrEntities($string)
|
||||||
|
{
|
||||||
|
return preg_replace_callback(
|
||||||
|
$this->_attrEntitiesRegex,
|
||||||
|
array($this, 'entityCallback'),
|
||||||
|
$string
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for substituteNonSpecialEntities() that does the work.
|
||||||
|
*
|
||||||
|
* @param array $matches PCRE matches array, with 0 the entire match, and
|
||||||
|
* either index 1, 2 or 3 set with a hex value, dec value,
|
||||||
|
* or string (respectively).
|
||||||
|
* @return string Replacement string.
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function entityCallback($matches)
|
||||||
|
{
|
||||||
|
$entity = $matches[0];
|
||||||
|
$hex_part = @$matches[1];
|
||||||
|
$dec_part = @$matches[2];
|
||||||
|
$named_part = empty($matches[3]) ? @$matches[4] : $matches[3];
|
||||||
|
if ($hex_part !== NULL && $hex_part !== "") {
|
||||||
|
return HTMLPurifier_Encoder::unichr(hexdec($hex_part));
|
||||||
|
} elseif ($dec_part !== NULL && $dec_part !== "") {
|
||||||
|
return HTMLPurifier_Encoder::unichr((int) $dec_part);
|
||||||
|
} else {
|
||||||
|
if (!$this->_entity_lookup) {
|
||||||
|
$this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
|
||||||
|
}
|
||||||
|
if (isset($this->_entity_lookup->table[$named_part])) {
|
||||||
|
return $this->_entity_lookup->table[$named_part];
|
||||||
|
} else {
|
||||||
|
// exact match didn't match anything, so test if
|
||||||
|
// any of the semicolon optional match the prefix.
|
||||||
|
// Test that this is an EXACT match is important to
|
||||||
|
// prevent infinite loop
|
||||||
|
if (!empty($matches[3])) {
|
||||||
|
return preg_replace_callback(
|
||||||
|
$this->_semiOptionalPrefixRegex,
|
||||||
|
array($this, 'entityCallback'),
|
||||||
|
$entity
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LEGACY CODE BELOW
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback regex string for parsing entities.
|
* Callback regex string for parsing entities.
|
||||||
* @type string
|
* @type string
|
||||||
@ -144,7 +276,7 @@ class HTMLPurifier_EntityParser
|
|||||||
$entity;
|
$entity;
|
||||||
} else {
|
} else {
|
||||||
return isset($this->_special_ent2dec[$matches[3]]) ?
|
return isset($this->_special_ent2dec[$matches[3]]) ?
|
||||||
$this->_special_ent2dec[$matches[3]] :
|
$this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] :
|
||||||
$entity;
|
$entity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -95,7 +95,10 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
|
|||||||
if ($tidy !== null) {
|
if ($tidy !== null) {
|
||||||
$this->_tidy = $tidy;
|
$this->_tidy = $tidy;
|
||||||
}
|
}
|
||||||
$html = preg_replace_callback('#<style(?:\s.*)?>(.+)</style>#isU', array($this, 'styleCallback'), $html);
|
// NB: this must be NON-greedy because if we have
|
||||||
|
// <style>foo</style> <style>bar</style>
|
||||||
|
// we must not grab foo</style> <style>bar
|
||||||
|
$html = preg_replace_callback('#<style(?:\s.*)?>(.*)<\/style>#isU', array($this, 'styleCallback'), $html);
|
||||||
$style_blocks = $this->_styleMatches;
|
$style_blocks = $this->_styleMatches;
|
||||||
$this->_styleMatches = array(); // reset
|
$this->_styleMatches = array(); // reset
|
||||||
$context->register('StyleBlocks', $style_blocks); // $context must not be reused
|
$context->register('StyleBlocks', $style_blocks); // $context must not be reused
|
||||||
|
@ -146,7 +146,7 @@ class HTMLPurifier_Generator
|
|||||||
$attr = $this->generateAttributes($token->attr, $token->name);
|
$attr = $this->generateAttributes($token->attr, $token->name);
|
||||||
if ($this->_flashCompat) {
|
if ($this->_flashCompat) {
|
||||||
if ($token->name == "object") {
|
if ($token->name == "object") {
|
||||||
$flash = new stdclass();
|
$flash = new stdClass();
|
||||||
$flash->attr = $token->attr;
|
$flash->attr = $token->attr;
|
||||||
$flash->param = array();
|
$flash->param = array();
|
||||||
$this->_flashStack[] = $flash;
|
$this->_flashStack[] = $flash;
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module adds the target-based noopener attribute transformation to a tags. It
|
||||||
|
* is enabled by HTML.TargetNoopener
|
||||||
|
*/
|
||||||
|
class HTMLPurifier_HTMLModule_TargetNoopener extends HTMLPurifier_HTMLModule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
public $name = 'TargetNoopener';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HTMLPurifier_Config $config
|
||||||
|
*/
|
||||||
|
public function setup($config) {
|
||||||
|
$a = $this->addBlankElement('a');
|
||||||
|
$a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoopener();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Module adds the target-based noreferrer attribute transformation to a tags. It
|
||||||
|
* is enabled by HTML.TargetNoreferrer
|
||||||
|
*/
|
||||||
|
class HTMLPurifier_HTMLModule_TargetNoreferrer extends HTMLPurifier_HTMLModule
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @type string
|
||||||
|
*/
|
||||||
|
public $name = 'TargetNoreferrer';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HTMLPurifier_Config $config
|
||||||
|
*/
|
||||||
|
public function setup($config) {
|
||||||
|
$a = $this->addBlankElement('a');
|
||||||
|
$a->attr_transform_post[] = new HTMLPurifier_AttrTransform_TargetNoreferrer();
|
||||||
|
}
|
||||||
|
}
|
@ -271,6 +271,14 @@ class HTMLPurifier_HTMLModuleManager
|
|||||||
if ($config->get('HTML.TargetBlank')) {
|
if ($config->get('HTML.TargetBlank')) {
|
||||||
$modules[] = 'TargetBlank';
|
$modules[] = 'TargetBlank';
|
||||||
}
|
}
|
||||||
|
// NB: HTML.TargetNoreferrer and HTML.TargetNoopener must be AFTER HTML.TargetBlank
|
||||||
|
// so that its post-attr-transform gets run afterwards.
|
||||||
|
if ($config->get('HTML.TargetNoreferrer')) {
|
||||||
|
$modules[] = 'TargetNoreferrer';
|
||||||
|
}
|
||||||
|
if ($config->get('HTML.TargetNoopener')) {
|
||||||
|
$modules[] = 'TargetNoopener';
|
||||||
|
}
|
||||||
|
|
||||||
// merge in custom modules
|
// merge in custom modules
|
||||||
$modules = array_merge($modules, $this->userModules);
|
$modules = array_merge($modules, $this->userModules);
|
||||||
|
@ -27,13 +27,18 @@ class HTMLPurifier_Injector_Linkify extends HTMLPurifier_Injector
|
|||||||
if (strpos($token->data, '://') === false) {
|
if (strpos($token->data, '://') === false) {
|
||||||
// our really quick heuristic failed, abort
|
// our really quick heuristic failed, abort
|
||||||
// this may not work so well if we want to match things like
|
// this may not work so well if we want to match things like
|
||||||
// "domainname.com", but then again, most people don't
|
// "google.com", but then again, most people don't
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// there is/are URL(s). Let's split the string:
|
// there is/are URL(s). Let's split the string.
|
||||||
// Note: this regex is extremely permissive
|
// We use this regex:
|
||||||
$bits = preg_split('#((?:https?|ftp)://[^\s\'",<>()]+)#Su', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
|
// https://gist.github.com/gruber/249502
|
||||||
|
// but with @cscott's backtracking fix and also
|
||||||
|
// the Unicode characters un-Unicodified.
|
||||||
|
$bits = preg_split(
|
||||||
|
'/\\b((?:[a-z][\\w\\-]+:(?:\\/{1,3}|[a-z0-9%])|www\\d{0,3}[.]|[a-z0-9.\\-]+[.][a-z]{2,4}\\/)(?:[^\\s()<>]|\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\))+(?:\\((?:[^\\s()<>]|(?:\\([^\\s()<>]+\\)))*\\)|[^\\s`!()\\[\\]{};:\'".,<>?\x{00ab}\x{00bb}\x{201c}\x{201d}\x{2018}\x{2019}]))/iu',
|
||||||
|
$token->data, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||||
|
|
||||||
|
|
||||||
$token = array();
|
$token = array();
|
||||||
|
@ -46,6 +46,12 @@ class HTMLPurifier_Injector_RemoveEmpty extends HTMLPurifier_Injector
|
|||||||
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
|
$this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp');
|
||||||
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
|
$this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions');
|
||||||
$this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
|
$this->exclude = $config->get('AutoFormat.RemoveEmpty.Predicate');
|
||||||
|
foreach ($this->exclude as $key => $attrs) {
|
||||||
|
if (!is_array($attrs)) {
|
||||||
|
// HACK, see HTMLPurifier/Printer/ConfigForm.php
|
||||||
|
$this->exclude[$key] = explode(';', $attrs);
|
||||||
|
}
|
||||||
|
}
|
||||||
$this->attrValidator = new HTMLPurifier_AttrValidator();
|
$this->attrValidator = new HTMLPurifier_AttrValidator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +36,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
|
|||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* These are all lower-case keys.
|
||||||
* @type array
|
* @type array
|
||||||
*/
|
*/
|
||||||
protected $allowedParam = array(
|
protected $allowedParam = array(
|
||||||
@ -43,7 +44,7 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
|
|||||||
'movie' => true,
|
'movie' => true,
|
||||||
'flashvars' => true,
|
'flashvars' => true,
|
||||||
'src' => true,
|
'src' => true,
|
||||||
'allowFullScreen' => true, // if omitted, assume to be 'false'
|
'allowfullscreen' => true, // if omitted, assume to be 'false'
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -93,9 +94,11 @@ class HTMLPurifier_Injector_SafeObject extends HTMLPurifier_Injector
|
|||||||
$token->attr['name'] === $this->addParam[$n]) {
|
$token->attr['name'] === $this->addParam[$n]) {
|
||||||
// keep token, and add to param stack
|
// keep token, and add to param stack
|
||||||
$this->paramStack[$i][$n] = true;
|
$this->paramStack[$i][$n] = true;
|
||||||
} elseif (isset($this->allowedParam[$n])) {
|
} elseif (isset($this->allowedParam[strtolower($n)])) {
|
||||||
// keep token, don't do anything to it
|
// keep token, don't do anything to it
|
||||||
// (could possibly check for duplicates here)
|
// (could possibly check for duplicates here)
|
||||||
|
// Note: In principle, parameters should be case sensitive.
|
||||||
|
// But it seems they are not really; so accept any case.
|
||||||
} else {
|
} else {
|
||||||
$token = false;
|
$token = false;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ class HTMLPurifier_Lexer
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_exists('DOMDocument') &&
|
if (class_exists('DOMDocument', false) &&
|
||||||
method_exists('DOMDocument', 'loadHTML') &&
|
method_exists('DOMDocument', 'loadHTML') &&
|
||||||
!extension_loaded('domxml')
|
!extension_loaded('domxml')
|
||||||
) {
|
) {
|
||||||
@ -169,21 +169,24 @@ class HTMLPurifier_Lexer
|
|||||||
''' => "'"
|
''' => "'"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
public function parseText($string, $config) {
|
||||||
|
return $this->parseData($string, false, $config);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function parseAttr($string, $config) {
|
||||||
|
return $this->parseData($string, true, $config);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses special entities into the proper characters.
|
* Parses special entities into the proper characters.
|
||||||
*
|
*
|
||||||
* This string will translate escaped versions of the special characters
|
* This string will translate escaped versions of the special characters
|
||||||
* into the correct ones.
|
* into the correct ones.
|
||||||
*
|
*
|
||||||
* @warning
|
|
||||||
* You should be able to treat the output of this function as
|
|
||||||
* completely parsed, but that's only because all other entities should
|
|
||||||
* have been handled previously in substituteNonSpecialEntities()
|
|
||||||
*
|
|
||||||
* @param string $string String character data to be parsed.
|
* @param string $string String character data to be parsed.
|
||||||
* @return string Parsed character data.
|
* @return string Parsed character data.
|
||||||
*/
|
*/
|
||||||
public function parseData($string)
|
public function parseData($string, $is_attr, $config)
|
||||||
{
|
{
|
||||||
// following functions require at least one character
|
// following functions require at least one character
|
||||||
if ($string === '') {
|
if ($string === '') {
|
||||||
@ -209,7 +212,15 @@ class HTMLPurifier_Lexer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// hmm... now we have some uncommon entities. Use the callback.
|
// hmm... now we have some uncommon entities. Use the callback.
|
||||||
$string = $this->_entity_parser->substituteSpecialEntities($string);
|
if ($config->get('Core.LegacyEntityDecoder')) {
|
||||||
|
$string = $this->_entity_parser->substituteSpecialEntities($string);
|
||||||
|
} else {
|
||||||
|
if ($is_attr) {
|
||||||
|
$string = $this->_entity_parser->substituteAttrEntities($string);
|
||||||
|
} else {
|
||||||
|
$string = $this->_entity_parser->substituteTextEntities($string);
|
||||||
|
}
|
||||||
|
}
|
||||||
return $string;
|
return $string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -323,7 +334,9 @@ class HTMLPurifier_Lexer
|
|||||||
}
|
}
|
||||||
|
|
||||||
// expand entities that aren't the big five
|
// expand entities that aren't the big five
|
||||||
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
|
if ($config->get('Core.LegacyEntityDecoder')) {
|
||||||
|
$html = $this->_entity_parser->substituteNonSpecialEntities($html);
|
||||||
|
}
|
||||||
|
|
||||||
// clean into wellformed UTF-8 string for an SGML context: this has
|
// clean into wellformed UTF-8 string for an SGML context: this has
|
||||||
// to be done after entity expansion because the entities sometimes
|
// to be done after entity expansion because the entities sometimes
|
||||||
@ -335,6 +348,13 @@ class HTMLPurifier_Lexer
|
|||||||
$html = preg_replace('#<\?.+?\?>#s', '', $html);
|
$html = preg_replace('#<\?.+?\?>#s', '', $html);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$hidden_elements = $config->get('Core.HiddenElements');
|
||||||
|
if ($config->get('Core.AggressivelyRemoveScript') &&
|
||||||
|
!($config->get('HTML.Trusted') || !$config->get('Core.RemoveScriptContents')
|
||||||
|
|| empty($hidden_elements["script"]))) {
|
||||||
|
$html = preg_replace('#<script[^>]*>.*?</script>#i', '', $html);
|
||||||
|
}
|
||||||
|
|
||||||
return $html;
|
return $html;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -345,12 +365,17 @@ class HTMLPurifier_Lexer
|
|||||||
public function extractBody($html)
|
public function extractBody($html)
|
||||||
{
|
{
|
||||||
$matches = array();
|
$matches = array();
|
||||||
$result = preg_match('!<body[^>]*>(.*)</body>!is', $html, $matches);
|
$result = preg_match('|(.*?)<body[^>]*>(.*)</body>|is', $html, $matches);
|
||||||
if ($result) {
|
if ($result) {
|
||||||
return $matches[1];
|
// Make sure it's not in a comment
|
||||||
} else {
|
$comment_start = strrpos($matches[1], '<!--');
|
||||||
return $html;
|
$comment_end = strrpos($matches[1], '-->');
|
||||||
|
if ($comment_start === false ||
|
||||||
|
($comment_end !== false && $comment_end > $comment_start)) {
|
||||||
|
return $matches[2];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return $html;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,12 +72,20 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
$doc->loadHTML($html);
|
$doc->loadHTML($html);
|
||||||
restore_error_handler();
|
restore_error_handler();
|
||||||
|
|
||||||
|
$body = $doc->getElementsByTagName('html')->item(0)-> // <html>
|
||||||
|
getElementsByTagName('body')->item(0); // <body>
|
||||||
|
|
||||||
|
$div = $body->getElementsByTagName('div')->item(0); // <div>
|
||||||
$tokens = array();
|
$tokens = array();
|
||||||
$this->tokenizeDOM(
|
$this->tokenizeDOM($div, $tokens, $config);
|
||||||
$doc->getElementsByTagName('html')->item(0)-> // <html>
|
// If the div has a sibling, that means we tripped across
|
||||||
getElementsByTagName('body')->item(0), // <body>
|
// a premature </div> tag. So remove the div we parsed,
|
||||||
$tokens
|
// and then tokenize the rest of body. We can't tokenize
|
||||||
);
|
// the sibling directly as we'll lose the tags in that case.
|
||||||
|
if ($div->nextSibling) {
|
||||||
|
$body->removeChild($div);
|
||||||
|
$this->tokenizeDOM($body, $tokens, $config);
|
||||||
|
}
|
||||||
return $tokens;
|
return $tokens;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,7 +96,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
* @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens.
|
* @param HTMLPurifier_Token[] $tokens Array-list of already tokenized tokens.
|
||||||
* @return HTMLPurifier_Token of node appended to previously passed tokens.
|
* @return HTMLPurifier_Token of node appended to previously passed tokens.
|
||||||
*/
|
*/
|
||||||
protected function tokenizeDOM($node, &$tokens)
|
protected function tokenizeDOM($node, &$tokens, $config)
|
||||||
{
|
{
|
||||||
$level = 0;
|
$level = 0;
|
||||||
$nodes = array($level => new HTMLPurifier_Queue(array($node)));
|
$nodes = array($level => new HTMLPurifier_Queue(array($node)));
|
||||||
@ -97,7 +105,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
while (!$nodes[$level]->isEmpty()) {
|
while (!$nodes[$level]->isEmpty()) {
|
||||||
$node = $nodes[$level]->shift(); // FIFO
|
$node = $nodes[$level]->shift(); // FIFO
|
||||||
$collect = $level > 0 ? true : false;
|
$collect = $level > 0 ? true : false;
|
||||||
$needEndingTag = $this->createStartNode($node, $tokens, $collect);
|
$needEndingTag = $this->createStartNode($node, $tokens, $collect, $config);
|
||||||
if ($needEndingTag) {
|
if ($needEndingTag) {
|
||||||
$closingNodes[$level][] = $node;
|
$closingNodes[$level][] = $node;
|
||||||
}
|
}
|
||||||
@ -127,7 +135,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
* @return bool if the token needs an endtoken
|
* @return bool if the token needs an endtoken
|
||||||
* @todo data and tagName properties don't seem to exist in DOMNode?
|
* @todo data and tagName properties don't seem to exist in DOMNode?
|
||||||
*/
|
*/
|
||||||
protected function createStartNode($node, &$tokens, $collect)
|
protected function createStartNode($node, &$tokens, $collect, $config)
|
||||||
{
|
{
|
||||||
// intercept non element nodes. WE MUST catch all of them,
|
// intercept non element nodes. WE MUST catch all of them,
|
||||||
// but we're not getting the character reference nodes because
|
// but we're not getting the character reference nodes because
|
||||||
@ -151,7 +159,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$tokens[] = $this->factory->createText($this->parseData($data));
|
$tokens[] = $this->factory->createText($this->parseText($data, $config));
|
||||||
return false;
|
return false;
|
||||||
} elseif ($node->nodeType === XML_COMMENT_NODE) {
|
} elseif ($node->nodeType === XML_COMMENT_NODE) {
|
||||||
// this is code is only invoked for comments in script/style in versions
|
// this is code is only invoked for comments in script/style in versions
|
||||||
@ -252,7 +260,7 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
* @param HTMLPurifier_Context $context
|
* @param HTMLPurifier_Context $context
|
||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
protected function wrapHTML($html, $config, $context)
|
protected function wrapHTML($html, $config, $context, $use_div = true)
|
||||||
{
|
{
|
||||||
$def = $config->getDefinition('HTML');
|
$def = $config->getDefinition('HTML');
|
||||||
$ret = '';
|
$ret = '';
|
||||||
@ -271,7 +279,11 @@ class HTMLPurifier_Lexer_DOMLex extends HTMLPurifier_Lexer
|
|||||||
$ret .= '<html><head>';
|
$ret .= '<html><head>';
|
||||||
$ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
|
$ret .= '<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />';
|
||||||
// No protection if $html contains a stray </div>!
|
// No protection if $html contains a stray </div>!
|
||||||
$ret .= '</head><body>' . $html . '</body></html>';
|
$ret .= '</head><body>';
|
||||||
|
if ($use_div) $ret .= '<div>';
|
||||||
|
$ret .= $html;
|
||||||
|
if ($use_div) $ret .= '</div>';
|
||||||
|
$ret .= '</body></html>';
|
||||||
return $ret;
|
return $ret;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -129,12 +129,12 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
// We are not inside tag and there still is another tag to parse
|
// We are not inside tag and there still is another tag to parse
|
||||||
$token = new
|
$token = new
|
||||||
HTMLPurifier_Token_Text(
|
HTMLPurifier_Token_Text(
|
||||||
$this->parseData(
|
$this->parseText(
|
||||||
substr(
|
substr(
|
||||||
$html,
|
$html,
|
||||||
$cursor,
|
$cursor,
|
||||||
$position_next_lt - $cursor
|
$position_next_lt - $cursor
|
||||||
)
|
), $config
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if ($maintain_line_numbers) {
|
if ($maintain_line_numbers) {
|
||||||
@ -154,11 +154,11 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
// Create Text of rest of string
|
// Create Text of rest of string
|
||||||
$token = new
|
$token = new
|
||||||
HTMLPurifier_Token_Text(
|
HTMLPurifier_Token_Text(
|
||||||
$this->parseData(
|
$this->parseText(
|
||||||
substr(
|
substr(
|
||||||
$html,
|
$html,
|
||||||
$cursor
|
$cursor
|
||||||
)
|
), $config
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if ($maintain_line_numbers) {
|
if ($maintain_line_numbers) {
|
||||||
@ -324,8 +324,8 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
$token = new
|
$token = new
|
||||||
HTMLPurifier_Token_Text(
|
HTMLPurifier_Token_Text(
|
||||||
'<' .
|
'<' .
|
||||||
$this->parseData(
|
$this->parseText(
|
||||||
substr($html, $cursor)
|
substr($html, $cursor), $config
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
if ($maintain_line_numbers) {
|
if ($maintain_line_numbers) {
|
||||||
@ -429,7 +429,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
if ($value === false) {
|
if ($value === false) {
|
||||||
$value = '';
|
$value = '';
|
||||||
}
|
}
|
||||||
return array($key => $this->parseData($value));
|
return array($key => $this->parseAttr($value, $config));
|
||||||
}
|
}
|
||||||
|
|
||||||
// setup loop environment
|
// setup loop environment
|
||||||
@ -518,7 +518,7 @@ class HTMLPurifier_Lexer_DirectLex extends HTMLPurifier_Lexer
|
|||||||
if ($value === false) {
|
if ($value === false) {
|
||||||
$value = '';
|
$value = '';
|
||||||
}
|
}
|
||||||
$array[$key] = $this->parseData($value);
|
$array[$key] = $this->parseAttr($value, $config);
|
||||||
$cursor++;
|
$cursor++;
|
||||||
} else {
|
} else {
|
||||||
// boolattr
|
// boolattr
|
||||||
|
@ -21,7 +21,7 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
|
|||||||
public function tokenizeHTML($html, $config, $context)
|
public function tokenizeHTML($html, $config, $context)
|
||||||
{
|
{
|
||||||
$new_html = $this->normalize($html, $config, $context);
|
$new_html = $this->normalize($html, $config, $context);
|
||||||
$new_html = $this->wrapHTML($new_html, $config, $context);
|
$new_html = $this->wrapHTML($new_html, $config, $context, false /* no div */);
|
||||||
try {
|
try {
|
||||||
$parser = new HTML5($new_html);
|
$parser = new HTML5($new_html);
|
||||||
$doc = $parser->save();
|
$doc = $parser->save();
|
||||||
@ -34,9 +34,9 @@ class HTMLPurifier_Lexer_PH5P extends HTMLPurifier_Lexer_DOMLex
|
|||||||
$tokens = array();
|
$tokens = array();
|
||||||
$this->tokenizeDOM(
|
$this->tokenizeDOM(
|
||||||
$doc->getElementsByTagName('html')->item(0)-> // <html>
|
$doc->getElementsByTagName('html')->item(0)-> // <html>
|
||||||
getElementsByTagName('body')->item(0) // <body>
|
getElementsByTagName('body')->item(0) // <body>
|
||||||
,
|
,
|
||||||
$tokens
|
$tokens, $config
|
||||||
);
|
);
|
||||||
return $tokens;
|
return $tokens;
|
||||||
}
|
}
|
||||||
@ -1515,6 +1515,7 @@ class HTML5
|
|||||||
// Consume the maximum number of characters possible, with the
|
// Consume the maximum number of characters possible, with the
|
||||||
// consumed characters case-sensitively matching one of the
|
// consumed characters case-sensitively matching one of the
|
||||||
// identifiers in the first column of the entities table.
|
// identifiers in the first column of the entities table.
|
||||||
|
|
||||||
$e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
|
$e_name = $this->characters('0-9A-Za-z;', $this->char + 1);
|
||||||
$len = strlen($e_name);
|
$len = strlen($e_name);
|
||||||
|
|
||||||
@ -1547,7 +1548,7 @@ class HTML5
|
|||||||
|
|
||||||
// Return a character token for the character corresponding to the
|
// Return a character token for the character corresponding to the
|
||||||
// entity name (as given by the second column of the entities table).
|
// entity name (as given by the second column of the entities table).
|
||||||
return html_entity_decode('&' . $entity . ';', ENT_QUOTES, 'UTF-8');
|
return html_entity_decode('&' . rtrim($entity, ';') . ';', ENT_QUOTES, 'UTF-8');
|
||||||
}
|
}
|
||||||
|
|
||||||
private function emitToken($token)
|
private function emitToken($token)
|
||||||
|
@ -327,6 +327,10 @@ class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer
|
|||||||
case HTMLPurifier_VarParser::HASH:
|
case HTMLPurifier_VarParser::HASH:
|
||||||
$nvalue = '';
|
$nvalue = '';
|
||||||
foreach ($value as $i => $v) {
|
foreach ($value as $i => $v) {
|
||||||
|
if (is_array($v)) {
|
||||||
|
// HACK
|
||||||
|
$v = implode(";", $v);
|
||||||
|
}
|
||||||
$nvalue .= "$i:$v" . PHP_EOL;
|
$nvalue .= "$i:$v" . PHP_EOL;
|
||||||
}
|
}
|
||||||
$value = $nvalue;
|
$value = $nvalue;
|
||||||
|
@ -165,7 +165,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
if (empty($zipper->front)) break;
|
if (empty($zipper->front)) break;
|
||||||
$token = $zipper->prev($token);
|
$token = $zipper->prev($token);
|
||||||
// indicate that other injectors should not process this token,
|
// indicate that other injectors should not process this token,
|
||||||
// but we need to reprocess it
|
// but we need to reprocess it. See Note [Injector skips]
|
||||||
unset($token->skip[$i]);
|
unset($token->skip[$i]);
|
||||||
$token->rewind = $i;
|
$token->rewind = $i;
|
||||||
if ($token instanceof HTMLPurifier_Token_Start) {
|
if ($token instanceof HTMLPurifier_Token_Start) {
|
||||||
@ -210,6 +210,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
if ($token instanceof HTMLPurifier_Token_Text) {
|
if ($token instanceof HTMLPurifier_Token_Text) {
|
||||||
foreach ($this->injectors as $i => $injector) {
|
foreach ($this->injectors as $i => $injector) {
|
||||||
if (isset($token->skip[$i])) {
|
if (isset($token->skip[$i])) {
|
||||||
|
// See Note [Injector skips]
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($token->rewind !== null && $token->rewind !== $i) {
|
if ($token->rewind !== null && $token->rewind !== $i) {
|
||||||
@ -367,6 +368,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
if ($ok) {
|
if ($ok) {
|
||||||
foreach ($this->injectors as $i => $injector) {
|
foreach ($this->injectors as $i => $injector) {
|
||||||
if (isset($token->skip[$i])) {
|
if (isset($token->skip[$i])) {
|
||||||
|
// See Note [Injector skips]
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($token->rewind !== null && $token->rewind !== $i) {
|
if ($token->rewind !== null && $token->rewind !== $i) {
|
||||||
@ -422,6 +424,7 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
$token->start = $current_parent;
|
$token->start = $current_parent;
|
||||||
foreach ($this->injectors as $i => $injector) {
|
foreach ($this->injectors as $i => $injector) {
|
||||||
if (isset($token->skip[$i])) {
|
if (isset($token->skip[$i])) {
|
||||||
|
// See Note [Injector skips]
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if ($token->rewind !== null && $token->rewind !== $i) {
|
if ($token->rewind !== null && $token->rewind !== $i) {
|
||||||
@ -534,12 +537,17 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
*/
|
*/
|
||||||
protected function processToken($token, $injector = -1)
|
protected function processToken($token, $injector = -1)
|
||||||
{
|
{
|
||||||
|
// Zend OpCache miscompiles $token = array($token), so
|
||||||
|
// avoid this pattern. See: https://github.com/ezyang/htmlpurifier/issues/108
|
||||||
|
|
||||||
// normalize forms of token
|
// normalize forms of token
|
||||||
if (is_object($token)) {
|
if (is_object($token)) {
|
||||||
$token = array(1, $token);
|
$tmp = $token;
|
||||||
|
$token = array(1, $tmp);
|
||||||
}
|
}
|
||||||
if (is_int($token)) {
|
if (is_int($token)) {
|
||||||
$token = array($token);
|
$tmp = $token;
|
||||||
|
$token = array($tmp);
|
||||||
}
|
}
|
||||||
if ($token === false) {
|
if ($token === false) {
|
||||||
$token = array(1);
|
$token = array(1);
|
||||||
@ -561,7 +569,12 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
|
list($old, $r) = $this->zipper->splice($this->token, $delete, $token);
|
||||||
|
|
||||||
if ($injector > -1) {
|
if ($injector > -1) {
|
||||||
// determine appropriate skips
|
// See Note [Injector skips]
|
||||||
|
// Determine appropriate skips. Here's what the code does:
|
||||||
|
// *If* we deleted one or more tokens, copy the skips
|
||||||
|
// of those tokens into the skips of the new tokens (in $token).
|
||||||
|
// Also, mark the newly inserted tokens as having come from
|
||||||
|
// $injector.
|
||||||
$oldskip = isset($old[0]) ? $old[0]->skip : array();
|
$oldskip = isset($old[0]) ? $old[0]->skip : array();
|
||||||
foreach ($token as $object) {
|
foreach ($token as $object) {
|
||||||
$object->skip = $oldskip;
|
$object->skip = $oldskip;
|
||||||
@ -597,4 +610,50 @@ class HTMLPurifier_Strategy_MakeWellFormed extends HTMLPurifier_Strategy
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note [Injector skips]
|
||||||
|
// ~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
// When I originally designed this class, the idea behind the 'skip'
|
||||||
|
// property of HTMLPurifier_Token was to help avoid infinite loops
|
||||||
|
// in injector processing. For example, suppose you wrote an injector
|
||||||
|
// that bolded swear words. Naively, you might write it so that
|
||||||
|
// whenever you saw ****, you replaced it with <strong>****</strong>.
|
||||||
|
//
|
||||||
|
// When this happens, we will reprocess all of the tokens with the
|
||||||
|
// other injectors. Now there is an opportunity for infinite loop:
|
||||||
|
// if we rerun the swear-word injector on these tokens, we might
|
||||||
|
// see **** and then reprocess again to get
|
||||||
|
// <strong><strong>****</strong></strong> ad infinitum.
|
||||||
|
//
|
||||||
|
// Thus, the idea of a skip is that once we process a token with
|
||||||
|
// an injector, we mark all of those tokens as having "come from"
|
||||||
|
// the injector, and we never run the injector again on these
|
||||||
|
// tokens.
|
||||||
|
//
|
||||||
|
// There were two more complications, however:
|
||||||
|
//
|
||||||
|
// - With HTMLPurifier_Injector_RemoveEmpty, we noticed that if
|
||||||
|
// you had <b><i></i></b>, after you removed the <i></i>, you
|
||||||
|
// really would like this injector to go back and reprocess
|
||||||
|
// the <b> tag, discovering that it is now empty and can be
|
||||||
|
// removed. So we reintroduced the possibility of infinite looping
|
||||||
|
// by adding a "rewind" function, which let you go back to an
|
||||||
|
// earlier point in the token stream and reprocess it with injectors.
|
||||||
|
// Needless to say, we need to UN-skip the token so it gets
|
||||||
|
// reprocessed.
|
||||||
|
//
|
||||||
|
// - Suppose that you successfuly process a token, replace it with
|
||||||
|
// one with your skip mark, but now another injector wants to
|
||||||
|
// process the skipped token with another token. Should you continue
|
||||||
|
// to skip that new token, or reprocess it? If you reprocess,
|
||||||
|
// you can end up with an infinite loop where one injector converts
|
||||||
|
// <a> to <b>, and then another injector converts it back. So
|
||||||
|
// we inherit the skips, but for some reason, I thought that we
|
||||||
|
// should inherit the skip from the first token of the token
|
||||||
|
// that we deleted. Why? Well, it seems to work OK.
|
||||||
|
//
|
||||||
|
// If I were to redesign this functionality, I would absolutely not
|
||||||
|
// go about doing it this way: the semantics are just not very well
|
||||||
|
// defined, and in any case you probably wanted to operate on trees,
|
||||||
|
// not token streams.
|
||||||
|
|
||||||
// vim: et sw=4 sts=4
|
// vim: et sw=4 sts=4
|
||||||
|
@ -26,7 +26,7 @@ abstract class HTMLPurifier_Token
|
|||||||
public $armor = array();
|
public $armor = array();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used during MakeWellFormed.
|
* Used during MakeWellFormed. See Note [Injector skips]
|
||||||
* @type
|
* @type
|
||||||
*/
|
*/
|
||||||
public $skip;
|
public $skip;
|
||||||
|
@ -85,11 +85,13 @@ class HTMLPurifier_URI
|
|||||||
$def = $config->getDefinition('URI');
|
$def = $config->getDefinition('URI');
|
||||||
$scheme_obj = $def->getDefaultScheme($config, $context);
|
$scheme_obj = $def->getDefaultScheme($config, $context);
|
||||||
if (!$scheme_obj) {
|
if (!$scheme_obj) {
|
||||||
// something funky happened to the default scheme object
|
if ($def->defaultScheme !== null) {
|
||||||
trigger_error(
|
// something funky happened to the default scheme object
|
||||||
'Default scheme object "' . $def->defaultScheme . '" was not readable',
|
trigger_error(
|
||||||
E_USER_WARNING
|
'Default scheme object "' . $def->defaultScheme . '" was not readable',
|
||||||
);
|
E_USER_WARNING
|
||||||
|
);
|
||||||
|
} // suppress error if it's null
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,9 +79,18 @@ class HTMLPurifier_URIScheme_data extends HTMLPurifier_URIScheme
|
|||||||
} else {
|
} else {
|
||||||
$raw_data = $data;
|
$raw_data = $data;
|
||||||
}
|
}
|
||||||
|
if ( strlen($raw_data) < 12 ) {
|
||||||
|
// error; exif_imagetype throws exception with small files,
|
||||||
|
// and this likely indicates a corrupt URI/failed parse anyway
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// XXX probably want to refactor this into a general mechanism
|
// XXX probably want to refactor this into a general mechanism
|
||||||
// for filtering arbitrary content types
|
// for filtering arbitrary content types
|
||||||
$file = tempnam("/tmp", "");
|
if (function_exists('sys_get_temp_dir')) {
|
||||||
|
$file = tempnam(sys_get_temp_dir(), "");
|
||||||
|
} else {
|
||||||
|
$file = tempnam("/tmp", "");
|
||||||
|
}
|
||||||
file_put_contents($file, $raw_data);
|
file_put_contents($file, $raw_data);
|
||||||
if (function_exists('exif_imagetype')) {
|
if (function_exists('exif_imagetype')) {
|
||||||
$image_code = exif_imagetype($file);
|
$image_code = exif_imagetype($file);
|
||||||
|
46
extlib/HTMLPurifier/HTMLPurifier/URIScheme/tel.php
Normal file
46
extlib/HTMLPurifier/HTMLPurifier/URIScheme/tel.php
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validates tel (for phone numbers).
|
||||||
|
*
|
||||||
|
* The relevant specifications for this protocol are RFC 3966 and RFC 5341,
|
||||||
|
* but this class takes a much simpler approach: we normalize phone
|
||||||
|
* numbers so that they only include (possibly) a leading plus,
|
||||||
|
* and then any number of digits and x'es.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class HTMLPurifier_URIScheme_tel extends HTMLPurifier_URIScheme
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @type bool
|
||||||
|
*/
|
||||||
|
public $browsable = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @type bool
|
||||||
|
*/
|
||||||
|
public $may_omit_host = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param HTMLPurifier_URI $uri
|
||||||
|
* @param HTMLPurifier_Config $config
|
||||||
|
* @param HTMLPurifier_Context $context
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function doValidate(&$uri, $config, $context)
|
||||||
|
{
|
||||||
|
$uri->userinfo = null;
|
||||||
|
$uri->host = null;
|
||||||
|
$uri->port = null;
|
||||||
|
|
||||||
|
// Delete all non-numeric characters, non-x characters
|
||||||
|
// from phone number, EXCEPT for a leading plus sign.
|
||||||
|
$uri->path = preg_replace('/(?!^\+)[^\dx]/', '',
|
||||||
|
// Normalize e(x)tension to lower-case
|
||||||
|
str_replace('X', 'x', $uri->path));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// vim: et sw=4 sts=4
|
@ -1 +1 @@
|
|||||||
4.7.0
|
4.9.3
|
@ -13,7 +13,7 @@
|
|||||||
* @category HTTP
|
* @category HTTP
|
||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @copyright 2008-2014 Alexey Borzov <avb@php.net>
|
* @copyright 2008-2016 Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
@ -21,7 +21,9 @@
|
|||||||
/**
|
/**
|
||||||
* A class representing an URL as per RFC 3986.
|
* A class representing an URL as per RFC 3986.
|
||||||
*/
|
*/
|
||||||
require_once 'Net/URL2.php';
|
if (!class_exists('Net_URL2', true)) {
|
||||||
|
require_once 'Net/URL2.php';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception class for HTTP_Request2 package
|
* Exception class for HTTP_Request2 package
|
||||||
@ -35,7 +37,7 @@ require_once 'HTTP/Request2/Exception.php';
|
|||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @version Release: 2.2.1
|
* @version Release: 2.3.0
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
* @link http://tools.ietf.org/html/rfc2616#section-5
|
* @link http://tools.ietf.org/html/rfc2616#section-5
|
||||||
*/
|
*/
|
||||||
@ -213,7 +215,7 @@ class HTTP_Request2 implements SplSubject
|
|||||||
$this->setMethod($method);
|
$this->setMethod($method);
|
||||||
}
|
}
|
||||||
$this->setHeader(
|
$this->setHeader(
|
||||||
'user-agent', 'HTTP_Request2/2.2.1 ' .
|
'user-agent', 'HTTP_Request2/2.3.0 ' .
|
||||||
'(http://pear.php.net/package/http_request2) PHP/' . phpversion()
|
'(http://pear.php.net/package/http_request2) PHP/' . phpversion()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -794,6 +796,11 @@ class HTTP_Request2 implements SplSubject
|
|||||||
* encoded by Content-Encoding</li>
|
* encoded by Content-Encoding</li>
|
||||||
* <li>'receivedBody' - after receiving the complete response
|
* <li>'receivedBody' - after receiving the complete response
|
||||||
* body, data is HTTP_Request2_Response object</li>
|
* body, data is HTTP_Request2_Response object</li>
|
||||||
|
* <li>'warning' - a problem arose during the request
|
||||||
|
* that is not severe enough to throw
|
||||||
|
* an Exception, data is the warning
|
||||||
|
* message (string). Currently dispatched if
|
||||||
|
* response body was received incompletely.</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
* Different adapters may not send all the event types. Mock adapter does
|
* Different adapters may not send all the event types. Mock adapter does
|
||||||
* not send any events to the observers.
|
* not send any events to the observers.
|
||||||
@ -1022,7 +1029,7 @@ class HTTP_Request2 implements SplSubject
|
|||||||
}
|
}
|
||||||
// (deprecated) mime_content_type function available
|
// (deprecated) mime_content_type function available
|
||||||
if (empty($info) && function_exists('mime_content_type')) {
|
if (empty($info) && function_exists('mime_content_type')) {
|
||||||
return mime_content_type($filename);
|
$info = mime_content_type($filename);
|
||||||
}
|
}
|
||||||
return empty($info)? 'application/octet-stream': $info;
|
return empty($info)? 'application/octet-stream': $info;
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* @category HTTP
|
* @category HTTP
|
||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @copyright 2008-2014 Alexey Borzov <avb@php.net>
|
* @copyright 2008-2016 Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
@ -34,7 +34,7 @@ require_once 'HTTP/Request2/Response.php';
|
|||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @version Release: 2.2.1
|
* @version Release: 2.3.0
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
abstract class HTTP_Request2_Adapter
|
abstract class HTTP_Request2_Adapter
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* @category HTTP
|
* @category HTTP
|
||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @copyright 2008-2014 Alexey Borzov <avb@php.net>
|
* @copyright 2008-2016 Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
@ -30,7 +30,7 @@ require_once 'HTTP/Request2/Adapter.php';
|
|||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @version Release: 2.2.1
|
* @version Release: 2.3.0
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
||||||
@ -116,6 +116,12 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
*/
|
*/
|
||||||
protected $eventReceivedHeaders = false;
|
protected $eventReceivedHeaders = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether 'sentBoody' event was sent to observers
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
protected $eventSentBody = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Position within request body
|
* Position within request body
|
||||||
* @var integer
|
* @var integer
|
||||||
@ -171,6 +177,7 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
$this->position = 0;
|
$this->position = 0;
|
||||||
$this->eventSentHeaders = false;
|
$this->eventSentHeaders = false;
|
||||||
$this->eventReceivedHeaders = false;
|
$this->eventReceivedHeaders = false;
|
||||||
|
$this->eventSentBody = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (false === curl_exec($ch = $this->createCurlHandle())) {
|
if (false === curl_exec($ch = $this->createCurlHandle())) {
|
||||||
@ -180,6 +187,9 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
}
|
}
|
||||||
if (isset($ch)) {
|
if (isset($ch)) {
|
||||||
$this->lastInfo = curl_getinfo($ch);
|
$this->lastInfo = curl_getinfo($ch);
|
||||||
|
if (CURLE_OK !== curl_errno($ch)) {
|
||||||
|
$this->request->setLastEvent('warning', curl_error($ch));
|
||||||
|
}
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,7 +201,7 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($jar = $request->getCookieJar()) {
|
if ($jar = $request->getCookieJar()) {
|
||||||
$jar->addCookiesFromResponse($response, $request->getUrl());
|
$jar->addCookiesFromResponse($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (0 < $this->lastInfo['size_download']) {
|
if (0 < $this->lastInfo['size_download']) {
|
||||||
@ -400,9 +410,12 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
protected function workaroundPhpBug47204($ch, &$headers)
|
protected function workaroundPhpBug47204($ch, &$headers)
|
||||||
{
|
{
|
||||||
// no redirects, no digest auth -> probably no rewind needed
|
// no redirects, no digest auth -> probably no rewind needed
|
||||||
|
// also apply workaround only for POSTs, othrerwise we get
|
||||||
|
// https://pear.php.net/bugs/bug.php?id=20440 for PUTs
|
||||||
if (!$this->request->getConfig('follow_redirects')
|
if (!$this->request->getConfig('follow_redirects')
|
||||||
&& (!($auth = $this->request->getAuth())
|
&& (!($auth = $this->request->getAuth())
|
||||||
|| HTTP_Request2::AUTH_DIGEST != $auth['scheme'])
|
|| HTTP_Request2::AUTH_DIGEST != $auth['scheme'])
|
||||||
|
|| HTTP_Request2::METHOD_POST !== $this->request->getMethod()
|
||||||
) {
|
) {
|
||||||
curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody'));
|
curl_setopt($ch, CURLOPT_READFUNCTION, array($this, 'callbackReadBody'));
|
||||||
|
|
||||||
@ -469,40 +482,36 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
*/
|
*/
|
||||||
protected function callbackWriteHeader($ch, $string)
|
protected function callbackWriteHeader($ch, $string)
|
||||||
{
|
{
|
||||||
// we may receive a second set of headers if doing e.g. digest auth
|
if (!$this->eventSentHeaders
|
||||||
if ($this->eventReceivedHeaders || !$this->eventSentHeaders) {
|
// we may receive a second set of headers if doing e.g. digest auth
|
||||||
// don't bother with 100-Continue responses (bug #15785)
|
// but don't bother with 100-Continue responses (bug #15785)
|
||||||
if (!$this->eventSentHeaders
|
|| $this->eventReceivedHeaders && $this->response->getStatus() >= 200
|
||||||
|| $this->response->getStatus() >= 200
|
) {
|
||||||
) {
|
$this->request->setLastEvent(
|
||||||
$this->request->setLastEvent(
|
'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
|
||||||
'sentHeaders', curl_getinfo($ch, CURLINFO_HEADER_OUT)
|
);
|
||||||
);
|
}
|
||||||
}
|
if (!$this->eventSentBody) {
|
||||||
$upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD);
|
$upload = curl_getinfo($ch, CURLINFO_SIZE_UPLOAD);
|
||||||
// if body wasn't read by a callback, send event with total body size
|
// if body wasn't read by the callback, send event with total body size
|
||||||
if ($upload > $this->position) {
|
if ($upload > $this->position) {
|
||||||
$this->request->setLastEvent(
|
$this->request->setLastEvent(
|
||||||
'sentBodyPart', $upload - $this->position
|
'sentBodyPart', $upload - $this->position
|
||||||
);
|
);
|
||||||
$this->position = $upload;
|
|
||||||
}
|
}
|
||||||
if ($upload && (!$this->eventSentHeaders
|
if ($upload > 0) {
|
||||||
|| $this->response->getStatus() >= 200)
|
|
||||||
) {
|
|
||||||
$this->request->setLastEvent('sentBody', $upload);
|
$this->request->setLastEvent('sentBody', $upload);
|
||||||
}
|
}
|
||||||
$this->eventSentHeaders = true;
|
|
||||||
// we'll need a new response object
|
|
||||||
if ($this->eventReceivedHeaders) {
|
|
||||||
$this->eventReceivedHeaders = false;
|
|
||||||
$this->response = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (empty($this->response)) {
|
$this->eventSentHeaders = true;
|
||||||
$this->response = new HTTP_Request2_Response(
|
$this->eventSentBody = true;
|
||||||
|
|
||||||
|
if ($this->eventReceivedHeaders || empty($this->response)) {
|
||||||
|
$this->eventReceivedHeaders = false;
|
||||||
|
$this->response = new HTTP_Request2_Response(
|
||||||
$string, false, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)
|
$string, false, curl_getinfo($ch, CURLINFO_EFFECTIVE_URL)
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$this->response->parseHeaderLine($string);
|
$this->response->parseHeaderLine($string);
|
||||||
if ('' == trim($string)) {
|
if ('' == trim($string)) {
|
||||||
@ -522,7 +531,7 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($jar = $this->request->getCookieJar()) {
|
if ($jar = $this->request->getCookieJar()) {
|
||||||
$jar->addCookiesFromResponse($this->response, $this->request->getUrl());
|
$jar->addCookiesFromResponse($this->response);
|
||||||
if (!$redirectUrl->isAbsolute()) {
|
if (!$redirectUrl->isAbsolute()) {
|
||||||
$redirectUrl = $this->request->getUrl()->resolve($redirectUrl);
|
$redirectUrl = $this->request->getUrl()->resolve($redirectUrl);
|
||||||
}
|
}
|
||||||
@ -532,6 +541,7 @@ class HTTP_Request2_Adapter_Curl extends HTTP_Request2_Adapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->eventReceivedHeaders = true;
|
$this->eventReceivedHeaders = true;
|
||||||
|
$this->eventSentBody = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return strlen($string);
|
return strlen($string);
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* @category HTTP
|
* @category HTTP
|
||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @copyright 2008-2014 Alexey Borzov <avb@php.net>
|
* @copyright 2008-2016 Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
@ -44,7 +44,7 @@ require_once 'HTTP/Request2/Adapter.php';
|
|||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @version Release: 2.2.1
|
* @version Release: 2.3.0
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
class HTTP_Request2_Adapter_Mock extends HTTP_Request2_Adapter
|
class HTTP_Request2_Adapter_Mock extends HTTP_Request2_Adapter
|
||||||
|
@ -13,7 +13,7 @@
|
|||||||
* @category HTTP
|
* @category HTTP
|
||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @copyright 2008-2014 Alexey Borzov <avb@php.net>
|
* @copyright 2008-2016 Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
@ -34,7 +34,7 @@ require_once 'HTTP/Request2/SocketWrapper.php';
|
|||||||
* @package HTTP_Request2
|
* @package HTTP_Request2
|
||||||
* @author Alexey Borzov <avb@php.net>
|
* @author Alexey Borzov <avb@php.net>
|
||||||
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
* @license http://opensource.org/licenses/BSD-3-Clause BSD 3-Clause License
|
||||||
* @version Release: 2.2.1
|
* @version Release: 2.3.0
|
||||||
* @link http://pear.php.net/package/HTTP_Request2
|
* @link http://pear.php.net/package/HTTP_Request2
|
||||||
*/
|
*/
|
||||||
class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
||||||
@ -147,7 +147,7 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
|||||||
|
|
||||||
|
|
||||||
if ($jar = $request->getCookieJar()) {
|
if ($jar = $request->getCookieJar()) {
|
||||||
$jar->addCookiesFromResponse($response, $request->getUrl());
|
$jar->addCookiesFromResponse($response);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$this->canKeepAlive($keepAlive, $response)) {
|
if (!$this->canKeepAlive($keepAlive, $response)) {
|
||||||
@ -261,9 +261,16 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
|||||||
foreach ($this->request->getConfig() as $name => $value) {
|
foreach ($this->request->getConfig() as $name => $value) {
|
||||||
if ('ssl_' == substr($name, 0, 4) && null !== $value) {
|
if ('ssl_' == substr($name, 0, 4) && null !== $value) {
|
||||||
if ('ssl_verify_host' == $name) {
|
if ('ssl_verify_host' == $name) {
|
||||||
if ($value) {
|
if (version_compare(phpversion(), '5.6', '<')) {
|
||||||
$options['ssl']['CN_match'] = $reqHost;
|
if ($value) {
|
||||||
|
$options['ssl']['CN_match'] = $reqHost;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
$options['ssl']['verify_peer_name'] = $value;
|
||||||
|
$options['ssl']['peer_name'] = $reqHost;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$options['ssl'][substr($name, 4)] = $value;
|
$options['ssl'][substr($name, 4)] = $value;
|
||||||
}
|
}
|
||||||
@ -281,7 +288,7 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
|||||||
|
|
||||||
// Changing SSL context options after connection is established does *not*
|
// Changing SSL context options after connection is established does *not*
|
||||||
// work, we need a new connection if options change
|
// work, we need a new connection if options change
|
||||||
$remote = ((!$secure || $httpProxy || $socksProxy)? 'tcp://': 'ssl://')
|
$remote = ((!$secure || $httpProxy || $socksProxy)? 'tcp://': 'tls://')
|
||||||
. $host . ':' . $port;
|
. $host . ':' . $port;
|
||||||
$socketKey = $remote . (
|
$socketKey = $remote . (
|
||||||
($secure && $httpProxy || $socksProxy)
|
($secure && $httpProxy || $socksProxy)
|
||||||
@ -312,12 +319,12 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
|||||||
$conninfo = "tcp://{$reqHost}:{$reqPort} via {$remote}";
|
$conninfo = "tcp://{$reqHost}:{$reqPort} via {$remote}";
|
||||||
} else {
|
} else {
|
||||||
$this->socket->enableCrypto();
|
$this->socket->enableCrypto();
|
||||||
$conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}";
|
$conninfo = "tls://{$reqHost}:{$reqPort} via {$remote}";
|
||||||
}
|
}
|
||||||
|
|
||||||
} elseif ($secure && $httpProxy && !$tunnel) {
|
} elseif ($secure && $httpProxy && !$tunnel) {
|
||||||
$this->establishTunnel();
|
$this->establishTunnel();
|
||||||
$conninfo = "ssl://{$reqHost}:{$reqPort} via {$remote}";
|
$conninfo = "tls://{$reqHost}:{$reqPort} via {$remote}";
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$this->socket = new HTTP_Request2_SocketWrapper(
|
$this->socket = new HTTP_Request2_SocketWrapper(
|
||||||
@ -1043,14 +1050,14 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
|||||||
$chunked = 'chunked' == $response->getHeader('transfer-encoding');
|
$chunked = 'chunked' == $response->getHeader('transfer-encoding');
|
||||||
$length = $response->getHeader('content-length');
|
$length = $response->getHeader('content-length');
|
||||||
$hasBody = false;
|
$hasBody = false;
|
||||||
if ($chunked || null === $length || 0 < intval($length)) {
|
// RFC 2616, section 4.4:
|
||||||
// RFC 2616, section 4.4:
|
// 3. ... If a message is received with both a
|
||||||
// 3. ... If a message is received with both a
|
// Transfer-Encoding header field and a Content-Length header field,
|
||||||
// Transfer-Encoding header field and a Content-Length header field,
|
// the latter MUST be ignored.
|
||||||
// the latter MUST be ignored.
|
$toRead = ($chunked || null === $length)? null: $length;
|
||||||
$toRead = ($chunked || null === $length)? null: $length;
|
$this->chunkLength = 0;
|
||||||
$this->chunkLength = 0;
|
|
||||||
|
|
||||||
|
if ($chunked || null === $length || 0 < intval($length)) {
|
||||||
while (!$this->socket->eof() && (is_null($toRead) || 0 < $toRead)) {
|
while (!$this->socket->eof() && (is_null($toRead) || 0 < $toRead)) {
|
||||||
if ($chunked) {
|
if ($chunked) {
|
||||||
$data = $this->readChunked($bufferSize);
|
$data = $this->readChunked($bufferSize);
|
||||||
@ -1075,6 +1082,11 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (0 !== $this->chunkLength || null !== $toRead && $toRead > 0) {
|
||||||
|
$this->request->setLastEvent(
|
||||||
|
'warning', 'transfer closed with outstanding read data remaining'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if ($hasBody) {
|
if ($hasBody) {
|
||||||
$this->request->setLastEvent('receivedBody', $response);
|
$this->request->setLastEvent('receivedBody', $response);
|
||||||
@ -1095,11 +1107,16 @@ class HTTP_Request2_Adapter_Socket extends HTTP_Request2_Adapter
|
|||||||
// at start of the next chunk?
|
// at start of the next chunk?
|
||||||
if (0 == $this->chunkLength) {
|
if (0 == $this->chunkLength) {
|
||||||
$line = $this->socket->readLine($bufferSize);
|
$line = $this->socket->readLine($bufferSize);
|
||||||
if (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
|
if ('' === $line && $this->socket->eof()) {
|
||||||
|
$this->chunkLength = -1; // indicate missing chunk
|
||||||
|
return '';
|
||||||
|
|
||||||
|
} elseif (!preg_match('/^([0-9a-f]+)/i', $line, $matches)) {
|
||||||
throw new HTTP_Request2_MessageException(
|
throw new HTTP_Request2_MessageException(
|
||||||
"Cannot decode chunked response, invalid chunk length '{$line}'",
|
"Cannot decode chunked response, invalid chunk length '{$line}'",
|
||||||
HTTP_Request2_Exception::DECODE_ERROR
|
HTTP_Request2_Exception::DECODE_ERROR
|
||||||
);
|
);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$this->chunkLength = hexdec($matches[1]);
|
$this->chunkLength = hexdec($matches[1]);
|
||||||
// Chunk with zero length indicates the end
|
// Chunk with zero length indicates the end
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user