From 22ff358ba8d1fd0396136e1de570d788dd0727b6 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 18 Feb 2010 18:20:48 +0000 Subject: OStatus sub/unsub updates: - fix for PuSH unsub verification - send Salmon notification on unsub --- plugins/OStatus/classes/Ostatus_profile.php | 44 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 21 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index be01cdfe1..486417617 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -484,7 +484,7 @@ class Ostatus_profile extends Memcached_DataObject } else { $this->sub_end = null; } - $this->lastupdate = common_sql_date(); + $this->lastupdate = common_sql_now(); return $this->update($original); } @@ -497,12 +497,13 @@ class Ostatus_profile extends Memcached_DataObject { $original = clone($this); - $this->verify_token = null; - $this->secret = null; - $this->sub_state = null; - $this->sub_start = null; - $this->sub_end = null; - $this->lastupdate = common_sql_date(); + // @fixme these should all be null, but DB_DataObject doesn't save null values...????? + $this->verify_token = ''; + $this->secret = ''; + $this->sub_state = ''; + $this->sub_start = ''; + $this->sub_end = ''; + $this->lastupdate = common_sql_now(); return $this->update($original); } @@ -527,24 +528,25 @@ class Ostatus_profile extends Memcached_DataObject ':' . $actor->id . ':' . time(); // @fixme - $entry = new Atom10Entry(); + //$entry = new Atom10Entry(); + $entry = new XMLStringer(); $entry->elementStart('entry'); $entry->element('id', null, $id); $entry->element('title', null, $text); $entry->element('summary', null, $text); - $entry->element('published', null, common_date_w3dtf()); + $entry->element('published', null, common_date_w3dtf(time())); $entry->element('activity:verb', null, $verb); - $entry->raw($profile->asAtomAuthor()); - $entry->raw($profile->asActivityActor()); + $entry->raw($actor->asAtomAuthor()); + $entry->raw($actor->asActivityActor()); $entry->raw($object->asActivityNoun('object')); - $entry->elmentEnd('entry'); + $entry->elementEnd('entry'); $feed = $this->atomFeed($actor); - $feed->initFeed(); + #$feed->initFeed(); $feed->addEntry($entry); - $feed->renderEntries(); - $feed->endFeed(); + #$feed->renderEntries(); + #$feed->endFeed(); $xml = $feed->getString(); common_log(LOG_INFO, "Posting to Salmon endpoint $salmon: $xml"); @@ -568,7 +570,7 @@ class Ostatus_profile extends Memcached_DataObject $feed = new Atom10Feed(); // @fixme should these be set up somewhere else? $feed->addNamespace('activity', 'http://activitystrea.ms/spec/1.0/'); - $feed->addNamesapce('thr', 'http://purl.org/syndication/thread/1.0'); + $feed->addNamespace('thr', 'http://purl.org/syndication/thread/1.0'); $feed->addNamespace('georss', 'http://www.georss.org/georss'); $feed->addNamespace('ostatus', 'http://ostatus.org/schema/1.0'); @@ -579,14 +581,14 @@ class Ostatus_profile extends Memcached_DataObject $feed->setUpdated(time()); $feed->setPublished(time()); - $feed->addLink(common_url('ApiTimelineUser', - array('id' => $actor->id, - 'type' => 'atom')), + $feed->addLink(common_local_url('ApiTimelineUser', + array('id' => $actor->id, + 'type' => 'atom')), array('rel' => 'self', 'type' => 'application/atom+xml')); - $feed->addLink(common_url('userbyid', - array('id' => $actor->id)), + $feed->addLink(common_local_url('userbyid', + array('id' => $actor->id)), array('rel' => 'alternate', 'type' => 'text/html')); -- cgit v1.2.3-54-g00ecf From 0dac13d197248bf24ea51cb7911d32286764c0c8 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 18 Feb 2010 21:22:21 +0000 Subject: OStatus refactoring to clean up profile vs feed and fix up subscription issues. PuSH subscription maintenance broken back out to FeedSub, letting Ostatus_profile deal with the profile level (user or group, with unique id URI) --- plugins/OStatus/OStatusPlugin.php | 53 +-- plugins/OStatus/actions/feedsubsettings.php | 99 ++--- plugins/OStatus/actions/pushcallback.php | 22 +- plugins/OStatus/actions/salmon.php | 32 +- plugins/OStatus/classes/FeedSub.php | 443 ++++++++++++++++++++++ plugins/OStatus/classes/Ostatus_profile.php | 544 +++++++++------------------- plugins/OStatus/lib/feeddiscovery.php | 50 ++- plugins/OStatus/lib/feedmunger.php | 350 ------------------ 8 files changed, 741 insertions(+), 852 deletions(-) create mode 100644 plugins/OStatus/classes/FeedSub.php delete mode 100644 plugins/OStatus/lib/feedmunger.php (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index e548a151c..4ebe4551e 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -1,17 +1,7 @@ -Author URI: http://status.net/ -*/ - /* * StatusNet - the distributed open-source microblogging tool - * Copyright (C) 2009, StatusNet, Inc. + * Copyright (C) 2009-2010, StatusNet, Inc. * * 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 @@ -28,17 +18,12 @@ Author URI: http://status.net/ */ /** - * @package FeedSubPlugin + * @package OStatusPlugin * @maintainer Brion Vibber */ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -define('FEEDSUB_SERVICE', 100); // fixme -- avoid hardcoding these? - -// We bundle the XML_Parse_Feed library... -set_include_path(get_include_path() . PATH_SEPARATOR . dirname(__FILE__) . '/extlib'); - class FeedSubException extends Exception { } @@ -258,24 +243,6 @@ class OStatusPlugin extends Plugin } } - /** - * Notify remote server when one of our users subscribes. - * @fixme Check and restart the PuSH subscription if needed - * - * @param User $user - * @param Profile $other - * @return hook return value - */ - function onEndSubscribe($user, $other) - { - $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); - if ($oprofile) { - // Notify the remote server of the unsub, if supported. - $oprofile->notify($user->getProfile(), ActivityVerb::FOLLOW, $oprofile); - } - return true; - } - /** * Notify remote server and garbage collect unused feeds on unsubscribe. * @fixme send these operations to background queues @@ -309,6 +276,7 @@ class OStatusPlugin extends Plugin function onCheckSchema() { $schema = Schema::get(); $schema->ensureTable('ostatus_profile', Ostatus_profile::schemaDef()); + $schema->ensureTable('feedsub', FeedSub::schemaDef()); $schema->ensureTable('hubsub', HubSub::schemaDef()); return true; } @@ -345,4 +313,19 @@ class OStatusPlugin extends Plugin return false; } } + + /** + * Send incoming PuSH feeds for OStatus endpoints in for processing. + * + * @param FeedSub $feedsub + * @param DOMDocument $feed + * @return mixed hook return code + */ + function onStartFeedSubReceive($feedsub, $feed) + { + $oprofile = Ostatus_profile::staticGet('feeduri', $feedsub->uri); + if ($oprofile) { + $oprofile->processFeed($feed); + } + } } diff --git a/plugins/OStatus/actions/feedsubsettings.php b/plugins/OStatus/actions/feedsubsettings.php index 6933c9bf2..3e1d0aa82 100644 --- a/plugins/OStatus/actions/feedsubsettings.php +++ b/plugins/OStatus/actions/feedsubsettings.php @@ -26,7 +26,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } class FeedSubSettingsAction extends ConnectSettingsAction { - protected $feedurl; + protected $profile_uri; protected $preview; protected $munger; @@ -88,7 +88,10 @@ class FeedSubSettingsAction extends ConnectSettingsAction $this->elementStart('ul', 'form_data'); $this->elementStart('li', array('id' => 'settings_twitter_login_button')); - $this->input('feedurl', _('Feed URL'), $this->feedurl, _('Enter the URL of a PubSubHubbub-enabled feed')); + $this->input('profile_uri', + _m('Feed URL'), + $this->profile_uri, + _m('Enter the profile URL of a PubSubHubbub-enabled feed')); $this->elementEnd('li'); $this->elementEnd('ul'); @@ -145,79 +148,55 @@ class FeedSubSettingsAction extends ConnectSettingsAction */ function validateFeed() { - $feedurl = trim($this->arg('feedurl')); + $profile_uri = trim($this->arg('profile_uri')); - if ($feedurl == '') { - $this->showForm(_m('Empty feed URL!')); + if ($profile_uri == '') { + $this->showForm(_m('Empty remote profile URL!')); return; } - $this->feedurl = $feedurl; + $this->profile_uri = $profile_uri; - // Get the canonical feed URI and check it + // @fixme validate, normalize bla bla try { - $discover = new FeedDiscovery(); - $uri = $discover->discoverFromURL($feedurl); + $oprofile = Ostatus_profile::ensureProfile($this->profile_uri); + $this->oprofile = $oprofile; + return true; } catch (FeedSubBadURLException $e) { - $this->showForm(_m('Invalid URL or could not reach server.')); - return false; + $err = _m('Invalid URL or could not reach server.'); } catch (FeedSubBadResponseException $e) { - $this->showForm(_m('Cannot read feed; server returned error.')); - return false; + $err = _m('Cannot read feed; server returned error.'); } catch (FeedSubEmptyException $e) { - $this->showForm(_m('Cannot read feed; server returned an empty page.')); - return false; + $err = _m('Cannot read feed; server returned an empty page.'); } catch (FeedSubBadHTMLException $e) { - $this->showForm(_m('Bad HTML, could not find feed link.')); - return false; + $err = _m('Bad HTML, could not find feed link.'); } catch (FeedSubNoFeedException $e) { - $this->showForm(_m('Could not find a feed linked from this URL.')); - return false; + $err = _m('Could not find a feed linked from this URL.'); } catch (FeedSubUnrecognizedTypeException $e) { - $this->showForm(_m('Not a recognized feed type.')); - return false; + $err = _m('Not a recognized feed type.'); } catch (FeedSubException $e) { // Any new ones we forgot about - $this->showForm(_m('Bad feed URL.')); - return false; + $err = sprintf(_m('Bad feed URL: %s %s'), get_class($e), $e->getMessage()); } - - $this->munger = $discover->feedMunger(); - $this->profile = $this->munger->ostatusProfile(); - if ($this->profile->huburi == '' && !common_config('feedsub', 'nohub')) { - $this->showForm(_m('Feed is not PuSH-enabled; cannot subscribe.')); - return false; - } - - return true; + $this->showForm($err); + return false; } function saveFeed() { if ($this->validateFeed()) { $this->preview = true; - $this->profile = Ostatus_profile::ensureProfile($this->munger); - if (!$this->profile) { - throw new ServerException("Feed profile was not saved properly."); - } - - // If not already in use, subscribe to updates via the hub - if ($this->profile->sub_start) { - common_log(LOG_INFO, __METHOD__ . ": double the fun! new sub for {$this->profile->feeduri} last subbed {$this->profile->sub_start}"); - } else { - $ok = $this->profile->subscribe(); - common_log(LOG_INFO, __METHOD__ . ": sub was $ok"); - if (!$ok) { - $this->showForm(_m('Feed subscription failed! Bad response from hub.')); - return; - } - } // And subscribe the current user to the local profile $user = common_current_user(); - if ($this->profile->isGroup()) { - $group = $this->profile->localGroup(); + if (!$this->oprofile->subscribe()) { + $this->showForm(_m("Failed to set up server-to-server subscription.")); + return; + } + + if ($this->oprofile->isGroup()) { + $group = $this->oprofile->localGroup(); if ($user->isMember($group)) { $this->showForm(_m('Already a member!')); } elseif (Group_member::join($this->profile->group_id, $user->id)) { @@ -226,13 +205,13 @@ class FeedSubSettingsAction extends ConnectSettingsAction $this->showForm(_m('Remote group join failed!')); } } else { - $local = $this->profile->localProfile(); + $local = $this->oprofile->localProfile(); if ($user->isSubscribed($local)) { $this->showForm(_m('Already subscribed!')); - } elseif ($user->subscribeTo($local)) { - $this->showForm(_m('Feed subscribed!')); + } elseif ($this->oprofile->subscribeLocalToRemote($user)) { + $this->showForm(_m('Remote user subscribed!')); } else { - $this->showForm(_m('Feed subscription failed!')); + $this->showForm(_m('Remote subscription failed!')); } } } @@ -248,17 +227,7 @@ class FeedSubSettingsAction extends ConnectSettingsAction function previewFeed() { - $profile = $this->munger->ostatusProfile(); - $notice = $this->munger->notice(0, true); // preview - - if ($notice) { - $this->element('b', null, 'Preview of latest post from this feed:'); - - $item = new NoticeList($notice, $this); - $item->show(); - } else { - $this->element('b', null, 'No posts in this feed yet.'); - } + $this->text('Profile preview should go here'); } function showScripts() diff --git a/plugins/OStatus/actions/pushcallback.php b/plugins/OStatus/actions/pushcallback.php index ed859a32f..7e1227a66 100644 --- a/plugins/OStatus/actions/pushcallback.php +++ b/plugins/OStatus/actions/pushcallback.php @@ -48,9 +48,9 @@ class PushCallbackAction extends Action throw new ServerException('Empty or invalid feed id', 400); } - $profile = Ostatus_profile::staticGet('id', $feedid); - if (!$profile) { - throw new ServerException('Unknown OStatus/PuSH feed id ' . $feedid, 400); + $feedsub = FeedSub::staticGet('id', $feedid); + if (!$feedsub) { + throw new ServerException('Unknown PuSH feed id ' . $feedid, 400); } $hmac = ''; @@ -62,7 +62,7 @@ class PushCallbackAction extends Action // @fixme Queue this to a background process; we should return // as quickly as possible from a distribution POST. - $profile->postUpdates($post, $hmac); + $feedsub->receive($post, $hmac); } /** @@ -81,29 +81,29 @@ class PushCallbackAction extends Action throw new ServerException("Bogus hub callback: bad mode", 404); } - $profile = Ostatus_profile::staticGet('feeduri', $topic); - if (!$profile) { + $feedsub = FeedSub::staticGet('uri', $topic); + if (!$feedsub) { common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback for unknown feed $topic"); throw new ServerException("Bogus hub callback: unknown feed", 404); } - if ($profile->verify_token !== $verify_token) { + if ($feedsub->verify_token !== $verify_token) { common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad token \"$verify_token\" for feed $topic"); throw new ServerException("Bogus hub callback: bad token", 404); } - if ($mode != $profile->sub_state) { - common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad mode \"$mode\" for feed $topic in state \"{$profile->sub_state}\""); + if ($mode != $feedsub->sub_state) { + common_log(LOG_WARNING, __METHOD__ . ": bogus hub callback with bad mode \"$mode\" for feed $topic in state \"{$feedsub->sub_state}\""); throw new ServerException("Bogus hub callback: mode doesn't match subscription state.", 404); } // OK! if ($mode == 'subscribe') { common_log(LOG_INFO, __METHOD__ . ': sub confirmed'); - $profile->confirmSubscribe($lease_seconds); + $feedsub->confirmSubscribe($lease_seconds); } else { common_log(LOG_INFO, __METHOD__ . ": unsub confirmed; deleting sub record for $topic"); - $profile->confirmUnsubscribe(); + $feedsub->confirmUnsubscribe(); } print $challenge; } diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index 224134cd7..ea5b8e4ea 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -68,6 +68,9 @@ class SalmonAction extends Action return true; } + /** + * @fixme probably call Ostatus_profile::processFeed + */ function handle($args) { common_log(LOG_INFO, 'Salmon: incoming post for user '. $this->user->id); @@ -95,6 +98,9 @@ class SalmonAction extends Action } } + /** + * @fixme probably call Ostatus_profile::processFeed + */ function handlePost() { switch ($this->act->object->type) { @@ -111,14 +117,23 @@ class SalmonAction extends Action $profile = $this->ensureProfile(); } + /** + * @fixme probably call Ostatus_profile::processFeed + */ function handleFollow() { } + /** + * @fixme probably call Ostatus_profile::processFeed + */ function handleFavorite() { } + /** + * @fixme probably call Ostatus_profile::processFeed + */ function handleShare() { } @@ -131,17 +146,13 @@ class SalmonAction extends Action throw new Exception("Received a salmon slap from unidentified actor."); } - $ostatusProfile = Ostatus_profile::staticGet('homeuri', $actor->id); - - if (empty($ostatusProfile)) { - return $this->createProfile(); - } else { - // XXX: can we receive a salmon slap from a group...? - assert(!empty($ostatusProfile->profile_id)); - return Profile::staticGet($ostatusProfile->profile_id); - } + $ostatusProfile = Ostatus_profile::ensureActorProfile($this->act); + return $oprofile->localProfile(); } + /** + * @fixme anything new in here probably should be merged into Ostatus_profile::ensureActorProfile and friends + */ function createProfile() { $actor = $this->act->actor; @@ -186,6 +197,9 @@ class SalmonAction extends Action return $profile; } + /** + * @fixme should be merged into Ostatus_profile + */ function nicknameFromURI($uri) { preg_match('/(\w+):/', $uri, $matches); diff --git a/plugins/OStatus/classes/FeedSub.php b/plugins/OStatus/classes/FeedSub.php new file mode 100644 index 000000000..dc2c0b710 --- /dev/null +++ b/plugins/OStatus/classes/FeedSub.php @@ -0,0 +1,443 @@ +. + */ + +/** + * @package OStatusPlugin + * @maintainer Brion Vibber + */ + +/* +PuSH subscription flow: + + $profile->subscribe() + generate random verification token + save to verify_token + sends a sub request to the hub... + + main/push/callback + hub sends confirmation back to us via GET + We verify the request, then echo back the challenge. + On our end, we save the time we subscribed and the lease expiration + + main/push/callback + hub sends us updates via POST + +*/ + +class FeedDBException extends FeedSubException +{ + public $obj; + + function __construct($obj) + { + parent::__construct('Database insert failure'); + $this->obj = $obj; + } +} + +/** + * FeedSub handles low-level PubHubSubbub (PuSH) subscriptions. + * Higher-level behavior building OStatus stuff on top is handled + * under Ostatus_profile. + */ +class FeedSub extends Memcached_DataObject +{ + public $__table = 'feedsub'; + + public $id; + public $feeduri; + + // PuSH subscription data + public $huburi; + public $secret; + public $verify_token; + public $sub_state; // subscribe, active, unsubscribe, inactive + public $sub_start; + public $sub_end; + public $last_update; + + public $created; + public $modified; + + public /*static*/ function staticGet($k, $v=null) + { + return parent::staticGet(__CLASS__, $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() + { + return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'uri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'huburi' => DB_DATAOBJECT_STR, + 'secret' => DB_DATAOBJECT_STR, + 'verify_token' => DB_DATAOBJECT_STR, + 'sub_state' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, + 'sub_end' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, + 'last_update' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + } + + static function schemaDef() + { + return array(new ColumnDef('id', 'integer', + /*size*/ null, + /*nullable*/ false, + /*key*/ 'PRI', + /*default*/ '0', + /*extra*/ null, + /*auto_increment*/ true), + new ColumnDef('uri', 'varchar', + 255, false, 'UNI'), + new ColumnDef('huburi', 'text', + null, true), + new ColumnDef('verify_token', 'text', + null, true), + new ColumnDef('secret', 'text', + null, true), + new ColumnDef('sub_state', "enum('subscribe','active','unsubscribe','inactive')", + null, false), + new ColumnDef('sub_start', 'datetime', + null, true), + new ColumnDef('sub_end', 'datetime', + null, true), + new ColumnDef('last_update', 'datetime', + null, false), + new ColumnDef('created', 'datetime', + null, false), + new ColumnDef('modified', 'datetime', + null, false)); + } + + /** + * return key definitions for DB_DataObject + * + * DB_DataObject needs to know about keys that the table has; this function + * defines them. + * + * @return array key definitions + */ + + function keys() + { + return array_keys($this->keyTypes()); + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. + * + * @return array key definitions + */ + + function keyTypes() + { + return array('id' => 'K', 'uri' => 'U'); + } + + function sequenceKey() + { + return array('id', true, false); + } + + /** + * Fetch the StatusNet-side profile for this feed + * @return Profile + */ + public function localProfile() + { + if ($this->profile_id) { + return Profile::staticGet('id', $this->profile_id); + } + return null; + } + + /** + * Fetch the StatusNet-side profile for this feed + * @return Profile + */ + public function localGroup() + { + if ($this->group_id) { + return User_group::staticGet('id', $this->group_id); + } + return null; + } + + /** + * @param string $feeduri + * @return FeedSub + * @throws FeedSubException if feed is invalid or lacks PuSH setup + */ + public static function ensureFeed($feeduri) + { + $current = self::staticGet('uri', $feeduri); + if ($current) { + return $current; + } + + $discover = new FeedDiscovery(); + $discover->discoverFromFeedURL($feeduri); + + $huburi = $discover->getAtomLink('hub'); + if (!$huburi) { + throw new FeedSubNoHubException(); + } + + $feedsub = new FeedSub(); + $feedsub->uri = $feeduri; + $feedsub->huburi = $huburi; + $feedsub->sub_state = 'inactive'; + + $feedsub->created = common_sql_now(); + $feedsub->modified = common_sql_now(); + + $result = $feedsub->insert(); + if (empty($result)) { + throw new FeedDBException($feedsub); + } + + return $feedsub; + } + + /** + * Send a subscription request to the hub for this feed. + * The hub will later send us a confirmation POST to /main/push/callback. + * + * @return bool true on success, false on failure + * @throws ServerException if feed state is not valid + */ + public function subscribe($mode='subscribe') + { + if ($this->sub_state && $this->sub_state != 'inactive') { + throw new ServerException("Attempting to start PuSH subscription to feed in state $this->sub_state"); + } + if (empty($this->huburi)) { + if (common_config('feedsub', 'nohub')) { + // Fake it! We're just testing remote feeds w/o hubs. + return true; + } else { + throw new ServerException("Attempting to start PuSH subscription for feed with no hub"); + } + } + + return $this->doSubscribe('subscribe'); + } + + /** + * Send a PuSH unsubscription request to the hub for this feed. + * The hub will later send us a confirmation POST to /main/push/callback. + * + * @return bool true on success, false on failure + * @throws ServerException if feed state is not valid + */ + public function unsubscribe() { + if ($this->sub_state != 'active') { + throw new ServerException("Attempting to end PuSH subscription to feed in state $this->sub_state"); + } + if (empty($this->huburi)) { + if (common_config('feedsub', 'nohub')) { + // Fake it! We're just testing remote feeds w/o hubs. + return true; + } else { + throw new ServerException("Attempting to end PuSH subscription for feed with no hub"); + } + } + + return $this->doSubscribe('unsubscribe'); + } + + protected function doSubscribe($mode) + { + $orig = clone($this); + $this->verify_token = common_good_rand(16); + if ($mode == 'subscribe') { + $this->secret = common_good_rand(32); + } + $this->sub_state = $mode; + $this->update($orig); + unset($orig); + + try { + $callback = common_local_url('pushcallback', array('feed' => $this->id)); + $headers = array('Content-Type: application/x-www-form-urlencoded'); + $post = array('hub.mode' => $mode, + 'hub.callback' => $callback, + 'hub.verify' => 'async', + 'hub.verify_token' => $this->verify_token, + 'hub.secret' => $this->secret, + //'hub.lease_seconds' => 0, + 'hub.topic' => $this->uri); + $client = new HTTPClient(); + $response = $client->post($this->huburi, $headers, $post); + $status = $response->getStatus(); + if ($status == 202) { + common_log(LOG_INFO, __METHOD__ . ': sub req ok, awaiting verification callback'); + return true; + } else if ($status == 204) { + common_log(LOG_INFO, __METHOD__ . ': sub req ok and verified'); + return true; + } else if ($status >= 200 && $status < 300) { + common_log(LOG_ERR, __METHOD__ . ": sub req returned unexpected HTTP $status: " . $response->getBody()); + return false; + } else { + common_log(LOG_ERR, __METHOD__ . ": sub req failed with HTTP $status: " . $response->getBody()); + return false; + } + } catch (Exception $e) { + // wtf! + common_log(LOG_ERR, __METHOD__ . ": error \"{$e->getMessage()}\" hitting hub $this->huburi subscribing to $this->uri"); + + $orig = clone($this); + $this->verify_token = null; + $this->sub_state = null; + $this->update($orig); + unset($orig); + + return false; + } + } + + /** + * Save PuSH subscription confirmation. + * Sets approximate lease start and end times and finalizes state. + * + * @param int $lease_seconds provided hub.lease_seconds parameter, if given + */ + public function confirmSubscribe($lease_seconds=0) + { + $original = clone($this); + + $this->sub_state = 'active'; + $this->sub_start = common_sql_date(time()); + if ($lease_seconds > 0) { + $this->sub_end = common_sql_date(time() + $lease_seconds); + } else { + $this->sub_end = null; + } + $this->lastupdate = common_sql_now(); + + return $this->update($original); + } + + /** + * Save PuSH unsubscription confirmation. + * Wipes active PuSH sub info and resets state. + */ + public function confirmUnsubscribe() + { + $original = clone($this); + + // @fixme these should all be null, but DB_DataObject doesn't save null values...????? + $this->verify_token = ''; + $this->secret = ''; + $this->sub_state = ''; + $this->sub_start = ''; + $this->sub_end = ''; + $this->lastupdate = common_sql_now(); + + return $this->update($original); + } + + /** + * Accept updates from a PuSH feed. If validated, this object and the + * feed (as a DOMDocument) will be passed to the StartFeedSubHandleFeed + * and EndFeedSubHandleFeed events for processing. + * + * @param string $post source of Atom or RSS feed + * @param string $hmac X-Hub-Signature header, if present + */ + public function receive($post, $hmac) + { + common_log(LOG_INFO, __METHOD__ . ": packet for \"$this->uri\"! $hmac $post"); + + if ($this->sub_state != 'active') { + common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH for inactive feed $this->uri (in state '$this->sub_state')"); + return; + } + + if ($post === '') { + common_log(LOG_ERR, __METHOD__ . ": ignoring empty post"); + return; + } + + if (!$this->validatePushSig($post, $hmac)) { + // Per spec we silently drop input with a bad sig, + // while reporting receipt to the server. + return; + } + + $feed = new DOMDocument(); + if (!$feed->loadXML($post)) { + // @fixme might help to include the err message + common_log(LOG_ERR, __METHOD__ . ": ignoring invalid XML"); + return; + } + + Event::handle('StartFeedSubReceive', array($this, $feed)); + Event::handle('EndFeedSubReceive', array($this, $feed)); + } + + /** + * Validate the given Atom chunk and HMAC signature against our + * shared secret that was set up at subscription time. + * + * If we don't have a shared secret, there should be no signature. + * If we we do, our the calculated HMAC should match theirs. + * + * @param string $post raw XML source as POSTed to us + * @param string $hmac X-Hub-Signature HTTP header value, or empty + * @return boolean true for a match + */ + protected function validatePushSig($post, $hmac) + { + if ($this->secret) { + if (preg_match('/^sha1=([0-9a-fA-F]{40})$/', $hmac, $matches)) { + $their_hmac = strtolower($matches[1]); + $our_hmac = hash_hmac('sha1', $post, $this->secret); + if ($their_hmac === $our_hmac) { + return true; + } + common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bad SHA-1 HMAC: got $their_hmac, expected $our_hmac"); + } else { + common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bogus HMAC '$hmac'"); + } + } else { + if (empty($hmac)) { + return true; + } else { + common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with unexpected HMAC '$hmac'"); + } + } + return false; + } + +} diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 486417617..1ce8ac491 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -18,62 +18,24 @@ */ /** - * @package FeedSubPlugin + * @package OStatusPlugin * @maintainer Brion Vibber */ -/* -PuSH subscription flow: - - $profile->subscribe() - generate random verification token - save to verify_token - sends a sub request to the hub... - - main/push/callback - hub sends confirmation back to us via GET - We verify the request, then echo back the challenge. - On our end, we save the time we subscribed and the lease expiration - - main/push/callback - hub sends us updates via POST - -*/ - -class FeedDBException extends FeedSubException -{ - public $obj; - - function __construct($obj) - { - parent::__construct('Database insert failure'); - $this->obj = $obj; - } -} - class Ostatus_profile extends Memcached_DataObject { public $__table = 'ostatus_profile'; - public $id; + public $uri; + public $profile_id; public $group_id; public $feeduri; - public $homeuri; - - // PuSH subscription data - public $huburi; - public $secret; - public $verify_token; - public $sub_state; // subscribe, active, unsubscribe - public $sub_start; - public $sub_end; - public $salmonuri; public $created; - public $lastupdate; + public $modified; public /*static*/ function staticGet($k, $v=null) { @@ -91,56 +53,30 @@ class Ostatus_profile extends Memcached_DataObject function table() { - return array('id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + return array('uri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, 'profile_id' => DB_DATAOBJECT_INT, 'group_id' => DB_DATAOBJECT_INT, 'feeduri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, - 'homeuri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, - 'huburi' => DB_DATAOBJECT_STR, - 'secret' => DB_DATAOBJECT_STR, - 'verify_token' => DB_DATAOBJECT_STR, - 'sub_state' => DB_DATAOBJECT_STR, - 'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, - 'sub_end' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, 'salmonuri' => DB_DATAOBJECT_STR, 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, - 'lastupdate' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); + 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); } static function schemaDef() { - return array(new ColumnDef('id', 'integer', - /*size*/ null, - /*nullable*/ false, - /*key*/ 'PRI', - /*default*/ '0', - /*extra*/ null, - /*auto_increment*/ true), + return array(new ColumnDef('uri', 'varchar', + 255, false, 'PRI'), new ColumnDef('profile_id', 'integer', null, true, 'UNI'), new ColumnDef('group_id', 'integer', null, true, 'UNI'), new ColumnDef('feeduri', 'varchar', 255, false, 'UNI'), - new ColumnDef('homeuri', 'varchar', - 255, false), - new ColumnDef('huburi', 'text', - null, true), - new ColumnDef('verify_token', 'varchar', - 32, true), - new ColumnDef('secret', 'varchar', - 64, true), - new ColumnDef('sub_state', "enum('subscribe','active','unsubscribe')", - null, true), - new ColumnDef('sub_start', 'datetime', - null, true), - new ColumnDef('sub_end', 'datetime', - null, true), new ColumnDef('salmonuri', 'text', null, true), new ColumnDef('created', 'datetime', null, false), - new ColumnDef('lastupdate', 'datetime', + new ColumnDef('modified', 'datetime', null, false)); } @@ -169,12 +105,12 @@ class Ostatus_profile extends Memcached_DataObject function keyTypes() { - return array('id' => 'K', 'profile_id' => 'U', 'group_id' => 'U', 'feeduri' => 'U'); + return array('uri' => 'K', 'profile_id' => 'U', 'group_id' => 'U', 'feeduri' => 'U'); } function sequenceKey() { - return array('id', true, false); + return array(false, false, false); } /** @@ -201,101 +137,6 @@ class Ostatus_profile extends Memcached_DataObject return null; } - /** - * @param FeedMunger $munger - * @param boolean $isGroup is this a group record? - * @return Ostatus_profile - */ - public static function ensureProfile($munger) - { - $profile = $munger->ostatusProfile(); - - $current = self::staticGet('feeduri', $profile->feeduri); - if ($current) { - // @fixme we should probably update info as necessary - return $current; - } - - $profile->query('BEGIN'); - - try { - $local = $munger->profile(); - - if ($profile->isGroup()) { - $group = new User_group(); - $group->nickname = $local->nickname . '@remote'; // @fixme - $group->fullname = $local->fullname; - $group->homepage = $local->homepage; - $group->location = $local->location; - $group->created = $local->created; - $group->insert(); - if (empty($result)) { - throw new FeedDBException($group); - } - $profile->group_id = $group->id; - } else { - $result = $local->insert(); - if (empty($result)) { - throw new FeedDBException($local); - } - $profile->profile_id = $local->id; - } - - $profile->created = common_sql_now(); - $profile->lastupdate = common_sql_now(); - $result = $profile->insert(); - if (empty($result)) { - throw new FeedDBException($profile); - } - - $profile->query('COMMIT'); - } catch (FeedDBException $e) { - common_log_db_error($e->obj, 'INSERT', __FILE__); - $profile->query('ROLLBACK'); - return false; - } - - $avatar = $munger->getAvatar(); - if ($avatar) { - try { - $profile->updateAvatar($avatar); - } catch (Exception $e) { - common_log(LOG_ERR, "Exception setting OStatus avatar: " . - $e->getMessage()); - } - } - - return $profile; - } - - /** - * Download and update given avatar image - * @param string $url - * @throws Exception in various failure cases - */ - public function updateAvatar($url) - { - // @fixme this should be better encapsulated - // ripped from oauthstore.php (for old OMB client) - $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); - copy($url, $temp_filename); - - // @fixme should we be using different ids? - $imagefile = new ImageFile($this->id, $temp_filename); - $filename = Avatar::filename($this->id, - image_type_to_extension($imagefile->type), - null, - common_timestamp()); - rename($temp_filename, Avatar::path($filename)); - if ($this->isGroup()) { - $group = $this->localGroup(); - $group->setOriginal($filename); - } else { - $profile = $this->localProfile(); - $profile->setOriginal($filename); - } - } - /** * Returns an XML string fragment with profile information as an * Activity Streams noun object with the given element type. @@ -345,7 +186,7 @@ class Ostatus_profile extends Memcached_DataObject $xs->element( 'id', null, - $this->homeuri); // ? + $this->uri); // ? $xs->element('title', null, $self->getBestName()); $xs->element( @@ -370,142 +211,95 @@ class Ostatus_profile extends Memcached_DataObject } /** - * Send a subscription request to the hub for this feed. - * The hub will later send us a confirmation POST to /main/push/callback. + * Subscribe a local user to this remote user. + * PuSH subscription will be started if necessary, and we'll + * send a Salmon notification to the remote server if available + * notifying them of the sub. * - * @return bool true on success, false on failure - * @throws ServerException if feed state is not valid + * @param User $user + * @return boolean success + * @throws FeedException */ - public function subscribe($mode='subscribe') + public function subscribeLocalToRemote(User $user) { - if ($this->sub_state != '') { - throw new ServerException("Attempting to start PuSH subscription to feed in state $this->sub_state"); + if ($this->isGroup()) { + throw new ServerException("Can't subscribe to a remote group"); } - if (empty($this->huburi)) { - if (common_config('feedsub', 'nohub')) { - // Fake it! We're just testing remote feeds w/o hubs. + + if ($this->subscribe()) { + if ($user->subscribeTo($this->localProfile())) { + $this->notify($user->getProfile(), ActivityVerb::FOLLOW, $this); return true; - } else { - throw new ServerException("Attempting to start PuSH subscription for feed with no hub"); } } - - return $this->doSubscribe('subscribe'); + return false; } /** - * Send a PuSH unsubscription request to the hub for this feed. - * The hub will later send us a confirmation POST to /main/push/callback. + * Mark this remote profile as subscribing to the given local user, + * and send appropriate notifications to the user. * - * @return bool true on success, false on failure - * @throws ServerException if feed state is not valid + * This will generally be in response to a subscription notification + * from a foreign site to our local Salmon response channel. + * + * @param User $user + * @return boolean success */ - public function unsubscribe() { - if ($this->sub_state != 'active') { - throw new ServerException("Attempting to end PuSH subscription to feed in state $this->sub_state"); - } - if (empty($this->huburi)) { - if (common_config('feedsub', 'nohub')) { - // Fake it! We're just testing remote feeds w/o hubs. - return true; - } else { - throw new ServerException("Attempting to end PuSH subscription for feed with no hub"); - } - } - - return $this->doSubscribe('unsubscribe'); - } - - protected function doSubscribe($mode) + public function subscribeRemoteToLocal(User $user) { - $orig = clone($this); - $this->verify_token = common_good_rand(16); - if ($mode == 'subscribe') { - $this->secret = common_good_rand(32); - } - $this->sub_state = $mode; - $this->update($orig); - unset($orig); - - try { - $callback = common_local_url('pushcallback', array('feed' => $this->id)); - $headers = array('Content-Type: application/x-www-form-urlencoded'); - $post = array('hub.mode' => $mode, - 'hub.callback' => $callback, - 'hub.verify' => 'async', - 'hub.verify_token' => $this->verify_token, - 'hub.secret' => $this->secret, - //'hub.lease_seconds' => 0, - 'hub.topic' => $this->feeduri); - $client = new HTTPClient(); - $response = $client->post($this->huburi, $headers, $post); - $status = $response->getStatus(); - if ($status == 202) { - common_log(LOG_INFO, __METHOD__ . ': sub req ok, awaiting verification callback'); - return true; - } else if ($status == 204) { - common_log(LOG_INFO, __METHOD__ . ': sub req ok and verified'); - return true; - } else if ($status >= 200 && $status < 300) { - common_log(LOG_ERR, __METHOD__ . ": sub req returned unexpected HTTP $status: " . $response->getBody()); - return false; - } else { - common_log(LOG_ERR, __METHOD__ . ": sub req failed with HTTP $status: " . $response->getBody()); - return false; - } - } catch (Exception $e) { - // wtf! - common_log(LOG_ERR, __METHOD__ . ": error \"{$e->getMessage()}\" hitting hub $this->huburi subscribing to $this->feeduri"); + if ($this->isGroup()) { + throw new ServerException("Remote groups can't subscribe to local users"); + } - $orig = clone($this); - $this->verify_token = null; - $this->sub_state = null; - $this->update($orig); - unset($orig); + // @fixme use regular channels for subbing, once they accept remote profiles + $sub = new Subscription(); + $sub->subscriber = $this->profile_id; + $sub->subscribed = $user->id; + $sub->created = common_sql_now(); // current time - return false; + if ($sub->insert()) { + // @fixme use subs_notify() if refactored to take profiles? + mail_subscribe_notify_profile($user, $this->localProfile()); + return true; } + return false; } /** - * Save PuSH subscription confirmation. - * Sets approximate lease start and end times and finalizes state. + * Send a subscription request to the hub for this feed. + * The hub will later send us a confirmation POST to /main/push/callback. * - * @param int $lease_seconds provided hub.lease_seconds parameter, if given + * @return bool true on success, false on failure + * @throws ServerException if feed state is not valid */ - public function confirmSubscribe($lease_seconds=0) + public function subscribe($mode='subscribe') { - $original = clone($this); - - $this->sub_state = 'active'; - $this->sub_start = common_sql_date(time()); - if ($lease_seconds > 0) { - $this->sub_end = common_sql_date(time() + $lease_seconds); - } else { - $this->sub_end = null; + $feedsub = FeedSub::ensureFeed($this->feeduri); + if ($feedsub->sub_state == 'active' || $feedsub->sub_state == 'subscribe') { + return true; + } else if ($feedsub->sub_state == '' || $feedsub->sub_state == 'inactive') { + return $feedsub->subscribe(); + } else if ('unsubscribe') { + throw new FeedSubException("Unsub is pending, can't subscribe..."); } - $this->lastupdate = common_sql_now(); - - return $this->update($original); } /** - * Save PuSH unsubscription confirmation. - * Wipes active PuSH sub info and resets state. + * Send a PuSH unsubscription request to the hub for this feed. + * The hub will later send us a confirmation POST to /main/push/callback. + * + * @return bool true on success, false on failure + * @throws ServerException if feed state is not valid */ - public function confirmUnsubscribe() - { - $original = clone($this); - - // @fixme these should all be null, but DB_DataObject doesn't save null values...????? - $this->verify_token = ''; - $this->secret = ''; - $this->sub_state = ''; - $this->sub_start = ''; - $this->sub_end = ''; - $this->lastupdate = common_sql_now(); - - return $this->update($original); + public function unsubscribe() { + $feedsub = FeedSub::staticGet('uri', $this->feeduri); + if ($feedsub->sub_state == 'active') { + return $feedsub->unsubscribe(); + } else if ($feedsub->sub_state == '' || $feedsub->sub_state == 'inactive' || $feedsub->sub_state == 'unsubscribe') { + return true; + } else if ($feedsub->sub_state == 'subscribe') { + throw new FeedSubException("Feed is awaiting subscription, can't unsub..."); + } } /** @@ -543,10 +337,7 @@ class Ostatus_profile extends Memcached_DataObject $entry->elementEnd('entry'); $feed = $this->atomFeed($actor); - #$feed->initFeed(); $feed->addEntry($entry); - #$feed->renderEntries(); - #$feed->endFeed(); $xml = $feed->getString(); common_log(LOG_INFO, "Posting to Salmon endpoint $salmon: $xml"); @@ -600,36 +391,10 @@ class Ostatus_profile extends Memcached_DataObject * Currently assumes that all items in the feed are new, * coming from a PuSH hub. * - * @param string $post source of Atom or RSS feed - * @param string $hmac X-Hub-Signature header, if present + * @param DOMDocument $feed */ - public function postUpdates($post, $hmac) + public function processFeed($feed) { - common_log(LOG_INFO, __METHOD__ . ": packet for \"$this->feeduri\"! $hmac $post"); - - if ($this->sub_state != 'active') { - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH for inactive feed $this->feeduri (in state '$this->sub_state')"); - return; - } - - if ($post === '') { - common_log(LOG_ERR, __METHOD__ . ": ignoring empty post"); - return; - } - - if (!$this->validatePushSig($post, $hmac)) { - // Per spec we silently drop input with a bad sig, - // while reporting receipt to the server. - return; - } - - $feed = new DOMDocument(); - if (!$feed->loadXML($post)) { - // @fixme might help to include the err message - common_log(LOG_ERR, __METHOD__ . ": ignoring invalid XML"); - return; - } - $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); if ($entries->length == 0) { common_log(LOG_ERR, __METHOD__ . ": no entries in feed update, ignoring"); @@ -642,40 +407,6 @@ class Ostatus_profile extends Memcached_DataObject } } - /** - * Validate the given Atom chunk and HMAC signature against our - * shared secret that was set up at subscription time. - * - * If we don't have a shared secret, there should be no signature. - * If we we do, our the calculated HMAC should match theirs. - * - * @param string $post raw XML source as POSTed to us - * @param string $hmac X-Hub-Signature HTTP header value, or empty - * @return boolean true for a match - */ - protected function validatePushSig($post, $hmac) - { - if ($this->secret) { - if (preg_match('/^sha1=([0-9a-fA-F]{40})$/', $hmac, $matches)) { - $their_hmac = strtolower($matches[1]); - $our_hmac = hash_hmac('sha1', $post, $this->secret); - if ($their_hmac === $our_hmac) { - return true; - } - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bad SHA-1 HMAC: got $their_hmac, expected $our_hmac"); - } else { - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with bogus HMAC '$hmac'"); - } - } else { - if (empty($hmac)) { - return true; - } else { - common_log(LOG_ERR, __METHOD__ . ": ignoring PuSH with unexpected HMAC '$hmac'"); - } - } - return false; - } - /** * Process a posted entry from this feed source. * @@ -704,14 +435,14 @@ class Ostatus_profile extends Memcached_DataObject { if ($this->isGroup()) { // @fixme validate these profiles in some way! - $oprofile = $this->ensureActorProfile($activity); + $oprofile = self::ensureActorProfile($activity); } else { - $actorUri = $this->getActorProfileURI($activity); - if ($actorUri == $this->homeuri) { + $actorUri = self::getActorProfileURI($activity); + if ($actorUri == $this->uri) { // @fixme check if profile info has changed and update it } else { // @fixme drop or reject the messages once we've got the canonical profile URI recorded sanely - common_log(LOG_INFO, "OStatus: Warning: non-group post with unexpected author: $actorUri expected $this->homeuri"); + common_log(LOG_INFO, "OStatus: Warning: non-group post with unexpected author: $actorUri expected $this->uri"); //return; } $oprofile = $this; @@ -787,6 +518,65 @@ class Ostatus_profile extends Memcached_DataObject return false; } + /** + * @param string $profile_url + * @return Ostatus_profile + * @throws FeedSubException + */ + public static function ensureProfile($profile_uri) + { + // Get the canonical feed URI and check it + $discover = new FeedDiscovery(); + $feeduri = $discover->discoverFromURL($profile_uri); + + $feedsub = FeedSub::ensureFeed($feeduri, $discover->feed); + $huburi = $discover->getAtomLink('hub'); + $salmonuri = $discover->getAtomLink('salmon'); + + if (!$huburi) { + // We can only deal with folks with a PuSH hub + throw new FeedSubNoHubException(); + } + + // Ok this is going to be a terrible hack! + // Won't be suitable for groups, empty feeds, or getting + // info that's only available on the profile page. + $entries = $discover->feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); + if (!$entries || $entries->length == 0) { + throw new FeedSubException('empty feed'); + } + $first = new Activity($entries->item(0), $discover->feed); + return self::ensureActorProfile($first, $feeduri); + } + + /** + * Download and update given avatar image + * @param string $url + * @throws Exception in various failure cases + */ + protected function updateAvatar($url) + { + // @fixme this should be better encapsulated + // ripped from oauthstore.php (for old OMB client) + $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); + copy($url, $temp_filename); + + // @fixme should we be using different ids? + $imagefile = new ImageFile($this->id, $temp_filename); + $filename = Avatar::filename($this->id, + image_type_to_extension($imagefile->type), + null, + common_timestamp()); + rename($temp_filename, Avatar::path($filename)); + if ($this->isGroup()) { + $group = $this->localGroup(); + $group->setOriginal($filename); + } else { + $profile = $this->localProfile(); + $profile->setOriginal($filename); + } + } + /** * Get an appropriate avatar image source URL, if available. * @@ -794,7 +584,7 @@ class Ostatus_profile extends Memcached_DataObject * @param DOMElement $feed * @return string */ - function getAvatar($actor, $feed) + protected static function getAvatar($actor, $feed) { $url = ''; $icon = ''; @@ -833,13 +623,18 @@ class Ostatus_profile extends Memcached_DataObject } /** - * @fixme move off of ostatus_profile or static? + * Fetch, or build if necessary, an Ostatus_profile for the actor + * in a given Activity Streams activity. + * + * @param Activity $activity + * @param string $feeduri if we already know the canonical feed URI! + * @return Ostatus_profile */ - function ensureActorProfile($activity) + public static function ensureActorProfile($activity, $feeduri=null) { - $profile = $this->getActorProfile($activity); + $profile = self::getActorProfile($activity); if (!$profile) { - $profile = $this->createActorProfile($activity); + $profile = self::createActorProfile($activity, $feeduri); } return $profile; } @@ -848,10 +643,10 @@ class Ostatus_profile extends Memcached_DataObject * @param Activity $activity * @return mixed matching Ostatus_profile or false if none known */ - function getActorProfile($activity) + protected static function getActorProfile($activity) { - $homeuri = $this->getActorProfileURI($activity); - return Ostatus_profile::staticGet('homeuri', $homeuri); + $homeuri = self::getActorProfileURI($activity); + return self::staticGet('uri', $homeuri); } /** @@ -859,7 +654,7 @@ class Ostatus_profile extends Memcached_DataObject * @return string * @throws ServerException */ - function getActorProfileURI($activity) + protected static function getActorProfileURI($activity) { $opts = array('allowed_schemes' => array('http', 'https')); $actor = $activity->actor; @@ -873,14 +668,19 @@ class Ostatus_profile extends Memcached_DataObject } /** - * + * @fixme validate stuff somewhere */ - function createActorProfile($activity) + protected static function createActorProfile($activity, $feeduri=null) { - $actor = $activity->actor(); - $homeuri = $this->getActivityProfileURI($activity); - $nickname = $this->getAuthorNick($activity); - $avatar = $this->getAvatar($actor, $feed); + $actor = $activity->actor; + $homeuri = self::getActorProfileURI($activity); + $nickname = self::getAuthorNick($activity); + $avatar = self::getAvatar($actor, $feed); + + if (!$homeuri) { + common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true)); + throw new ServerException("No profile URI"); + } $profile = new Profile(); $profile->nickname = $nickname; @@ -894,9 +694,7 @@ class Ostatus_profile extends Memcached_DataObject // @todo lat/lon/location? $ok = $profile->insert(); - if ($ok) { - $this->updateAvatar($profile, $avatar); - } else { + if (!$ok) { throw new ServerException("Can't save local profile"); } @@ -904,11 +702,15 @@ class Ostatus_profile extends Memcached_DataObject // or need to split out some of the feed stuff // so we can leave it empty until later. $oprofile = new Ostatus_profile(); - $oprofile->homeuri = $homeuri; + $oprofile->uri = $homeuri; + if ($feeduri) { + $oprofile->feeduri = $feeduri; + } $oprofile->profile_id = $profile->id; $ok = $oprofile->insert(); if ($ok) { + $oprofile->updateAvatar($avatar); return $oprofile; } else { throw new ServerException("Can't save OStatus profile"); @@ -920,13 +722,13 @@ class Ostatus_profile extends Memcached_DataObject * @param Activity $activity * @return string */ - function getAuthorNick($activity) + protected static function getAuthorNick($activity) { // @fixme not technically part of the actor? foreach (array($activity->entry, $activity->feed) as $source) { - $author = ActivityUtil::child($source, 'author', Activity::ATOM); + $author = ActivityUtils::child($source, 'author', Activity::ATOM); if ($author) { - $name = ActivityUtil::child($author, 'name', Activity::ATOM); + $name = ActivityUtils::child($author, 'name', Activity::ATOM); if ($name) { return trim($name->textContent); } diff --git a/plugins/OStatus/lib/feeddiscovery.php b/plugins/OStatus/lib/feeddiscovery.php index 39985fc90..7afb71bdc 100644 --- a/plugins/OStatus/lib/feeddiscovery.php +++ b/plugins/OStatus/lib/feeddiscovery.php @@ -48,6 +48,14 @@ class FeedSubNoFeedException extends FeedSubException { } +class FeedSubBadXmlException extends FeedSubException +{ +} + +class FeedSubNoHubException extends FeedSubException +{ +} + /** * Given a web page or feed URL, discover the final location of the feed * and return its current contents. @@ -57,21 +65,25 @@ class FeedSubNoFeedException extends FeedSubException * if ($feed->discoverFromURL($url)) { * print $feed->uri; * print $feed->type; - * processFeed($feed->body); + * processFeed($feed->feed); // DOMDocument * } */ class FeedDiscovery { public $uri; public $type; - public $body; + public $feed; + /** Post-initialize query helper... */ + public function getLink($rel, $type=null) + { + // @fixme check for non-Atom links in RSS2 feeds as well + return self::getAtomLink($rel, $type); + } - public function feedMunger() + public function getAtomLink($rel, $type=null) { - require_once 'XML/Feed/Parser.php'; - $feed = new XML_Feed_Parser($this->body, false, false, true); // @fixme - return new FeedMunger($feed, $this->uri); + return ActivityUtils::getLink($this->feed->documentElement, $rel, $type); } /** @@ -90,6 +102,7 @@ class FeedDiscovery $client = new HTTPClient(); $response = $client->get($url); } catch (HTTP_Request2_Exception $e) { + common_log(LOG_ERR, __METHOD__ . " Failure for $url - " . $e->getMessage()); throw new FeedSubBadURLException($e); } @@ -107,7 +120,12 @@ class FeedDiscovery return $this->initFromResponse($response); } - + + function discoverFromFeedURL($url) + { + return $this->discoverFromURL($url, false); + } + function initFromResponse($response) { if (!$response->isOk()) { @@ -122,16 +140,26 @@ class FeedDiscovery $type = $response->getHeader('Content-Type'); if (preg_match('!^(text/xml|application/xml|application/(rss|atom)\+xml)!i', $type)) { - $this->uri = $sourceurl; - $this->type = $type; - $this->body = $body; - return true; + return $this->init($sourceurl, $type, $body); } else { common_log(LOG_WARNING, "Unrecognized feed type $type for $sourceurl"); throw new FeedSubUnrecognizedTypeException($type); } } + function init($sourceurl, $type, $body) + { + $feed = new DOMDocument(); + if ($feed->loadXML($body)) { + $this->uri = $sourceurl; + $this->type = $type; + $this->feed = $feed; + return $this->uri; + } else { + throw new FeedSubBadXmlException($url); + } + } + /** * @param string $url source URL, used to resolve relative links * @param string $body HTML body text diff --git a/plugins/OStatus/lib/feedmunger.php b/plugins/OStatus/lib/feedmunger.php deleted file mode 100644 index e8c46de90..000000000 --- a/plugins/OStatus/lib/feedmunger.php +++ /dev/null @@ -1,350 +0,0 @@ -. - */ - -/** - * @package FeedSubPlugin - * @maintainer Brion Vibber - */ - -if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } - -class FeedSubPreviewNotice extends Notice -{ - protected $fetched = true; - - function __construct($profile) - { - $this->profile = $profile; - $this->profile_id = 0; - } - - function getProfile() - { - return $this->profile; - } - - function find() - { - return true; - } - - function fetch() - { - $got = $this->fetched; - $this->fetched = false; - return $got; - } -} - -class FeedSubPreviewProfile extends Profile -{ - function getAvatar($width, $height=null) - { - return new FeedSubPreviewAvatar($width, $height, $this->avatar); - } -} - -class FeedSubPreviewAvatar extends Avatar -{ - function __construct($width, $height, $remote) - { - $this->remoteImage = $remote; - } - - function displayUrl() { - return $this->remoteImage; - } -} - -class FeedMunger -{ - /** - * @param XML_Feed_Parser $feed - */ - function __construct($feed, $url=null) - { - $this->feed = $feed; - $this->url = $url; - } - - function ostatusProfile() - { - $profile = new Ostatus_profile(); - $profile->feeduri = $this->url; - $profile->homeuri = $this->feed->link; - $profile->huburi = $this->getHubLink(); - $salmon = $this->getSalmonLink(); - if ($salmon) { - $profile->salmonuri = $salmon; - } - return $profile; - } - - function getAtomLink($item, $attribs=array()) - { - // XML_Feed_Parser gets confused by multiple elements. - $dom = $item->model; - - // Note that RSS feeds would embed an so this should work for both. - /// http://code.google.com/p/pubsubhubbub/wiki/RssFeeds - // - $links = $dom->getElementsByTagNameNS('http://www.w3.org/2005/Atom', 'link'); - for ($i = 0; $i < $links->length; $i++) { - $node = $links->item($i); - if ($node->hasAttributes()) { - $href = $node->attributes->getNamedItem('href'); - if ($href) { - $matches = 0; - foreach ($attribs as $name => $val) { - $attrib = $node->attributes->getNamedItem($name); - if ($attrib && $attrib->value == $val) { - $matches++; - } - } - if ($matches == count($attribs)) { - return $href->value; - } - } - } - } - return false; - } - - function getRssLink($item) - { - // XML_Feed_Parser gets confused by multiple elements. - $dom = $item->model; - - // Note that RSS feeds would embed an so this should work for both. - /// http://code.google.com/p/pubsubhubbub/wiki/RssFeeds - // - $links = $dom->getElementsByTagName('link'); - for ($i = 0; $i < $links->length; $i++) { - $node = $links->item($i); - if (!$node->hasAttributes()) { - return $node->textContent; - } - } - return false; - } - - function getAltLink($item) - { - // Check for an atom link... - $link = $this->getAtomLink($item, array('rel' => 'alternate', 'type' => 'text/html')); - if (!$link) { - $link = $this->getRssLink($item); - } - return $link; - } - - function getHubLink() - { - return $this->getAtomLink($this->feed, array('rel' => 'hub')); - } - - function getSalmonLink() - { - return $this->getAtomLink($this->feed, array('rel' => 'salmon')); - } - - function getSelfLink() - { - return $this->getAtomLink($this->feed, array('rel' => 'self')); - } - - /** - * Get an appropriate avatar image source URL, if available. - * @return mixed string or false - */ - function getAvatar() - { - $logo = $this->feed->logo; - if ($logo) { - return $logo; - } - $icon = $this->feed->icon; - if ($icon) { - return $icon; - } - return common_path('plugins/OStatus/images/48px-Feed-icon.svg.png'); - } - - function profile($preview=false) - { - if ($preview) { - $profile = new FeedSubPreviewProfile(); - } else { - $profile = new Profile(); - } - - // @todo validate/normalize nick? - $profile->nickname = $this->feed->title; - $profile->fullname = $this->feed->title; - $profile->homepage = $this->getAltLink($this->feed); - $profile->bio = $this->feed->description; - $profile->profileurl = $this->getAltLink($this->feed); - - if ($preview) { - $profile->avatar = $this->getAvatar(); - } - - // @todo tags from categories - // @todo lat/lon/location? - - return $profile; - } - - function notice($index=1, $preview=false) - { - $entry = $this->feed->getEntryByOffset($index); - if (!$entry) { - return null; - } - - if ($preview) { - $notice = new FeedSubPreviewNotice($this->profile(true)); - $notice->id = -1; - } else { - $notice = new Notice(); - $notice->profile_id = $this->profileIdForEntry($index); - } - - $link = $this->getAltLink($entry); - if (empty($link)) { - if (preg_match('!^https?://!', $entry->id)) { - $link = $entry->id; - common_log(LOG_DEBUG, "No link on entry, using URL from id: $link"); - } - } - $notice->uri = $link; - $notice->url = $link; - $notice->content = $this->noticeFromEntry($entry); - $notice->rendered = common_render_content($notice->content, $notice); // @fixme this is failing on group posts - $notice->created = common_sql_date($entry->updated); // @fixme - $notice->is_local = Notice::GATEWAY; - $notice->source = 'feed'; - - $location = $this->getLocation($entry); - if ($location) { - if ($location->location_id) { - $notice->location_ns = $location->location_ns; - $notice->location_id = $location->location_id; - } - $notice->lat = $location->lat; - $notice->lon = $location->lon; - } - - return $notice; - } - - function profileIdForEntry($index=1) - { - // hack hack hack - // should get profile for this entry's author... - $feeduri = $this->getSelfLink(); - $remote = Ostatus_profile::staticGet('feeduri', $feeduri); - if ($remote) { - return $remote->profile_id; - } else { - throw new Exception("Can't find feed profile for $feeduri"); - } - } - - /** - * Parse location given as a GeoRSS-simple point, if provided. - * http://www.georss.org/simple - * - * @param feed item $entry - * @return mixed Location or false - */ - function getLocation($entry) - { - $dom = $entry->model; - $points = $dom->getElementsByTagNameNS('http://www.georss.org/georss', 'point'); - - for ($i = 0; $i < $points->length; $i++) { - $point = $points->item(0)->textContent; - $point = str_replace(',', ' ', $point); // per spec "treat commas as whitespace" - $point = preg_replace('/\s+/', ' ', $point); - $point = trim($point); - $coords = explode(' ', $point); - if (count($coords) == 2) { - list($lat, $lon) = $coords; - if (is_numeric($lat) && is_numeric($lon)) { - common_log(LOG_INFO, "Looking up location for $lat $lon from georss"); - return Location::fromLatLon($lat, $lon); - } - } - common_log(LOG_ERR, "Ignoring bogus georss:point value $point"); - } - - return false; - } - - /** - * @param XML_Feed_Type $entry - * @return string notice text, within post size limit - */ - function noticeFromEntry($entry) - { - $max = Notice::maxContent(); - $ellipsis = "\xe2\x80\xa6"; // U+2026 HORIZONTAL ELLIPSIS - $title = $entry->title; - $link = $entry->link; - - // @todo We can get entries like this: - // $cats = $entry->getCategory('category', array(0, true)); - // but it feels like an awful hack. If it's accessible cleanly, - // try adding #hashtags from the categories/tags on a post. - - $title = $entry->title; - $link = $this->getAltLink($entry); - if ($link) { - // Blog post or such... - // @todo Should we force a language here? - $format = _m('New post: "%1$s" %2$s'); - $out = sprintf($format, $title, $link); - - // Trim link if needed... - if (mb_strlen($out) > $max) { - $link = common_shorten_url($link); - $out = sprintf($format, $title, $link); - } - - // Trim title if needed... - if (mb_strlen($out) > $max) { - $used = mb_strlen($out) - mb_strlen($title); - $available = $max - $used - mb_strlen($ellipsis); - $title = mb_substr($title, 0, $available) . $ellipsis; - $out = sprintf($format, $title, $link); - } - } else { - // No link? Consider a bare status update. - if (mb_strlen($title) > $max) { - $available = $max - mb_strlen($ellipsis); - $out = mb_substr($title, 0, $available) . $ellipsis; - } else { - $out = $title; - } - } - - return $out; - } -} -- cgit v1.2.3-54-g00ecf From 48edade751cca3ac7363808264a76c470a520528 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 18 Feb 2010 22:18:14 -0500 Subject: add ActivityContext class and test it --- plugins/OStatus/actions/salmon.php | 1 + plugins/OStatus/classes/Ostatus_profile.php | 32 +---------- plugins/OStatus/lib/activity.php | 85 +++++++++++++++++++++++++++- plugins/OStatus/tests/ActivityParseTests.php | 53 ++++++++++++++++- 4 files changed, 138 insertions(+), 33 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index ea5b8e4ea..e9d6015f4 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -71,6 +71,7 @@ class SalmonAction extends Action /** * @fixme probably call Ostatus_profile::processFeed */ + function handle($args) { common_log(LOG_INFO, 'Salmon: incoming post for user '. $this->user->id); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 1ce8ac491..e0cb467e5 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -488,36 +488,6 @@ class Ostatus_profile extends Memcached_DataObject $params); } - /** - * Parse location given as a GeoRSS-simple point, if provided. - * http://www.georss.org/simple - * - * @param feed item $entry - * @return mixed Location or false - */ - function getLocation($dom) - { - $points = $dom->getElementsByTagNameNS('http://www.georss.org/georss', 'point'); - - for ($i = 0; $i < $points->length; $i++) { - $point = $points->item(0)->textContent; - $point = str_replace(',', ' ', $point); // per spec "treat commas as whitespace" - $point = preg_replace('/\s+/', ' ', $point); - $point = trim($point); - $coords = explode(' ', $point); - if (count($coords) == 2) { - list($lat, $lon) = $coords; - if (is_numeric($lat) && is_numeric($lon)) { - common_log(LOG_INFO, "Looking up location for $lat $lon from georss"); - return Location::fromLatLon($lat, $lon); - } - } - common_log(LOG_ERR, "Ignoring bogus georss:point value $point"); - } - - return false; - } - /** * @param string $profile_url * @return Ostatus_profile @@ -560,7 +530,7 @@ class Ostatus_profile extends Memcached_DataObject // ripped from oauthstore.php (for old OMB client) $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); copy($url, $temp_filename); - + // @fixme should we be using different ids? $imagefile = new ImageFile($this->id, $temp_filename); $filename = Avatar::filename($this->id, diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index 3ed613dc7..5bc8f78e5 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -311,6 +311,87 @@ class ActivityVerb const LEAVE = 'http://ostatus.org/schema/1.0/leave'; } +class ActivityContext +{ + public $replyToID; + public $replyToUrl; + public $location; + public $attention = array(); + public $conversation; + + const THR = 'http://purl.org/syndication/thread/1.0'; + const GEORSS = 'http://www.georss.org/georss'; + const OSTATUS = 'http://ostatus.org/schema/1.0'; + + const INREPLYTO = 'in-reply-to'; + const REF = 'ref'; + const HREF = 'href'; + + const POINT = 'point'; + + const ATTENTION = 'ostatus:attention'; + const CONVERSATION = 'ostatus:conversation'; + + function __construct($element) + { + $replyToEl = ActivityUtils::child($element, self::INREPLYTO, self::THR); + + if (!empty($replyToEl)) { + $this->replyToID = $replyToEl->getAttribute(self::REF); + $this->replyToUrl = $replyToEl->getAttribute(self::HREF); + } + + $this->location = $this->getLocation($element); + + $this->conversation = ActivityUtils::getLink($element, self::CONVERSATION); + + // Multiple attention links allowed + + $links = $element->getElementsByTagNameNS(ActivityUtils::ATOM, ActivityUtils::LINK); + + for ($i = 0; $i < $links->length; $i++) { + + $link = $links->item($i); + + $linkRel = $link->getAttribute(ActivityUtils::REL); + + if ($linkRel == self::ATTENTION) { + $this->attention[] = $link->getAttribute(self::HREF); + } + } + } + + /** + * Parse location given as a GeoRSS-simple point, if provided. + * http://www.georss.org/simple + * + * @param feed item $entry + * @return mixed Location or false + */ + function getLocation($dom) + { + $points = $dom->getElementsByTagNameNS(self::GEORSS, self::POINT); + + for ($i = 0; $i < $points->length; $i++) { + $point = $points->item($i)->textContent; + $point = str_replace(',', ' ', $point); // per spec "treat commas as whitespace" + $point = preg_replace('/\s+/', ' ', $point); + $point = trim($point); + $coords = explode(' ', $point); + if (count($coords) == 2) { + list($lat, $lon) = $coords; + if (is_numeric($lat) && is_numeric($lon)) { + common_log(LOG_INFO, "Looking up location for $lat $lon from georss"); + return Location::fromLatLon($lat, $lon); + } + } + common_log(LOG_ERR, "Ignoring bogus georss:point value $point"); + } + + return null; + } +} + /** * An activity in the ActivityStrea.ms world * @@ -426,7 +507,9 @@ class Activity $contextEl = $this->_child($entry, self::CONTEXT); if (!empty($contextEl)) { - $this->context = new ActivityObject($contextEl); + $this->context = new ActivityContext($contextEl); + } else { + $this->context = new ActivityContext($entry); } $targetEl = $this->_child($entry, self::TARGET); diff --git a/plugins/OStatus/tests/ActivityParseTests.php b/plugins/OStatus/tests/ActivityParseTests.php index fa8bcdda2..35b4b0f9d 100644 --- a/plugins/OStatus/tests/ActivityParseTests.php +++ b/plugins/OStatus/tests/ActivityParseTests.php @@ -57,12 +57,35 @@ class ActivityParseTests extends PHPUnit_Framework_TestCase $this->assertEquals($act->object->summary, 'Some text.'); $this->assertEquals($act->object->link, 'http://example.org/2003/12/13/atom03.html'); - $this->assertTrue(empty($act->context)); + $this->assertFalse(empty($act->context)); + $this->assertTrue(empty($act->target)); $this->assertEquals($act->entry, $entry); $this->assertEquals($act->feed, $feed); } + + public function testExample4() + { + global $_example4; + $dom = DOMDocument::loadXML($_example4); + + $entry = $dom->documentElement; + + $act = new Activity($entry); + + $this->assertFalse(empty($act)); + $this->assertEquals(1266547958, $act->time); + $this->assertEquals('http://example.net/notice/14', $act->link); + + $this->assertFalse(empty($act->context)); + $this->assertEquals('http://example.net/notice/12', $act->context->replyToID); + $this->assertEquals('http://example.net/notice/12', $act->context->replyToUrl); + $this->assertEquals('http://example.net/conversation/11', $act->context->conversation); + $this->assertEquals(array('http://example.net/user/1'), $act->context->attention); + + $this->assertFalse(empty($act->actor)); + } } $_example1 = << EXAMPLE3; + +$_example4 = << + + @evan now is the time for all good men to come to the aid of their country. #thetime + @evan now is the time for all good men to come to the aid of their country. #thetime + + spock + http://example.net/user/2 + + + http://activitystrea.ms/schema/1.0/person + http://example.net/user/2 + spock + + + + http://example.net/notice/14 + 2010-02-19T02:52:38+00:00 + 2010-02-19T02:52:38+00:00 + + + + + @<span class="vcard"><a href="http://example.net/user/1" class="url"><span class="fn nickname">evan</span></a></span> now is the time for all good men to come to the aid of their country. #<span class="tag"><a href="http://example.net/tag/thetime" rel="tag">thetime</a></span> + + +EXAMPLE4; -- cgit v1.2.3-54-g00ecf From 114eb310ca4f53e09c98f2337850829a9036c133 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 19 Feb 2010 10:29:06 -0800 Subject: OStatus: fix up Salmon endpoint detection/saving, timestamp fixes. --- plugins/OStatus/actions/salmon.php | 2 +- plugins/OStatus/classes/FeedSub.php | 2 +- plugins/OStatus/classes/Ostatus_profile.php | 18 +++++++++++++----- 3 files changed, 15 insertions(+), 7 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index e9d6015f4..2da9db9eb 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -142,7 +142,7 @@ class SalmonAction extends Action function ensureProfile() { $actor = $this->act->actor; - + common_log(LOG_DEBUG, "Received salmon bit: " . var_export($this->act, true)); if (empty($actor->id)) { throw new Exception("Received a salmon slap from unidentified actor."); } diff --git a/plugins/OStatus/classes/FeedSub.php b/plugins/OStatus/classes/FeedSub.php index dc2c0b710..bf9d063fa 100644 --- a/plugins/OStatus/classes/FeedSub.php +++ b/plugins/OStatus/classes/FeedSub.php @@ -99,7 +99,7 @@ class FeedSub extends Memcached_DataObject 'sub_state' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, 'sub_start' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, 'sub_end' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, - 'last_update' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, + 'last_update' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME, 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index e0cb467e5..319b76a66 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -328,7 +328,7 @@ class Ostatus_profile extends Memcached_DataObject $entry->element('id', null, $id); $entry->element('title', null, $text); $entry->element('summary', null, $text); - $entry->element('published', null, common_date_w3dtf(time())); + $entry->element('published', null, common_date_w3dtf(common_sql_now())); $entry->element('activity:verb', null, $verb); $entry->raw($actor->asAtomAuthor()); @@ -516,7 +516,7 @@ class Ostatus_profile extends Memcached_DataObject throw new FeedSubException('empty feed'); } $first = new Activity($entries->item(0), $discover->feed); - return self::ensureActorProfile($first, $feeduri); + return self::ensureActorProfile($first, $feeduri, $salmonuri); } /** @@ -598,13 +598,14 @@ class Ostatus_profile extends Memcached_DataObject * * @param Activity $activity * @param string $feeduri if we already know the canonical feed URI! + * @param string $salmonuri if we already know the salmon return channel URI * @return Ostatus_profile */ - public static function ensureActorProfile($activity, $feeduri=null) + public static function ensureActorProfile($activity, $feeduri=null, $salmonuri=null) { $profile = self::getActorProfile($activity); if (!$profile) { - $profile = self::createActorProfile($activity, $feeduri); + $profile = self::createActorProfile($activity, $feeduri, $salmonuri); } return $profile; } @@ -640,7 +641,7 @@ class Ostatus_profile extends Memcached_DataObject /** * @fixme validate stuff somewhere */ - protected static function createActorProfile($activity, $feeduri=null) + protected static function createActorProfile($activity, $feeduri=null, $salmonuri=null) { $actor = $activity->actor; $homeuri = self::getActorProfileURI($activity); @@ -674,10 +675,17 @@ class Ostatus_profile extends Memcached_DataObject $oprofile = new Ostatus_profile(); $oprofile->uri = $homeuri; if ($feeduri) { + // If we don't have these, we can look them up later. $oprofile->feeduri = $feeduri; + if ($salmonuri) { + $oprofile->salmonuri = $salmonuri; + } } $oprofile->profile_id = $profile->id; + $oprofile->created = common_sql_now(); + $oprofile->modified = common_sql_now(); + $ok = $oprofile->insert(); if ($ok) { $oprofile->updateAvatar($avatar); -- cgit v1.2.3-54-g00ecf From a1a3ab1c58cf8636c4e9ac6b9d44bdc18946a547 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 19 Feb 2010 12:08:07 -0800 Subject: OStatus: hooked up follow/unfollow events on Salmon endpoint to create/destroy remote subscriber relationships --- plugins/OStatus/OStatusPlugin.php | 9 +++- plugins/OStatus/actions/feedsubsettings.php | 8 ---- plugins/OStatus/actions/salmon.php | 66 +++++++++++++++++++++++++---- plugins/OStatus/classes/Ostatus_profile.php | 22 +++++++--- 4 files changed, 81 insertions(+), 24 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 4ebe4551e..4c43d8a30 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -253,17 +253,22 @@ class OStatusPlugin extends Plugin */ function onEndUnsubscribe($user, $other) { + if ($user instanceof Profile) { + $profile = $user; + } else if ($user instanceof Profile) { + $profile = $user->getProfile(); + } $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); if ($oprofile) { // Notify the remote server of the unsub, if supported. - $oprofile->notify($user->getProfile(), ActivityVerb::UNFOLLOW, $oprofile); + $oprofile->notify($profile, ActivityVerb::UNFOLLOW, $oprofile); // Drop the PuSH subscription if there are no other subscribers. $sub = new Subscription(); $sub->subscribed = $other->id; $sub->limit(1); if (!$sub->find(true)) { - common_log(LOG_INFO, "Unsubscribing from now-unused feed $oprofile->feeduri on hub $oprofile->huburi"); + common_log(LOG_INFO, "Unsubscribing from now-unused feed $oprofile->feeduri"); $oprofile->unsubscribe(); } } diff --git a/plugins/OStatus/actions/feedsubsettings.php b/plugins/OStatus/actions/feedsubsettings.php index 3e1d0aa82..aee4cee9a 100644 --- a/plugins/OStatus/actions/feedsubsettings.php +++ b/plugins/OStatus/actions/feedsubsettings.php @@ -68,14 +68,6 @@ class FeedSubSettingsAction extends ConnectSettingsAction $profile = $user->getProfile(); - $fuser = null; - - $flink = Foreign_link::getByUserID($user->id, FEEDSUB_SERVICE); - - if (!empty($flink)) { - $fuser = $flink->getForeignUser(); - } - $this->elementStart('form', array('method' => 'post', 'id' => 'form_settings_feedsub', 'class' => 'form_settings', diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index 2da9db9eb..1ed322ed2 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -63,13 +63,17 @@ class SalmonAction extends Action // XXX: check that document element is Atom entry // XXX: check the signature - $this->act = new Activity($dom->documentElement); + $entries = $dom->getElementsByTagNameNS(Activity::ATOM, 'entry'); + if ($entries && $entries->length) { + // @fixme is it legit to have multiple entries? + $this->act = new Activity($entries->item(0), $dom->documentElement); + } return true; } /** - * @fixme probably call Ostatus_profile::processFeed + * Check the posted activity type and break out to appropriate processing. */ function handle($args) @@ -94,13 +98,19 @@ class SalmonAction extends Action case ActivityVerb::FRIEND: $this->handleFollow(); break; + case ActivityVerb::UNFOLLOW: + $this->handleUnfollow(); + break; } Event::handle('EndHandleSalmon', array($this->user, $this->activity)); } } /** - * @fixme probably call Ostatus_profile::processFeed + * We've gotten a post event on the Salmon backchannel, probably a reply. + * + * @todo validate if we need to handle this post, then call into + * ostatus_profile's general incoming-post handling. */ function handlePost() { @@ -116,43 +126,81 @@ class SalmonAction extends Action } $profile = $this->ensureProfile(); + // @fixme do something with the post } /** - * @fixme probably call Ostatus_profile::processFeed + * We've gotten a follow/subscribe notification from a remote user. + * Save a subscription relationship for them. */ function handleFollow() { + $oprofile = $this->ensureProfile(); + if ($oprofile) { + common_log(LOG_INFO, "Setting up subscription from remote {$oprofile->uri} to local {$this->user->nickname}"); + $oprofile->subscribeRemoteToLocal($this->user); + } else { + common_log(LOG_INFO, "Can't set up subscription from remote; missing profile."); + } + } + + /** + * We've gotten an unfollow/unsubscribe notification from a remote user. + * Check if we have a subscription relationship for them and kill it. + * + * @fixme probably catch exceptions on fail? + */ + function handleUnfollow() + { + $oprofile = $this->ensureProfile(); + if ($oprofile) { + common_log(LOG_INFO, "Canceling subscription from remote {$oprofile->uri} to local {$this->user->nickname}"); + Subscription::cancel($oprofile->localProfile(), $this->user->getProfile()); + } else { + common_log(LOG_ERR, "Can't cancel subscription from remote, didn't find the profile"); + } } /** - * @fixme probably call Ostatus_profile::processFeed + * Remote user likes one of our posts. + * Confirm the post is ours, and save a local favorite event. */ function handleFavorite() { } /** - * @fixme probably call Ostatus_profile::processFeed + * Remote user doesn't like one of our posts after all! + * Confirm the post is ours, and save a local favorite event. + */ + function handleUnfavorite() + { + } + + /** + * Hmmmm */ function handleShare() { } + /** + * @return Ostatus_profile + */ function ensureProfile() { $actor = $this->act->actor; common_log(LOG_DEBUG, "Received salmon bit: " . var_export($this->act, true)); if (empty($actor->id)) { + common_log(LOG_ERR, "broken actor: " . var_export($actor, true)); throw new Exception("Received a salmon slap from unidentified actor."); } - $ostatusProfile = Ostatus_profile::ensureActorProfile($this->act); - return $oprofile->localProfile(); + return Ostatus_profile::ensureActorProfile($this->act); } /** - * @fixme anything new in here probably should be merged into Ostatus_profile::ensureActorProfile and friends + * @fixme merge into Ostatus_profile::ensureActorProfile and friends */ function createProfile() { diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 319b76a66..97d8eec10 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -310,8 +310,15 @@ class Ostatus_profile extends Memcached_DataObject * @param $verb eg Activity::SUBSCRIBE or Activity::JOIN * @param $object object of the action; if null, the remote entity itself is assumed */ - public function notify(Profile $actor, $verb, $object=null) + public function notify($actor, $verb, $object=null) { + if (!($actor instanceof Profile)) { + $type = gettype($actor); + if ($type == 'object') { + $type = get_class($actor); + } + throw new ServerException("Invalid actor passed to " . __METHOD__ . ": " . $type); + } if ($object == null) { $object = $this; } @@ -340,7 +347,7 @@ class Ostatus_profile extends Memcached_DataObject $feed->addEntry($entry); $xml = $feed->getString(); - common_log(LOG_INFO, "Posting to Salmon endpoint $salmon: $xml"); + common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml"); $salmon = new Salmon(); // ? $salmon->post($this->salmonuri, $xml); @@ -531,9 +538,14 @@ class Ostatus_profile extends Memcached_DataObject $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); copy($url, $temp_filename); + if ($this->isGroup()) { + $id = $this->group_id; + } else { + $id = $this->profile_id; + } // @fixme should we be using different ids? - $imagefile = new ImageFile($this->id, $temp_filename); - $filename = Avatar::filename($this->id, + $imagefile = new ImageFile($id, $temp_filename); + $filename = Avatar::filename($id, image_type_to_extension($imagefile->type), null, common_timestamp()); @@ -646,7 +658,7 @@ class Ostatus_profile extends Memcached_DataObject $actor = $activity->actor; $homeuri = self::getActorProfileURI($activity); $nickname = self::getAuthorNick($activity); - $avatar = self::getAvatar($actor, $feed); + $avatar = self::getAvatar($actor, $activity->feed); if (!$homeuri) { common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true)); -- cgit v1.2.3-54-g00ecf From 557df3d3f78dbfce656c4c8e3ddf82ee0e34da0a Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 19 Feb 2010 16:21:17 -0800 Subject: OStatus: sub/unsub notifications working again. Fixed up autodetection of feed info at profile setup time --- plugins/OStatus/OStatusPlugin.php | 2 ++ plugins/OStatus/actions/salmon.php | 13 +++++++++--- plugins/OStatus/classes/Ostatus_profile.php | 31 +++++++++++++++++++---------- plugins/OStatus/lib/salmon.php | 3 +++ 4 files changed, 35 insertions(+), 14 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 4c43d8a30..1b58d3c35 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -331,6 +331,8 @@ class OStatusPlugin extends Plugin $oprofile = Ostatus_profile::staticGet('feeduri', $feedsub->uri); if ($oprofile) { $oprofile->processFeed($feed); + } else { + common_log(LOG_DEBUG, "No ostatus profile for incoming feed $feedsub->uri"); } } } diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index 7a4474ff6..43d79cf4a 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -34,6 +34,8 @@ class SalmonAction extends Action function prepare($args) { + StatusNet::setApi(true); // Send smaller error pages + parent::prepare($args); if ($_SERVER['REQUEST_METHOD'] != 'POST') { @@ -63,8 +65,12 @@ class SalmonAction extends Action // XXX: check that document element is Atom entry // XXX: check the signature - $this->act = new Activity($dom->documentElement); - + // We need to run an entry into Activity, so get the first one + $entries = $dom->getElementsByTagNameNS(Activity::ATOM, 'entry'); + if ($entries && $entries->length) { + // @fixme is it legit to have multiple entries? + $this->act = new Activity($entries->item(0), $dom->documentElement); + } return true; } @@ -74,7 +80,9 @@ class SalmonAction extends Action function handle($args) { + StatusNet::setApi(true); // Send smaller error pages common_log(LOG_INFO, 'Salmon: incoming post for user '. $this->user->id); + common_log(LOG_DEBUG, "Received salmon bit: " . var_export($this->act, true)); // TODO : Insert new $xml -> notice code @@ -254,7 +262,6 @@ class SalmonAction extends Action function ensureProfile() { $actor = $this->act->actor; - common_log(LOG_DEBUG, "Received salmon bit: " . var_export($this->act, true)); if (empty($actor->id)) { common_log(LOG_ERR, "broken actor: " . var_export($actor, true)); throw new Exception("Received a salmon slap from unidentified actor."); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 97d8eec10..5fe135f96 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -56,7 +56,7 @@ class Ostatus_profile extends Memcached_DataObject return array('uri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, 'profile_id' => DB_DATAOBJECT_INT, 'group_id' => DB_DATAOBJECT_INT, - 'feeduri' => DB_DATAOBJECT_STR + DB_DATAOBJECT_NOTNULL, + 'feeduri' => DB_DATAOBJECT_STR, 'salmonuri' => DB_DATAOBJECT_STR, 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); @@ -71,7 +71,7 @@ class Ostatus_profile extends Memcached_DataObject new ColumnDef('group_id', 'integer', null, true, 'UNI'), new ColumnDef('feeduri', 'varchar', - 255, false, 'UNI'), + 255, true, 'UNI'), new ColumnDef('salmonuri', 'text', null, true), new ColumnDef('created', 'datetime', @@ -272,7 +272,7 @@ class Ostatus_profile extends Memcached_DataObject * @return bool true on success, false on failure * @throws ServerException if feed state is not valid */ - public function subscribe($mode='subscribe') + public function subscribe() { $feedsub = FeedSub::ensureFeed($this->feeduri); if ($feedsub->sub_state == 'active' || $feedsub->sub_state == 'subscribe') { @@ -506,7 +506,7 @@ class Ostatus_profile extends Memcached_DataObject $discover = new FeedDiscovery(); $feeduri = $discover->discoverFromURL($profile_uri); - $feedsub = FeedSub::ensureFeed($feeduri, $discover->feed); + //$feedsub = FeedSub::ensureFeed($feeduri, $discover->feed); $huburi = $discover->getAtomLink('hub'); $salmonuri = $discover->getAtomLink('salmon'); @@ -665,6 +665,20 @@ class Ostatus_profile extends Memcached_DataObject throw new ServerException("No profile URI"); } + if (!$feeduri || !$salmonuri) { + // Get the canonical feed URI and check it + $discover = new FeedDiscovery(); + $feeduri = $discover->discoverFromURL($homeuri); + + $huburi = $discover->getAtomLink('hub'); + $salmonuri = $discover->getAtomLink('salmon'); + + if (!$huburi) { + // We can only deal with folks with a PuSH hub + throw new FeedSubNoHubException(); + } + } + $profile = new Profile(); $profile->nickname = $nickname; $profile->fullname = $actor->displayName; @@ -686,13 +700,8 @@ class Ostatus_profile extends Memcached_DataObject // so we can leave it empty until later. $oprofile = new Ostatus_profile(); $oprofile->uri = $homeuri; - if ($feeduri) { - // If we don't have these, we can look them up later. - $oprofile->feeduri = $feeduri; - if ($salmonuri) { - $oprofile->salmonuri = $salmonuri; - } - } + $oprofile->feeduri = $feeduri; + $oprofile->salmonuri = $salmonuri; $oprofile->profile_id = $profile->id; $oprofile->created = common_sql_now(); diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index 8c77222a6..df17a7006 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -41,9 +41,12 @@ class Salmon $client->setBody($xml); $response = $client->post($endpoint_uri, $headers); } catch (HTTP_Request2_Exception $e) { + common_log(LOG_ERR, "Salmon post to $endpoint_uri failed: " . $e->getMessage()); return false; } if ($response->getStatus() != 200) { + common_log(LOG_ERR, "Salmon at $endpoint_uri returned status " . + $response->getStatus() . ': ' . $response->getBody()); return false; } -- cgit v1.2.3-54-g00ecf From 50db2d5d69a8769dc2ddcc937afb130bcce0971d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 19 Feb 2010 17:01:38 -0800 Subject: OStatus: Salmon notifications now being generated moderately correctly. :) Needs to be an not a . --- plugins/OStatus/actions/salmon.php | 12 +++++------- plugins/OStatus/classes/Ostatus_profile.php | 15 +++++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/actions/salmon.php b/plugins/OStatus/actions/salmon.php index 43d79cf4a..f7c86dc0c 100644 --- a/plugins/OStatus/actions/salmon.php +++ b/plugins/OStatus/actions/salmon.php @@ -62,15 +62,13 @@ class SalmonAction extends Action $dom = DOMDocument::loadXML($xml); - // XXX: check that document element is Atom entry + if ($dom->documentElement->namespaceURI != Activity::ATOM || + $dom->documentElement->localName != 'entry') { + $this->clientError(_m('Salmon post must be an Atom entry.')); + } // XXX: check the signature - // We need to run an entry into Activity, so get the first one - $entries = $dom->getElementsByTagNameNS(Activity::ATOM, 'entry'); - if ($entries && $entries->length) { - // @fixme is it legit to have multiple entries? - $this->act = new Activity($entries->item(0), $dom->documentElement); - } + $this->act = new Activity($dom->documentElement); return true; } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 5fe135f96..b14b4c9a3 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -329,9 +329,15 @@ class Ostatus_profile extends Memcached_DataObject ':' . $actor->id . ':' . time(); // @fixme - //$entry = new Atom10Entry(); + // @fixme consolidate all these NS settings somewhere + $attributes = array('xmlns' => Activity::ATOM, + 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', + 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', + 'xmlns:georss' => 'http://www.georss.org/georss', + 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0'); + $entry = new XMLStringer(); - $entry->elementStart('entry'); + $entry->elementStart('entry', $attributes); $entry->element('id', null, $id); $entry->element('title', null, $text); $entry->element('summary', null, $text); @@ -343,10 +349,7 @@ class Ostatus_profile extends Memcached_DataObject $entry->raw($object->asActivityNoun('object')); $entry->elementEnd('entry'); - $feed = $this->atomFeed($actor); - $feed->addEntry($entry); - - $xml = $feed->getString(); + $xml = $entry->getString(); common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml"); $salmon = new Salmon(); // ? -- cgit v1.2.3-54-g00ecf From ed45df045f661e9c3b85e0657986c99c320914f0 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 20 Feb 2010 11:17:54 -0500 Subject: Cool bug! Technically good PHP syntax --- plugins/OStatus/classes/Ostatus_profile.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index b14b4c9a3..9f5c60561 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -149,7 +149,6 @@ class Ostatus_profile extends Memcached_DataObject function asActivityNoun($element) { $xs = new XMLStringer(true); - $avatarHref = Avatar::defaultImage(AVATAR_PROFILE_SIZE); $avatarType = 'image/png'; if ($this->isGroup()) { @@ -173,8 +172,8 @@ class Ostatus_profile extends Memcached_DataObject $self = $this->localProfile(); $avatar = $self->getAvatar(AVATAR_PROFILE_SIZE); if ($avatar) { - $avatarHref = $avatar-> - $avatarType = $avatar->mediatype; + $avatarHref = $avatar->url; + $avatarType = $avatar->mediatype; } } $xs->elementStart('activity:' . $element); @@ -672,10 +671,10 @@ class Ostatus_profile extends Memcached_DataObject // Get the canonical feed URI and check it $discover = new FeedDiscovery(); $feeduri = $discover->discoverFromURL($homeuri); - + $huburi = $discover->getAtomLink('hub'); $salmonuri = $discover->getAtomLink('salmon'); - + if (!$huburi) { // We can only deal with folks with a PuSH hub throw new FeedSubNoHubException(); -- cgit v1.2.3-54-g00ecf From ab4ec095e811f86c50fbc1cd71144b11d6b86d50 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 20 Feb 2010 11:38:05 -0500 Subject: adjust URI, URL, and location in Ostatus_profile::processPost --- plugins/OStatus/classes/Ostatus_profile.php | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 9f5c60561..4dd565288 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -457,28 +457,33 @@ class Ostatus_profile extends Memcached_DataObject $oprofile = $this; } - if ($activity->object->link) { - $sourceUri = $activity->object->link; - } else if (preg_match('!^https?://!', $activity->object->id)) { - $sourceUri = $activity->object->id; - } else { - common_log(LOG_INFO, "OStatus: ignoring post with no source link: id $activity->object->id"); - return; - } + $sourceUri = $activity->object->id; $dupe = Notice::staticGet('uri', $sourceUri); + if ($dupe) { - common_log(LOG_INFO, "OStatus: ignoring duplicate post: $noticeLink"); + common_log(LOG_INFO, "OStatus: ignoring duplicate post: $sourceUri"); return; } + $sourceUrl = null; + + if ($activity->object->link) { + $sourceUrl = $activity->object->link; + } else if (preg_match('!^https?://!', $activity->object->id)) { + $sourceUrl = $activity->object->id; + } + // @fixme sanitize and save HTML content if available + $content = $activity->object->title; $params = array('is_local' => Notice::REMOTE_OMB, + 'url' => $sourceUrl, 'uri' => $sourceUri); - $location = $this->getEntryLocation($activity->entry); + $location = $activity->context->location; + if ($location) { $params['lat'] = $location->lat; $params['lon'] = $location->lon; -- cgit v1.2.3-54-g00ecf From 96c6019638f6407b38fbc5a45485a3797af47adb Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sat, 20 Feb 2010 19:58:20 -0500 Subject: Add support for favor and disfavor notification Added support for favoring and disfavoring in OStatusPlugin. Needed to represent the Notice as an activity:object, so added some code for that in lib/activity.php. Also, made some small changes to OStatusPlugin so it handled having a non-default argument $object correctly. --- plugins/OStatus/OStatusPlugin.php | 50 +++++++++++++++++++++----- plugins/OStatus/classes/Ostatus_profile.php | 6 ++-- plugins/OStatus/lib/activity.php | 55 +++++++++++++++++++++++++++-- 3 files changed, 98 insertions(+), 13 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 9e6d03177..98e048c14 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -255,7 +255,7 @@ class OStatusPlugin extends Plugin { if ($user instanceof Profile) { $profile = $user; - } else if ($user instanceof Profile) { + } else if ($user instanceof User) { $profile = $user->getProfile(); } $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); @@ -353,30 +353,64 @@ class OStatusPlugin extends Plugin // We have a local user subscribing to a remote profile; make the // magic happen! - $oprofile->notify($subscriber, ActivityVerb::FOLLOW); + $oprofile->notify($subscriber, ActivityVerb::FOLLOW, $oprofile); return true; } - function onEndUnsubscribe($subscriber, $other) + function onEndFavor($profile, $notice) { - $user = User::staticGet('id', $subscriber->id); + // is the favorer a local user? + + $user = User::staticGet('id', $profile->id); if (empty($user)) { return true; } - $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); + // is the author an OStatus remote user? + + $oprofile = Ostatus_profile::staticGet('profile_id', $notice->profile_id); if (empty($oprofile)) { return true; } - // We have a local user subscribing to a remote profile; make the - // magic happen! + // Local user faved an Ostatus profile's notice; notify them! + + $obj = ActivityObject::fromNotice($notice); + + $oprofile->notify($profile, + ActivityVerb::FAVORITE, + $obj->asString()); + return true; + } + + function onEndDisfavor($profile, $notice) + { + // is the favorer a local user? + + $user = User::staticGet('id', $profile->id); + + if (empty($user)) { + return true; + } + + // is the author an OStatus remote user? + + $oprofile = Ostatus_profile::staticGet('profile_id', $notice->profile_id); + + if (empty($oprofile)) { + return true; + } + + // Local user faved an Ostatus profile's notice; notify them! - $oprofile->notify($subscriber, ActivityVerb::UNFOLLOW); + $obj = ActivityObject::fromNotice($notice); + $oprofile->notify($profile, + ActivityVerb::UNFAVORITE, + $obj->asString()); return true; } } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 4dd565288..5bd899bc4 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -307,7 +307,7 @@ class Ostatus_profile extends Memcached_DataObject * * @param Profile $actor * @param $verb eg Activity::SUBSCRIBE or Activity::JOIN - * @param $object object of the action; if null, the remote entity itself is assumed + * @param string $object object of the action; if null, the remote entity itself is assumed */ public function notify($actor, $verb, $object=null) { @@ -319,7 +319,7 @@ class Ostatus_profile extends Memcached_DataObject throw new ServerException("Invalid actor passed to " . __METHOD__ . ": " . $type); } if ($object == null) { - $object = $this; + $object = $this->asActivityNoun('object'); } if ($this->salmonuri) { $text = 'update'; // @fixme @@ -345,7 +345,7 @@ class Ostatus_profile extends Memcached_DataObject $entry->element('activity:verb', null, $verb); $entry->raw($actor->asAtomAuthor()); $entry->raw($actor->asActivityActor()); - $entry->raw($object->asActivityNoun('object')); + $entry->raw($object); $entry->elementEnd('entry'); $xml = $entry->getString(); diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index 5bc8f78e5..6d15f85b0 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -199,7 +199,7 @@ class ActivityObject public $link; public $source; - /** + /** * Constructor * * This probably needs to be refactored @@ -209,8 +209,12 @@ class ActivityObject * @param DOMElement $element DOM thing to turn into an Activity thing */ - function __construct($element) + function __construct($element = null) { + if (empty($element)) { + return; + } + $this->element = $element; if ($element->tagName == 'author') { @@ -279,6 +283,53 @@ class ActivityObject } } } + + static fromNotice($notice) + { + $object = new ActivityObject(); + + $object->type = ActivityObject::NOTE; + + $object->id = $notice->uri; + $object->title = $notice->content; + $object->content = $notice->rendered; + $object->link = $notice->bestUrl(); + + return $object; + } + + function asString($tag='activity:object') + { + $xs = new XMLStringer(true); + + $xs->elementStart($tag); + + $xs->element('activity:object-type', null, $this->type); + + $xs->element(self::ID, null, $this->id); + + if (!empty($this->title)) { + $xs->element(self::TITLE, null, $this->title); + } + + if (!empty($this->summary)) { + $xs->element(self::SUMMARY, null, $this->summary); + } + + if (!empty($this->content)) { + // XXX: assuming HTML content here + $xs->element(self::CONTENT, array('type' => 'html'), $this->content); + } + + if (!empty($this->link)) { + $xs->element('link', array('rel' => 'alternate', 'type' => 'text/html'), + $this->content); + } + + $xs->elementEnd($tag); + + return $xs->getString(); + } } /** -- cgit v1.2.3-54-g00ecf From 5df61141d28426329d2f4e50e61c131d43d1e405 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Sat, 20 Feb 2010 18:25:40 -0800 Subject: OStatus: fix regressions from merge --- plugins/OStatus/classes/Ostatus_profile.php | 4 ++-- plugins/OStatus/lib/activity.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 5bd899bc4..d9cb7a6e1 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -319,7 +319,7 @@ class Ostatus_profile extends Memcached_DataObject throw new ServerException("Invalid actor passed to " . __METHOD__ . ": " . $type); } if ($object == null) { - $object = $this->asActivityNoun('object'); + $object = $this; } if ($this->salmonuri) { $text = 'update'; // @fixme @@ -345,7 +345,7 @@ class Ostatus_profile extends Memcached_DataObject $entry->element('activity:verb', null, $verb); $entry->raw($actor->asAtomAuthor()); $entry->raw($actor->asActivityActor()); - $entry->raw($object); + $entry->raw($object->asActivityNoun('object')); $entry->elementEnd('entry'); $xml = $entry->getString(); diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index 6d15f85b0..7563488b7 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -284,7 +284,7 @@ class ActivityObject } } - static fromNotice($notice) + static function fromNotice($notice) { $object = new ActivityObject(); -- cgit v1.2.3-54-g00ecf From 4e90bd34e9aff1abd97a92b62fc7c48bf1ee5a9c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 21 Feb 2010 10:53:11 -0500 Subject: activity notification in Ostatus_profile --- plugins/OStatus/classes/Ostatus_profile.php | 31 +++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index d9cb7a6e1..55f347a02 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -305,9 +305,9 @@ class Ostatus_profile extends Memcached_DataObject * Send an Activity Streams notification to the remote Salmon endpoint, * if so configured. * - * @param Profile $actor - * @param $verb eg Activity::SUBSCRIBE or Activity::JOIN - * @param string $object object of the action; if null, the remote entity itself is assumed + * @param Profile $actor Actor who did the activity + * @param string $verb Activity::SUBSCRIBE or Activity::JOIN + * @param Object $object object of the action; must define asActivityNoun($tag) */ public function notify($actor, $verb, $object=null) { @@ -322,11 +322,12 @@ class Ostatus_profile extends Memcached_DataObject $object = $this; } if ($this->salmonuri) { - $text = 'update'; // @fixme - $id = 'tag:' . common_config('site', 'server') . - ':' . $verb . - ':' . $actor->id . - ':' . time(); // @fixme + + $text = 'update'; + $id = TagURI::mint('%s:%s:%s', + $verb, + $actor->getURI(), + common_date_iso8601(date())); // @fixme consolidate all these NS settings somewhere $attributes = array('xmlns' => Activity::ATOM, @@ -356,6 +357,20 @@ class Ostatus_profile extends Memcached_DataObject } } + public function notifyActivity($activity) + { + if ($this->salmonuri) { + + $xml = $activity->asString(); + + $salmon = new Salmon(); // ? + + $salmon->post($this->salmonuri, $xml); + } + + return; + } + function getBestName() { if ($this->isGroup()) { -- cgit v1.2.3-54-g00ecf From 1a86bf9c65d2579d9245c6edcc968fed3d674f39 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 21 Feb 2010 12:56:46 -0500 Subject: try different ways to get a profile from a feed --- plugins/OStatus/classes/Ostatus_profile.php | 177 +++++++++++++++++++++------- 1 file changed, 132 insertions(+), 45 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 55f347a02..793d45112 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -537,18 +537,58 @@ class Ostatus_profile extends Memcached_DataObject throw new FeedSubNoHubException(); } - // Ok this is going to be a terrible hack! - // Won't be suitable for groups, empty feeds, or getting - // info that's only available on the profile page. + // Try to get a profile from the feed activity:subject + + $feedEl = $discover->feed->documentElement; + + $subject = ActivityUtils::child($feedEl, Activity::SUBJECT, Activity::SPEC); + + if (!empty($subject)) { + $subjObject = new ActivityObject($subject); + return self::ensureActivityObjectProfile($subjObject, $feeduri, $salmonuri); + } + + // Otherwise, try the feed author + + $author = ActivityUtils::child($feedEl, Activity::AUTHOR, Activity::ATOM); + + if (!empty($author)) { + $authorObject = new ActivityObject($author); + return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri); + } + + // Sheesh. Not a very nice feed! Let's try fingerpoken in the + // entries. + $entries = $discover->feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); - if (!$entries || $entries->length == 0) { - throw new FeedSubException('empty feed'); + + if (!empty($entries) && $entries->length > 0) { + + $entry = $entries->item(0); + + $actor = ActivityUtils::child($entry, Activity::ACTOR, Activity::SPEC); + + if (!empty($actor)) { + $actorObject = new ActivityObject($actor); + return self::ensureActivityObjectProfile($actorObject, $feeduri, $salmonuri); + + } + + $author = ActivityUtils::child($entry, Activity::AUTHOR, Activity::ATOM); + + if (!empty($author)) { + $authorObject = new ActivityObject($author); + return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri); + } } - $first = new Activity($entries->item(0), $discover->feed); - return self::ensureActorProfile($first, $feeduri, $salmonuri); + + // XXX: make some educated guesses here + + throw new FeedSubException("Can't find enough profile information to make a feed."); } /** + * * Download and update given avatar image * @param string $url * @throws Exception in various failure cases @@ -581,6 +621,12 @@ class Ostatus_profile extends Memcached_DataObject } } + protected static function getActivityObjectAvatar($object) + { + // XXX: go poke around in the feed + return $object->avatar; + } + /** * Get an appropriate avatar image source URL, if available. * @@ -588,6 +634,7 @@ class Ostatus_profile extends Memcached_DataObject * @param DOMElement $feed * @return string */ + protected static function getAvatar($actor, $feed) { $url = ''; @@ -635,11 +682,17 @@ class Ostatus_profile extends Memcached_DataObject * @param string $salmonuri if we already know the salmon return channel URI * @return Ostatus_profile */ + public static function ensureActorProfile($activity, $feeduri=null, $salmonuri=null) { - $profile = self::getActorProfile($activity); + return self::ensureActivityObjectProfile($activity->actor, $feeduri, $salmonuri); + } + + public static function ensureActivityObjectProfile($object, $feeduri=null, $salmonuri=null) + { + $profile = self::getActivityObjectProfile($object); if (!$profile) { - $profile = self::createActorProfile($activity, $feeduri, $salmonuri); + $profile = self::createActivityObjectProfile($object, $feeduri, $salmonuri); } return $profile; } @@ -650,8 +703,18 @@ class Ostatus_profile extends Memcached_DataObject */ protected static function getActorProfile($activity) { - $homeuri = self::getActorProfileURI($activity); - return self::staticGet('uri', $homeuri); + return self::getActivityObjectProfile($activity->actor); + } + + protected static function getActivityObjectProfile($object) + { + $uri = self::getActivityObjectProfileURI($object); + return Ostatus_profile::staticGet('homeuri', $uri); + } + + protected static function getActorProfileURI($activity) + { + return self::getActivityObjectProfileURI($activity->actor); } /** @@ -659,15 +722,14 @@ class Ostatus_profile extends Memcached_DataObject * @return string * @throws ServerException */ - protected static function getActorProfileURI($activity) + protected static function getActivityObjectProfileURI($object) { $opts = array('allowed_schemes' => array('http', 'https')); - $actor = $activity->actor; - if ($actor->id && Validate::uri($actor->id, $opts)) { - return $actor->id; + if ($object->id && Validate::uri($object->id, $opts)) { + return $object->id; } - if ($actor->link && Validate::uri($actor->link, $opts)) { - return $actor->link; + if ($object->link && Validate::uri($object->link, $opts)) { + return $object->link; } throw new ServerException("No author ID URI found"); } @@ -675,12 +737,19 @@ class Ostatus_profile extends Memcached_DataObject /** * @fixme validate stuff somewhere */ + protected static function createActorProfile($activity, $feeduri=null, $salmonuri=null) { $actor = $activity->actor; - $homeuri = self::getActorProfileURI($activity); - $nickname = self::getAuthorNick($activity); - $avatar = self::getAvatar($actor, $activity->feed); + + self::createActivityObjectProfile($actor, $feeduri, $salmonuri); + } + + protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null) + { + $homeuri = self::getActivityObjectProfileURI($object); + $nickname = self::getActivityObjectNickname($object); + $avatar = self::getActivityObjectAvatar($object); if (!$homeuri) { common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true)); @@ -703,9 +772,10 @@ class Ostatus_profile extends Memcached_DataObject $profile = new Profile(); $profile->nickname = $nickname; - $profile->fullname = $actor->displayName; - $profile->homepage = $actor->link; // @fixme - $profile->profileurl = $homeuri; + $profile->fullname = $object->title; + $profile->profileurl = $object->link; + $profile->created = common_sql_now(); + // @fixme bio // @fixme tags/categories // @fixme location? @@ -713,6 +783,7 @@ class Ostatus_profile extends Memcached_DataObject // @todo lat/lon/location? $ok = $profile->insert(); + if (!$ok) { throw new ServerException("Can't save local profile"); } @@ -720,16 +791,19 @@ class Ostatus_profile extends Memcached_DataObject // @fixme either need to do feed discovery here // or need to split out some of the feed stuff // so we can leave it empty until later. + $oprofile = new Ostatus_profile(); - $oprofile->uri = $homeuri; - $oprofile->feeduri = $feeduri; - $oprofile->salmonuri = $salmonuri; + + $oprofile->uri = $homeuri; + $oprofile->feeduri = $feeduri; + $oprofile->salmonuri = $salmonuri; $oprofile->profile_id = $profile->id; - $oprofile->created = common_sql_now(); - $oprofile->modified = common_sql_now(); + $oprofile->created = common_sql_now(); + $oprofile->modified = common_sql_now(); $ok = $oprofile->insert(); + if ($ok) { $oprofile->updateAvatar($avatar); return $oprofile; @@ -738,24 +812,37 @@ class Ostatus_profile extends Memcached_DataObject } } - /** - * @fixme move this into Activity? - * @param Activity $activity - * @return string - */ - protected static function getAuthorNick($activity) - { - // @fixme not technically part of the actor? - foreach (array($activity->entry, $activity->feed) as $source) { - $author = ActivityUtils::child($source, 'author', Activity::ATOM); - if ($author) { - $name = ActivityUtils::child($author, 'name', Activity::ATOM); - if ($name) { - return trim($name->textContent); - } - } + protected static function getActivityObjectNickname($object) + { + // XXX: check whatever PoCo calls a nickname first + + $nickname = self::nicknameFromURI($object->id); + + if (empty($nickname)) { + $nickname = common_nicknamize($object->title); } - return false; + + return $nickname; } + protected static function nicknameFromURI($uri) + { + preg_match('/(\w+):/', $uri, $matches); + + $protocol = $matches[1]; + + switch ($protocol) { + case 'acct': + case 'mailto': + if (preg_match("/^$protocol:(.*)?@.*\$/", $uri, $matches)) { + return common_canonical_nickname($matches[1]); + } + return null; + case 'http': + return common_url_to_nickname($uri); + break; + default: + return null; + } + } } -- cgit v1.2.3-54-g00ecf From 068d8f92200e6fcef31db2fdca956a745422a1ac Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 21 Feb 2010 13:31:58 -0500 Subject: correct call to time() in Ostatus_profile::notify() --- plugins/OStatus/classes/Ostatus_profile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 793d45112..aab316c34 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -327,7 +327,7 @@ class Ostatus_profile extends Memcached_DataObject $id = TagURI::mint('%s:%s:%s', $verb, $actor->getURI(), - common_date_iso8601(date())); + common_date_iso8601(time())); // @fixme consolidate all these NS settings somewhere $attributes = array('xmlns' => Activity::ATOM, -- cgit v1.2.3-54-g00ecf From 16a43b1154baf967183279c5e291a080cb2d5868 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 21 Feb 2010 14:14:45 -0500 Subject: slightly more robust remote profile creation --- plugins/OStatus/classes/Ostatus_profile.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index aab316c34..3b79f32c6 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -709,7 +709,7 @@ class Ostatus_profile extends Memcached_DataObject protected static function getActivityObjectProfile($object) { $uri = self::getActivityObjectProfileURI($object); - return Ostatus_profile::staticGet('homeuri', $uri); + return Ostatus_profile::staticGet('uri', $uri); } protected static function getActorProfileURI($activity) @@ -747,9 +747,9 @@ class Ostatus_profile extends Memcached_DataObject protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null) { - $homeuri = self::getActivityObjectProfileURI($object); + $homeuri = $object->id; $nickname = self::getActivityObjectNickname($object); - $avatar = self::getActivityObjectAvatar($object); + $avatar = self::getActivityObjectAvatar($object); if (!$homeuri) { common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true)); @@ -782,9 +782,9 @@ class Ostatus_profile extends Memcached_DataObject // @todo tags from categories // @todo lat/lon/location? - $ok = $profile->insert(); + $profile_id = $profile->insert(); - if (!$ok) { + if (!$profile_id) { throw new ServerException("Can't save local profile"); } @@ -797,7 +797,7 @@ class Ostatus_profile extends Memcached_DataObject $oprofile->uri = $homeuri; $oprofile->feeduri = $feeduri; $oprofile->salmonuri = $salmonuri; - $oprofile->profile_id = $profile->id; + $oprofile->profile_id = $profile_id; $oprofile->created = common_sql_now(); $oprofile->modified = common_sql_now(); -- cgit v1.2.3-54-g00ecf From 10f6c023f4573868326c4b6599bdfb66cffdd7d6 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 21 Feb 2010 16:23:55 -0500 Subject: include namespaces in posted activities in notifyActivity() --- plugins/OStatus/classes/Ostatus_profile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 3b79f32c6..b67e20202 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -361,7 +361,7 @@ class Ostatus_profile extends Memcached_DataObject { if ($this->salmonuri) { - $xml = $activity->asString(); + $xml = $activity->asString(true); $salmon = new Salmon(); // ? -- cgit v1.2.3-54-g00ecf From bf23c35495bf713ec662649b2e8964e90da93239 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 21 Feb 2010 21:37:12 -0500 Subject: Add OStatus_profile::ensureWebfinger() --- plugins/OStatus/classes/Ostatus_profile.php | 103 ++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index b67e20202..700168c11 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -845,4 +845,107 @@ class Ostatus_profile extends Memcached_DataObject return null; } } + + public static function ensureWebfinger($addr) + { + // First, look it up + + $oprofile = Ostatus_profile::staticGet('uri', 'acct:'.$addr); + + if (!empty($oprofile)) { + return $oprofile; + } + + // Now, try some discovery + + $wf = new Webfinger(); + + $result = $wf->lookup($addr); + + if (!$result) { + return null; + } + + foreach ($result->links as $link) { + switch ($link['rel']) { + case Webfinger::PROFILEPAGE: + $profileUrl = $link['href']; + break; + case 'salmon': + $salmonEndpoint = $link['href']; + break; + case Webfinger::UPDATESFROM: + $feedUrl = $link['href']; + break; + default: + common_log(LOG_NOTICE, "Don't know what to do with rel = '{$link['rel']}'"); + break; + } + } + + // If we got a feed URL, try that + + if (isset($feedUrl)) { + try { + $oprofile = self::ensureProfile($feedUrl); + return $oprofile; + } catch (Exception $e) { + common_log(LOG_WARNING, "Failed creating profile from feed URL '$feedUrl': " . $e->getMessage()); + // keep looking + } + } + + // If we got a profile page, try that! + + if (isset($profileUrl)) { + try { + $oprofile = self::ensureProfile($profileUrl); + return $oprofile; + } catch (Exception $e) { + common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage()); + // keep looking + } + } + + // XXX: try hcard + // XXX: try FOAF + + if (isset($salmonEndpoint)) { + + // An account URL, a salmon endpoint, and a dream? Not much to go + // on, but let's give it a try + + $uri = 'acct:'.$addr; + + $profile = new Profile(); + + $profile->nickname = self::nicknameFromUri($uri); + $profile->created = common_sql_now(); + + $profile_id = $profile->insert(); + + if (!$profile_id) { + common_log_db_error($profile, 'INSERT', __FILE__); + throw new Exception("Couldn't save profile for '$addr'"); + } + + $oprofile = new Ostatus_profile(); + + $oprofile->uri = $uri; + $oprofile->salmonuri = $salmonEndpoint; + $oprofile->profile_id = $profile_id; + $oprofile->created = common_sql_now(); + + $result = $oprofile->insert(); + + if (!$result) { + common_log_db_error($oprofile, 'INSERT', __FILE__); + throw new Exception("Couldn't save ostatus_profile for '$addr'"); + } + + return $oprofile; + } + + return null; + } } -- cgit v1.2.3-54-g00ecf From be70dd3677242fe46d41a0a8882abe6e46428b5f Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 21 Feb 2010 21:57:09 -0500 Subject: work harder to preserve info when creating new Ostatus_profile --- plugins/OStatus/classes/Ostatus_profile.php | 67 +++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 13 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 700168c11..3bed1c2aa 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -522,7 +522,7 @@ class Ostatus_profile extends Memcached_DataObject * @return Ostatus_profile * @throws FeedSubException */ - public static function ensureProfile($profile_uri) + public static function ensureProfile($profile_uri, $hints=array()) { // Get the canonical feed URI and check it $discover = new FeedDiscovery(); @@ -545,7 +545,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($subject)) { $subjObject = new ActivityObject($subject); - return self::ensureActivityObjectProfile($subjObject, $feeduri, $salmonuri); + return self::ensureActivityObjectProfile($subjObject, $feeduri, $salmonuri, $hints); } // Otherwise, try the feed author @@ -554,7 +554,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($author)) { $authorObject = new ActivityObject($author); - return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri); + return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri, $hints); } // Sheesh. Not a very nice feed! Let's try fingerpoken in the @@ -570,7 +570,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($actor)) { $actorObject = new ActivityObject($actor); - return self::ensureActivityObjectProfile($actorObject, $feeduri, $salmonuri); + return self::ensureActivityObjectProfile($actorObject, $feeduri, $salmonuri, $hints); } @@ -578,7 +578,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($author)) { $authorObject = new ActivityObject($author); - return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri); + return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri, $hints); } } @@ -688,11 +688,11 @@ class Ostatus_profile extends Memcached_DataObject return self::ensureActivityObjectProfile($activity->actor, $feeduri, $salmonuri); } - public static function ensureActivityObjectProfile($object, $feeduri=null, $salmonuri=null) + public static function ensureActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) { $profile = self::getActivityObjectProfile($object); if (!$profile) { - $profile = self::createActivityObjectProfile($object, $feeduri, $salmonuri); + $profile = self::createActivityObjectProfile($object, $feeduri, $salmonuri, $hints); } return $profile; } @@ -745,10 +745,10 @@ class Ostatus_profile extends Memcached_DataObject self::createActivityObjectProfile($actor, $feeduri, $salmonuri); } - protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null) + protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) { $homeuri = $object->id; - $nickname = self::getActivityObjectNickname($object); + $nickname = self::getActivityObjectNickname($object, $hints); $avatar = self::getActivityObjectAvatar($object); if (!$homeuri) { @@ -756,6 +756,18 @@ class Ostatus_profile extends Memcached_DataObject throw new ServerException("No profile URI"); } + if (empty($feeduri)) { + if (array_key_exists('feedurl', $hints)) { + $feeduri = $hints['feedurl']; + } + } + + if (empty($salmonuri)) { + if (array_key_exists('salmon', $hints)) { + $salmonuri = $hints['salmon']; + } + } + if (!$feeduri || !$salmonuri) { // Get the canonical feed URI and check it $discover = new FeedDiscovery(); @@ -773,7 +785,11 @@ class Ostatus_profile extends Memcached_DataObject $profile = new Profile(); $profile->nickname = $nickname; $profile->fullname = $object->title; - $profile->profileurl = $object->link; + if (!empty($object->link)) { + $profile->profileurl = $object->link; + } else if (array_key_exists('profileurl', $hints)) { + $profile->profileurl = $hints['profileurl']; + } $profile->created = common_sql_now(); // @fixme bio @@ -812,12 +828,24 @@ class Ostatus_profile extends Memcached_DataObject } } - protected static function getActivityObjectNickname($object) + protected static function getActivityObjectNickname($object, $hints=array()) { // XXX: check whatever PoCo calls a nickname first + // Try the definitive ID + $nickname = self::nicknameFromURI($object->id); + // Try a Webfinger if one was passed (way) down + + if (empty($nickname)) { + if (array_key_exists('webfinger', $hints)) { + $nickname = self::nicknameFromURI($hints['webfinger']); + } + } + + // Try the name + if (empty($nickname)) { $nickname = common_nicknamize($object->title); } @@ -883,11 +911,16 @@ class Ostatus_profile extends Memcached_DataObject } } + $hints = array('webfinger' => $addr, + 'profileurl' => $profileUrl, + 'feedurl' => $feedUrl, + 'salmon' => $salmonEndpoint); + // If we got a feed URL, try that if (isset($feedUrl)) { try { - $oprofile = self::ensureProfile($feedUrl); + $oprofile = self::ensureProfile($feedUrl, $hints); return $oprofile; } catch (Exception $e) { common_log(LOG_WARNING, "Failed creating profile from feed URL '$feedUrl': " . $e->getMessage()); @@ -899,7 +932,7 @@ class Ostatus_profile extends Memcached_DataObject if (isset($profileUrl)) { try { - $oprofile = self::ensureProfile($profileUrl); + $oprofile = self::ensureProfile($profileUrl, $hints); return $oprofile; } catch (Exception $e) { common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage()); @@ -922,6 +955,10 @@ class Ostatus_profile extends Memcached_DataObject $profile->nickname = self::nicknameFromUri($uri); $profile->created = common_sql_now(); + if (isset($profileUrl)) { + $profile->profileurl = $profileUrl; + } + $profile_id = $profile->insert(); if (!$profile_id) { @@ -936,6 +973,10 @@ class Ostatus_profile extends Memcached_DataObject $oprofile->profile_id = $profile_id; $oprofile->created = common_sql_now(); + if (isset($feedUrl)) { + $profile->feeduri = $feedUrl; + } + $result = $oprofile->insert(); if (!$result) { -- cgit v1.2.3-54-g00ecf From 5207783765328a3d6f0101f143edb8807247bcfe Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Sun, 21 Feb 2010 19:51:11 -0800 Subject: OStatus: record source profile & saving method in ostatus_source table; this allows us to distinguish posts that have come through an unverified group feed --- plugins/OStatus/OStatusPlugin.php | 1 + plugins/OStatus/classes/Ostatus_profile.php | 4 +- plugins/OStatus/classes/Ostatus_source.php | 114 ++++++++++++++++++++++++++++ plugins/OStatus/lib/salmonaction.php | 13 +++- 4 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 plugins/OStatus/classes/Ostatus_source.php (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 641765dae..5b9e3be2b 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -305,6 +305,7 @@ class OStatusPlugin extends Plugin function onCheckSchema() { $schema = Schema::get(); $schema->ensureTable('ostatus_profile', Ostatus_profile::schemaDef()); + $schema->ensureTable('ostatus_source', Ostatus_source::schemaDef()); $schema->ensureTable('feedsub', FeedSub::schemaDef()); $schema->ensureTable('hubsub', HubSub::schemaDef()); return true; diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 3bed1c2aa..71885bcdc 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -508,13 +508,15 @@ class Ostatus_profile extends Memcached_DataObject } } - // @fixme save detailed ostatus source info // @fixme ensure that groups get handled correctly $saved = Notice::saveNew($oprofile->localProfile()->id, $content, 'ostatus', $params); + + // Record which feed this came through... + Ostatus_source::saveNew($saved, $this, 'push'); } /** diff --git a/plugins/OStatus/classes/Ostatus_source.php b/plugins/OStatus/classes/Ostatus_source.php new file mode 100644 index 000000000..e6ce7d442 --- /dev/null +++ b/plugins/OStatus/classes/Ostatus_source.php @@ -0,0 +1,114 @@ +. + */ + +/** + * @package OStatusPlugin + * @maintainer Brion Vibber + */ + +class Ostatus_source extends Memcached_DataObject +{ + public $__table = 'ostatus_source'; + + public $notice_id; // notice we're referring to + public $profile_uri; // uri of the ostatus_profile this came through -- may be a group feed + public $method; // push or salmon + + public /*static*/ function staticGet($k, $v=null) + { + return parent::staticGet(__CLASS__, $k, $v); + } + + /** + * return table definition for DB_DataObject + * + * DB_DataObject needs to know something about the table to manipulate + * instances. This method provides all the DB_DataObject needs to know. + * + * @return array array of column definitions + */ + + function table() + { + return array('notice_id' => DB_DATAOBJECT_INT + DB_DATAOBJECT_NOTNULL, + 'profile_uri' => DB_DATAOBJECT_STR, + 'method' => DB_DATAOBJECT_STR); + } + + static function schemaDef() + { + return array(new ColumnDef('notice_id', 'integer', + null, false, 'PRI'), + new ColumnDef('profile_uri', 'varchar', + 255, false), + new ColumnDef('method', "ENUM('push','salmon')", + null, false)); + } + + /** + * return key definitions for DB_DataObject + * + * DB_DataObject needs to know about keys that the table has; this function + * defines them. + * + * @return array key definitions + */ + + function keys() + { + return array_keys($this->keyTypes()); + } + + /** + * return key definitions for Memcached_DataObject + * + * Our caching system uses the same key definitions, but uses a different + * method to get them. + * + * @return array key definitions + */ + + function keyTypes() + { + return array('notice_id' => 'K'); + } + + function sequenceKey() + { + return array(false, false, false); + } + + /** + * Save a remote notice source record; this helps indicate how trusted we are. + * @param string $method + */ + public static function saveNew(Notice $notice, Ostatus_profile $oprofile, $method) + { + $osource = new Ostatus_source(); + $osource->notice_id = $notice->id; + $osource->profile_uri = $oprofile->uri; + $osource->method = $method; + if ($osource->insert()) { + return true; + } else { + common_log_db_error($osource, 'INSERT', __FILE__); + return false; + } + } +} diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php index 11c411c7d..d93cc9aa4 100644 --- a/plugins/OStatus/lib/salmonaction.php +++ b/plugins/OStatus/lib/salmonaction.php @@ -202,9 +202,14 @@ class SalmonAction extends Action $options['created'] = common_sql_time($this->act->time); } - return Notice::saveNew($oprofile->profile_id, - $content, - 'ostatus+salmon', - $options); + $saved = Notice::saveNew($oprofile->profile_id, + $content, + 'ostatus+salmon', + $options); + + // Record that this was saved through a validated Salmon source + // @fixme actually do the signature validation! + Ostatus_source::saveNew($saved, $oprofile, 'salmon'); + return $saved; } } -- cgit v1.2.3-54-g00ecf From 75fdef209245bf9424d4b995a42e4dfd980469d2 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 22 Feb 2010 07:57:44 -0500 Subject: handle poco nicknames in Ostatus_profile --- plugins/OStatus/classes/Ostatus_profile.php | 4 +++- plugins/OStatus/lib/activity.php | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 71885bcdc..0e12f8fc6 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -832,7 +832,9 @@ class Ostatus_profile extends Memcached_DataObject protected static function getActivityObjectNickname($object, $hints=array()) { - // XXX: check whatever PoCo calls a nickname first + if (!empty($object->nickname)) { + return common_nicknamize($object->nickname); + } // Try the definitive ID diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index af83f8bc6..a26248f19 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -31,6 +31,12 @@ if (!defined('STATUSNET')) { exit(1); } +class PoCo +{ + const NS = 'http://portablecontacts.net/spec/1.0'; + const USERNAME = 'preferredUsername'; +} + /** * Utilities for turning DOMish things into Activityish things * @@ -319,7 +325,8 @@ class ActivityObject $this->displayName = $this->title; // @fixme we may have multiple avatars with different resolutions specified - $this->avatar = ActivityUtils::getLink($element, 'avatar'); + $this->avatar = ActivityUtils::getLink($element, 'avatar'); + $this->nickname = ActivityUtils::childContent($element, PoCo::USERNAME, PoCo::NS); } } -- cgit v1.2.3-54-g00ecf From 06f155c02df91ae81eb4401c738815ee46b802a6 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 22 Feb 2010 09:43:27 -0800 Subject: OStatus: initial hookup of remote group membership (notice delivery not yet working quite right) - added a temp config var to disable salmon magic signatures until they're working consistently --- plugins/OStatus/OStatusPlugin.php | 93 ++++++++++++++- plugins/OStatus/actions/groupsalmon.php | 77 ++++++++++++- plugins/OStatus/actions/ostatussub.php | 2 +- plugins/OStatus/classes/Ostatus_profile.php | 169 +++++++++++++++++++++------- plugins/OStatus/lib/activity.php | 6 + plugins/OStatus/lib/salmon.php | 21 +++- plugins/OStatus/lib/salmonaction.php | 23 +++- 7 files changed, 336 insertions(+), 55 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 7c6c0c69f..061ed4bd1 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -211,7 +211,7 @@ class OStatusPlugin extends Plugin // FIXME: this needs to go out in a queue handler - $xml = ''; + $xml = ''; $xml .= $notice->asAtomEntry(true, true); $salmon = new Salmon(); @@ -402,6 +402,97 @@ class OStatusPlugin extends Plugin return true; } + /** + * When one of our local users tries to join a remote group, + * notify the remote server. If the notification is rejected, + * deny the join. + * + * @param User_group $group + * @param User $user + * + * @return mixed hook return value + */ + + function onStartJoinGroup($group, $user) + { + $oprofile = Ostatus_profile::staticGet('group_id', $group->id); + if ($oprofile) { + $member = Profile::staticGet($user->id); + + $act = new Activity(); + $act->id = TagURI::mint('join:%d:%d:%s', + $member->id, + $group->id, + common_date_iso8601(time())); + + $act->actor = ActivityObject::fromProfile($member); + $act->verb = ActivityVerb::JOIN; + $act->object = $oprofile->asActivityObject(); + + $act->time = time(); + $act->title = _m("Join"); + $act->content = sprintf(_m("%s has joined group %s."), + $member->getBestName(), + $oprofile->getBestName()); + + if ($oprofile->notifyActivity($act)) { + return true; + } else { + throw new ServerException(_m("Failed joining remote group.")); + } + } + } + + /** + * When one of our local users leaves a remote group, notify the remote + * server. + * + * @fixme Might be good to schedule a resend of the leave notification + * if it failed due to a transitory error. We've canceled the local + * membership already anyway, but if the remote server comes back up + * it'll be left with a stray membership record. + * + * @param User_group $group + * @param User $user + * + * @return mixed hook return value + */ + + function onEndLeaveGroup($group, $user) + { + $oprofile = Ostatus_profile::staticGet('group_id', $group->id); + if ($oprofile) { + // Drop the PuSH subscription if there are no other subscribers. + + $members = $group->getMembers(0, 1); + if ($members->N == 0) { + common_log(LOG_INFO, "Unsubscribing from now-unused group feed $oprofile->feeduri"); + $oprofile->unsubscribe(); + } + + + $member = Profile::staticGet($user->id); + + $act = new Activity(); + $act->id = TagURI::mint('leave:%d:%d:%s', + $member->id, + $group->id, + common_date_iso8601(time())); + + $act->actor = ActivityObject::fromProfile($member); + $act->verb = ActivityVerb::LEAVE; + $act->object = $oprofile->asActivityObject(); + + $act->time = time(); + $act->title = _m("Leave"); + $act->content = sprintf(_m("%s has left group %s."), + $member->getBestName(), + $oprofile->getBestName()); + + $oprofile->notifyActivity($act); + } + } + /** * Notify remote users when their notices get favorited. * diff --git a/plugins/OStatus/actions/groupsalmon.php b/plugins/OStatus/actions/groupsalmon.php index 64ae9f3cc..2e4fe9443 100644 --- a/plugins/OStatus/actions/groupsalmon.php +++ b/plugins/OStatus/actions/groupsalmon.php @@ -88,21 +88,96 @@ class GroupsalmonAction extends SalmonAction * Save a subscription relationship for them. */ + /** + * Postel's law: consider a "follow" notification as a "join". + */ function handleFollow() { - $this->handleJoin(); // ??? + $this->handleJoin(); } + /** + * Postel's law: consider an "unfollow" notification as a "leave". + */ function handleUnfollow() { + $this->handleLeave(); } /** * A remote user joined our group. + * @fixme move permission checks and event call into common code, + * currently we're doing the main logic in joingroup action + * and so have to repeat it here. */ function handleJoin() { + $oprofile = $this->ensureProfile(); + if (!$oprofile) { + $this->clientError(_m("Can't read profile to set up group membership.")); + } + if ($oprofile->isGroup()) { + $this->clientError(_m("Groups can't join groups.")); + } + + common_log(LOG_INFO, "Remote profile {$oprofile->uri} joining local group {$this->group->nickname}"); + $profile = $oprofile->localProfile(); + + if ($profile->isMember($this->group)) { + // Already a member; we'll take it silently to aid in resolving + // inconsistencies on the other side. + return true; + } + + if (Group_block::isBlocked($this->group, $profile)) { + $this->clientError(_('You have been blocked from that group by the admin.'), 403); + return false; + } + + try { + // @fixme that event currently passes a user from main UI + // Event should probably move into Group_member::join + // and take a Profile object. + // + //if (Event::handle('StartJoinGroup', array($this->group, $profile))) { + Group_member::join($this->group->id, $profile->id); + //Event::handle('EndJoinGroup', array($this->group, $profile)); + //} + } catch (Exception $e) { + $this->serverError(sprintf(_m('Could not join remote user %1$s to group %2$s.'), + $oprofile->uri, $this->group->nickname)); + } + } + + /** + * A remote user left our group. + */ + + function handleLeave() + { + $oprofile = $this->ensureProfile(); + if (!$oprofile) { + $this->clientError(_m("Can't read profile to cancel group membership.")); + } + if ($oprofile->isGroup()) { + $this->clientError(_m("Groups can't join groups.")); + } + + common_log(LOG_INFO, "Remote profile {$oprofile->uri} leaving local group {$this->group->nickname}"); + $profile = $oprofile->localProfile(); + + try { + // @fixme event needs to be refactored as above + //if (Event::handle('StartLeaveGroup', array($this->group, $profile))) { + Group_member::leave($this->group->id, $profile->id); + //Event::handle('EndLeaveGroup', array($this->group, $profile)); + //} + } catch (Exception $e) { + $this->serverError(sprintf(_m('Could not remove remote user %1$s from group %2$s.'), + $oprofile->uri, $this->group->nickname)); + return; + } } } diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index 95dec19af..592ae387e 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -248,7 +248,7 @@ class OStatusSubAction extends Action $group = $this->oprofile->localGroup(); if ($user->isMember($group)) { $this->showForm(_m('Already a member!')); - } elseif (Group_member::join($this->profile->group_id, $user->id)) { + } elseif (Group_member::join($this->oprofile->group_id, $user->id)) { $this->showForm(_m('Joined remote group!')); } else { $this->showForm(_m('Remote group join failed!')); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 0e12f8fc6..c0e39add8 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -137,12 +137,49 @@ class Ostatus_profile extends Memcached_DataObject return null; } + /** + * Returns an ActivityObject describing this remote user or group profile. + * Can then be used to generate Atom chunks. + * + * @return ActivityObject + */ + function asActivityObject() + { + if ($this->isGroup()) { + $object = new ActivityObject(); + $object->type = 'http://activitystrea.ms/schema/1.0/group'; + $object->id = $this->uri; + $self = $this->localGroup(); + + // @fixme put a standard getAvatar() interface on groups too + if ($self->homepage_logo) { + $object->avatar = $self->homepage_logo; + $map = array('png' => 'image/png', + 'jpg' => 'image/jpeg', + 'jpeg' => 'image/jpeg', + 'gif' => 'image/gif'); + $extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION); + if (isset($map[$extension])) { + // @fixme this ain't used/saved yet + $object->avatarType = $map[$extension]; + } + } + + $object->link = $this->uri; // @fixme accurate? + return $object; + } else { + return ActivityObject::fromProfile($this->localProfile()); + } + } + /** * Returns an XML string fragment with profile information as an * Activity Streams noun object with the given element type. * * Assumes that 'activity' namespace has been previously defined. * + * @fixme replace with wrappers on asActivityObject when it's got everything. + * * @param string $element one of 'actor', 'subject', 'object', 'target' * @return string */ @@ -202,11 +239,19 @@ class Ostatus_profile extends Memcached_DataObject } /** - * Damn dirty hack! + * @return boolean true if this is a remote group */ function isGroup() { - return (strpos($this->feeduri, '/groups/') !== false); + if ($this->profile_id && !$this->group_id) { + return false; + } else if ($this->group_id && !$this->profile_id) { + return true; + } else if ($this->group_id && $this->profile_id) { + throw new ServerException("Invalid ostatus_profile state: both group and profile IDs set for $this->uri"); + } else { + throw new ServerException("Invalid ostatus_profile state: both group and profile IDs empty for $this->uri"); + } } /** @@ -353,22 +398,24 @@ class Ostatus_profile extends Memcached_DataObject common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml"); $salmon = new Salmon(); // ? - $salmon->post($this->salmonuri, $xml); + return $salmon->post($this->salmonuri, $xml); } + return false; } public function notifyActivity($activity) { if ($this->salmonuri) { - $xml = $activity->asString(true); + $xml = '' . + $activity->asString(true); $salmon = new Salmon(); // ? - $salmon->post($this->salmonuri, $xml); + return $salmon->post($this->salmonuri, $xml); } - return; + return false; } function getBestName() @@ -597,10 +644,23 @@ class Ostatus_profile extends Memcached_DataObject */ protected function updateAvatar($url) { + if ($this->isGroup()) { + $self = $this->localGroup(); + } else { + $self = $this->localProfile(); + } + if (!$self) { + throw new ServerException(sprintf( + _m("Tried to update avatar for unsaved remote profile %s"), + $this->uri)); + } + // @fixme this should be better encapsulated // ripped from oauthstore.php (for old OMB client) $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar'); - copy($url, $temp_filename); + if (!copy($url, $temp_filename)) { + throw new ServerException(sprintf(_m("Unable to fetch avatar from %s"), $url)); + } if ($this->isGroup()) { $id = $this->group_id; @@ -614,13 +674,7 @@ class Ostatus_profile extends Memcached_DataObject null, common_timestamp()); rename($temp_filename, Avatar::path($filename)); - if ($this->isGroup()) { - $group = $this->localGroup(); - $group->setOriginal($filename); - } else { - $profile = $this->localProfile(); - $profile->setOriginal($filename); - } + $self->setOriginal($filename); } protected static function getActivityObjectAvatar($object) @@ -747,6 +801,18 @@ class Ostatus_profile extends Memcached_DataObject self::createActivityObjectProfile($actor, $feeduri, $salmonuri); } + /** + * Create local ostatus_profile and profile/user_group entries for + * the provided remote user or group. + * + * @param ActivityObject $object + * @param string $feeduri + * @param string $salmonuri + * @param array $hints + * + * @fixme fold $feeduri/$salmonuri into $hints + * @return Ostatus_profile + */ protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) { $homeuri = $object->id; @@ -784,46 +850,65 @@ class Ostatus_profile extends Memcached_DataObject } } - $profile = new Profile(); - $profile->nickname = $nickname; - $profile->fullname = $object->title; - if (!empty($object->link)) { - $profile->profileurl = $object->link; - } else if (array_key_exists('profileurl', $hints)) { - $profile->profileurl = $hints['profileurl']; - } - $profile->created = common_sql_now(); - - // @fixme bio - // @fixme tags/categories - // @fixme location? - // @todo tags from categories - // @todo lat/lon/location? - - $profile_id = $profile->insert(); - - if (!$profile_id) { - throw new ServerException("Can't save local profile"); - } - - // @fixme either need to do feed discovery here - // or need to split out some of the feed stuff - // so we can leave it empty until later. - $oprofile = new Ostatus_profile(); $oprofile->uri = $homeuri; $oprofile->feeduri = $feeduri; $oprofile->salmonuri = $salmonuri; - $oprofile->profile_id = $profile_id; $oprofile->created = common_sql_now(); $oprofile->modified = common_sql_now(); + if ($object->type == ActivityObject::PERSON) { + $profile = new Profile(); + $profile->nickname = $nickname; + $profile->fullname = $object->title; + if (!empty($object->link)) { + $profile->profileurl = $object->link; + } else if (array_key_exists('profileurl', $hints)) { + $profile->profileurl = $hints['profileurl']; + } + $profile->created = common_sql_now(); + + // @fixme bio + // @fixme tags/categories + // @fixme location? + // @todo tags from categories + // @todo lat/lon/location? + + $oprofile->profile_id = $profile->insert(); + + if (!$oprofile->profile_id) { + throw new ServerException("Can't save local profile"); + } + } else { + $group = new User_group(); + $group->nickname = $nickname; + $group->fullname = $object->title; + // @fixme no canonical profileurl; using homepage instead for now + $group->homepage = $homeuri; + $group->created = common_sql_now(); + + // @fixme homepage + // @fixme bio + // @fixme tags/categories + // @fixme location? + // @todo tags from categories + // @todo lat/lon/location? + + $oprofile->group_id = $group->insert(); + + if (!$oprofile->group_id) { + throw new ServerException("Can't save local profile"); + } + } + $ok = $oprofile->insert(); if ($ok) { - $oprofile->updateAvatar($avatar); + if ($avatar) { + $oprofile->updateAvatar($avatar); + } return $oprofile; } else { throw new ServerException("Can't save OStatus profile"); diff --git a/plugins/OStatus/lib/activity.php b/plugins/OStatus/lib/activity.php index a26248f19..6cb9881bf 100644 --- a/plugins/OStatus/lib/activity.php +++ b/plugins/OStatus/lib/activity.php @@ -367,6 +367,9 @@ class ActivityObject return $object; } + /** + * @fixme missing avatar, bio info, etc + */ static function fromProfile($profile) { $object = new ActivityObject(); @@ -379,6 +382,9 @@ class ActivityObject return $object; } + /** + * @fixme missing avatar, bio info, etc + */ function asString($tag='activity:object') { $xs = new XMLStringer(true); diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index 53925dc3f..b5f178cc6 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -28,15 +28,26 @@ */ class Salmon { + /** + * Sign and post the given Atom entry as a Salmon message. + * + * @fixme pass through the actor for signing? + * + * @param string $endpoint_uri + * @param string $xml + * @return boolean success + */ public function post($endpoint_uri, $xml) { if (empty($endpoint_uri)) { - return FALSE; + return false; } - $xml = $this->createMagicEnv($xml); - - $headers = array('Content-type: application/atom+xml'); + if (!common_config('ostatus', 'skip_signatures')) { + $xml = $this->createMagicEnv($xml); + } + + $headers = array('Content-Type: application/atom+xml'); try { $client = new HTTPClient(); @@ -51,7 +62,7 @@ class Salmon $response->getStatus() . ': ' . $response->getBody()); return false; } - + return true; } public function createMagicEnv($text) diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php index 09a042975..83cf0b8f8 100644 --- a/plugins/OStatus/lib/salmonaction.php +++ b/plugins/OStatus/lib/salmonaction.php @@ -41,7 +41,7 @@ class SalmonAction extends Action $this->clientError(_('This method requires a POST.')); } - if ($_SERVER['CONTENT_TYPE'] != 'application/atom+xml') { + if (empty($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/atom+xml') { $this->clientError(_('Salmon requires application/atom+xml')); } @@ -57,11 +57,13 @@ class SalmonAction extends Action // Check the signature $salmon = new Salmon; - if (!$salmon->verifyMagicEnv($dom)) { - common_log(LOG_DEBUG, "Salmon signature verification failed."); - $this->clientError(_m('Salmon signature verification failed.')); + if (!common_config('ostatus', 'skip_signatures')) { + if (!$salmon->verifyMagicEnv($dom)) { + common_log(LOG_DEBUG, "Salmon signature verification failed."); + $this->clientError(_m('Salmon signature verification failed.')); + } } - + $this->act = new Activity($dom->documentElement); return true; } @@ -101,6 +103,9 @@ class SalmonAction extends Action case ActivityVerb::JOIN: $this->handleJoin(); break; + case ActivityVerb::LEAVE: + $this->handleLeave(); + break; default: throw new ClientException(_("Unimplemented.")); } @@ -154,6 +159,14 @@ class SalmonAction extends Action throw new ClientException(_("Unimplemented!")); } + /** + * Hmmmm + */ + function handleLeave() + { + throw new ClientException(_("Unimplemented!")); + } + /** * @return Ostatus_profile */ -- cgit v1.2.3-54-g00ecf From d410df040684f443d14bd921c450ca464d52c9d4 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Feb 2010 00:44:45 +0000 Subject: OStatus group delivery initial implementation. - added rel="ostatus:attention" links for group delivery - added events for plugins to override group profile/permalink pages - pulled Notice::saveGroups up to save-time so we can override; it's relatively cheap and gives us a clean list of target groups for distrib time even with customized delivery. - fixed notice::getGroups to return group objects as expected - added some doc on new parameters to Notice::saveNew - 'groups' list of group IDs to push to in place of parsing - messages that come in via PuSH and contain local group targets are delivered to local group members - messages that come in via PuSH and contain remote group targets are delivered to local members of the remote group Todo: - handle group posts that only come through Salmon - handle conflicts in case something comes in both through Salmon and PuSH - better source verification - need a cleaner interface to look up groups by URI - need a way to handle remote groups with conflicting names --- actions/apitimelinegroup.php | 3 +- classes/Notice.php | 111 +++++++++++++++++++++++-- classes/User_group.php | 18 +++- lib/distribqueuehandler.php | 14 +--- plugins/OStatus/OStatusPlugin.php | 16 ++++ plugins/OStatus/classes/Ostatus_profile.php | 75 +++++++++++++++-- plugins/OStatus/lib/hubdistribqueuehandler.php | 2 +- 7 files changed, 207 insertions(+), 32 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/actions/apitimelinegroup.php b/actions/apitimelinegroup.php index 1d0c4afdd..0bb4860ea 100644 --- a/actions/apitimelinegroup.php +++ b/actions/apitimelinegroup.php @@ -176,7 +176,8 @@ class ApiTimelineGroupAction extends ApiPrivateAuthAction $atom->addEntryFromNotices($this->notices); - $this->raw($atom->getString()); + //$this->raw($atom->getString()); + print $atom->getString(); // temp hack until PuSH feeds are redone cleanly } catch (Atom10FeedException $e) { $this->serverError( diff --git a/classes/Notice.php b/classes/Notice.php index a12839d72..754c126ed 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -187,7 +187,14 @@ class Notice extends Memcached_DataObject * int 'location_ns' geoname namespace to interpret location_id * int 'reply_to'; notice ID this is a reply to * int 'repeat_of'; notice ID this is a repeat of - * string 'uri' permalink to notice; defaults to local notice URL + * string 'uri' unique ID for notice; defaults to local notice URL + * string 'url' permalink to notice; defaults to local notice URL + * string 'rendered' rendered HTML version of content + * array 'replies' list of profile URIs for reply delivery in + * place of extracting @-replies from content. + * array 'groups' list of group IDs to deliver to, in place of + * extracting ! tags from content + * @fixme tag override * * @return Notice * @throws ClientException @@ -342,6 +349,12 @@ class Notice extends Memcached_DataObject $notice->saveReplies(); } + if (isset($groups)) { + $notice->saveKnownGroups($groups); + } else { + $notice->saveGroups(); + } + $notice->distribute(); return $notice; @@ -692,7 +705,22 @@ class Notice extends Memcached_DataObject return $ni; } - function addToInboxes($groups, $recipients) + /** + * Adds this notice to the inboxes of each local user who should receive + * it, based on author subscriptions, group memberships, and @-replies. + * + * Warning: running a second time currently will make items appear + * multiple times in users' inboxes. + * + * @fixme make more robust against errors + * @fixme break up massive deliveries to smaller background tasks + * + * @param array $groups optional list of Group objects; + * if left empty, will be loaded from group_inbox records + * @param array $recipient optional list of reply profile ids + * if left empty, will be loaded from reply records + */ + function addToInboxes($groups=null, $recipients=null) { $ni = $this->whoGets($groups, $recipients); @@ -742,6 +770,42 @@ class Notice extends Memcached_DataObject } /** + * Record this notice to the given group inboxes for delivery. + * Overrides the regular parsing of !group markup. + * + * @param string $group_ids + * @fixme might prefer URIs as identifiers, as for replies? + * best with generalizations on user_group to support + * remote groups better. + */ + function saveKnownGroups($group_ids) + { + if (!is_array($group_ids)) { + throw new ServerException("Bad type provided to saveKnownGroups"); + } + + $groups = array(); + foreach ($group_ids as $id) { + $group = User_group::staticGet('id', $id); + if ($group) { + common_log(LOG_ERR, "Local delivery to group id $id, $group->nickname"); + $result = $this->addToGroupInbox($group); + if (!$result) { + common_log_db_error($gi, 'INSERT', __FILE__); + } + + // @fixme should we save the tags here or not? + $groups[] = clone($group); + } else { + common_log(LOG_ERR, "Local delivery to group id $id skipped, doesn't exist"); + } + } + + return $groups; + } + + /** + * Parse !group delivery and record targets into group_inbox. * @return array of Group objects */ function saveGroups() @@ -824,6 +888,19 @@ class Notice extends Memcached_DataObject return true; } + /** + * Save reply records indicating that this notice needs to be + * delivered to the local users with the given URIs. + * + * Since this is expected to be used when saving foreign-sourced + * messages, we won't deliver to any remote targets as that's the + * source service's responsibility. + * + * @fixme Unlike saveReplies() there's no mail notification here. + * Move that to distrib queue handler? + * + * @param array of unique identifier URIs for recipients + */ function saveKnownReplies($uris) { foreach ($uris as $uri) { @@ -845,6 +922,13 @@ class Notice extends Memcached_DataObject } /** + * Pull @-replies from this message's content in StatusNet markup format + * and save reply records indicating that this message needs to be + * delivered to those users. + * + * Side effect: local recipients get e-mail notifications here. + * @fixme move mail notifications to distrib? + * * @return array of integer profile IDs */ @@ -934,9 +1018,10 @@ class Notice extends Memcached_DataObject } /** - * Same calculation as saveGroups but without the saving - * @fixme merge the functions - * @return array of Group_inbox objects + * Pull list of groups this notice needs to be delivered to, + * as previously recorded by saveGroups() or saveKnownGroups(). + * + * @return array of Group objects */ function getGroups() { @@ -959,7 +1044,10 @@ class Notice extends Memcached_DataObject if ($gi->find()) { while ($gi->fetch()) { - $groups[] = clone($gi); + $group = User_group::staticGet('id', $gi->group_id); + if ($group) { + $groups[] = $group; + } } } @@ -1063,6 +1151,17 @@ class Notice extends Memcached_DataObject } } + $groups = $this->getGroups(); + + foreach ($groups as $group) { + $xs->element( + 'link', array( + 'rel' => 'ostatus:attention', + 'href' => $group->permalink() + ) + ); + } + if (!empty($this->repeat_of)) { $repeat = Notice::staticGet('id', $this->repeat_of); if (!empty($repeat)) { diff --git a/classes/User_group.php b/classes/User_group.php index 379e6b721..1382aa407 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -39,14 +39,24 @@ class User_group extends Memcached_DataObject function homeUrl() { - return common_local_url('showgroup', - array('nickname' => $this->nickname)); + $url = null; + if (Event::handle('StartUserGroupHomeUrl', array($this, &$url))) { + $url = common_local_url('showgroup', + array('nickname' => $this->nickname)); + } + Event::handle('EndUserGroupHomeUrl', array($this, &$url)); + return $url; } function permalink() { - return common_local_url('groupbyid', - array('id' => $this->id)); + $url = null; + if (Event::handle('StartUserGroupPermalink', array($this, &$url))) { + $url = common_local_url('groupbyid', + array('id' => $this->id)); + } + Event::handle('EndUserGroupPermalink', array($this, &$url)); + return $url; } function getNotices($offset, $limit, $since_id=null, $max_id=null) diff --git a/lib/distribqueuehandler.php b/lib/distribqueuehandler.php index c31b675c1..dc183fb36 100644 --- a/lib/distribqueuehandler.php +++ b/lib/distribqueuehandler.php @@ -69,19 +69,7 @@ class DistribQueueHandler } try { - $groups = $notice->saveGroups(); - } catch (Exception $e) { - $this->logit($notice, $e); - } - - try { - $recipients = $notice->getReplies(); - } catch (Exception $e) { - $this->logit($notice, $e); - } - - try { - $notice->addToInboxes($groups, $recipients); + $notice->addToInboxes(); } catch (Exception $e) { $this->logit($notice, $e); } diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 061ed4bd1..472008419 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -591,6 +591,22 @@ class OStatusPlugin extends Plugin return true; } + function onStartUserGroupHomeUrl($group, &$url) + { + return $this->onStartUserGroupPermalink($group, &$url); + } + + function onStartUserGroupPermalink($group, &$url) + { + $oprofile = Ostatus_profile::staticGet('group_id', $group->id); + if ($oprofile) { + // @fixme this should probably be in the user_group table + // @fixme this uri not guaranteed to be a profile page + $url = $oprofile->uri; + return false; + } + } + function onStartShowSubscriptionsContent($action) { $user = common_current_user(); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index c0e39add8..e8cc13c6c 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -501,6 +501,7 @@ class Ostatus_profile extends Memcached_DataObject /** * Process an incoming post activity from this remote feed. * @param Activity $activity + * @fixme break up this function, it's getting nasty long */ protected function processPost($activity) { @@ -518,7 +519,6 @@ class Ostatus_profile extends Memcached_DataObject } $oprofile = $this; } - $sourceUri = $activity->object->id; $dupe = Notice::staticGet('uri', $sourceUri); @@ -555,15 +555,76 @@ class Ostatus_profile extends Memcached_DataObject } } - // @fixme ensure that groups get handled correctly + $profile = $oprofile->localProfile(); + $params['groups'] = array(); + $params['replies'] = array(); + if ($activity->context) { + foreach ($activity->context->attention as $recipient) { + $roprofile = Ostatus_profile::staticGet('uri', $recipient); + if ($roprofile) { + if ($roprofile->isGroup()) { + // Deliver to local recipients of this remote group. + // @fixme sender verification? + $params['groups'][] = $roprofile->group_id; + continue; + } else { + // Delivery to remote users is the source service's job. + continue; + } + } + + $user = User::staticGet('uri', $recipient); + if ($user) { + // An @-reply directed to a local user. + // @fixme sender verification, spam etc? + $params['replies'][] = $recipient; + continue; + } + + // @fixme we need a uri on user_group + // $group = User_group::staticGet('uri', $recipient); + $template = common_local_url('groupbyid', array('id' => '31337')); + $template = preg_quote($template, '/'); + $template = str_replace('31337', '(\d+)', $template); + common_log(LOG_DEBUG, $template); + if (preg_match("/$template/", $recipient, $matches)) { + $id = $matches[1]; + $group = User_group::staticGet('id', $id); + if ($group) { + // Deliver to all members of this local group. + // @fixme sender verification? + if ($profile->isMember($group)) { + common_log(LOG_DEBUG, "delivering to group $id $group->nickname"); + $params['groups'][] = $group->id; + } else { + common_log(LOG_DEBUG, "not delivering to group $id $group->nickname because sender $profile->nickname is not a member"); + } + continue; + } else { + common_log(LOG_DEBUG, "not delivering to missing group $id"); + } + } else { + common_log(LOG_DEBUG, "not delivering to groups for $recipient"); + } + } + } - $saved = Notice::saveNew($oprofile->localProfile()->id, - $content, - 'ostatus', - $params); + try { + $saved = Notice::saveNew($profile->id, + $content, + 'ostatus', + $params); + } catch (Exception $e) { + common_log(LOG_ERR, "Failed saving notice entry for $sourceUri: " . $e->getMessage()); + return; + } // Record which feed this came through... - Ostatus_source::saveNew($saved, $this, 'push'); + try { + Ostatus_source::saveNew($saved, $this, 'push'); + } catch (Exception $e) { + common_log(LOG_ERR, "Failed saving ostatus_source entry for $saved->notice_id: " . $e->getMessage()); + } } /** diff --git a/plugins/OStatus/lib/hubdistribqueuehandler.php b/plugins/OStatus/lib/hubdistribqueuehandler.php index 30a427e3f..c2bd630f9 100644 --- a/plugins/OStatus/lib/hubdistribqueuehandler.php +++ b/plugins/OStatus/lib/hubdistribqueuehandler.php @@ -36,7 +36,7 @@ class HubDistribQueueHandler extends QueueHandler $this->pushUser($notice); foreach ($notice->getGroups() as $group) { - $this->pushGroup($notice, $group->group_id); + $this->pushGroup($notice, $group->id); } return true; } -- cgit v1.2.3-54-g00ecf From 90d34b26c66ceb5d37a9c2782356b46361a523cc Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Feb 2010 20:44:27 +0000 Subject: OStatus: do PuSH subscription setup from subscribe/join event hooks, so resubscribing directly from a profile/group list works correctly if there aren't active subscriptions at the moment. --- lib/activity.php | 4 +- plugins/OStatus/OStatusPlugin.php | 158 +++++++++++++++++----------- plugins/OStatus/actions/ostatussub.php | 5 - plugins/OStatus/classes/Ostatus_profile.php | 26 ++++- 4 files changed, 127 insertions(+), 66 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/lib/activity.php b/lib/activity.php index 3689dac38..04c57c561 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -823,7 +823,9 @@ class Activity if ($namespace) { $attrs = array('xmlns' => 'http://www.w3.org/2005/Atom', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', - 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0'); + 'xmlns:georss' => 'http://www.georss.org/georss', + 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', + 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0'); } else { $attrs = array(); } diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index db4a0af35..629645767 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -251,58 +251,6 @@ class OStatusPlugin extends Plugin return true; } - /** - * Notify remote server and garbage collect unused feeds on unsubscribe. - * @fixme send these operations to background queues - * - * @param User $user - * @param Profile $other - * @return hook return value - */ - function onEndUnsubscribe($profile, $other) - { - $user = User::staticGet('id', $profile->id); - - if (empty($user)) { - return true; - } - - $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); - - if (empty($oprofile)) { - return true; - } - - // Drop the PuSH subscription if there are no other subscribers. - - if ($other->subscriberCount() == 0) { - common_log(LOG_INFO, "Unsubscribing from now-unused feed $oprofile->feeduri"); - $oprofile->unsubscribe(); - } - - $act = new Activity(); - - $act->verb = ActivityVerb::UNFOLLOW; - - $act->id = TagURI::mint('unfollow:%d:%d:%s', - $profile->id, - $other->id, - common_date_iso8601(time())); - - $act->time = time(); - $act->title = _("Unfollow"); - $act->content = sprintf(_("%s stopped following %s."), - $profile->getBestName(), - $other->getBestName()); - - $act->actor = ActivityObject::fromProfile($profile); - $act->object = ActivityObject::fromProfile($other); - - $oprofile->notifyActivity($act); - - return true; - } - /** * Make sure necessary tables are filled out. */ @@ -366,6 +314,50 @@ class OStatusPlugin extends Plugin } } + /** + * When about to subscribe to a remote user, start a server-to-server + * PuSH subscription if needed. If we can't establish that, abort. + * + * @fixme If something else aborts later, we could end up with a stray + * PuSH subscription. This is relatively harmless, though. + * + * @param Profile $subscriber + * @param Profile $other + * + * @return hook return code + * + * @throws Exception + */ + function onStartSubscribe($subscriber, $other) + { + $user = User::staticGet('id', $subscriber->id); + + if (empty($user)) { + return true; + } + + $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); + + if (empty($oprofile)) { + return true; + } + + if (!$oprofile->subscribe()) { + throw new Exception(_m('Could not set up remote subscription.')); + } + } + + /** + * Having established a remote subscription, send a notification to the + * remote OStatus profile's endpoint. + * + * @param Profile $subscriber + * @param Profile $other + * + * @return hook return code + * + * @throws Exception + */ function onEndSubscribe($subscriber, $other) { $user = User::staticGet('id', $subscriber->id); @@ -403,6 +395,54 @@ class OStatusPlugin extends Plugin return true; } + /** + * Notify remote server and garbage collect unused feeds on unsubscribe. + * @fixme send these operations to background queues + * + * @param User $user + * @param Profile $other + * @return hook return value + */ + function onEndUnsubscribe($profile, $other) + { + $user = User::staticGet('id', $profile->id); + + if (empty($user)) { + return true; + } + + $oprofile = Ostatus_profile::staticGet('profile_id', $other->id); + + if (empty($oprofile)) { + return true; + } + + // Drop the PuSH subscription if there are no other subscribers. + $oprofile->garbageCollect(); + + $act = new Activity(); + + $act->verb = ActivityVerb::UNFOLLOW; + + $act->id = TagURI::mint('unfollow:%d:%d:%s', + $profile->id, + $other->id, + common_date_iso8601(time())); + + $act->time = time(); + $act->title = _("Unfollow"); + $act->content = sprintf(_("%s stopped following %s."), + $profile->getBestName(), + $other->getBestName()); + + $act->actor = ActivityObject::fromProfile($profile); + $act->object = ActivityObject::fromProfile($other); + + $oprofile->notifyActivity($act); + + return true; + } + /** * When one of our local users tries to join a remote group, * notify the remote server. If the notification is rejected, @@ -418,6 +458,10 @@ class OStatusPlugin extends Plugin { $oprofile = Ostatus_profile::staticGet('group_id', $group->id); if ($oprofile) { + if (!$oprofile->subscribe()) { + throw new Exception(_m('Could not set up remote group membership.')); + } + $member = Profile::staticGet($user->id); $act = new Activity(); @@ -439,7 +483,8 @@ class OStatusPlugin extends Plugin if ($oprofile->notifyActivity($act)) { return true; } else { - throw new ServerException(_m("Failed joining remote group.")); + $oprofile->garbageCollect(); + throw new Exception(_m("Failed joining remote group.")); } } } @@ -464,12 +509,7 @@ class OStatusPlugin extends Plugin $oprofile = Ostatus_profile::staticGet('group_id', $group->id); if ($oprofile) { // Drop the PuSH subscription if there are no other subscribers. - - $members = $group->getMembers(0, 1); - if ($members->N == 0) { - common_log(LOG_INFO, "Unsubscribing from now-unused group feed $oprofile->feeduri"); - $oprofile->unsubscribe(); - } + $oprofile->garbageCollect(); $member = Profile::staticGet($user->id); diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index df9aa80b0..b3569e695 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -324,11 +324,6 @@ class OStatusSubAction extends Action // And subscribe the current user to the local profile $user = common_current_user(); - if (!$this->oprofile->subscribe()) { - $this->showForm(_m("Failed to set up server-to-server subscription.")); - return; - } - if ($this->oprofile->isGroup()) { $group = $this->oprofile->localGroup(); if ($user->isMember($group)) { diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index e8cc13c6c..91b957fa2 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -346,6 +346,29 @@ class Ostatus_profile extends Memcached_DataObject } } + /** + * Check if this remote profile has any active local subscriptions, and + * if not drop the PuSH subscription feed. + * + * @return boolean + */ + public function garbageCollect() + { + if ($this->isGroup()) { + $members = $this->localGroup()->getMembers(0, 1); + $count = $members->N; + } else { + $count = $this->localProfile()->subscriberCount(); + } + if ($count == 0) { + common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $oprofile->feeduri"); + $this->unsubscribe(); + return true; + } else { + return false; + } + } + /** * Send an Activity Streams notification to the remote Salmon endpoint, * if so configured. @@ -379,7 +402,8 @@ class Ostatus_profile extends Memcached_DataObject 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:georss' => 'http://www.georss.org/georss', - 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0'); + 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', + 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0'); $entry = new XMLStringer(); $entry->elementStart('entry', $attributes); -- cgit v1.2.3-54-g00ecf From d6ad7332475f1cc4ab45d55fc04ef491d5f3999d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Feb 2010 21:47:14 +0000 Subject: OStatus: fixes for link/id and text extraction gets import of Buzz feeds working. --- plugins/OStatus/OStatusPlugin.php | 26 ++++++++++++++------------ plugins/OStatus/classes/Ostatus_profile.php | 17 ++++++++++++++--- 2 files changed, 28 insertions(+), 15 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 3a9e77c2a..724634924 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -287,13 +287,19 @@ class OStatusPlugin extends Plugin function onStartNoticeSourceLink($notice, &$name, &$url, &$title) { if ($notice->source == 'ostatus') { - $bits = parse_url($notice->uri); - $domain = $bits['host']; - - $name = $domain; - $url = $notice->uri; - $title = sprintf(_m("Sent from %s via OStatus"), $domain); - return false; + if ($notice->url) { + $bits = parse_url($notice->url); + $domain = $bits['host']; + if (substr($domain, 0, 4) == 'www.') { + $name = substr($domain, 4); + } else { + $name = $domain; + } + + $url = $notice->url; + $title = sprintf(_m("Sent from %s via OStatus"), $domain); + return false; + } } } @@ -509,12 +515,8 @@ class OStatusPlugin extends Plugin $oprofile = Ostatus_profile::staticGet('group_id', $group->id); if ($oprofile) { // Drop the PuSH subscription if there are no other subscribers. + $oprofile->garbageCollect(); - $members = $group->getMembers(0, 1); - if ($members->N == 0) { - common_log(LOG_INFO, "Unsubscribing from now-unused group feed $oprofile->feeduri"); - $oprofile->unsubscribe(); - } $member = Profile::staticGet($user->id); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 91b957fa2..4998809bc 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -556,17 +556,28 @@ class Ostatus_profile extends Memcached_DataObject if ($activity->object->link) { $sourceUrl = $activity->object->link; + } else if ($activity->link) { + $sourceUrl = $activity->link; } else if (preg_match('!^https?://!', $activity->object->id)) { $sourceUrl = $activity->object->id; } - // @fixme sanitize and save HTML content if available + // Get (safe!) HTML and text versions of the content - $content = $activity->object->title; + require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); + + $html = $activity->object->content; + + $purifier = new HTMLPurifier(); + + $rendered = $purifier->purify($html); + + $content = html_entity_decode(strip_tags($rendered)); $params = array('is_local' => Notice::REMOTE_OMB, 'url' => $sourceUrl, - 'uri' => $sourceUri); + 'uri' => $sourceUri, + 'rendered' => $rendered); $location = $activity->context->location; -- cgit v1.2.3-54-g00ecf From 584b87cfe57bd2d683101194040e3563f0706536 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 01:09:52 +0000 Subject: OStatus: consolidate the low-level notice save code between Salmon and PuSH input paths. Validation etc remains at higher levels. --- plugins/OStatus/OStatusPlugin.php | 2 +- plugins/OStatus/classes/Ostatus_profile.php | 214 ++++++++++++++++------------ plugins/OStatus/lib/salmonaction.php | 50 +------ 3 files changed, 125 insertions(+), 141 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 724634924..35f952935 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -314,7 +314,7 @@ class OStatusPlugin extends Plugin { $oprofile = Ostatus_profile::staticGet('feeduri', $feedsub->uri); if ($oprofile) { - $oprofile->processFeed($feed); + $oprofile->processFeed($feed, 'push'); } else { common_log(LOG_DEBUG, "No ostatus profile for incoming feed $feedsub->uri"); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 4998809bc..6beaf0f5d 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -488,7 +488,7 @@ class Ostatus_profile extends Memcached_DataObject * * @param DOMDocument $feed */ - public function processFeed($feed) + public function processFeed($feed, $source) { $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); if ($entries->length == 0) { @@ -498,7 +498,7 @@ class Ostatus_profile extends Memcached_DataObject for ($i = 0; $i < $entries->length; $i++) { $entry = $entries->item($i); - $this->processEntry($entry, $feed); + $this->processEntry($entry, $feed, $source); } } @@ -508,15 +508,12 @@ class Ostatus_profile extends Memcached_DataObject * @param DOMElement $entry * @param DOMElement $feed for context */ - protected function processEntry($entry, $feed) + public function processEntry($entry, $feed, $source) { $activity = new Activity($entry, $feed); - $debug = var_export($activity, true); - common_log(LOG_DEBUG, $debug); - if ($activity->verb == ActivityVerb::POST) { - $this->processPost($activity); + $this->processPost($activity, $source); } else { common_log(LOG_INFO, "Ignoring activity with unrecognized verb $activity->verb"); } @@ -525,35 +522,47 @@ class Ostatus_profile extends Memcached_DataObject /** * Process an incoming post activity from this remote feed. * @param Activity $activity + * @param string $method 'push' or 'salmon' + * @return mixed saved Notice or false * @fixme break up this function, it's getting nasty long */ - protected function processPost($activity) + public function processPost($activity, $method) { if ($this->isGroup()) { + // A group feed will contain posts from multiple authors. // @fixme validate these profiles in some way! $oprofile = self::ensureActorProfile($activity); + if ($oprofile->isGroup()) { + // Groups can't post notices in StatusNet. + common_log(LOG_WARNING, "OStatus: skipping post with group listed as author: $oprofile->uri in feed from $this->uri"); + return false; + } } else { + // Individual user feeds may contain only posts from themselves. + // Authorship is validated against the profile URI on upper layers, + // through PuSH setup or Salmon signature checks. $actorUri = self::getActorProfileURI($activity); if ($actorUri == $this->uri) { // @fixme check if profile info has changed and update it } else { - // @fixme drop or reject the messages once we've got the canonical profile URI recorded sanely - common_log(LOG_INFO, "OStatus: Warning: non-group post with unexpected author: $actorUri expected $this->uri"); - //return; + common_log(LOG_WARNING, "OStatus: skipping post with bad author: got $actorUri expected $this->uri"); + return false; } $oprofile = $this; } - $sourceUri = $activity->object->id; + // The id URI will be used as a unique identifier for for the notice, + // protecting against duplicate saves. It isn't required to be a URL; + // tag: URIs for instance are found in Google Buzz feeds. + $sourceUri = $activity->object->id; $dupe = Notice::staticGet('uri', $sourceUri); - if ($dupe) { common_log(LOG_INFO, "OStatus: ignoring duplicate post: $sourceUri"); - return; + return false; } + // We'll also want to save a web link to the original notice, if provided. $sourceUrl = null; - if ($activity->object->link) { $sourceUrl = $activity->object->link; } else if ($activity->link) { @@ -563,103 +572,126 @@ class Ostatus_profile extends Memcached_DataObject } // Get (safe!) HTML and text versions of the content - - require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); - - $html = $activity->object->content; - - $purifier = new HTMLPurifier(); - - $rendered = $purifier->purify($html); - + $rendered = $this->purify($activity->object->content); $content = html_entity_decode(strip_tags($rendered)); - $params = array('is_local' => Notice::REMOTE_OMB, + $options = array('is_local' => Notice::REMOTE_OMB, 'url' => $sourceUrl, 'uri' => $sourceUri, - 'rendered' => $rendered); + 'rendered' => $rendered, + 'replies' => array(), + 'groups' => array()); - $location = $activity->context->location; + // Check for optional attributes... - if ($location) { - $params['lat'] = $location->lat; - $params['lon'] = $location->lon; - if ($location->location_id) { - $params['location_ns'] = $location->location_ns; - $params['location_id'] = $location->location_id; - } + if (!empty($activity->time)) { + $options['created'] = common_sql_date($activity->time); } - $profile = $oprofile->localProfile(); - $params['groups'] = array(); - $params['replies'] = array(); if ($activity->context) { - foreach ($activity->context->attention as $recipient) { - $roprofile = Ostatus_profile::staticGet('uri', $recipient); - if ($roprofile) { - if ($roprofile->isGroup()) { - // Deliver to local recipients of this remote group. - // @fixme sender verification? - $params['groups'][] = $roprofile->group_id; - continue; - } else { - // Delivery to remote users is the source service's job. - continue; - } - } - - $user = User::staticGet('uri', $recipient); - if ($user) { - // An @-reply directed to a local user. - // @fixme sender verification, spam etc? - $params['replies'][] = $recipient; - continue; + // Any individual or group attn: targets? + $replies = $activity->context->attention; + $options['groups'] = $this->filterReplies($oprofile, $replies); + $options['replies'] = $replies; + + // Maintain direct reply associations + // @fixme what about conversation ID? + if (!empty($activity->context->replyToID)) { + $orig = Notice::staticGet('uri', + $activity->context->replyToID); + if (!empty($orig)) { + $options['reply_to'] = $orig->id; } - - // @fixme we need a uri on user_group - // $group = User_group::staticGet('uri', $recipient); - $template = common_local_url('groupbyid', array('id' => '31337')); - $template = preg_quote($template, '/'); - $template = str_replace('31337', '(\d+)', $template); - common_log(LOG_DEBUG, $template); - if (preg_match("/$template/", $recipient, $matches)) { - $id = $matches[1]; - $group = User_group::staticGet('id', $id); - if ($group) { - // Deliver to all members of this local group. - // @fixme sender verification? - if ($profile->isMember($group)) { - common_log(LOG_DEBUG, "delivering to group $id $group->nickname"); - $params['groups'][] = $group->id; - } else { - common_log(LOG_DEBUG, "not delivering to group $id $group->nickname because sender $profile->nickname is not a member"); - } - continue; - } else { - common_log(LOG_DEBUG, "not delivering to missing group $id"); - } - } else { - common_log(LOG_DEBUG, "not delivering to groups for $recipient"); + } + + $location = $activity->context->location; + if ($location) { + $options['lat'] = $location->lat; + $options['lon'] = $location->lon; + if ($location->location_id) { + $options['location_ns'] = $location->location_ns; + $options['location_id'] = $location->location_id; } } } try { - $saved = Notice::saveNew($profile->id, + $saved = Notice::saveNew($oprofile->profile_id, $content, 'ostatus', - $params); + $options); + if ($saved) { + Ostatus_source::saveNew($saved, $this, $method); + } } catch (Exception $e) { - common_log(LOG_ERR, "Failed saving notice entry for $sourceUri: " . $e->getMessage()); - return; + common_log(LOG_ERR, "OStatus save of remote message $sourceUri failed: " . $e->getMessage()); + throw $e; } + common_log(LOG_INFO, "OStatus saved remote message $sourceUri as notice id $saved->id"); + return $saved; + } - // Record which feed this came through... - try { - Ostatus_source::saveNew($saved, $this, 'push'); - } catch (Exception $e) { - common_log(LOG_ERR, "Failed saving ostatus_source entry for $saved->notice_id: " . $e->getMessage()); + /** + * Clean up HTML + */ + protected function purify($html) + { + // @fixme disable caching or set a sane temp dir + require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); + $purifier = new HTMLPurifier(); + return $purifier->purify($html); + } + + /** + * Filters a list of recipient ID URIs to just those for local delivery. + * @param Ostatus_profile local profile of sender + * @param array in/out &$attention_uris set of URIs, will be pruned on output + * @return array of group IDs + */ + protected function filterReplies($sender, &$attention_uris) + { + $groups = array(); + $replies = array(); + foreach ($attention_uris as $recipient) { + // Is the recipient a local user? + $user = User::staticGet('uri', $recipient); + if ($user) { + // @fixme sender verification, spam etc? + $replies[] = $recipient; + continue; + } + + // Is the recipient a remote group? + $oprofile = Ostatus_profile::staticGet('uri', $recipient); + if ($oprofile) { + if ($oprofile->isGroup()) { + // Deliver to local members of this remote group. + // @fixme sender verification? + $groups[] = $oprofile->group_id; + } + continue; + } + + // Is the recipient a local group? + // @fixme we need a uri on user_group + // $group = User_group::staticGet('uri', $recipient); + $template = common_local_url('groupbyid', array('id' => '31337')); + $template = preg_quote($template, '/'); + $template = str_replace('31337', '(\d+)', $template); + if (preg_match("/$template/", $recipient, $matches)) { + $id = $matches[1]; + $group = User_group::staticGet('id', $id); + if ($group) { + // Deliver to all members of this local group if allowed. + if ($sender->localProfile()->isMember($group)) { + $groups[] = $group->id; + } + continue; + } + } } + $attention_uris = $replies; + return $groups; } /** diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php index 83cf0b8f8..9aac2ed52 100644 --- a/plugins/OStatus/lib/salmonaction.php +++ b/plugins/OStatus/lib/salmonaction.php @@ -185,54 +185,6 @@ class SalmonAction extends Action function saveNotice() { $oprofile = $this->ensureProfile(); - - // Get (safe!) HTML and text versions of the content - - require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); - - $html = $this->act->object->content; - - $purifier = new HTMLPurifier(); - - $rendered = $purifier->purify($html); - - $content = html_entity_decode(strip_tags($rendered)); - - $options = array('is_local' => Notice::REMOTE_OMB, - 'uri' => $this->act->object->id, - 'url' => $this->act->object->link, - 'rendered' => $rendered, - 'replies' => $this->act->context->attention); - - if (!empty($this->act->context->location)) { - $options['lat'] = $location->lat; - $options['lon'] = $location->lon; - if ($location->location_id) { - $options['location_ns'] = $location->location_ns; - $options['location_id'] = $location->location_id; - } - } - - if (!empty($this->act->context->replyToID)) { - $orig = Notice::staticGet('uri', - $this->act->context->replyToID); - if (!empty($orig)) { - $options['reply_to'] = $orig->id; - } - } - - if (!empty($this->act->time)) { - $options['created'] = common_sql_date($this->act->time); - } - - $saved = Notice::saveNew($oprofile->profile_id, - $content, - 'ostatus+salmon', - $options); - - // Record that this was saved through a validated Salmon source - // @fixme actually do the signature validation! - Ostatus_source::saveNew($saved, $oprofile, 'salmon'); - return $saved; + return $oprofile->processPost($this->act, 'salmon'); } } -- cgit v1.2.3-54-g00ecf From 2e58802cc9959763f28e2f43c8e0cd0dbe7bcd8e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 02:19:13 +0000 Subject: OStatus: fix group delivery, send reply/group Salmon pings from background. --- plugins/OStatus/OStatusPlugin.php | 28 +--- plugins/OStatus/actions/groupsalmon.php | 9 +- plugins/OStatus/classes/Ostatus_profile.php | 15 +- plugins/OStatus/lib/hubdistribqueuehandler.php | 182 -------------------- plugins/OStatus/lib/ostatusqueuehandler.php | 223 +++++++++++++++++++++++++ plugins/OStatus/lib/salmonoutqueuehandler.php | 44 +++++ 6 files changed, 295 insertions(+), 206 deletions(-) delete mode 100644 plugins/OStatus/lib/hubdistribqueuehandler.php create mode 100644 plugins/OStatus/lib/ostatusqueuehandler.php create mode 100644 plugins/OStatus/lib/salmonoutqueuehandler.php (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 35f952935..9376c048d 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -78,11 +78,16 @@ class OStatusPlugin extends Plugin */ function onEndInitializeQueueManager(QueueManager $qm) { + // Prepare outgoing distributions after notice save. + $qm->connect('ostatus', 'OStatusQueueHandler'); + // Outgoing from our internal PuSH hub $qm->connect('hubverify', 'HubVerifyQueueHandler'); - $qm->connect('hubdistrib', 'HubDistribQueueHandler'); $qm->connect('hubout', 'HubOutQueueHandler'); + // Outgoing Salmon replies (when we don't need a return value) + $qm->connect('salmonout', 'SalmonOutQueueHandler'); + // Incoming from a foreign PuSH hub $qm->connect('pushinput', 'PushInputQueueHandler'); return true; @@ -93,7 +98,7 @@ class OStatusPlugin extends Plugin */ function onStartEnqueueNotice($notice, &$transports) { - $transports[] = 'hubdistrib'; + $transports[] = 'ostatus'; return true; } @@ -199,25 +204,6 @@ class OStatusPlugin extends Plugin function onEndNoticeSave($notice) { - $mentioned = $notice->getReplies(); - - foreach ($mentioned as $profile_id) { - - $oprofile = Ostatus_profile::staticGet('profile_id', $profile_id); - - if (!empty($oprofile) && !empty($oprofile->salmonuri)) { - - common_log(LOG_INFO, "Sending notice '{$notice->uri}' to remote profile '{$oprofile->uri}'."); - - // FIXME: this needs to go out in a queue handler - - $xml = ''; - $xml .= $notice->asAtomEntry(true, true); - - $salmon = new Salmon(); - $salmon->post($oprofile->salmonuri, $xml); - } - } } /** diff --git a/plugins/OStatus/actions/groupsalmon.php b/plugins/OStatus/actions/groupsalmon.php index 2e4fe9443..29377b5fa 100644 --- a/plugins/OStatus/actions/groupsalmon.php +++ b/plugins/OStatus/actions/groupsalmon.php @@ -46,6 +46,11 @@ class GroupsalmonAction extends SalmonAction $this->clientError(_('No such group.')); } + $oprofile = Ostatus_profile::staticGet('group_id', $id); + if ($oprofile) { + $this->clientError(_m("Can't accept remote posts for a remote group.")); + } + return true; } @@ -74,13 +79,13 @@ class GroupsalmonAction extends SalmonAction throw new ClientException("Not to the attention of anyone."); } else { $uri = common_local_url('groupbyid', array('id' => $this->group->id)); - if (!in_array($context->attention, $uri)) { + if (!in_array($uri, $context->attention)) { throw new ClientException("Not to the attention of this group."); } } $profile = $this->ensureProfile(); - // @fixme save the post + $this->saveNotice(); } /** diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 6beaf0f5d..82dbf773d 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -650,6 +650,7 @@ class Ostatus_profile extends Memcached_DataObject */ protected function filterReplies($sender, &$attention_uris) { + common_log(LOG_DEBUG, "Original reply recipients: " . implode(', ', $attention_uris)); $groups = array(); $replies = array(); foreach ($attention_uris as $recipient) { @@ -668,6 +669,8 @@ class Ostatus_profile extends Memcached_DataObject // Deliver to local members of this remote group. // @fixme sender verification? $groups[] = $oprofile->group_id; + } else { + common_log(LOG_DEBUG, "Skipping reply to remote profile $recipient"); } continue; } @@ -683,14 +686,24 @@ class Ostatus_profile extends Memcached_DataObject $group = User_group::staticGet('id', $id); if ($group) { // Deliver to all members of this local group if allowed. - if ($sender->localProfile()->isMember($group)) { + $profile = $sender->localProfile(); + if ($profile->isMember($group)) { $groups[] = $group->id; + } else { + common_log(LOG_DEBUG, "Skipping reply to local group $group->nickname as sender $profile->id is not a member"); } continue; + } else { + common_log(LOG_DEBUG, "Skipping reply to bogus group $recipient"); } } + + common_log(LOG_DEBUG, "Skipping reply to unrecognized profile $recipient"); + } $attention_uris = $replies; + common_log(LOG_DEBUG, "Local reply recipients: " . implode(', ', $replies)); + common_log(LOG_DEBUG, "Local group recipients: " . implode(', ', $groups)); return $groups; } diff --git a/plugins/OStatus/lib/hubdistribqueuehandler.php b/plugins/OStatus/lib/hubdistribqueuehandler.php deleted file mode 100644 index c2bd630f9..000000000 --- a/plugins/OStatus/lib/hubdistribqueuehandler.php +++ /dev/null @@ -1,182 +0,0 @@ -. - */ - -/** - * Send a PuSH subscription verification from our internal hub. - * Queue up final distribution for - * @package Hub - * @author Brion Vibber - */ -class HubDistribQueueHandler extends QueueHandler -{ - function transport() - { - return 'hubdistrib'; - } - - function handle($notice) - { - assert($notice instanceof Notice); - - $this->pushUser($notice); - foreach ($notice->getGroups() as $group) { - $this->pushGroup($notice, $group->id); - } - return true; - } - - function pushUser($notice) - { - // See if there's any PuSH subscriptions, including OStatus clients. - // @fixme handle group subscriptions as well - // http://identi.ca/api/statuses/user_timeline/1.atom - $feed = common_local_url('ApiTimelineUser', - array('id' => $notice->profile_id, - 'format' => 'atom')); - $this->pushFeed($feed, array($this, 'userFeedForNotice'), $notice); - } - - function pushGroup($notice, $group_id) - { - $feed = common_local_url('ApiTimelineGroup', - array('id' => $group_id, - 'format' => 'atom')); - $this->pushFeed($feed, array($this, 'groupFeedForNotice'), $group_id, $notice); - } - - /** - * @param string $feed URI to the feed - * @param callable $callback function to generate Atom feed update if needed - * any additional params are passed to the callback. - */ - function pushFeed($feed, $callback) - { - $hub = common_config('ostatus', 'hub'); - if ($hub) { - $this->pushFeedExternal($feed, $hub); - } - - $sub = new HubSub(); - $sub->topic = $feed; - if ($sub->find()) { - $args = array_slice(func_get_args(), 2); - $atom = call_user_func_array($callback, $args); - $this->pushFeedInternal($atom, $sub); - } else { - common_log(LOG_INFO, "No PuSH subscribers for $feed"); - } - return true; - } - - /** - * Ping external hub about this update. - * The hub will pull the feed and check for new items later. - * Not guaranteed safe in an environment with database replication. - * - * @param string $feed feed topic URI - * @param string $hub PuSH hub URI - * @fixme can consolidate pings for user & group posts - */ - function pushFeedExternal($feed, $hub) - { - $client = new HTTPClient(); - try { - $data = array('hub.mode' => 'publish', - 'hub.url' => $feed); - $response = $client->post($hub, array(), $data); - if ($response->getStatus() == 204) { - common_log(LOG_INFO, "PuSH ping to hub $hub for $feed ok"); - return true; - } else { - common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed with HTTP " . - $response->getStatus() . ': ' . - $response->getBody()); - } - } catch (Exception $e) { - common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed: " . $e->getMessage()); - return false; - } - } - - /** - * Queue up direct feed update pushes to subscribers on our internal hub. - * @param string $atom update feed, containing only new/changed items - * @param HubSub $sub open query of subscribers - */ - function pushFeedInternal($atom, $sub) - { - common_log(LOG_INFO, "Preparing $sub->N PuSH distribution(s) for $sub->topic"); - $qm = QueueManager::get(); - while ($sub->fetch()) { - $sub->distribute($atom); - } - } - - /** - * Build a single-item version of the sending user's Atom feed. - * @param Notice $notice - * @return string - */ - function userFeedForNotice($notice) - { - // @fixme this feels VERY hacky... - // should probably be a cleaner way to do it - - ob_start(); - $api = new ApiTimelineUserAction(); - $api->prepare(array('id' => $notice->profile_id, - 'format' => 'atom', - 'max_id' => $notice->id, - 'since_id' => $notice->id - 1)); - $api->showTimeline(); - $feed = ob_get_clean(); - - // ...and override the content-type back to something normal... eww! - // hope there's no other headers that got set while we weren't looking. - header('Content-Type: text/html; charset=utf-8'); - - common_log(LOG_DEBUG, $feed); - return $feed; - } - - function groupFeedForNotice($group_id, $notice) - { - // @fixme this feels VERY hacky... - // should probably be a cleaner way to do it - - ob_start(); - $api = new ApiTimelineGroupAction(); - $args = array('id' => $group_id, - 'format' => 'atom', - 'max_id' => $notice->id, - 'since_id' => $notice->id - 1); - $api->prepare($args); - $api->handle($args); - $feed = ob_get_clean(); - - // ...and override the content-type back to something normal... eww! - // hope there's no other headers that got set while we weren't looking. - header('Content-Type: text/html; charset=utf-8'); - - common_log(LOG_DEBUG, $feed); - return $feed; - } - -} - diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php new file mode 100644 index 000000000..c1e50bffa --- /dev/null +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -0,0 +1,223 @@ +. + */ + +/** + * Prepare PuSH and Salmon distributions for an outgoing message. + * + * @package OStatusPlugin + * @author Brion Vibber + */ +class OStatusQueueHandler extends QueueHandler +{ + function transport() + { + return 'ostatus'; + } + + function handle($notice) + { + assert($notice instanceof Notice); + + $this->notice = $notice; + $this->user = User::staticGet($notice->profile_id); + + $this->pushUser(); + + foreach ($notice->getGroups() as $group) { + $oprofile = Ostatus_profile::staticGet('group_id', $group->id); + if ($oprofile) { + $this->pingReply($oprofile); + } else { + $this->pushGroup($group->id); + } + } + + foreach ($notice->getReplies() as $profile_id) { + $oprofile = Ostatus_profile::staticGet('profile_id', $profile_id); + if ($oprofile) { + $this->pingReply($oprofile); + } + } + + return true; + } + + function pushUser() + { + if ($this->user) { + // For local posts, ping the PuSH hub to update their feed. + // http://identi.ca/api/statuses/user_timeline/1.atom + $feed = common_local_url('ApiTimelineUser', + array('id' => $this->user->id, + 'format' => 'atom')); + $this->pushFeed($feed, array($this, 'userFeedForNotice')); + } + } + + function pushGroup($group_id) + { + // For a local group, ping the PuSH hub to update its feed. + // Updates may come from either a local or a remote user. + $feed = common_local_url('ApiTimelineGroup', + array('id' => $group_id, + 'format' => 'atom')); + $this->pushFeed($feed, array($this, 'groupFeedForNotice'), $group_id); + } + + function pingReply($oprofile) + { + if ($this->user) { + if (!empty($oprofile->salmonuri)) { + // For local posts, send a Salmon ping to the mentioned + // remote user or group. + // @fixme as an optimization we can skip this if the + // remote profile is subscribed to the author. + + common_log(LOG_INFO, "Prepping to send notice '{$this->notice->uri}' to remote profile '{$oprofile->uri}'."); + + $xml = ''; + $xml .= $this->notice->asAtomEntry(true, true); + + $data = array('salmonuri' => $oprofile->salmonuri, + 'entry' => $xml); + + $qm = QueueManager::get(); + $qm->enqueue($data, 'salmonout'); + } + } + } + + /** + * @param string $feed URI to the feed + * @param callable $callback function to generate Atom feed update if needed + * any additional params are passed to the callback. + */ + function pushFeed($feed, $callback) + { + $hub = common_config('ostatus', 'hub'); + if ($hub) { + $this->pushFeedExternal($feed, $hub); + } + + $sub = new HubSub(); + $sub->topic = $feed; + if ($sub->find()) { + $args = array_slice(func_get_args(), 2); + $atom = call_user_func_array($callback, $args); + $this->pushFeedInternal($atom, $sub); + } else { + common_log(LOG_INFO, "No PuSH subscribers for $feed"); + } + return true; + } + + /** + * Ping external hub about this update. + * The hub will pull the feed and check for new items later. + * Not guaranteed safe in an environment with database replication. + * + * @param string $feed feed topic URI + * @param string $hub PuSH hub URI + * @fixme can consolidate pings for user & group posts + */ + function pushFeedExternal($feed, $hub) + { + $client = new HTTPClient(); + try { + $data = array('hub.mode' => 'publish', + 'hub.url' => $feed); + $response = $client->post($hub, array(), $data); + if ($response->getStatus() == 204) { + common_log(LOG_INFO, "PuSH ping to hub $hub for $feed ok"); + return true; + } else { + common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed with HTTP " . + $response->getStatus() . ': ' . + $response->getBody()); + } + } catch (Exception $e) { + common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed: " . $e->getMessage()); + return false; + } + } + + /** + * Queue up direct feed update pushes to subscribers on our internal hub. + * @param string $atom update feed, containing only new/changed items + * @param HubSub $sub open query of subscribers + */ + function pushFeedInternal($atom, $sub) + { + common_log(LOG_INFO, "Preparing $sub->N PuSH distribution(s) for $sub->topic"); + while ($sub->fetch()) { + $sub->distribute($atom); + } + } + + /** + * Build a single-item version of the sending user's Atom feed. + * @return string + */ + function userFeedForNotice() + { + // @fixme this feels VERY hacky... + // should probably be a cleaner way to do it + + ob_start(); + $api = new ApiTimelineUserAction(); + $api->prepare(array('id' => $this->notice->profile_id, + 'format' => 'atom', + 'max_id' => $this->notice->id, + 'since_id' => $this->notice->id - 1)); + $api->showTimeline(); + $feed = ob_get_clean(); + + // ...and override the content-type back to something normal... eww! + // hope there's no other headers that got set while we weren't looking. + header('Content-Type: text/html; charset=utf-8'); + + common_log(LOG_DEBUG, $feed); + return $feed; + } + + function groupFeedForNotice($group_id) + { + // @fixme this feels VERY hacky... + // should probably be a cleaner way to do it + + ob_start(); + $api = new ApiTimelineGroupAction(); + $args = array('id' => $group_id, + 'format' => 'atom', + 'max_id' => $this->notice->id, + 'since_id' => $this->notice->id - 1); + $api->prepare($args); + $api->handle($args); + $feed = ob_get_clean(); + + // ...and override the content-type back to something normal... eww! + // hope there's no other headers that got set while we weren't looking. + header('Content-Type: text/html; charset=utf-8'); + + common_log(LOG_DEBUG, $feed); + return $feed; + } + +} + diff --git a/plugins/OStatus/lib/salmonoutqueuehandler.php b/plugins/OStatus/lib/salmonoutqueuehandler.php new file mode 100644 index 000000000..536ff94af --- /dev/null +++ b/plugins/OStatus/lib/salmonoutqueuehandler.php @@ -0,0 +1,44 @@ +. + */ + +/** + * Send a Salmon notification in the background. + * @package OStatusPlugin + * @author Brion Vibber + */ +class SalmonOutQueueHandler extends QueueHandler +{ + function transport() + { + return 'salmonout'; + } + + function handle($data) + { + assert(is_array($data)); + assert(is_string($data['salmonuri'])); + assert(is_string($data['entry'])); + + $salmon = new Salmon(); + $salmon->post($data['salmonuri'], $data['entry']); + + // @fixme detect failure and attempt to resend + return true; + } +} -- cgit v1.2.3-54-g00ecf From 07214f1370547fcc64db34ce8c8a84ec70e0d5bd Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 19:06:10 +0000 Subject: OStatus: save updated profile bits when they come in over the wire; fix up nicknames --- plugins/OStatus/classes/Ostatus_profile.php | 147 +++++++++++++++++++++------- 1 file changed, 112 insertions(+), 35 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 82dbf773d..9f9efb96e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -33,6 +33,7 @@ class Ostatus_profile extends Memcached_DataObject public $feeduri; public $salmonuri; + public $avatar; // remote URL of the last avatar we saved public $created; public $modified; @@ -58,6 +59,7 @@ class Ostatus_profile extends Memcached_DataObject 'group_id' => DB_DATAOBJECT_INT, 'feeduri' => DB_DATAOBJECT_STR, 'salmonuri' => DB_DATAOBJECT_STR, + 'avatar' => DB_DATAOBJECT_STR, 'created' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL, 'modified' => DB_DATAOBJECT_STR + DB_DATAOBJECT_DATE + DB_DATAOBJECT_TIME + DB_DATAOBJECT_NOTNULL); } @@ -74,6 +76,8 @@ class Ostatus_profile extends Memcached_DataObject 255, true, 'UNI'), new ColumnDef('salmonuri', 'text', null, true), + new ColumnDef('avatar', 'text', + null, true), new ColumnDef('created', 'datetime', null, false), new ColumnDef('modified', 'datetime', @@ -543,7 +547,8 @@ class Ostatus_profile extends Memcached_DataObject // through PuSH setup or Salmon signature checks. $actorUri = self::getActorProfileURI($activity); if ($actorUri == $this->uri) { - // @fixme check if profile info has changed and update it + // Check if profile info has changed and update it + $this->updateFromActivityObject($activity->actor); } else { common_log(LOG_WARNING, "OStatus: skipping post with bad author: got $actorUri expected $this->uri"); return false; @@ -785,6 +790,11 @@ class Ostatus_profile extends Memcached_DataObject */ protected function updateAvatar($url) { + if ($url == $this->avatar) { + // We've already got this one. + return; + } + if ($this->isGroup()) { $self = $this->localGroup(); } else { @@ -816,12 +826,28 @@ class Ostatus_profile extends Memcached_DataObject common_timestamp()); rename($temp_filename, Avatar::path($filename)); $self->setOriginal($filename); + + $orig = clone($this); + $this->avatar = $url; + $this->update($orig); } - protected static function getActivityObjectAvatar($object) + /** + * Pull avatar URL from ActivityObject or profile hints + * + * @param ActivityObject $object + * @param array $hints + * @return mixed URL string or false + */ + + protected static function getActivityObjectAvatar($object, $hints=array()) { - // XXX: go poke around in the feed - return $object->avatar; + if ($object->avatar) { + return $object->avatar; + } else if (array_key_exists('avatar', $hints)) { + return $hints['avatar']; + } + return false; } /** @@ -888,7 +914,9 @@ class Ostatus_profile extends Memcached_DataObject public static function ensureActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) { $profile = self::getActivityObjectProfile($object); - if (!$profile) { + if ($profile) { + $profile->updateFromActivityObject($object, $hints); + } else { $profile = self::createActivityObjectProfile($object, $feeduri, $salmonuri, $hints); } return $profile; @@ -957,8 +985,6 @@ class Ostatus_profile extends Memcached_DataObject protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) { $homeuri = $object->id; - $nickname = self::getActivityObjectNickname($object, $hints); - $avatar = self::getActivityObjectAvatar($object); if (!$homeuri) { common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true)); @@ -1002,43 +1028,19 @@ class Ostatus_profile extends Memcached_DataObject if ($object->type == ActivityObject::PERSON) { $profile = new Profile(); - $profile->nickname = $nickname; - $profile->fullname = $object->title; - if (!empty($object->link)) { - $profile->profileurl = $object->link; - } else if (array_key_exists('profileurl', $hints)) { - $profile->profileurl = $hints['profileurl']; - } - $profile->created = common_sql_now(); - - // @fixme bio - // @fixme tags/categories - // @fixme location? - // @todo tags from categories - // @todo lat/lon/location? - + self::updateProfile($profile, $object, $hints); + $profile->created = common_sql_now(); + $oprofile->profile_id = $profile->insert(); - if (!$oprofile->profile_id) { throw new ServerException("Can't save local profile"); } } else { $group = new User_group(); - $group->nickname = $nickname; - $group->fullname = $object->title; - // @fixme no canonical profileurl; using homepage instead for now - $group->homepage = $homeuri; $group->created = common_sql_now(); - - // @fixme homepage - // @fixme bio - // @fixme tags/categories - // @fixme location? - // @todo tags from categories - // @todo lat/lon/location? + self::updateGroup($group, $object, $hints); $oprofile->group_id = $group->insert(); - if (!$oprofile->group_id) { throw new ServerException("Can't save local profile"); } @@ -1047,6 +1049,7 @@ class Ostatus_profile extends Memcached_DataObject $ok = $oprofile->insert(); if ($ok) { + $avatar = self::getActivityObjectAvatar($object, $hints); if ($avatar) { $oprofile->updateAvatar($avatar); } @@ -1056,8 +1059,82 @@ class Ostatus_profile extends Memcached_DataObject } } + /** + * Save any updated profile information to our local copy. + * @param ActivityObject $object + * @param array $hints + */ + protected function updateFromActivityObject($object, $hints=array()) + { + if ($this->isGroup()) { + $group = $this->localGroup(); + self::updateGroup($group, $object, $hints); + } else { + $profile = $this->localProfile(); + self::updateProfile($profile, $object, $hints); + } + $avatar = self::getActivityObjectAvatar($object, $hints); + if ($avatar) { + $this->updateAvatar($avatar); + } + } + + protected static function updateProfile($profile, $object, $hints=array()) + { + $orig = clone($profile); + + $profile->nickname = self::getActivityObjectNickname($object, $hints); + $profile->fullname = $object->title; + if (!empty($object->link)) { + $profile->profileurl = $object->link; + } else if (array_key_exists('profileurl', $hints)) { + $profile->profileurl = $hints['profileurl']; + } + + // @fixme bio + // @fixme tags/categories + // @fixme location? + // @todo tags from categories + // @todo lat/lon/location? + + if ($profile->id) { + common_log(LOG_DEBUG, "Updating OStatus profile $profile->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true)); + $profile->update($orig); + } + } + + protected static function updateGroup($group, $object, $hints=array()) + { + $orig = clone($group); + + // @fixme need to make nick unique etc *hack hack* + $group->nickname = self::getActivityObjectNickname($object, $hints); + $group->fullname = $object->title; + + // @fixme no canonical profileurl; using homepage instead for now + $group->homepage = $object->id; + + // @fixme homepage + // @fixme bio + // @fixme tags/categories + // @fixme location? + // @todo tags from categories + // @todo lat/lon/location? + + if ($group->id) { + common_log(LOG_DEBUG, "Updating OStatus group $group->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true)); + $group->update($orig); + } + } + + protected static function getActivityObjectNickname($object, $hints=array()) { + if ($object->poco) { + if (!empty($object->poco->preferredUsername)) { + return common_nicknamize($object->poco->preferredUsername); + } + } if (!empty($object->nickname)) { return common_nicknamize($object->nickname); } -- cgit v1.2.3-54-g00ecf From 269d567d9440e3c943f67aad428efce9d112385c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 24 Feb 2010 15:20:06 -0500 Subject: use Subscription::start() for remote subscribes --- plugins/OStatus/classes/Ostatus_profile.php | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 9f9efb96e..e8ab06522 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -299,18 +299,9 @@ class Ostatus_profile extends Memcached_DataObject throw new ServerException("Remote groups can't subscribe to local users"); } - // @fixme use regular channels for subbing, once they accept remote profiles - $sub = new Subscription(); - $sub->subscriber = $this->profile_id; - $sub->subscribed = $user->id; - $sub->created = common_sql_now(); // current time - - if ($sub->insert()) { - // @fixme use subs_notify() if refactored to take profiles? - mail_subscribe_notify_profile($user, $this->localProfile()); - return true; - } - return false; + Subscription::start($this->localProfile(), $user->getProfile()); + + return true; } /** @@ -1127,7 +1118,6 @@ class Ostatus_profile extends Memcached_DataObject } } - protected static function getActivityObjectNickname($object, $hints=array()) { if ($object->poco) { -- cgit v1.2.3-54-g00ecf From c36bdc1ba535dc3e2dc9098dbe40735b1955d96d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 20:36:36 +0000 Subject: - break OMB profile update pings to a background queue - add event hooks to profile update pings - send Salmon pings with custom update-profile event to OStatus subscribees and groups (subscribers will see it on your next post) - fix OStatus queues with overlong transport names, should work on DB queues now - Ostatus_profile::notifyActivity() and ::notifyDeferred() now can take XML, Notice, or Activity for convenience --- lib/activity.php | 3 ++ lib/profilequeuehandler.php | 48 ++++++++++++++++++++++++ lib/queuemanager.php | 3 ++ lib/util.php | 14 ++++--- plugins/OStatus/OStatusPlugin.php | 53 ++++++++++++++++++++++++-- plugins/OStatus/actions/pushcallback.php | 2 +- plugins/OStatus/classes/HubSub.php | 2 +- plugins/OStatus/classes/Ostatus_profile.php | 46 ++++++++++++++++++++--- plugins/OStatus/lib/hubconfqueuehandler.php | 54 +++++++++++++++++++++++++++ plugins/OStatus/lib/hubverifyqueuehandler.php | 54 --------------------------- plugins/OStatus/lib/ostatusqueuehandler.php | 22 +++-------- plugins/OStatus/lib/pushinputqueuehandler.php | 49 ------------------------ plugins/OStatus/lib/pushinqueuehandler.php | 49 ++++++++++++++++++++++++ plugins/OStatus/lib/salmonoutqueuehandler.php | 44 ---------------------- plugins/OStatus/lib/salmonqueuehandler.php | 44 ++++++++++++++++++++++ 15 files changed, 308 insertions(+), 179 deletions(-) create mode 100644 lib/profilequeuehandler.php create mode 100644 plugins/OStatus/lib/hubconfqueuehandler.php delete mode 100644 plugins/OStatus/lib/hubverifyqueuehandler.php delete mode 100644 plugins/OStatus/lib/pushinputqueuehandler.php create mode 100644 plugins/OStatus/lib/pushinqueuehandler.php delete mode 100644 plugins/OStatus/lib/salmonoutqueuehandler.php create mode 100644 plugins/OStatus/lib/salmonqueuehandler.php (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/lib/activity.php b/lib/activity.php index fa4ae0274..33932ad0e 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -691,6 +691,9 @@ class ActivityVerb const UNFAVORITE = 'http://ostatus.org/schema/1.0/unfavorite'; const UNFOLLOW = 'http://ostatus.org/schema/1.0/unfollow'; const LEAVE = 'http://ostatus.org/schema/1.0/leave'; + + // For simple profile-update pings; no content to share. + const UPDATE_PROFILE = 'http://ostatus.org/schema/1.0/update-profile'; } class ActivityContext diff --git a/lib/profilequeuehandler.php b/lib/profilequeuehandler.php new file mode 100644 index 000000000..e8a00aef3 --- /dev/null +++ b/lib/profilequeuehandler.php @@ -0,0 +1,48 @@ +. + */ + +/** + * @package QueueHandler + * @maintainer Brion Vibber + */ + +class ProfileQueueHandler extends QueueHandler +{ + + function transport() + { + return 'profile'; + } + + function handle($profile) + { + if (!($profile instanceof Profile)) { + common_log(LOG_ERR, "Got a bogus profile, not broadcasting"); + return true; + } + + if (Event::handle('StartBroadcastProfile', array($profile))) { + require_once(INSTALLDIR.'/lib/omb.php'); + omb_broadcast_profile($profile); + } + Event::handle('EndBroadcastProfile', array($profile)); + return true; + } + +} diff --git a/lib/queuemanager.php b/lib/queuemanager.php index 8f8c8f133..9fdc80110 100644 --- a/lib/queuemanager.php +++ b/lib/queuemanager.php @@ -262,6 +262,9 @@ abstract class QueueManager extends IoManager $this->connect('sms', 'SmsQueueHandler'); } + // Broadcasting profile updates to OMB remote subscribers + $this->connect('profile', 'ProfileQueueHandler'); + // XMPP output handlers... if (common_config('xmpp', 'enabled')) { // Delivery prep, read by queuedaemon.php: diff --git a/lib/util.php b/lib/util.php index 7fb2c6c4b..9354431f2 100644 --- a/lib/util.php +++ b/lib/util.php @@ -1119,12 +1119,16 @@ function common_enqueue_notice($notice) return true; } -function common_broadcast_profile($profile) +/** + * Broadcast profile updates to OMB and other remote subscribers. + * + * Since this may be slow with a lot of subscribers or bad remote sites, + * this is run through the background queues if possible. + */ +function common_broadcast_profile(Profile $profile) { - // XXX: optionally use a queue system like http://code.google.com/p/microapps/wiki/NQDQ - require_once(INSTALLDIR.'/lib/omb.php'); - omb_broadcast_profile($profile); - // XXX: Other broadcasts...? + $qm = QueueManager::get(); + $qm->enqueue($profile, "profile"); return true; } diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 9376c048d..90abe034d 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -82,14 +82,14 @@ class OStatusPlugin extends Plugin $qm->connect('ostatus', 'OStatusQueueHandler'); // Outgoing from our internal PuSH hub - $qm->connect('hubverify', 'HubVerifyQueueHandler'); + $qm->connect('hubconf', 'HubConfQueueHandler'); $qm->connect('hubout', 'HubOutQueueHandler'); // Outgoing Salmon replies (when we don't need a return value) - $qm->connect('salmonout', 'SalmonOutQueueHandler'); + $qm->connect('salmon', 'SalmonQueueHandler'); // Incoming from a foreign PuSH hub - $qm->connect('pushinput', 'PushInputQueueHandler'); + $qm->connect('pushin', 'PushInQueueHandler'); return true; } @@ -656,4 +656,51 @@ class OStatusPlugin extends Plugin return true; } + + /** + * Ping remote profiles with updates to this profile. + * Salmon pings are queued for background processing. + */ + function onEndBroadcastProfile(Profile $profile) + { + $user = User::staticGet('id', $profile->id); + + // Find foreign accounts I'm subscribed to that support Salmon pings. + // + // @fixme we could run updates through the PuSH feed too, + // in which case we can skip Salmon pings to folks who + // are also subscribed to me. + $sql = "SELECT * FROM ostatus_profile " . + "WHERE profile_id IN " . + "(SELECT subscribed FROM subscription WHERE subscriber=%d) " . + "OR group_id IN " . + "(SELECT group_id FROM group_member WHERE profile_id=%d)"; + $oprofile = new Ostatus_profile(); + $oprofile->query(sprintf($sql, $profile->id, $profile->id)); + + if ($oprofile->N == 0) { + common_log(LOG_DEBUG, "No OStatus remote subscribees for $profile->nickname"); + return true; + } + + $act = new Activity(); + + $act->verb = ActivityVerb::UPDATE_PROFILE; + $act->id = TagURI::mint('update-profile:%d:%s', + $profile->id, + common_date_iso8601(time())); + $act->time = time(); + $act->title = _m("Profile update"); + $act->content = sprintf(_m("%s has updated their profile page."), + $profile->getBestName()); + + $act->actor = ActivityObject::fromProfile($profile); + $act->object = $act->actor; + + while ($oprofile->fetch()) { + $oprofile->notifyDeferred($act); + } + + return true; + } } diff --git a/plugins/OStatus/actions/pushcallback.php b/plugins/OStatus/actions/pushcallback.php index 4184f0e0c..9a2067b8c 100644 --- a/plugins/OStatus/actions/pushcallback.php +++ b/plugins/OStatus/actions/pushcallback.php @@ -68,7 +68,7 @@ class PushCallbackAction extends Action 'post' => $post, 'hmac' => $hmac); $qm = QueueManager::get(); - $qm->enqueue($data, 'pushinput'); + $qm->enqueue($data, 'pushin'); } /** diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php index eae2928c3..1ac181fee 100644 --- a/plugins/OStatus/classes/HubSub.php +++ b/plugins/OStatus/classes/HubSub.php @@ -164,7 +164,7 @@ class HubSub extends Memcached_DataObject 'token' => $token, 'retries' => $retries); $qm = QueueManager::get(); - $qm->enqueue($data, 'hubverify'); + $qm->enqueue($data, 'hubconf'); } /** diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 9f9efb96e..61505206e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -431,21 +431,57 @@ class Ostatus_profile extends Memcached_DataObject return false; } - public function notifyActivity($activity) + /** + * Send a Salmon notification ping immediately, and confirm that we got + * an acceptable response from the remote site. + * + * @param mixed $entry XML string, Notice, or Activity + * @return boolean success + */ + public function notifyActivity($entry) { if ($this->salmonuri) { + $salmon = new Salmon(); + return $salmon->post($this->salmonuri, $this->notifyPrepXml($entry)); + } - $xml = '' . - $activity->asString(true); + return false; + } - $salmon = new Salmon(); // ? + /** + * Queue a Salmon notification for later. If queues are disabled we'll + * send immediately but won't get the return value. + * + * @param mixed $entry XML string, Notice, or Activity + * @return boolean success + */ + public function notifyDeferred($entry) + { + if ($this->salmonuri) { + $data = array('salmonuri' => $this->salmonuri, + 'entry' => $this->notifyPrepXml($entry)); - return $salmon->post($this->salmonuri, $xml); + $qm = QueueManager::get(); + return $qm->enqueue($data, 'salmon'); } return false; } + protected function notifyPrepXml($entry) + { + $preamble = ''; + if (is_string($entry)) { + return $entry; + } else if ($entry instanceof Activity) { + return $preamble . $entry->asString(true); + } else if ($entry instanceof Notice) { + return $preamble . $entry->asAtomEntry(true, true); + } else { + throw new ServerException("Invalid type passed to Ostatus_profile::notify; must be XML string or Activity entry"); + } + } + function getBestName() { if ($this->isGroup()) { diff --git a/plugins/OStatus/lib/hubconfqueuehandler.php b/plugins/OStatus/lib/hubconfqueuehandler.php new file mode 100644 index 000000000..c8e0b72fe --- /dev/null +++ b/plugins/OStatus/lib/hubconfqueuehandler.php @@ -0,0 +1,54 @@ +. + */ + +/** + * Send a PuSH subscription verification from our internal hub. + * @package Hub + * @author Brion Vibber + */ +class HubConfQueueHandler extends QueueHandler +{ + function transport() + { + return 'hubconf'; + } + + function handle($data) + { + $sub = $data['sub']; + $mode = $data['mode']; + $token = $data['token']; + + assert($sub instanceof HubSub); + assert($mode === 'subscribe' || $mode === 'unsubscribe'); + + common_log(LOG_INFO, __METHOD__ . ": $mode $sub->callback $sub->topic"); + try { + $sub->verify($mode, $token); + } catch (Exception $e) { + common_log(LOG_ERR, "Failed PuSH $mode verify to $sub->callback for $sub->topic: " . + $e->getMessage()); + // @fixme schedule retry? + // @fixme just kill it? + } + + return true; + } +} + diff --git a/plugins/OStatus/lib/hubverifyqueuehandler.php b/plugins/OStatus/lib/hubverifyqueuehandler.php deleted file mode 100644 index 7ce9e1431..000000000 --- a/plugins/OStatus/lib/hubverifyqueuehandler.php +++ /dev/null @@ -1,54 +0,0 @@ -. - */ - -/** - * Send a PuSH subscription verification from our internal hub. - * @package Hub - * @author Brion Vibber - */ -class HubVerifyQueueHandler extends QueueHandler -{ - function transport() - { - return 'hubverify'; - } - - function handle($data) - { - $sub = $data['sub']; - $mode = $data['mode']; - $token = $data['token']; - - assert($sub instanceof HubSub); - assert($mode === 'subscribe' || $mode === 'unsubscribe'); - - common_log(LOG_INFO, __METHOD__ . ": $mode $sub->callback $sub->topic"); - try { - $sub->verify($mode, $token); - } catch (Exception $e) { - common_log(LOG_ERR, "Failed PuSH $mode verify to $sub->callback for $sub->topic: " . - $e->getMessage()); - // @fixme schedule retry? - // @fixme just kill it? - } - - return true; - } -} - diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php index c1e50bffa..0da85600f 100644 --- a/plugins/OStatus/lib/ostatusqueuehandler.php +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -83,23 +83,11 @@ class OStatusQueueHandler extends QueueHandler function pingReply($oprofile) { if ($this->user) { - if (!empty($oprofile->salmonuri)) { - // For local posts, send a Salmon ping to the mentioned - // remote user or group. - // @fixme as an optimization we can skip this if the - // remote profile is subscribed to the author. - - common_log(LOG_INFO, "Prepping to send notice '{$this->notice->uri}' to remote profile '{$oprofile->uri}'."); - - $xml = ''; - $xml .= $this->notice->asAtomEntry(true, true); - - $data = array('salmonuri' => $oprofile->salmonuri, - 'entry' => $xml); - - $qm = QueueManager::get(); - $qm->enqueue($data, 'salmonout'); - } + // For local posts, send a Salmon ping to the mentioned + // remote user or group. + // @fixme as an optimization we can skip this if the + // remote profile is subscribed to the author. + $oprofile->notifyDeferred($this->notice); } } diff --git a/plugins/OStatus/lib/pushinputqueuehandler.php b/plugins/OStatus/lib/pushinputqueuehandler.php deleted file mode 100644 index cbd9139b5..000000000 --- a/plugins/OStatus/lib/pushinputqueuehandler.php +++ /dev/null @@ -1,49 +0,0 @@ -. - */ - -/** - * Process a feed distribution POST from a PuSH hub. - * @package FeedSub - * @author Brion Vibber - */ - -class PushInputQueueHandler extends QueueHandler -{ - function transport() - { - return 'pushinput'; - } - - function handle($data) - { - assert(is_array($data)); - - $feedsub_id = $data['feedsub_id']; - $post = $data['post']; - $hmac = $data['hmac']; - - $feedsub = FeedSub::staticGet('id', $feedsub_id); - if ($feedsub) { - $feedsub->receive($post, $hmac); - } else { - common_log(LOG_ERR, "Discarding POST to unknown feed subscription id $feedsub_id"); - } - return true; - } -} diff --git a/plugins/OStatus/lib/pushinqueuehandler.php b/plugins/OStatus/lib/pushinqueuehandler.php new file mode 100644 index 000000000..a90f52df2 --- /dev/null +++ b/plugins/OStatus/lib/pushinqueuehandler.php @@ -0,0 +1,49 @@ +. + */ + +/** + * Process a feed distribution POST from a PuSH hub. + * @package FeedSub + * @author Brion Vibber + */ + +class PushInQueueHandler extends QueueHandler +{ + function transport() + { + return 'pushin'; + } + + function handle($data) + { + assert(is_array($data)); + + $feedsub_id = $data['feedsub_id']; + $post = $data['post']; + $hmac = $data['hmac']; + + $feedsub = FeedSub::staticGet('id', $feedsub_id); + if ($feedsub) { + $feedsub->receive($post, $hmac); + } else { + common_log(LOG_ERR, "Discarding POST to unknown feed subscription id $feedsub_id"); + } + return true; + } +} diff --git a/plugins/OStatus/lib/salmonoutqueuehandler.php b/plugins/OStatus/lib/salmonoutqueuehandler.php deleted file mode 100644 index 536ff94af..000000000 --- a/plugins/OStatus/lib/salmonoutqueuehandler.php +++ /dev/null @@ -1,44 +0,0 @@ -. - */ - -/** - * Send a Salmon notification in the background. - * @package OStatusPlugin - * @author Brion Vibber - */ -class SalmonOutQueueHandler extends QueueHandler -{ - function transport() - { - return 'salmonout'; - } - - function handle($data) - { - assert(is_array($data)); - assert(is_string($data['salmonuri'])); - assert(is_string($data['entry'])); - - $salmon = new Salmon(); - $salmon->post($data['salmonuri'], $data['entry']); - - // @fixme detect failure and attempt to resend - return true; - } -} diff --git a/plugins/OStatus/lib/salmonqueuehandler.php b/plugins/OStatus/lib/salmonqueuehandler.php new file mode 100644 index 000000000..aa97018dc --- /dev/null +++ b/plugins/OStatus/lib/salmonqueuehandler.php @@ -0,0 +1,44 @@ +. + */ + +/** + * Send a Salmon notification in the background. + * @package OStatusPlugin + * @author Brion Vibber + */ +class SalmonQueueHandler extends QueueHandler +{ + function transport() + { + return 'salmon'; + } + + function handle($data) + { + assert(is_array($data)); + assert(is_string($data['salmonuri'])); + assert(is_string($data['entry'])); + + $salmon = new Salmon(); + $salmon->post($data['salmonuri'], $data['entry']); + + // @fixme detect failure and attempt to resend + return true; + } +} -- cgit v1.2.3-54-g00ecf From ec4899e6179f2d9b368e6fc04041dd72c2ac2596 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 22:16:17 +0000 Subject: OStatus: disable HTMLPurify cache unless we've configured a writable path for it. Updated plugin README with available config options. Cleanup for a bad element fallback lookup in Activity --- lib/activity.php | 26 ++++++++------- plugins/OStatus/README | 50 ++++++++++++++++++----------- plugins/OStatus/classes/Ostatus_profile.php | 22 +++++++++++-- 3 files changed, 66 insertions(+), 32 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/lib/activity.php b/lib/activity.php index 33932ad0e..25727bf2b 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -167,16 +167,18 @@ class PoCo PoCo::NS ); - $formatted = ActivityUtils::childContent( - $addressEl, - PoCoAddress::FORMATTED, - self::NS - ); + if (!empty($addressEl)) { + $formatted = ActivityUtils::childContent( + $addressEl, + PoCoAddress::FORMATTED, + self::NS + ); - if (!empty($formatted)) { - $address = new PoCoAddress(); - $address->formatted = $formatted; - return $address; + if (!empty($formatted)) { + $address = new PoCoAddress(); + $address->formatted = $formatted; + return $address; + } } return null; @@ -292,7 +294,7 @@ class ActivityUtils * @return string related link, if any */ - static function getLink($element, $rel, $type=null) + static function getLink(DOMNode $element, $rel, $type=null) { $links = $element->getElementsByTagnameNS(self::ATOM, self::LINK); @@ -320,7 +322,7 @@ class ActivityUtils * @return DOMElement found element or null */ - static function child($element, $tag, $namespace=self::ATOM) + static function child(DOMNode $element, $tag, $namespace=self::ATOM) { $els = $element->childNodes; if (empty($els) || $els->length == 0) { @@ -345,7 +347,7 @@ class ActivityUtils * @return string content of the child */ - static function childContent($element, $tag, $namespace=self::ATOM) + static function childContent(DOMNode $element, $tag, $namespace=self::ATOM) { $el = self::child($element, $tag, $namespace); diff --git a/plugins/OStatus/README b/plugins/OStatus/README index cbf3adbb9..09a59e349 100644 --- a/plugins/OStatus/README +++ b/plugins/OStatus/README @@ -2,23 +2,37 @@ Plugin to support importing updates from external RSS and Atom feeds into your t Uses PubSubHubbub for push feed updates; currently non-PuSH feeds cannot be subscribed. +Configuration options available: + +$config['ostatus']['hub'] + (default internal hub) + Set to URL of an external PuSH hub to use it instead of our internal hub. + +$config['ostatus']['hub_retries'] + (default 0) + Number of times to retry a PuSH send to consumers if using internal hub + +$config['ostatus']['purify_cache'] + (default cache disabled) + Set to a writable cache directory for HTMLPurifier's configuration settings, can speed up processing of remote messages (have not tested by how much) + + +For testing, shouldn't be used in production: + +$config['ostatus']['skip_signatures'] + (default use signatures) + Disable generation and validation of Salmon magicenv signatures + +$config['feedsub']['nohub'] + (default require hub) + Allow low-level feed subscription setup for feeds without hubs. + Not actually usable at this stage, OStatus will check for hubs too + and we have no polling backend. + + Todo: -* set feed icon avatar for actual profiles as well as for preview -* use channel image and/or favicon for avatar? -* garbage-collect subscriptions that are no longer being used -* administrative way to kill feeds? -* functional l10n -* clean up subscription form look and workflow -* use ajax for test/preview in subscription form -* rssCloud support? (Does anything use it that doesn't support PuSH as well?) -* possibly a polling daemon to support non-PuSH feeds? -* likely problems with multiple feeds from the same site, such as category feeds on a blog - (currently each feed would publish a separate notice on a separate profile, but pointing to the same post URI.) - (could use the local URI I guess, but that's so icky!) -* problems with Atom feeds that list but don't have the type - (such as http://atomgen.appspot.com/feed/5 demo feed); currently it's not recognized and we end up with the feed's master URI -* make it easier to see what you're subscribed to and unsub from things -* saner treatment of fullname/nickname? +* fully functional l10n +* redo non-OStatus feed support +** rssCloud support? +** possibly a polling daemon to support non-PuSH feeds? * make use of tags/categories from feeds -* update feed profile data when it changes -* XML_Feed_Parser has major problems with category and link tags; consider replacing? diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 5e38a523e..c755a094e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -668,9 +668,27 @@ class Ostatus_profile extends Memcached_DataObject */ protected function purify($html) { - // @fixme disable caching or set a sane temp dir require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); - $purifier = new HTMLPurifier(); + + // By default Purifier wants to cache data to its own code directories, + // and spews error messages if they're not writable. + $config = HTMLPurifier_Config::createDefault(); + if (common_config('ostatus', 'purify_cache')) { + $config->set('Cache.SerializerPath', common_config('ostatus', 'purify_cache')); + } else { + // Although recommended in the documentation, this produces a notice: + // "Core.DefinitionCache is an alias, preferred directive name is Cache.DefinitionImpl" + // If I then follow *those* directions, I get a warning and it doesn't work: + // "Cannot set undefined directive Core.DefinitionImpl" + // So... lesser of two evils. Suppressing the notice from output, + // though it'll still be seen and logged by StatusNet's error handler. + $old = error_reporting(); + error_reporting($old & ~E_NOTICE); + $config->set('Core.DefinitionCache', null); + error_reporting($old); + } + + $purifier = new HTMLPurifier($config); return $purifier->purify($html); } -- cgit v1.2.3-54-g00ecf From 93507a192755494417eb0433edd315880752c857 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 23:28:01 +0000 Subject: OStatus: handle update-profile Salmon pings --- plugins/OStatus/classes/Ostatus_profile.php | 4 +-- plugins/OStatus/lib/salmonaction.php | 55 +++++++++++++++-------------- 2 files changed, 31 insertions(+), 28 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index c755a094e..33b0ceb9e 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -971,7 +971,7 @@ class Ostatus_profile extends Memcached_DataObject * @param Activity $activity * @return mixed matching Ostatus_profile or false if none known */ - protected static function getActorProfile($activity) + public static function getActorProfile($activity) { return self::getActivityObjectProfile($activity->actor); } @@ -1109,7 +1109,7 @@ class Ostatus_profile extends Memcached_DataObject * @param ActivityObject $object * @param array $hints */ - protected function updateFromActivityObject($object, $hints=array()) + public function updateFromActivityObject($object, $hints=array()) { if ($this->isGroup()) { $group = $this->localGroup(); diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php index 9aac2ed52..a03169101 100644 --- a/plugins/OStatus/lib/salmonaction.php +++ b/plugins/OStatus/lib/salmonaction.php @@ -38,11 +38,11 @@ class SalmonAction extends Action parent::prepare($args); if ($_SERVER['REQUEST_METHOD'] != 'POST') { - $this->clientError(_('This method requires a POST.')); + $this->clientError(_m('This method requires a POST.')); } if (empty($_SERVER['CONTENT_TYPE']) || $_SERVER['CONTENT_TYPE'] != 'application/atom+xml') { - $this->clientError(_('Salmon requires application/atom+xml')); + $this->clientError(_m('Salmon requires application/atom+xml')); } $xml = file_get_contents('php://input'); @@ -76,8 +76,7 @@ class SalmonAction extends Action { StatusNet::setApi(true); // Send smaller error pages - // TODO : Insert new $xml -> notice code - + common_log(LOG_DEBUG, "Got a " . $this->act->verb); if (Event::handle('StartHandleSalmon', array($this->activity))) { switch ($this->act->verb) { @@ -106,8 +105,11 @@ class SalmonAction extends Action case ActivityVerb::LEAVE: $this->handleLeave(); break; + case ActivityVerb::UPDATE_PROFILE: + $this->handleUpdateProfile(); + break; default: - throw new ClientException(_("Unimplemented.")); + throw new ClientException(_m("Unrecognized activity type.")); } Event::handle('EndHandleSalmon', array($this->activity)); } @@ -115,56 +117,57 @@ class SalmonAction extends Action function handlePost() { - throw new ClientException(_("Unimplemented!")); + throw new ClientException(_m("This target doesn't understand posts.")); } function handleFollow() { - throw new ClientException(_("Unimplemented!")); + throw new ClientException(_m("This target doesn't understand follows.")); } function handleUnfollow() { - throw new ClientException(_("Unimplemented!")); + throw new ClientException(_m("This target doesn't understand unfollows.")); } function handleFavorite() { - throw new ClientException(_("Unimplemented!")); + throw new ClientException(_m("This target doesn't understand favorites.")); } - /** - * Remote user doesn't like one of our posts after all! - * Confirm the post is ours, and delete a local favorite event. - */ - function handleUnfavorite() { - throw new ClientException(_("Unimplemented!")); + throw new ClientException(_m("This target doesn't understand unfavorites.")); } - /** - * Hmmmm - */ function handleShare() { - throw new ClientException(_("Unimplemented!")); + throw new ClientException(_m("This target doesn't understand share events.")); } - /** - * Hmmmm - */ function handleJoin() { - throw new ClientException(_("Unimplemented!")); + throw new ClientException(_m("This target doesn't understand joins.")); + } + + function handleLeave() + { + throw new ClientException(_m("This target doesn't understand leave events.")); } /** - * Hmmmm + * Remote user sent us an update to their profile. + * If we already know them, accept the updates. */ - function handleLeave() + function handleUpdateProfile() { - throw new ClientException(_("Unimplemented!")); + $oprofile = Ostatus_profile::getActorProfile($this->act); + if ($oprofile) { + common_log(LOG_INFO, "Got a profile-update ping from $oprofile->uri"); + $oprofile->updateFromActivityObject($this->act->actor); + } else { + common_log(LOG_INFO, "Ignoring profile-update ping from unknown " . $this->act->actor->id); + } } /** -- cgit v1.2.3-54-g00ecf From e18e659ca3b40ade283f51c91a263507d7c6694e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Feb 2010 23:39:40 +0000 Subject: Drop HTMLPurifier; we don't need its extra capabilities and we're already using htmLawed which is lighter-weight. --- extlib/HTMLPurifier/HTMLPurifier.auto.php | 11 - extlib/HTMLPurifier/HTMLPurifier.autoload.php | 21 - extlib/HTMLPurifier/HTMLPurifier.func.php | 23 - extlib/HTMLPurifier/HTMLPurifier.includes.php | 208 -- extlib/HTMLPurifier/HTMLPurifier.kses.php | 30 - extlib/HTMLPurifier/HTMLPurifier.path.php | 11 - extlib/HTMLPurifier/HTMLPurifier.php | 237 -- extlib/HTMLPurifier/HTMLPurifier.safe-includes.php | 202 - .../HTMLPurifier/HTMLPurifier/AttrCollections.php | 128 - extlib/HTMLPurifier/HTMLPurifier/AttrDef.php | 87 - extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php | 87 - .../HTMLPurifier/AttrDef/CSS/AlphaValue.php | 21 - .../HTMLPurifier/AttrDef/CSS/Background.php | 87 - .../AttrDef/CSS/BackgroundPosition.php | 126 - .../HTMLPurifier/AttrDef/CSS/Border.php | 43 - .../HTMLPurifier/AttrDef/CSS/Color.php | 78 - .../HTMLPurifier/AttrDef/CSS/Composite.php | 38 - .../AttrDef/CSS/DenyElementDecorator.php | 28 - .../HTMLPurifier/AttrDef/CSS/Filter.php | 54 - .../HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php | 149 - .../HTMLPurifier/AttrDef/CSS/FontFamily.php | 90 - .../AttrDef/CSS/ImportantDecorator.php | 40 - .../HTMLPurifier/AttrDef/CSS/Length.php | 47 - .../HTMLPurifier/AttrDef/CSS/ListStyle.php | 78 - .../HTMLPurifier/AttrDef/CSS/Multiple.php | 58 - .../HTMLPurifier/AttrDef/CSS/Number.php | 69 - .../HTMLPurifier/AttrDef/CSS/Percentage.php | 40 - .../HTMLPurifier/AttrDef/CSS/TextDecoration.php | 38 - .../HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php | 56 - extlib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php | 65 - .../HTMLPurifier/AttrDef/HTML/Bool.php | 28 - .../HTMLPurifier/AttrDef/HTML/Class.php | 34 - .../HTMLPurifier/AttrDef/HTML/Color.php | 32 - .../HTMLPurifier/AttrDef/HTML/FrameTarget.php | 21 - .../HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php | 70 - .../HTMLPurifier/AttrDef/HTML/Length.php | 41 - .../HTMLPurifier/AttrDef/HTML/LinkTypes.php | 53 - .../HTMLPurifier/AttrDef/HTML/MultiLength.php | 41 - .../HTMLPurifier/AttrDef/HTML/Nmtokens.php | 52 - .../HTMLPurifier/AttrDef/HTML/Pixels.php | 48 - .../HTMLPurifier/HTMLPurifier/AttrDef/Integer.php | 73 - extlib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php | 73 - .../HTMLPurifier/HTMLPurifier/AttrDef/Switch.php | 34 - extlib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php | 15 - extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php | 77 - .../HTMLPurifier/AttrDef/URI/Email.php | 17 - .../HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php | 21 - .../HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php | 62 - .../HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php | 39 - .../HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php | 99 - extlib/HTMLPurifier/HTMLPurifier/AttrTransform.php | 56 - .../HTMLPurifier/AttrTransform/Background.php | 23 - .../HTMLPurifier/AttrTransform/BdoDir.php | 19 - .../HTMLPurifier/AttrTransform/BgColor.php | 23 - .../HTMLPurifier/AttrTransform/BoolToCSS.php | 36 - .../HTMLPurifier/AttrTransform/Border.php | 18 - .../HTMLPurifier/AttrTransform/EnumToCSS.php | 58 - .../HTMLPurifier/AttrTransform/ImgRequired.php | 42 - .../HTMLPurifier/AttrTransform/ImgSpace.php | 44 - .../HTMLPurifier/AttrTransform/Input.php | 40 - .../HTMLPurifier/AttrTransform/Lang.php | 28 - .../HTMLPurifier/AttrTransform/Length.php | 27 - .../HTMLPurifier/AttrTransform/Name.php | 21 - .../HTMLPurifier/AttrTransform/NameSync.php | 27 - .../HTMLPurifier/AttrTransform/SafeEmbed.php | 15 - .../HTMLPurifier/AttrTransform/SafeObject.php | 16 - .../HTMLPurifier/AttrTransform/SafeParam.php | 50 - .../HTMLPurifier/AttrTransform/ScriptRequired.php | 16 - .../HTMLPurifier/AttrTransform/Textarea.php | 18 - extlib/HTMLPurifier/HTMLPurifier/AttrTypes.php | 77 - extlib/HTMLPurifier/HTMLPurifier/AttrValidator.php | 162 - extlib/HTMLPurifier/HTMLPurifier/Bootstrap.php | 98 - extlib/HTMLPurifier/HTMLPurifier/CSSDefinition.php | 292 -- extlib/HTMLPurifier/HTMLPurifier/ChildDef.php | 48 - .../HTMLPurifier/ChildDef/Chameleon.php | 48 - .../HTMLPurifier/HTMLPurifier/ChildDef/Custom.php | 90 - .../HTMLPurifier/HTMLPurifier/ChildDef/Empty.php | 20 - .../HTMLPurifier/ChildDef/Optional.php | 26 - .../HTMLPurifier/ChildDef/Required.php | 117 - .../HTMLPurifier/ChildDef/StrictBlockquote.php | 88 - .../HTMLPurifier/HTMLPurifier/ChildDef/Table.php | 142 - extlib/HTMLPurifier/HTMLPurifier/Config.php | 580 --- extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php | 158 - .../ConfigSchema/Builder/ConfigSchema.php | 44 - .../HTMLPurifier/ConfigSchema/Builder/Xml.php | 106 - .../HTMLPurifier/ConfigSchema/Exception.php | 11 - .../HTMLPurifier/ConfigSchema/Interchange.php | 42 - .../ConfigSchema/Interchange/Directive.php | 77 - .../HTMLPurifier/ConfigSchema/Interchange/Id.php | 37 - .../ConfigSchema/InterchangeBuilder.php | 180 - .../HTMLPurifier/ConfigSchema/Validator.php | 206 -- .../HTMLPurifier/ConfigSchema/ValidatorAtom.php | 66 - .../HTMLPurifier/ConfigSchema/schema.ser | Bin 12999 -> 0 bytes .../ConfigSchema/schema/Attr.AllowedClasses.txt | 8 - .../schema/Attr.AllowedFrameTargets.txt | 12 - .../ConfigSchema/schema/Attr.AllowedRel.txt | 9 - .../ConfigSchema/schema/Attr.AllowedRev.txt | 9 - .../ConfigSchema/schema/Attr.ClassUseCDATA.txt | 19 - .../ConfigSchema/schema/Attr.DefaultImageAlt.txt | 11 - .../schema/Attr.DefaultInvalidImage.txt | 9 - .../schema/Attr.DefaultInvalidImageAlt.txt | 8 - .../ConfigSchema/schema/Attr.DefaultTextDir.txt | 10 - .../ConfigSchema/schema/Attr.EnableID.txt | 16 - .../ConfigSchema/schema/Attr.ForbiddenClasses.txt | 8 - .../ConfigSchema/schema/Attr.IDBlacklist.txt | 5 - .../ConfigSchema/schema/Attr.IDBlacklistRegexp.txt | 9 - .../ConfigSchema/schema/Attr.IDPrefix.txt | 12 - .../ConfigSchema/schema/Attr.IDPrefixLocal.txt | 14 - .../schema/AutoFormat.AutoParagraph.txt | 31 - .../ConfigSchema/schema/AutoFormat.Custom.txt | 12 - .../schema/AutoFormat.DisplayLinkURI.txt | 11 - .../ConfigSchema/schema/AutoFormat.Linkify.txt | 12 - .../schema/AutoFormat.PurifierLinkify.DocURL.txt | 12 - .../schema/AutoFormat.PurifierLinkify.txt | 12 - ...utoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt | 11 - .../schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt | 15 - .../ConfigSchema/schema/AutoFormat.RemoveEmpty.txt | 46 - .../ConfigSchema/schema/CSS.AllowImportant.txt | 8 - .../ConfigSchema/schema/CSS.AllowTricky.txt | 11 - .../ConfigSchema/schema/CSS.AllowedProperties.txt | 18 - .../ConfigSchema/schema/CSS.DefinitionRev.txt | 11 - .../ConfigSchema/schema/CSS.MaxImgLength.txt | 16 - .../ConfigSchema/schema/CSS.Proprietary.txt | 10 - .../ConfigSchema/schema/Cache.DefinitionImpl.txt | 14 - .../ConfigSchema/schema/Cache.SerializerPath.txt | 13 - .../ConfigSchema/schema/Core.AggressivelyFixLt.txt | 18 - .../ConfigSchema/schema/Core.CollectErrors.txt | 12 - .../ConfigSchema/schema/Core.ColorKeywords.txt | 28 - .../schema/Core.ConvertDocumentToFragment.txt | 14 - .../Core.DirectLexLineNumberSyncInterval.txt | 17 - .../ConfigSchema/schema/Core.Encoding.txt | 15 - .../schema/Core.EscapeInvalidChildren.txt | 10 - .../ConfigSchema/schema/Core.EscapeInvalidTags.txt | 7 - .../schema/Core.EscapeNonASCIICharacters.txt | 13 - .../ConfigSchema/schema/Core.HiddenElements.txt | 19 - .../ConfigSchema/schema/Core.Language.txt | 10 - .../ConfigSchema/schema/Core.LexerImpl.txt | 34 - .../schema/Core.MaintainLineNumbers.txt | 16 - .../ConfigSchema/schema/Core.RemoveInvalidImg.txt | 12 - .../schema/Core.RemoveScriptContents.txt | 12 - .../ConfigSchema/schema/Filter.Custom.txt | 11 - .../schema/Filter.ExtractStyleBlocks.Escaping.txt | 14 - .../schema/Filter.ExtractStyleBlocks.Scope.txt | 29 - .../schema/Filter.ExtractStyleBlocks.TidyImpl.txt | 16 - .../schema/Filter.ExtractStyleBlocks.txt | 74 - .../ConfigSchema/schema/Filter.YouTube.txt | 11 - .../ConfigSchema/schema/HTML.Allowed.txt | 22 - .../ConfigSchema/schema/HTML.AllowedAttributes.txt | 19 - .../ConfigSchema/schema/HTML.AllowedElements.txt | 18 - .../ConfigSchema/schema/HTML.AllowedModules.txt | 20 - .../schema/HTML.Attr.Name.UseCDATA.txt | 11 - .../ConfigSchema/schema/HTML.BlockWrapper.txt | 18 - .../ConfigSchema/schema/HTML.CoreModules.txt | 23 - .../ConfigSchema/schema/HTML.CustomDoctype.txt | 9 - .../ConfigSchema/schema/HTML.DefinitionID.txt | 33 - .../ConfigSchema/schema/HTML.DefinitionRev.txt | 16 - .../ConfigSchema/schema/HTML.Doctype.txt | 11 - .../schema/HTML.ForbiddenAttributes.txt | 21 - .../ConfigSchema/schema/HTML.ForbiddenElements.txt | 20 - .../ConfigSchema/schema/HTML.MaxImgLength.txt | 14 - .../ConfigSchema/schema/HTML.Parent.txt | 12 - .../ConfigSchema/schema/HTML.Proprietary.txt | 12 - .../ConfigSchema/schema/HTML.SafeEmbed.txt | 14 - .../ConfigSchema/schema/HTML.SafeObject.txt | 14 - .../ConfigSchema/schema/HTML.Strict.txt | 9 - .../ConfigSchema/schema/HTML.TidyAdd.txt | 8 - .../ConfigSchema/schema/HTML.TidyLevel.txt | 24 - .../ConfigSchema/schema/HTML.TidyRemove.txt | 8 - .../ConfigSchema/schema/HTML.Trusted.txt | 8 - .../ConfigSchema/schema/HTML.XHTML.txt | 11 - .../schema/Output.CommentScriptContents.txt | 10 - .../ConfigSchema/schema/Output.Newline.txt | 13 - .../ConfigSchema/schema/Output.SortAttr.txt | 14 - .../ConfigSchema/schema/Output.TidyFormat.txt | 25 - .../ConfigSchema/schema/Test.ForceNoIconv.txt | 7 - .../ConfigSchema/schema/URI.AllowedSchemes.txt | 15 - .../HTMLPurifier/ConfigSchema/schema/URI.Base.txt | 17 - .../ConfigSchema/schema/URI.DefaultScheme.txt | 10 - .../ConfigSchema/schema/URI.DefinitionID.txt | 11 - .../ConfigSchema/schema/URI.DefinitionRev.txt | 11 - .../ConfigSchema/schema/URI.Disable.txt | 14 - .../ConfigSchema/schema/URI.DisableExternal.txt | 11 - .../schema/URI.DisableExternalResources.txt | 13 - .../ConfigSchema/schema/URI.DisableResources.txt | 12 - .../HTMLPurifier/ConfigSchema/schema/URI.Host.txt | 19 - .../ConfigSchema/schema/URI.HostBlacklist.txt | 9 - .../ConfigSchema/schema/URI.MakeAbsolute.txt | 13 - .../HTMLPurifier/ConfigSchema/schema/URI.Munge.txt | 83 - .../ConfigSchema/schema/URI.MungeResources.txt | 17 - .../ConfigSchema/schema/URI.MungeSecretKey.txt | 30 - .../schema/URI.OverrideAllowedSchemes.txt | 9 - .../HTMLPurifier/ConfigSchema/schema/info.ini | 3 - extlib/HTMLPurifier/HTMLPurifier/ContentSets.php | 155 - extlib/HTMLPurifier/HTMLPurifier/Context.php | 82 - extlib/HTMLPurifier/HTMLPurifier/Definition.php | 39 - .../HTMLPurifier/HTMLPurifier/DefinitionCache.php | 108 - .../HTMLPurifier/DefinitionCache/Decorator.php | 62 - .../DefinitionCache/Decorator/Cleanup.php | 43 - .../DefinitionCache/Decorator/Memory.php | 46 - .../DefinitionCache/Decorator/Template.php.in | 47 - .../HTMLPurifier/DefinitionCache/Null.php | 39 - .../HTMLPurifier/DefinitionCache/Serializer.php | 172 - .../HTMLPurifier/DefinitionCache/Serializer/README | 3 - .../HTMLPurifier/DefinitionCacheFactory.php | 91 - extlib/HTMLPurifier/HTMLPurifier/Doctype.php | 60 - .../HTMLPurifier/HTMLPurifier/DoctypeRegistry.php | 103 - extlib/HTMLPurifier/HTMLPurifier/ElementDef.php | 176 - extlib/HTMLPurifier/HTMLPurifier/Encoder.php | 426 --- extlib/HTMLPurifier/HTMLPurifier/EntityLookup.php | 44 - .../HTMLPurifier/EntityLookup/entities.ser | 1 - extlib/HTMLPurifier/HTMLPurifier/EntityParser.php | 144 - .../HTMLPurifier/HTMLPurifier/ErrorCollector.php | 209 -- extlib/HTMLPurifier/HTMLPurifier/ErrorStruct.php | 60 - extlib/HTMLPurifier/HTMLPurifier/Exception.php | 12 - extlib/HTMLPurifier/HTMLPurifier/Filter.php | 46 - .../HTMLPurifier/Filter/ExtractStyleBlocks.php | 135 - .../HTMLPurifier/HTMLPurifier/Filter/YouTube.php | 39 - extlib/HTMLPurifier/HTMLPurifier/Generator.php | 183 - .../HTMLPurifier/HTMLPurifier/HTMLDefinition.php | 420 --- extlib/HTMLPurifier/HTMLPurifier/HTMLModule.php | 244 -- .../HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php | 31 - .../HTMLPurifier/HTMLModule/CommonAttributes.php | 26 - .../HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php | 38 - .../HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php | 118 - .../HTMLPurifier/HTMLModule/Hypertext.php | 31 - .../HTMLPurifier/HTMLPurifier/HTMLModule/Image.php | 40 - .../HTMLPurifier/HTMLModule/Legacy.php | 143 - .../HTMLPurifier/HTMLPurifier/HTMLModule/List.php | 35 - .../HTMLPurifier/HTMLPurifier/HTMLModule/Name.php | 21 - .../HTMLModule/NonXMLCommonAttributes.php | 14 - .../HTMLPurifier/HTMLModule/Object.php | 47 - .../HTMLPurifier/HTMLModule/Presentation.php | 36 - .../HTMLPurifier/HTMLModule/Proprietary.php | 33 - .../HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php | 27 - .../HTMLPurifier/HTMLModule/SafeEmbed.php | 33 - .../HTMLPurifier/HTMLModule/SafeObject.php | 50 - .../HTMLPurifier/HTMLModule/Scripting.php | 54 - .../HTMLPurifier/HTMLModule/StyleAttribute.php | 24 - .../HTMLPurifier/HTMLModule/Tables.php | 66 - .../HTMLPurifier/HTMLModule/Target.php | 23 - .../HTMLPurifier/HTMLPurifier/HTMLModule/Text.php | 71 - .../HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php | 207 -- .../HTMLPurifier/HTMLModule/Tidy/Name.php | 24 - .../HTMLPurifier/HTMLModule/Tidy/Proprietary.php | 23 - .../HTMLPurifier/HTMLModule/Tidy/Strict.php | 21 - .../HTMLPurifier/HTMLModule/Tidy/Transitional.php | 9 - .../HTMLPurifier/HTMLModule/Tidy/XHTML.php | 17 - .../HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php | 161 - .../HTMLModule/XMLCommonAttributes.php | 14 - .../HTMLPurifier/HTMLModuleManager.php | 403 -- extlib/HTMLPurifier/HTMLPurifier/IDAccumulator.php | 53 - extlib/HTMLPurifier/HTMLPurifier/Injector.php | 239 -- .../HTMLPurifier/Injector/AutoParagraph.php | 340 -- .../HTMLPurifier/Injector/DisplayLinkURI.php | 26 - .../HTMLPurifier/HTMLPurifier/Injector/Linkify.php | 46 - .../HTMLPurifier/Injector/PurifierLinkify.php | 45 - .../HTMLPurifier/Injector/RemoveEmpty.php | 51 - .../HTMLPurifier/Injector/SafeObject.php | 87 - extlib/HTMLPurifier/HTMLPurifier/Language.php | 163 - .../HTMLPurifier/Language/classes/en-x-test.php | 12 - .../HTMLPurifier/Language/messages/en-x-test.php | 11 - .../Language/messages/en-x-testmini.php | 12 - .../HTMLPurifier/Language/messages/en.php | 62 - .../HTMLPurifier/HTMLPurifier/LanguageFactory.php | 198 - extlib/HTMLPurifier/HTMLPurifier/Length.php | 115 - extlib/HTMLPurifier/HTMLPurifier/Lexer.php | 298 -- extlib/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php | 213 -- .../HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php | 490 --- .../HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php | 106 - extlib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php | 3906 -------------------- .../HTMLPurifier/HTMLPurifier/PercentEncoder.php | 98 - extlib/HTMLPurifier/HTMLPurifier/Printer.php | 176 - .../HTMLPurifier/Printer/CSSDefinition.php | 38 - .../HTMLPurifier/Printer/ConfigForm.css | 10 - .../HTMLPurifier/Printer/ConfigForm.js | 5 - .../HTMLPurifier/Printer/ConfigForm.php | 368 -- .../HTMLPurifier/Printer/HTMLDefinition.php | 272 -- extlib/HTMLPurifier/HTMLPurifier/PropertyList.php | 86 - .../HTMLPurifier/PropertyListIterator.php | 32 - extlib/HTMLPurifier/HTMLPurifier/Strategy.php | 26 - .../HTMLPurifier/Strategy/Composite.php | 25 - extlib/HTMLPurifier/HTMLPurifier/Strategy/Core.php | 18 - .../HTMLPurifier/Strategy/FixNesting.php | 328 -- .../HTMLPurifier/Strategy/MakeWellFormed.php | 457 --- .../Strategy/RemoveForeignElements.php | 171 - .../HTMLPurifier/Strategy/ValidateAttributes.php | 39 - extlib/HTMLPurifier/HTMLPurifier/StringHash.php | 39 - .../HTMLPurifier/HTMLPurifier/StringHashParser.php | 110 - extlib/HTMLPurifier/HTMLPurifier/TagTransform.php | 36 - .../HTMLPurifier/TagTransform/Font.php | 96 - .../HTMLPurifier/TagTransform/Simple.php | 35 - extlib/HTMLPurifier/HTMLPurifier/Token.php | 57 - extlib/HTMLPurifier/HTMLPurifier/Token/Comment.php | 22 - extlib/HTMLPurifier/HTMLPurifier/Token/Empty.php | 11 - extlib/HTMLPurifier/HTMLPurifier/Token/End.php | 19 - extlib/HTMLPurifier/HTMLPurifier/Token/Start.php | 11 - extlib/HTMLPurifier/HTMLPurifier/Token/Tag.php | 56 - extlib/HTMLPurifier/HTMLPurifier/Token/Text.php | 33 - extlib/HTMLPurifier/HTMLPurifier/TokenFactory.php | 94 - extlib/HTMLPurifier/HTMLPurifier/URI.php | 173 - extlib/HTMLPurifier/HTMLPurifier/URIDefinition.php | 93 - extlib/HTMLPurifier/HTMLPurifier/URIFilter.php | 45 - .../HTMLPurifier/URIFilter/DisableExternal.php | 23 - .../URIFilter/DisableExternalResources.php | 12 - .../HTMLPurifier/URIFilter/HostBlacklist.php | 21 - .../HTMLPurifier/URIFilter/MakeAbsolute.php | 114 - .../HTMLPurifier/HTMLPurifier/URIFilter/Munge.php | 58 - extlib/HTMLPurifier/HTMLPurifier/URIParser.php | 70 - extlib/HTMLPurifier/HTMLPurifier/URIScheme.php | 42 - extlib/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php | 43 - .../HTMLPurifier/HTMLPurifier/URIScheme/http.php | 20 - .../HTMLPurifier/HTMLPurifier/URIScheme/https.php | 12 - .../HTMLPurifier/HTMLPurifier/URIScheme/mailto.php | 27 - .../HTMLPurifier/HTMLPurifier/URIScheme/news.php | 22 - .../HTMLPurifier/HTMLPurifier/URIScheme/nntp.php | 20 - .../HTMLPurifier/URISchemeRegistry.php | 68 - extlib/HTMLPurifier/HTMLPurifier/UnitConverter.php | 254 -- extlib/HTMLPurifier/HTMLPurifier/VarParser.php | 154 - .../HTMLPurifier/VarParser/Flexible.php | 96 - .../HTMLPurifier/HTMLPurifier/VarParser/Native.php | 26 - .../HTMLPurifier/VarParserException.php | 11 - extlib/HTMLPurifier/LICENSE | 504 --- plugins/OStatus/README | 4 - plugins/OStatus/classes/Ostatus_profile.php | 25 +- 324 files changed, 3 insertions(+), 24231 deletions(-) delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.auto.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.autoload.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.func.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.includes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.kses.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.path.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier.safe-includes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrCollections.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Nmtokens.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email/SimpleCheck.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Background.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Length.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeObject.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeParam.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrTypes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/AttrValidator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Bootstrap.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/CSSDefinition.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef/Optional.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Config.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ContentSets.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Context.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Definition.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Memory.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Null.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php delete mode 100755 extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Doctype.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ElementDef.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Encoder.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/EntityLookup.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/EntityParser.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ErrorCollector.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/ErrorStruct.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Exception.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Filter.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Generator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Proprietary.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Strict.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTML.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/XHTMLAndHTML4.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/IDAccumulator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Injector.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Language.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-test.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Language/messages/en.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/LanguageFactory.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Length.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Lexer.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Lexer/DOMLex.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Lexer/DirectLex.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/PercentEncoder.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Printer.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/PropertyList.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Strategy.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Strategy/Composite.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Strategy/Core.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/StringHash.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/StringHashParser.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/TagTransform.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/TagTransform/Font.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Token.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Token/Comment.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Token/Empty.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Token/End.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Token/Start.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Token/Tag.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/Token/Text.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/TokenFactory.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URI.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIDefinition.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIFilter.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternal.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIParser.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIScheme.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIScheme/http.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIScheme/https.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIScheme/mailto.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIScheme/news.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/UnitConverter.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/VarParser.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/VarParser/Native.php delete mode 100644 extlib/HTMLPurifier/HTMLPurifier/VarParserException.php delete mode 100644 extlib/HTMLPurifier/LICENSE (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/extlib/HTMLPurifier/HTMLPurifier.auto.php b/extlib/HTMLPurifier/HTMLPurifier.auto.php deleted file mode 100644 index 1960c399f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier.auto.php +++ /dev/null @@ -1,11 +0,0 @@ -purify($html, $config); -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier.includes.php b/extlib/HTMLPurifier/HTMLPurifier.includes.php deleted file mode 100644 index 7cfb97060..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier.includes.php +++ /dev/null @@ -1,208 +0,0 @@ - $attributes) { - $allowed_elements[$element] = true; - foreach ($attributes as $attribute => $x) { - $allowed_attributes["$element.$attribute"] = true; - } - } - $config->set('HTML.AllowedElements', $allowed_elements); - $config->set('HTML.AllowedAttributes', $allowed_attributes); - $allowed_schemes = array(); - if ($allowed_protocols !== null) { - $config->set('URI.AllowedSchemes', $allowed_protocols); - } - $purifier = new HTMLPurifier($config); - return $purifier->purify($string); -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier.path.php b/extlib/HTMLPurifier/HTMLPurifier.path.php deleted file mode 100644 index 39b1b6531..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier.path.php +++ /dev/null @@ -1,11 +0,0 @@ -config = HTMLPurifier_Config::create($config); - - $this->strategy = new HTMLPurifier_Strategy_Core(); - - } - - /** - * Adds a filter to process the output. First come first serve - * @param $filter HTMLPurifier_Filter object - */ - public function addFilter($filter) { - trigger_error('HTMLPurifier->addFilter() is deprecated, use configuration directives in the Filter namespace or Filter.Custom', E_USER_WARNING); - $this->filters[] = $filter; - } - - /** - * Filters an HTML snippet/document to be XSS-free and standards-compliant. - * - * @param $html String of HTML to purify - * @param $config HTMLPurifier_Config object for this operation, if omitted, - * defaults to the config object specified during this - * object's construction. The parameter can also be any type - * that HTMLPurifier_Config::create() supports. - * @return Purified HTML - */ - public function purify($html, $config = null) { - - // :TODO: make the config merge in, instead of replace - $config = $config ? HTMLPurifier_Config::create($config) : $this->config; - - // implementation is partially environment dependant, partially - // configuration dependant - $lexer = HTMLPurifier_Lexer::create($config); - - $context = new HTMLPurifier_Context(); - - // setup HTML generator - $this->generator = new HTMLPurifier_Generator($config, $context); - $context->register('Generator', $this->generator); - - // set up global context variables - if ($config->get('Core.CollectErrors')) { - // may get moved out if other facilities use it - $language_factory = HTMLPurifier_LanguageFactory::instance(); - $language = $language_factory->create($config, $context); - $context->register('Locale', $language); - - $error_collector = new HTMLPurifier_ErrorCollector($context); - $context->register('ErrorCollector', $error_collector); - } - - // setup id_accumulator context, necessary due to the fact that - // AttrValidator can be called from many places - $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); - $context->register('IDAccumulator', $id_accumulator); - - $html = HTMLPurifier_Encoder::convertToUTF8($html, $config, $context); - - // setup filters - $filter_flags = $config->getBatch('Filter'); - $custom_filters = $filter_flags['Custom']; - unset($filter_flags['Custom']); - $filters = array(); - foreach ($filter_flags as $filter => $flag) { - if (!$flag) continue; - if (strpos($filter, '.') !== false) continue; - $class = "HTMLPurifier_Filter_$filter"; - $filters[] = new $class; - } - foreach ($custom_filters as $filter) { - // maybe "HTMLPurifier_Filter_$filter", but be consistent with AutoFormat - $filters[] = $filter; - } - $filters = array_merge($filters, $this->filters); - // maybe prepare(), but later - - for ($i = 0, $filter_size = count($filters); $i < $filter_size; $i++) { - $html = $filters[$i]->preFilter($html, $config, $context); - } - - // purified HTML - $html = - $this->generator->generateFromTokens( - // list of tokens - $this->strategy->execute( - // list of un-purified tokens - $lexer->tokenizeHTML( - // un-purified HTML - $html, $config, $context - ), - $config, $context - ) - ); - - for ($i = $filter_size - 1; $i >= 0; $i--) { - $html = $filters[$i]->postFilter($html, $config, $context); - } - - $html = HTMLPurifier_Encoder::convertFromUTF8($html, $config, $context); - $this->context =& $context; - return $html; - } - - /** - * Filters an array of HTML snippets - * @param $config Optional HTMLPurifier_Config object for this operation. - * See HTMLPurifier::purify() for more details. - * @return Array of purified HTML - */ - public function purifyArray($array_of_html, $config = null) { - $context_array = array(); - foreach ($array_of_html as $key => $html) { - $array_of_html[$key] = $this->purify($html, $config); - $context_array[$key] = $this->context; - } - $this->context = $context_array; - return $array_of_html; - } - - /** - * Singleton for enforcing just one HTML Purifier in your system - * @param $prototype Optional prototype HTMLPurifier instance to - * overload singleton with, or HTMLPurifier_Config - * instance to configure the generated version with. - */ - public static function instance($prototype = null) { - if (!self::$instance || $prototype) { - if ($prototype instanceof HTMLPurifier) { - self::$instance = $prototype; - } elseif ($prototype) { - self::$instance = new HTMLPurifier($prototype); - } else { - self::$instance = new HTMLPurifier(); - } - } - return self::$instance; - } - - /** - * @note Backwards compatibility, see instance() - */ - public static function getInstance($prototype = null) { - return HTMLPurifier::instance($prototype); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier.safe-includes.php b/extlib/HTMLPurifier/HTMLPurifier.safe-includes.php deleted file mode 100644 index cf2c1d617..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier.safe-includes.php +++ /dev/null @@ -1,202 +0,0 @@ -attr_collections as $coll_i => $coll) { - if (!isset($this->info[$coll_i])) { - $this->info[$coll_i] = array(); - } - foreach ($coll as $attr_i => $attr) { - if ($attr_i === 0 && isset($this->info[$coll_i][$attr_i])) { - // merge in includes - $this->info[$coll_i][$attr_i] = array_merge( - $this->info[$coll_i][$attr_i], $attr); - continue; - } - $this->info[$coll_i][$attr_i] = $attr; - } - } - } - // perform internal expansions and inclusions - foreach ($this->info as $name => $attr) { - // merge attribute collections that include others - $this->performInclusions($this->info[$name]); - // replace string identifiers with actual attribute objects - $this->expandIdentifiers($this->info[$name], $attr_types); - } - } - - /** - * Takes a reference to an attribute associative array and performs - * all inclusions specified by the zero index. - * @param &$attr Reference to attribute array - */ - public function performInclusions(&$attr) { - if (!isset($attr[0])) return; - $merge = $attr[0]; - $seen = array(); // recursion guard - // loop through all the inclusions - for ($i = 0; isset($merge[$i]); $i++) { - if (isset($seen[$merge[$i]])) continue; - $seen[$merge[$i]] = true; - // foreach attribute of the inclusion, copy it over - if (!isset($this->info[$merge[$i]])) continue; - foreach ($this->info[$merge[$i]] as $key => $value) { - if (isset($attr[$key])) continue; // also catches more inclusions - $attr[$key] = $value; - } - if (isset($this->info[$merge[$i]][0])) { - // recursion - $merge = array_merge($merge, $this->info[$merge[$i]][0]); - } - } - unset($attr[0]); - } - - /** - * Expands all string identifiers in an attribute array by replacing - * them with the appropriate values inside HTMLPurifier_AttrTypes - * @param &$attr Reference to attribute array - * @param $attr_types HTMLPurifier_AttrTypes instance - */ - public function expandIdentifiers(&$attr, $attr_types) { - - // because foreach will process new elements we add, make sure we - // skip duplicates - $processed = array(); - - foreach ($attr as $def_i => $def) { - // skip inclusions - if ($def_i === 0) continue; - - if (isset($processed[$def_i])) continue; - - // determine whether or not attribute is required - if ($required = (strpos($def_i, '*') !== false)) { - // rename the definition - unset($attr[$def_i]); - $def_i = trim($def_i, '*'); - $attr[$def_i] = $def; - } - - $processed[$def_i] = true; - - // if we've already got a literal object, move on - if (is_object($def)) { - // preserve previous required - $attr[$def_i]->required = ($required || $attr[$def_i]->required); - continue; - } - - if ($def === false) { - unset($attr[$def_i]); - continue; - } - - if ($t = $attr_types->get($def)) { - $attr[$def_i] = $t; - $attr[$def_i]->required = $required; - } else { - unset($attr[$def_i]); - } - } - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef.php deleted file mode 100644 index d32fa62d6..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef.php +++ /dev/null @@ -1,87 +0,0 @@ - by removing - * leading and trailing whitespace, ignoring line feeds, and replacing - * carriage returns and tabs with spaces. While most useful for HTML - * attributes specified as CDATA, it can also be applied to most CSS - * values. - * - * @note This method is not entirely standards compliant, as trim() removes - * more types of whitespace than specified in the spec. In practice, - * this is rarely a problem, as those extra characters usually have - * already been removed by HTMLPurifier_Encoder. - * - * @warning This processing is inconsistent with XML's whitespace handling - * as specified by section 3.3.3 and referenced XHTML 1.0 section - * 4.7. However, note that we are NOT necessarily - * parsing XML, thus, this behavior may still be correct. We - * assume that newlines have been normalized. - */ - public function parseCDATA($string) { - $string = trim($string); - $string = str_replace(array("\n", "\t", "\r"), ' ', $string); - return $string; - } - - /** - * Factory method for creating this class from a string. - * @param $string String construction info - * @return Created AttrDef object corresponding to $string - */ - public function make($string) { - // default implementation, return a flyweight of this object. - // If $string has an effect on the returned object (i.e. you - // need to overload this method), it is best - // to clone or instantiate new copies. (Instantiation is safer.) - return $this; - } - - /** - * Removes spaces from rgb(0, 0, 0) so that shorthand CSS properties work - * properly. THIS IS A HACK! - */ - protected function mungeRgb($string) { - return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php deleted file mode 100644 index 953e70675..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php +++ /dev/null @@ -1,87 +0,0 @@ -parseCDATA($css); - - $definition = $config->getCSSDefinition(); - - // 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); - $propvalues = array(); - - /** - * Name of the current CSS property being validated. - */ - $property = false; - $context->register('CurrentCSSProperty', $property); - - foreach ($declarations as $declaration) { - if (!$declaration) continue; - if (!strpos($declaration, ':')) continue; - list($property, $value) = explode(':', $declaration, 2); - $property = trim($property); - $value = trim($value); - $ok = false; - do { - if (isset($definition->info[$property])) { - $ok = true; - break; - } - if (ctype_lower($property)) break; - $property = strtolower($property); - if (isset($definition->info[$property])) { - $ok = true; - break; - } - } while(0); - if (!$ok) continue; - // inefficient call, since the validator will do this again - if (strtolower(trim($value)) !== 'inherit') { - // inherit works for everything (but only on the base property) - $result = $definition->info[$property]->validate( - $value, $config, $context ); - } else { - $result = 'inherit'; - } - if ($result === false) continue; - $propvalues[$property] = $result; - } - - $context->destroy('CurrentCSSProperty'); - - // procedure does not write the new CSS simultaneously, so it's - // slightly inefficient, but it's the only way of getting rid of - // duplicates. Perhaps config to optimize it, but not now. - - $new_declarations = ''; - foreach ($propvalues as $prop => $value) { - $new_declarations .= "$prop:$value;"; - } - - return $new_declarations ? $new_declarations : false; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php deleted file mode 100644 index 292c040d4..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/AlphaValue.php +++ /dev/null @@ -1,21 +0,0 @@ - 1.0) $result = '1'; - return $result; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php deleted file mode 100644 index 3a3d20cd6..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Background.php +++ /dev/null @@ -1,87 +0,0 @@ -getCSSDefinition(); - $this->info['background-color'] = $def->info['background-color']; - $this->info['background-image'] = $def->info['background-image']; - $this->info['background-repeat'] = $def->info['background-repeat']; - $this->info['background-attachment'] = $def->info['background-attachment']; - $this->info['background-position'] = $def->info['background-position']; - } - - public function validate($string, $config, $context) { - - // regular pre-processing - $string = $this->parseCDATA($string); - if ($string === '') return false; - - // munge rgb() decl if necessary - $string = $this->mungeRgb($string); - - // assumes URI doesn't have spaces in it - $bits = explode(' ', strtolower($string)); // bits to process - - $caught = array(); - $caught['color'] = false; - $caught['image'] = false; - $caught['repeat'] = false; - $caught['attachment'] = false; - $caught['position'] = false; - - $i = 0; // number of catches - $none = false; - - foreach ($bits as $bit) { - if ($bit === '') continue; - foreach ($caught as $key => $status) { - if ($key != 'position') { - if ($status !== false) continue; - $r = $this->info['background-' . $key]->validate($bit, $config, $context); - } else { - $r = $bit; - } - if ($r === false) continue; - if ($key == 'position') { - if ($caught[$key] === false) $caught[$key] = ''; - $caught[$key] .= $r . ' '; - } else { - $caught[$key] = $r; - } - $i++; - break; - } - } - - if (!$i) return false; - if ($caught['position'] !== false) { - $caught['position'] = $this->info['background-position']-> - validate($caught['position'], $config, $context); - } - - $ret = array(); - foreach ($caught as $value) { - if ($value === false) continue; - $ret[] = $value; - } - - if (empty($ret)) return false; - return implode(' ', $ret); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php deleted file mode 100644 index 35df3985e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/BackgroundPosition.php +++ /dev/null @@ -1,126 +0,0 @@ - | | left | center | right - ] - [ - | | top | center | bottom - ]? - ] | - [ // this signifies that the vertical and horizontal adjectives - // can be arbitrarily ordered, however, there can only be two, - // one of each, or none at all - [ - left | center | right - ] || - [ - top | center | bottom - ] - ] - top, left = 0% - center, (none) = 50% - bottom, right = 100% -*/ - -/* QuirksMode says: - keyword + length/percentage must be ordered correctly, as per W3C - - Internet Explorer and Opera, however, support arbitrary ordering. We - should fix it up. - - Minor issue though, not strictly necessary. -*/ - -// control freaks may appreciate the ability to convert these to -// percentages or something, but it's not necessary - -/** - * Validates the value of background-position. - */ -class HTMLPurifier_AttrDef_CSS_BackgroundPosition extends HTMLPurifier_AttrDef -{ - - protected $length; - protected $percentage; - - public function __construct() { - $this->length = new HTMLPurifier_AttrDef_CSS_Length(); - $this->percentage = new HTMLPurifier_AttrDef_CSS_Percentage(); - } - - public function validate($string, $config, $context) { - $string = $this->parseCDATA($string); - $bits = explode(' ', $string); - - $keywords = array(); - $keywords['h'] = false; // left, right - $keywords['v'] = false; // top, bottom - $keywords['c'] = false; // center - $measures = array(); - - $i = 0; - - $lookup = array( - 'top' => 'v', - 'bottom' => 'v', - 'left' => 'h', - 'right' => 'h', - 'center' => 'c' - ); - - foreach ($bits as $bit) { - if ($bit === '') continue; - - // test for keyword - $lbit = ctype_lower($bit) ? $bit : strtolower($bit); - if (isset($lookup[$lbit])) { - $status = $lookup[$lbit]; - $keywords[$status] = $lbit; - $i++; - } - - // test for length - $r = $this->length->validate($bit, $config, $context); - if ($r !== false) { - $measures[] = $r; - $i++; - } - - // test for percentage - $r = $this->percentage->validate($bit, $config, $context); - if ($r !== false) { - $measures[] = $r; - $i++; - } - - } - - if (!$i) return false; // no valid values were caught - - - $ret = array(); - - // first keyword - if ($keywords['h']) $ret[] = $keywords['h']; - elseif (count($measures)) $ret[] = array_shift($measures); - elseif ($keywords['c']) { - $ret[] = $keywords['c']; - $keywords['c'] = false; // prevent re-use: center = center center - } - - if ($keywords['v']) $ret[] = $keywords['v']; - elseif (count($measures)) $ret[] = array_shift($measures); - elseif ($keywords['c']) $ret[] = $keywords['c']; - - if (empty($ret)) return false; - return implode(' ', $ret); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php deleted file mode 100644 index 42a1d1b4a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Border.php +++ /dev/null @@ -1,43 +0,0 @@ -getCSSDefinition(); - $this->info['border-width'] = $def->info['border-width']; - $this->info['border-style'] = $def->info['border-style']; - $this->info['border-top-color'] = $def->info['border-top-color']; - } - - public function validate($string, $config, $context) { - $string = $this->parseCDATA($string); - $string = $this->mungeRgb($string); - $bits = explode(' ', $string); - $done = array(); // segments we've finished - $ret = ''; // return value - foreach ($bits as $bit) { - foreach ($this->info as $propname => $validator) { - if (isset($done[$propname])) continue; - $r = $validator->validate($bit, $config, $context); - if ($r !== false) { - $ret .= $r . ' '; - $done[$propname] = true; - break; - } - } - } - return rtrim($ret); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php deleted file mode 100644 index 07f95a671..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php +++ /dev/null @@ -1,78 +0,0 @@ -get('Core.ColorKeywords'); - - $color = trim($color); - if ($color === '') return false; - - $lower = strtolower($color); - if (isset($colors[$lower])) return $colors[$lower]; - - if (strpos($color, 'rgb(') !== false) { - // rgb literal handling - $length = strlen($color); - if (strpos($color, ')') !== $length - 1) return false; - $triad = substr($color, 4, $length - 4 - 1); - $parts = explode(',', $triad); - if (count($parts) !== 3) return false; - $type = false; // to ensure that they're all the same type - $new_parts = array(); - foreach ($parts as $part) { - $part = trim($part); - if ($part === '') return false; - $length = strlen($part); - if ($part[$length - 1] === '%') { - // handle percents - if (!$type) { - $type = 'percentage'; - } elseif ($type !== 'percentage') { - return false; - } - $num = (float) substr($part, 0, $length - 1); - if ($num < 0) $num = 0; - if ($num > 100) $num = 100; - $new_parts[] = "$num%"; - } else { - // handle integers - if (!$type) { - $type = 'integer'; - } elseif ($type !== 'integer') { - return false; - } - $num = (int) $part; - if ($num < 0) $num = 0; - if ($num > 255) $num = 255; - $new_parts[] = (string) $num; - } - } - $new_triad = implode(',', $new_parts); - $color = "rgb($new_triad)"; - } else { - // hexadecimal handling - if ($color[0] === '#') { - $hex = substr($color, 1); - } else { - $hex = $color; - $color = '#' . $color; - } - $length = strlen($hex); - if ($length !== 3 && $length !== 6) return false; - if (!ctype_xdigit($hex)) return false; - } - - return $color; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php deleted file mode 100644 index de1289cba..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Composite.php +++ /dev/null @@ -1,38 +0,0 @@ -defs = $defs; - } - - public function validate($string, $config, $context) { - foreach ($this->defs as $i => $def) { - $result = $this->defs[$i]->validate($string, $config, $context); - if ($result !== false) return $result; - } - return false; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php deleted file mode 100644 index 6599c5b2d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/DenyElementDecorator.php +++ /dev/null @@ -1,28 +0,0 @@ -def = $def; - $this->element = $element; - } - /** - * Checks if CurrentToken is set and equal to $this->element - */ - public function validate($string, $config, $context) { - $token = $context->get('CurrentToken', true); - if ($token && $token->name == $this->element) return false; - return $this->def->validate($string, $config, $context); - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php deleted file mode 100644 index 147894b86..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Filter.php +++ /dev/null @@ -1,54 +0,0 @@ -intValidator = new HTMLPurifier_AttrDef_Integer(); - } - - public function validate($value, $config, $context) { - $value = $this->parseCDATA($value); - if ($value === 'none') return $value; - // if we looped this we could support multiple filters - $function_length = strcspn($value, '('); - $function = trim(substr($value, 0, $function_length)); - if ($function !== 'alpha' && - $function !== 'Alpha' && - $function !== 'progid:DXImageTransform.Microsoft.Alpha' - ) return false; - $cursor = $function_length + 1; - $parameters_length = strcspn($value, ')', $cursor); - $parameters = substr($value, $cursor, $parameters_length); - $params = explode(',', $parameters); - $ret_params = array(); - $lookup = array(); - foreach ($params as $param) { - list($key, $value) = explode('=', $param); - $key = trim($key); - $value = trim($value); - if (isset($lookup[$key])) continue; - if ($key !== 'opacity') continue; - $value = $this->intValidator->validate($value, $config, $context); - if ($value === false) continue; - $int = (int) $value; - if ($int > 100) $value = '100'; - if ($int < 0) $value = '0'; - $ret_params[] = "$key=$value"; - $lookup[$key] = true; - } - $ret_parameters = implode(',', $ret_params); - $ret_function = "$function($ret_parameters)"; - return $ret_function; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php deleted file mode 100644 index 699ee0b70..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Font.php +++ /dev/null @@ -1,149 +0,0 @@ -getCSSDefinition(); - $this->info['font-style'] = $def->info['font-style']; - $this->info['font-variant'] = $def->info['font-variant']; - $this->info['font-weight'] = $def->info['font-weight']; - $this->info['font-size'] = $def->info['font-size']; - $this->info['line-height'] = $def->info['line-height']; - $this->info['font-family'] = $def->info['font-family']; - } - - public function validate($string, $config, $context) { - - static $system_fonts = array( - 'caption' => true, - 'icon' => true, - 'menu' => true, - 'message-box' => true, - 'small-caption' => true, - 'status-bar' => true - ); - - // regular pre-processing - $string = $this->parseCDATA($string); - if ($string === '') return false; - - // check if it's one of the keywords - $lowercase_string = strtolower($string); - if (isset($system_fonts[$lowercase_string])) { - return $lowercase_string; - } - - $bits = explode(' ', $string); // bits to process - $stage = 0; // this indicates what we're looking for - $caught = array(); // which stage 0 properties have we caught? - $stage_1 = array('font-style', 'font-variant', 'font-weight'); - $final = ''; // output - - for ($i = 0, $size = count($bits); $i < $size; $i++) { - if ($bits[$i] === '') continue; - switch ($stage) { - - // attempting to catch font-style, font-variant or font-weight - case 0: - foreach ($stage_1 as $validator_name) { - if (isset($caught[$validator_name])) continue; - $r = $this->info[$validator_name]->validate( - $bits[$i], $config, $context); - if ($r !== false) { - $final .= $r . ' '; - $caught[$validator_name] = true; - break; - } - } - // all three caught, continue on - if (count($caught) >= 3) $stage = 1; - if ($r !== false) break; - - // attempting to catch font-size and perhaps line-height - case 1: - $found_slash = false; - if (strpos($bits[$i], '/') !== false) { - list($font_size, $line_height) = - explode('/', $bits[$i]); - if ($line_height === '') { - // ooh, there's a space after the slash! - $line_height = false; - $found_slash = true; - } - } else { - $font_size = $bits[$i]; - $line_height = false; - } - $r = $this->info['font-size']->validate( - $font_size, $config, $context); - if ($r !== false) { - $final .= $r; - // attempt to catch line-height - if ($line_height === false) { - // we need to scroll forward - for ($j = $i + 1; $j < $size; $j++) { - if ($bits[$j] === '') continue; - if ($bits[$j] === '/') { - if ($found_slash) { - return false; - } else { - $found_slash = true; - continue; - } - } - $line_height = $bits[$j]; - break; - } - } else { - // slash already found - $found_slash = true; - $j = $i; - } - if ($found_slash) { - $i = $j; - $r = $this->info['line-height']->validate( - $line_height, $config, $context); - if ($r !== false) { - $final .= '/' . $r; - } - } - $final .= ' '; - $stage = 2; - break; - } - return false; - - // attempting to catch font-family - case 2: - $font_family = - implode(' ', array_slice($bits, $i, $size - $i)); - $r = $this->info['font-family']->validate( - $font_family, $config, $context); - if ($r !== false) { - $final .= $r . ' '; - // processing completed successfully - return rtrim($final); - } - return false; - } - } - return false; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php deleted file mode 100644 index 705ac893d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php +++ /dev/null @@ -1,90 +0,0 @@ - true, - 'sans-serif' => true, - 'monospace' => true, - 'fantasy' => true, - 'cursive' => true - ); - - // assume that no font names contain commas in them - $fonts = explode(',', $string); - $final = ''; - foreach($fonts as $font) { - $font = trim($font); - if ($font === '') continue; - // match a generic name - if (isset($generic_names[$font])) { - $final .= $font . ', '; - continue; - } - // match a quoted name - if ($font[0] === '"' || $font[0] === "'") { - $length = strlen($font); - if ($length <= 2) continue; - $quote = $font[0]; - if ($font[$length - 1] !== $quote) continue; - $font = substr($font, 1, $length - 2); - - $new_font = ''; - for ($i = 0, $c = strlen($font); $i < $c; $i++) { - if ($font[$i] === '\\') { - $i++; - if ($i >= $c) { - $new_font .= '\\'; - break; - } - if (ctype_xdigit($font[$i])) { - $code = $font[$i]; - for ($a = 1, $i++; $i < $c && $a < 6; $i++, $a++) { - if (!ctype_xdigit($font[$i])) break; - $code .= $font[$i]; - } - // We have to be extremely careful when adding - // new characters, to make sure we're not breaking - // the encoding. - $char = HTMLPurifier_Encoder::unichr(hexdec($code)); - if (HTMLPurifier_Encoder::cleanUTF8($char) === '') continue; - $new_font .= $char; - if ($i < $c && trim($font[$i]) !== '') $i--; - continue; - } - if ($font[$i] === "\n") continue; - } - $new_font .= $font[$i]; - } - - $font = $new_font; - } - // $font is a pure representation of the font name - - if (ctype_alnum($font) && $font !== '') { - // very simple font, allow it in unharmed - $final .= $font . ', '; - continue; - } - - // complicated font, requires quoting - - // armor single quotes and new lines - $font = str_replace("\\", "\\\\", $font); - $font = str_replace("'", "\\'", $font); - $final .= "'$font', "; - } - $final = rtrim($final, ', '); - if ($final === '') return false; - return $final; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php deleted file mode 100644 index 4e6b35e5a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ImportantDecorator.php +++ /dev/null @@ -1,40 +0,0 @@ -def = $def; - $this->allow = $allow; - } - /** - * Intercepts and removes !important if necessary - */ - public function validate($string, $config, $context) { - // test for ! and important tokens - $string = trim($string); - $is_important = false; - // :TODO: optimization: test directly for !important and ! important - if (strlen($string) >= 9 && substr($string, -9) === 'important') { - $temp = rtrim(substr($string, 0, -9)); - // use a temp, because we might want to restore important - if (strlen($temp) >= 1 && substr($temp, -1) === '!') { - $string = rtrim(substr($temp, 0, -1)); - $is_important = true; - } - } - $string = $this->def->validate($string, $config, $context); - if ($this->allow && $is_important) $string .= ' !important'; - return $string; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php deleted file mode 100644 index a07ec5813..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Length.php +++ /dev/null @@ -1,47 +0,0 @@ -min = $min !== null ? HTMLPurifier_Length::make($min) : null; - $this->max = $max !== null ? HTMLPurifier_Length::make($max) : null; - } - - public function validate($string, $config, $context) { - $string = $this->parseCDATA($string); - - // Optimizations - if ($string === '') return false; - if ($string === '0') return '0'; - if (strlen($string) === 1) return false; - - $length = HTMLPurifier_Length::make($string); - if (!$length->isValid()) return false; - - if ($this->min) { - $c = $length->compareTo($this->min); - if ($c === false) return false; - if ($c < 0) return false; - } - if ($this->max) { - $c = $length->compareTo($this->max); - if ($c === false) return false; - if ($c > 0) return false; - } - - return $length->toString(); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php deleted file mode 100644 index 4406868c0..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/ListStyle.php +++ /dev/null @@ -1,78 +0,0 @@ -getCSSDefinition(); - $this->info['list-style-type'] = $def->info['list-style-type']; - $this->info['list-style-position'] = $def->info['list-style-position']; - $this->info['list-style-image'] = $def->info['list-style-image']; - } - - public function validate($string, $config, $context) { - - // regular pre-processing - $string = $this->parseCDATA($string); - if ($string === '') return false; - - // assumes URI doesn't have spaces in it - $bits = explode(' ', strtolower($string)); // bits to process - - $caught = array(); - $caught['type'] = false; - $caught['position'] = false; - $caught['image'] = false; - - $i = 0; // number of catches - $none = false; - - foreach ($bits as $bit) { - if ($i >= 3) return; // optimization bit - if ($bit === '') continue; - foreach ($caught as $key => $status) { - if ($status !== false) continue; - $r = $this->info['list-style-' . $key]->validate($bit, $config, $context); - if ($r === false) continue; - if ($r === 'none') { - if ($none) continue; - else $none = true; - if ($key == 'image') continue; - } - $caught[$key] = $r; - $i++; - break; - } - } - - if (!$i) return false; - - $ret = array(); - - // construct type - if ($caught['type']) $ret[] = $caught['type']; - - // construct image - if ($caught['image']) $ret[] = $caught['image']; - - // construct position - if ($caught['position']) $ret[] = $caught['position']; - - if (empty($ret)) return false; - return implode(' ', $ret); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php deleted file mode 100644 index 4d62a40d7..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Multiple.php +++ /dev/null @@ -1,58 +0,0 @@ -single = $single; - $this->max = $max; - } - - public function validate($string, $config, $context) { - $string = $this->parseCDATA($string); - if ($string === '') return false; - $parts = explode(' ', $string); // parseCDATA replaced \r, \t and \n - $length = count($parts); - $final = ''; - for ($i = 0, $num = 0; $i < $length && $num < $this->max; $i++) { - if (ctype_space($parts[$i])) continue; - $result = $this->single->validate($parts[$i], $config, $context); - if ($result !== false) { - $final .= $result . ' '; - $num++; - } - } - if ($final === '') return false; - return rtrim($final); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php deleted file mode 100644 index 3f99e12ec..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Number.php +++ /dev/null @@ -1,69 +0,0 @@ -non_negative = $non_negative; - } - - /** - * @warning Some contexts do not pass $config, $context. These - * variables should not be used without checking HTMLPurifier_Length - */ - public function validate($number, $config, $context) { - - $number = $this->parseCDATA($number); - - if ($number === '') return false; - if ($number === '0') return '0'; - - $sign = ''; - switch ($number[0]) { - case '-': - if ($this->non_negative) return false; - $sign = '-'; - case '+': - $number = substr($number, 1); - } - - if (ctype_digit($number)) { - $number = ltrim($number, '0'); - return $number ? $sign . $number : '0'; - } - - // Period is the only non-numeric character allowed - if (strpos($number, '.') === false) return false; - - list($left, $right) = explode('.', $number, 2); - - if ($left === '' && $right === '') return false; - if ($left !== '' && !ctype_digit($left)) return false; - - $left = ltrim($left, '0'); - $right = rtrim($right, '0'); - - if ($right === '') { - return $left ? $sign . $left : '0'; - } elseif (!ctype_digit($right)) { - return false; - } - - return $sign . $left . '.' . $right; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php deleted file mode 100644 index c34b8fc3c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Percentage.php +++ /dev/null @@ -1,40 +0,0 @@ -number_def = new HTMLPurifier_AttrDef_CSS_Number($non_negative); - } - - public function validate($string, $config, $context) { - - $string = $this->parseCDATA($string); - - if ($string === '') return false; - $length = strlen($string); - if ($length === 1) return false; - if ($string[$length - 1] !== '%') return false; - - $number = substr($string, 0, $length - 1); - $number = $this->number_def->validate($number, $config, $context); - - if ($number === false) return false; - return "$number%"; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php deleted file mode 100644 index 772c922d8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/TextDecoration.php +++ /dev/null @@ -1,38 +0,0 @@ - true, - 'overline' => true, - 'underline' => true, - ); - - $string = strtolower($this->parseCDATA($string)); - - if ($string === 'none') return $string; - - $parts = explode(' ', $string); - $final = ''; - foreach ($parts as $part) { - if (isset($allowed_values[$part])) { - $final .= $part . ' '; - } - } - $final = rtrim($final); - if ($final === '') return false; - return $final; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php deleted file mode 100644 index 435d7930b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php +++ /dev/null @@ -1,56 +0,0 @@ -parseCDATA($uri_string); - if (strpos($uri_string, 'url(') !== 0) return false; - $uri_string = substr($uri_string, 4); - $new_length = strlen($uri_string) - 1; - if ($uri_string[$new_length] != ')') return false; - $uri = trim(substr($uri_string, 0, $new_length)); - - if (!empty($uri) && ($uri[0] == "'" || $uri[0] == '"')) { - $quote = $uri[0]; - $new_length = strlen($uri) - 1; - if ($uri[$new_length] !== $quote) return false; - $uri = substr($uri, 1, $new_length - 1); - } - - $keys = array( '(', ')', ',', ' ', '"', "'"); - $values = array('\\(', '\\)', '\\,', '\\ ', '\\"', "\\'"); - $uri = str_replace($values, $keys, $uri); - - $result = parent::validate($uri, $config, $context); - - if ($result === false) return false; - - // escape necessary characters according to CSS spec - // except for the comma, none of these should appear in the - // URI at all - $result = str_replace($keys, $values, $result); - - return "url($result)"; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php deleted file mode 100644 index 5d603ebcc..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Enum.php +++ /dev/null @@ -1,65 +0,0 @@ -valid_values = array_flip($valid_values); - $this->case_sensitive = $case_sensitive; - } - - public function validate($string, $config, $context) { - $string = trim($string); - if (!$this->case_sensitive) { - // we may want to do full case-insensitive libraries - $string = ctype_lower($string) ? $string : strtolower($string); - } - $result = isset($this->valid_values[$string]); - - return $result ? $string : false; - } - - /** - * @param $string In form of comma-delimited list of case-insensitive - * valid values. Example: "foo,bar,baz". Prepend "s:" to make - * case sensitive - */ - public function make($string) { - if (strlen($string) > 2 && $string[0] == 's' && $string[1] == ':') { - $string = substr($string, 2); - $sensitive = true; - } else { - $sensitive = false; - } - $values = explode(',', $string); - return new HTMLPurifier_AttrDef_Enum($values, $sensitive); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php deleted file mode 100644 index e06987eb8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Bool.php +++ /dev/null @@ -1,28 +0,0 @@ -name = $name;} - - public function validate($string, $config, $context) { - if (empty($string)) return false; - return $this->name; - } - - /** - * @param $string Name of attribute - */ - public function make($string) { - return new HTMLPurifier_AttrDef_HTML_Bool($string); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php deleted file mode 100644 index 370068d97..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Class.php +++ /dev/null @@ -1,34 +0,0 @@ -getDefinition('HTML')->doctype->name; - if ($name == "XHTML 1.1" || $name == "XHTML 2.0") { - return parent::split($string, $config, $context); - } else { - return preg_split('/\s+/', $string); - } - } - protected function filter($tokens, $config, $context) { - $allowed = $config->get('Attr.AllowedClasses'); - $forbidden = $config->get('Attr.ForbiddenClasses'); - $ret = array(); - foreach ($tokens as $token) { - if ( - ($allowed === null || isset($allowed[$token])) && - !isset($forbidden[$token]) && - // We need this O(n) check because of PHP's array - // implementation that casts -0 to 0. - !in_array($token, $ret, true) - ) { - $ret[] = $token; - } - } - return $ret; - } -} diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php deleted file mode 100644 index d01e20454..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Color.php +++ /dev/null @@ -1,32 +0,0 @@ -get('Core.ColorKeywords'); - - $string = trim($string); - - if (empty($string)) return false; - if (isset($colors[$string])) return $colors[$string]; - if ($string[0] === '#') $hex = substr($string, 1); - else $hex = $string; - - $length = strlen($hex); - if ($length !== 3 && $length !== 6) return false; - if (!ctype_xdigit($hex)) return false; - if ($length === 3) $hex = $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2]; - - return "#$hex"; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php deleted file mode 100644 index ae6ea7c01..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/FrameTarget.php +++ /dev/null @@ -1,21 +0,0 @@ -valid_values === false) $this->valid_values = $config->get('Attr.AllowedFrameTargets'); - return parent::validate($string, $config, $context); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php deleted file mode 100644 index 81d03762d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php +++ /dev/null @@ -1,70 +0,0 @@ -get('Attr.EnableID')) return false; - - $id = trim($id); // trim it first - - if ($id === '') return false; - - $prefix = $config->get('Attr.IDPrefix'); - if ($prefix !== '') { - $prefix .= $config->get('Attr.IDPrefixLocal'); - // prevent re-appending the prefix - if (strpos($id, $prefix) !== 0) $id = $prefix . $id; - } elseif ($config->get('Attr.IDPrefixLocal') !== '') { - trigger_error('%Attr.IDPrefixLocal cannot be used unless '. - '%Attr.IDPrefix is set', E_USER_WARNING); - } - - //if (!$this->ref) { - $id_accumulator =& $context->get('IDAccumulator'); - if (isset($id_accumulator->ids[$id])) return false; - //} - - // we purposely avoid using regex, hopefully this is faster - - if (ctype_alpha($id)) { - $result = true; - } else { - if (!ctype_alpha(@$id[0])) return false; - $trim = trim( // primitive style of regexps, I suppose - $id, - 'A..Za..z0..9:-._' - ); - $result = ($trim === ''); - } - - $regexp = $config->get('Attr.IDBlacklistRegexp'); - if ($regexp && preg_match($regexp, $id)) { - return false; - } - - if (/*!$this->ref && */$result) $id_accumulator->add($id); - - // if no change was made to the ID, return the result - // else, return the new id if stripping whitespace made it - // valid, or return false. - return $result ? $id : false; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php deleted file mode 100644 index a242f9c23..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Length.php +++ /dev/null @@ -1,41 +0,0 @@ - 100) return '100%'; - - return ((string) $points) . '%'; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php deleted file mode 100644 index 76d25ed08..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/LinkTypes.php +++ /dev/null @@ -1,53 +0,0 @@ - 'AllowedRel', - 'rev' => 'AllowedRev' - ); - if (!isset($configLookup[$name])) { - trigger_error('Unrecognized attribute name for link '. - 'relationship.', E_USER_ERROR); - return; - } - $this->name = $configLookup[$name]; - } - - public function validate($string, $config, $context) { - - $allowed = $config->get('Attr.' . $this->name); - if (empty($allowed)) return false; - - $string = $this->parseCDATA($string); - $parts = explode(' ', $string); - - // lookup to prevent duplicates - $ret_lookup = array(); - foreach ($parts as $part) { - $part = strtolower(trim($part)); - if (!isset($allowed[$part])) continue; - $ret_lookup[$part] = true; - } - - if (empty($ret_lookup)) return false; - $string = implode(' ', array_keys($ret_lookup)); - - return $string; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php deleted file mode 100644 index c72fc76e4..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/MultiLength.php +++ /dev/null @@ -1,41 +0,0 @@ -split($string, $config, $context); - $tokens = $this->filter($tokens, $config, $context); - if (empty($tokens)) return false; - return implode(' ', $tokens); - - } - - /** - * Splits a space separated list of tokens into its constituent parts. - */ - protected function split($string, $config, $context) { - // OPTIMIZABLE! - // do the preg_match, capture all subpatterns for reformulation - - // we don't support U+00A1 and up codepoints or - // escaping because I don't know how to do that with regexps - // and plus it would complicate optimization efforts (you never - // see that anyway). - $pattern = '/(?:(?<=\s)|\A)'. // look behind for space or string start - '((?:--|-?[A-Za-z_])[A-Za-z_\-0-9]*)'. - '(?:(?=\s)|\z)/'; // look ahead for space or string end - preg_match_all($pattern, $string, $matches); - return $matches[1]; - } - - /** - * Template method for removing certain tokens based on arbitrary criteria. - * @note If we wanted to be really functional, we'd do an array_filter - * with a callback. But... we're not. - */ - protected function filter($tokens, $config, $context) { - return $tokens; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php deleted file mode 100644 index 4cb2c1b85..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/Pixels.php +++ /dev/null @@ -1,48 +0,0 @@ -max = $max; - } - - public function validate($string, $config, $context) { - - $string = trim($string); - if ($string === '0') return $string; - if ($string === '') return false; - $length = strlen($string); - if (substr($string, $length - 2) == 'px') { - $string = substr($string, 0, $length - 2); - } - if (!is_numeric($string)) return false; - $int = (int) $string; - - if ($int < 0) return '0'; - - // upper-bound value, extremely high values can - // crash operating systems, see - // WARNING, above link WILL crash you if you're using Windows - - if ($this->max !== null && $int > $this->max) return (string) $this->max; - - return (string) $int; - - } - - public function make($string) { - if ($string === '') $max = null; - else $max = (int) $string; - $class = get_class($this); - return new $class($max); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php deleted file mode 100644 index d59738d2a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Integer.php +++ /dev/null @@ -1,73 +0,0 @@ -negative = $negative; - $this->zero = $zero; - $this->positive = $positive; - } - - public function validate($integer, $config, $context) { - - $integer = $this->parseCDATA($integer); - if ($integer === '') return false; - - // we could possibly simply typecast it to integer, but there are - // certain fringe cases that must not return an integer. - - // clip leading sign - if ( $this->negative && $integer[0] === '-' ) { - $digits = substr($integer, 1); - if ($digits === '0') $integer = '0'; // rm minus sign for zero - } elseif( $this->positive && $integer[0] === '+' ) { - $digits = $integer = substr($integer, 1); // rm unnecessary plus - } else { - $digits = $integer; - } - - // test if it's numeric - if (!ctype_digit($digits)) return false; - - // perform scope tests - if (!$this->zero && $integer == 0) return false; - if (!$this->positive && $integer > 0) return false; - if (!$this->negative && $integer < 0) return false; - - return $integer; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php deleted file mode 100644 index 10e6da56d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Lang.php +++ /dev/null @@ -1,73 +0,0 @@ - 8 || !ctype_alnum($subtags[1])) { - return $new_string; - } - if (!ctype_lower($subtags[1])) $subtags[1] = strtolower($subtags[1]); - - $new_string .= '-' . $subtags[1]; - if ($num_subtags == 2) return $new_string; - - // process all other subtags, index 2 and up - for ($i = 2; $i < $num_subtags; $i++) { - $length = strlen($subtags[$i]); - if ($length == 0 || $length > 8 || !ctype_alnum($subtags[$i])) { - return $new_string; - } - if (!ctype_lower($subtags[$i])) { - $subtags[$i] = strtolower($subtags[$i]); - } - $new_string .= '-' . $subtags[$i]; - } - - return $new_string; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php deleted file mode 100644 index c9e3ed193..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Switch.php +++ /dev/null @@ -1,34 +0,0 @@ -tag = $tag; - $this->withTag = $with_tag; - $this->withoutTag = $without_tag; - } - - public function validate($string, $config, $context) { - $token = $context->get('CurrentToken', true); - if (!$token || $token->name !== $this->tag) { - return $this->withoutTag->validate($string, $config, $context); - } else { - return $this->withTag->validate($string, $config, $context); - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php deleted file mode 100644 index c6216cc53..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/Text.php +++ /dev/null @@ -1,15 +0,0 @@ -parseCDATA($string); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php deleted file mode 100644 index 01a6d83e9..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI.php +++ /dev/null @@ -1,77 +0,0 @@ -parser = new HTMLPurifier_URIParser(); - $this->embedsResource = (bool) $embeds_resource; - } - - public function make($string) { - $embeds = (bool) $string; - return new HTMLPurifier_AttrDef_URI($embeds); - } - - public function validate($uri, $config, $context) { - - if ($config->get('URI.Disable')) return false; - - $uri = $this->parseCDATA($uri); - - // parse the URI - $uri = $this->parser->parse($uri); - if ($uri === false) return false; - - // add embedded flag to context for validators - $context->register('EmbeddedURI', $this->embedsResource); - - $ok = false; - do { - - // generic validation - $result = $uri->validate($config, $context); - if (!$result) break; - - // chained filtering - $uri_def = $config->getDefinition('URI'); - $result = $uri_def->filter($uri, $config, $context); - if (!$result) break; - - // scheme-specific validation - $scheme_obj = $uri->getSchemeObj($config, $context); - if (!$scheme_obj) break; - if ($this->embedsResource && !$scheme_obj->browsable) break; - $result = $scheme_obj->validate($uri, $config, $context); - if (!$result) break; - - // Post chained filtering - $result = $uri_def->postFilter($uri, $config, $context); - if (!$result) break; - - // survived gauntlet - $ok = true; - - } while (false); - - $context->destroy('EmbeddedURI'); - if (!$ok) return false; - - // back to string - return $uri->toString(); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php deleted file mode 100644 index bfee9d166..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Email.php +++ /dev/null @@ -1,17 +0,0 @@ -" - // that needs more percent encoding to be done - if ($string == '') return false; - $string = trim($string); - $result = preg_match('/^[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i', $string); - return $result ? $string : false; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php deleted file mode 100644 index 2156c10c6..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php +++ /dev/null @@ -1,62 +0,0 @@ -ipv4 = new HTMLPurifier_AttrDef_URI_IPv4(); - $this->ipv6 = new HTMLPurifier_AttrDef_URI_IPv6(); - } - - public function validate($string, $config, $context) { - $length = strlen($string); - if ($string === '') return ''; - if ($length > 1 && $string[0] === '[' && $string[$length-1] === ']') { - //IPv6 - $ip = substr($string, 1, $length - 2); - $valid = $this->ipv6->validate($ip, $config, $context); - if ($valid === false) return false; - return '['. $valid . ']'; - } - - // need to do checks on unusual encodings too - $ipv4 = $this->ipv4->validate($string, $config, $context); - if ($ipv4 !== false) return $ipv4; - - // A regular domain name. - - // This breaks I18N domain names, but we don't have proper IRI support, - // so force users to insert Punycode. If there's complaining we'll - // try to fix things into an international friendly form. - - // The productions describing this are: - $a = '[a-z]'; // alpha - $an = '[a-z0-9]'; // alphanum - $and = '[a-z0-9-]'; // alphanum | "-" - // domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum - $domainlabel = "$an($and*$an)?"; - // toplabel = alpha | alpha *( alphanum | "-" ) alphanum - $toplabel = "$a($and*$an)?"; - // hostname = *( domainlabel "." ) toplabel [ "." ] - $match = preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string); - if (!$match) return false; - - return $string; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php deleted file mode 100644 index ec4cf591b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv4.php +++ /dev/null @@ -1,39 +0,0 @@ -ip4) $this->_loadRegex(); - - if (preg_match('#^' . $this->ip4 . '$#s', $aIP)) - { - return $aIP; - } - - return false; - - } - - /** - * Lazy load function to prevent regex from being stuffed in - * cache. - */ - protected function _loadRegex() { - $oct = '(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])'; // 0-255 - $this->ip4 = "(?:{$oct}\\.{$oct}\\.{$oct}\\.{$oct})"; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php deleted file mode 100644 index 9454e9be5..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/IPv6.php +++ /dev/null @@ -1,99 +0,0 @@ -ip4) $this->_loadRegex(); - - $original = $aIP; - - $hex = '[0-9a-fA-F]'; - $blk = '(?:' . $hex . '{1,4})'; - $pre = '(?:/(?:12[0-8]|1[0-1][0-9]|[1-9][0-9]|[0-9]))'; // /0 - /128 - - // prefix check - if (strpos($aIP, '/') !== false) - { - if (preg_match('#' . $pre . '$#s', $aIP, $find)) - { - $aIP = substr($aIP, 0, 0-strlen($find[0])); - unset($find); - } - else - { - return false; - } - } - - // IPv4-compatiblity check - if (preg_match('#(?<=:'.')' . $this->ip4 . '$#s', $aIP, $find)) - { - $aIP = substr($aIP, 0, 0-strlen($find[0])); - $ip = explode('.', $find[0]); - $ip = array_map('dechex', $ip); - $aIP .= $ip[0] . $ip[1] . ':' . $ip[2] . $ip[3]; - unset($find, $ip); - } - - // compression check - $aIP = explode('::', $aIP); - $c = count($aIP); - if ($c > 2) - { - return false; - } - elseif ($c == 2) - { - list($first, $second) = $aIP; - $first = explode(':', $first); - $second = explode(':', $second); - - if (count($first) + count($second) > 8) - { - return false; - } - - while(count($first) < 8) - { - array_push($first, '0'); - } - - array_splice($first, 8 - count($second), 8, $second); - $aIP = $first; - unset($first,$second); - } - else - { - $aIP = explode(':', $aIP[0]); - } - $c = count($aIP); - - if ($c != 8) - { - return false; - } - - // All the pieces should be 16-bit hex strings. Are they? - foreach ($aIP as $piece) - { - if (!preg_match('#^[0-9a-fA-F]{4}$#s', sprintf('%04s', $piece))) - { - return false; - } - } - - return $original; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform.php deleted file mode 100644 index e61d3e01b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform.php +++ /dev/null @@ -1,56 +0,0 @@ -confiscateAttr($attr, 'background'); - // some validation should happen here - - $this->prependCSS($attr, "background-image:url($background);"); - - return $attr; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php deleted file mode 100644 index 4d1a05665..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BdoDir.php +++ /dev/null @@ -1,19 +0,0 @@ -get('Attr.DefaultTextDir'); - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php deleted file mode 100644 index ad3916bb9..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BgColor.php +++ /dev/null @@ -1,23 +0,0 @@ -confiscateAttr($attr, 'bgcolor'); - // some validation should happen here - - $this->prependCSS($attr, "background-color:$bgcolor;"); - - return $attr; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php deleted file mode 100644 index 51159b671..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/BoolToCSS.php +++ /dev/null @@ -1,36 +0,0 @@ -attr = $attr; - $this->css = $css; - } - - public function transform($attr, $config, $context) { - if (!isset($attr[$this->attr])) return $attr; - unset($attr[$this->attr]); - $this->prependCSS($attr, $this->css); - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php deleted file mode 100644 index 476b0b079..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Border.php +++ /dev/null @@ -1,18 +0,0 @@ -confiscateAttr($attr, 'border'); - // some validation should happen here - $this->prependCSS($attr, "border:{$border_width}px solid;"); - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php deleted file mode 100644 index 2a5b4514a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/EnumToCSS.php +++ /dev/null @@ -1,58 +0,0 @@ -attr = $attr; - $this->enumToCSS = $enum_to_css; - $this->caseSensitive = (bool) $case_sensitive; - } - - public function transform($attr, $config, $context) { - - if (!isset($attr[$this->attr])) return $attr; - - $value = trim($attr[$this->attr]); - unset($attr[$this->attr]); - - if (!$this->caseSensitive) $value = strtolower($value); - - if (!isset($this->enumToCSS[$value])) { - return $attr; - } - - $this->prependCSS($attr, $this->enumToCSS[$value]); - - return $attr; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php deleted file mode 100644 index a219479a0..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php +++ /dev/null @@ -1,42 +0,0 @@ -get('Core.RemoveInvalidImg')) return $attr; - $attr['src'] = $config->get('Attr.DefaultInvalidImage'); - $src = false; - } - - if (!isset($attr['alt'])) { - if ($src) { - $alt = $config->get('Attr.DefaultImageAlt'); - if ($alt === null) { - $attr['alt'] = basename($attr['src']); - } else { - $attr['alt'] = $alt; - } - } else { - $attr['alt'] = $config->get('Attr.DefaultInvalidImageAlt'); - } - } - - return $attr; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php deleted file mode 100644 index fd84c10c3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgSpace.php +++ /dev/null @@ -1,44 +0,0 @@ - array('left', 'right'), - 'vspace' => array('top', 'bottom') - ); - - public function __construct($attr) { - $this->attr = $attr; - if (!isset($this->css[$attr])) { - trigger_error(htmlspecialchars($attr) . ' is not valid space attribute'); - } - } - - public function transform($attr, $config, $context) { - - if (!isset($attr[$this->attr])) return $attr; - - $width = $this->confiscateAttr($attr, $this->attr); - // some validation could happen here - - if (!isset($this->css[$this->attr])) return $attr; - - $style = ''; - foreach ($this->css[$this->attr] as $suffix) { - $property = "margin-$suffix"; - $style .= "$property:{$width}px;"; - } - - $this->prependCSS($attr, $style); - - return $attr; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php deleted file mode 100644 index 16829552d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Input.php +++ /dev/null @@ -1,40 +0,0 @@ -pixels = new HTMLPurifier_AttrDef_HTML_Pixels(); - } - - public function transform($attr, $config, $context) { - if (!isset($attr['type'])) $t = 'text'; - else $t = strtolower($attr['type']); - if (isset($attr['checked']) && $t !== 'radio' && $t !== 'checkbox') { - unset($attr['checked']); - } - if (isset($attr['maxlength']) && $t !== 'text' && $t !== 'password') { - unset($attr['maxlength']); - } - if (isset($attr['size']) && $t !== 'text' && $t !== 'password') { - $result = $this->pixels->validate($attr['size'], $config, $context); - if ($result === false) unset($attr['size']); - else $attr['size'] = $result; - } - if (isset($attr['src']) && $t !== 'image') { - unset($attr['src']); - } - if (!isset($attr['value']) && ($t === 'radio' || $t === 'checkbox')) { - $attr['value'] = ''; - } - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php deleted file mode 100644 index 5869e7f82..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Lang.php +++ /dev/null @@ -1,28 +0,0 @@ -name = $name; - $this->cssName = $css_name ? $css_name : $name; - } - - public function transform($attr, $config, $context) { - if (!isset($attr[$this->name])) return $attr; - $length = $this->confiscateAttr($attr, $this->name); - if(ctype_digit($length)) $length .= 'px'; - $this->prependCSS($attr, $this->cssName . ":$length;"); - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php deleted file mode 100644 index 15315bc73..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Name.php +++ /dev/null @@ -1,21 +0,0 @@ -get('HTML.Attr.Name.UseCDATA')) return $attr; - if (!isset($attr['name'])) return $attr; - $id = $this->confiscateAttr($attr, 'name'); - if ( isset($attr['id'])) return $attr; - $attr['id'] = $id; - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php deleted file mode 100644 index a95638c14..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/NameSync.php +++ /dev/null @@ -1,27 +0,0 @@ -idDef = new HTMLPurifier_AttrDef_HTML_ID(); - } - - public function transform($attr, $config, $context) { - if (!isset($attr['name'])) return $attr; - $name = $attr['name']; - if (isset($attr['id']) && $attr['id'] === $name) return $attr; - $result = $this->idDef->validate($name, $config, $context); - if ($result === false) unset($attr['name']); - else $attr['name'] = $result; - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php deleted file mode 100644 index 4da449981..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/SafeEmbed.php +++ /dev/null @@ -1,15 +0,0 @@ -uri = new HTMLPurifier_AttrDef_URI(true); // embedded - } - - public function transform($attr, $config, $context) { - // If we add support for other objects, we'll need to alter the - // transforms. - switch ($attr['name']) { - // application/x-shockwave-flash - // Keep this synchronized with Injector/SafeObject.php - case 'allowScriptAccess': - $attr['value'] = 'never'; - break; - case 'allowNetworking': - $attr['value'] = 'internal'; - break; - case 'wmode': - $attr['value'] = 'window'; - break; - case 'movie': - $attr['value'] = $this->uri->validate($attr['value'], $config, $context); - break; - // add other cases to support other param name/value pairs - default: - $attr['name'] = $attr['value'] = null; - } - return $attr; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php deleted file mode 100644 index 4499050a2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ScriptRequired.php +++ /dev/null @@ -1,16 +0,0 @@ - - */ -class HTMLPurifier_AttrTransform_ScriptRequired extends HTMLPurifier_AttrTransform -{ - public function transform($attr, $config, $context) { - if (!isset($attr['type'])) { - $attr['type'] = 'text/javascript'; - } - return $attr; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php deleted file mode 100644 index 81ac3488b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/Textarea.php +++ /dev/null @@ -1,18 +0,0 @@ - - */ -class HTMLPurifier_AttrTransform_Textarea extends HTMLPurifier_AttrTransform -{ - - public function transform($attr, $config, $context) { - // Calculated from Firefox - if (!isset($attr['cols'])) $attr['cols'] = '22'; - if (!isset($attr['rows'])) $attr['rows'] = '3'; - return $attr; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTypes.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTypes.php deleted file mode 100644 index fc2ea4e58..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrTypes.php +++ /dev/null @@ -1,77 +0,0 @@ -info['Enum'] = new HTMLPurifier_AttrDef_Enum(); - $this->info['Bool'] = new HTMLPurifier_AttrDef_HTML_Bool(); - - $this->info['CDATA'] = new HTMLPurifier_AttrDef_Text(); - $this->info['ID'] = new HTMLPurifier_AttrDef_HTML_ID(); - $this->info['Length'] = new HTMLPurifier_AttrDef_HTML_Length(); - $this->info['MultiLength'] = new HTMLPurifier_AttrDef_HTML_MultiLength(); - $this->info['NMTOKENS'] = new HTMLPurifier_AttrDef_HTML_Nmtokens(); - $this->info['Pixels'] = new HTMLPurifier_AttrDef_HTML_Pixels(); - $this->info['Text'] = new HTMLPurifier_AttrDef_Text(); - $this->info['URI'] = new HTMLPurifier_AttrDef_URI(); - $this->info['LanguageCode'] = new HTMLPurifier_AttrDef_Lang(); - $this->info['Color'] = new HTMLPurifier_AttrDef_HTML_Color(); - - // unimplemented aliases - $this->info['ContentType'] = new HTMLPurifier_AttrDef_Text(); - $this->info['ContentTypes'] = new HTMLPurifier_AttrDef_Text(); - $this->info['Charsets'] = new HTMLPurifier_AttrDef_Text(); - $this->info['Character'] = new HTMLPurifier_AttrDef_Text(); - - // "proprietary" types - $this->info['Class'] = new HTMLPurifier_AttrDef_HTML_Class(); - - // number is really a positive integer (one or more digits) - // FIXME: ^^ not always, see start and value of list items - $this->info['Number'] = new HTMLPurifier_AttrDef_Integer(false, false, true); - } - - /** - * Retrieves a type - * @param $type String type name - * @return Object AttrDef for type - */ - public function get($type) { - - // determine if there is any extra info tacked on - if (strpos($type, '#') !== false) list($type, $string) = explode('#', $type, 2); - else $string = ''; - - if (!isset($this->info[$type])) { - trigger_error('Cannot retrieve undefined attribute type ' . $type, E_USER_ERROR); - return; - } - - return $this->info[$type]->make($string); - - } - - /** - * Sets a new implementation for a type - * @param $type String type name - * @param $impl Object AttrDef for type - */ - public function set($type, $impl) { - $this->info[$type] = $impl; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrValidator.php b/extlib/HTMLPurifier/HTMLPurifier/AttrValidator.php deleted file mode 100644 index 829a0f8f2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/AttrValidator.php +++ /dev/null @@ -1,162 +0,0 @@ -getHTMLDefinition(); - $e =& $context->get('ErrorCollector', true); - - // initialize IDAccumulator if necessary - $ok =& $context->get('IDAccumulator', true); - if (!$ok) { - $id_accumulator = HTMLPurifier_IDAccumulator::build($config, $context); - $context->register('IDAccumulator', $id_accumulator); - } - - // initialize CurrentToken if necessary - $current_token =& $context->get('CurrentToken', true); - if (!$current_token) $context->register('CurrentToken', $token); - - if ( - !$token instanceof HTMLPurifier_Token_Start && - !$token instanceof HTMLPurifier_Token_Empty - ) return $token; - - // create alias to global definition array, see also $defs - // DEFINITION CALL - $d_defs = $definition->info_global_attr; - - // don't update token until the very end, to ensure an atomic update - $attr = $token->attr; - - // do global transformations (pre) - // nothing currently utilizes this - foreach ($definition->info_attr_transform_pre as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - - // do local transformations only applicable to this element (pre) - // ex.

to

- foreach ($definition->info[$token->name]->attr_transform_pre as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - - // create alias to this element's attribute definition array, see - // also $d_defs (global attribute definition array) - // DEFINITION CALL - $defs = $definition->info[$token->name]->attr; - - $attr_key = false; - $context->register('CurrentAttr', $attr_key); - - // iterate through all the attribute keypairs - // Watch out for name collisions: $key has previously been used - foreach ($attr as $attr_key => $value) { - - // call the definition - if ( isset($defs[$attr_key]) ) { - // there is a local definition defined - if ($defs[$attr_key] === false) { - // We've explicitly been told not to allow this element. - // This is usually when there's a global definition - // that must be overridden. - // Theoretically speaking, we could have a - // AttrDef_DenyAll, but this is faster! - $result = false; - } else { - // validate according to the element's definition - $result = $defs[$attr_key]->validate( - $value, $config, $context - ); - } - } elseif ( isset($d_defs[$attr_key]) ) { - // there is a global definition defined, validate according - // to the global definition - $result = $d_defs[$attr_key]->validate( - $value, $config, $context - ); - } else { - // system never heard of the attribute? DELETE! - $result = false; - } - - // put the results into effect - if ($result === false || $result === null) { - // this is a generic error message that should replaced - // with more specific ones when possible - if ($e) $e->send(E_ERROR, 'AttrValidator: Attribute removed'); - - // remove the attribute - unset($attr[$attr_key]); - } elseif (is_string($result)) { - // generally, if a substitution is happening, there - // was some sort of implicit correction going on. We'll - // delegate it to the attribute classes to say exactly what. - - // simple substitution - $attr[$attr_key] = $result; - } else { - // nothing happens - } - - // we'd also want slightly more complicated substitution - // involving an array as the return value, - // although we're not sure how colliding attributes would - // resolve (certain ones would be completely overriden, - // others would prepend themselves). - } - - $context->destroy('CurrentAttr'); - - // post transforms - - // global (error reporting untested) - foreach ($definition->info_attr_transform_post as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - - // local (error reporting untested) - foreach ($definition->info[$token->name]->attr_transform_post as $transform) { - $attr = $transform->transform($o = $attr, $config, $context); - if ($e) { - if ($attr != $o) $e->send(E_NOTICE, 'AttrValidator: Attributes transformed', $o, $attr); - } - } - - $token->attr = $attr; - - // destroy CurrentToken if we made it ourselves - if (!$current_token) $context->destroy('CurrentToken'); - - } - - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Bootstrap.php b/extlib/HTMLPurifier/HTMLPurifier/Bootstrap.php deleted file mode 100644 index 559f61a23..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Bootstrap.php +++ /dev/null @@ -1,98 +0,0 @@ - -if (!defined('PHP_EOL')) { - switch (strtoupper(substr(PHP_OS, 0, 3))) { - case 'WIN': - define('PHP_EOL', "\r\n"); - break; - case 'DAR': - define('PHP_EOL', "\r"); - break; - default: - define('PHP_EOL', "\n"); - } -} - -/** - * Bootstrap class that contains meta-functionality for HTML Purifier such as - * the autoload function. - * - * @note - * This class may be used without any other files from HTML Purifier. - */ -class HTMLPurifier_Bootstrap -{ - - /** - * Autoload function for HTML Purifier - * @param $class Class to load - */ - public static function autoload($class) { - $file = HTMLPurifier_Bootstrap::getPath($class); - if (!$file) return false; - require HTMLPURIFIER_PREFIX . '/' . $file; - return true; - } - - /** - * Returns the path for a specific class. - */ - public static function getPath($class) { - if (strncmp('HTMLPurifier', $class, 12) !== 0) return false; - // Custom implementations - if (strncmp('HTMLPurifier_Language_', $class, 22) === 0) { - $code = str_replace('_', '-', substr($class, 22)); - $file = 'HTMLPurifier/Language/classes/' . $code . '.php'; - } else { - $file = str_replace('_', '/', $class) . '.php'; - } - if (!file_exists(HTMLPURIFIER_PREFIX . '/' . $file)) return false; - return $file; - } - - /** - * "Pre-registers" our autoloader on the SPL stack. - */ - public static function registerAutoload() { - $autoload = array('HTMLPurifier_Bootstrap', 'autoload'); - if ( ($funcs = spl_autoload_functions()) === false ) { - spl_autoload_register($autoload); - } elseif (function_exists('spl_autoload_unregister')) { - $compat = version_compare(PHP_VERSION, '5.1.2', '<=') && - version_compare(PHP_VERSION, '5.1.0', '>='); - foreach ($funcs as $func) { - if (is_array($func)) { - // :TRICKY: There are some compatibility issues and some - // places where we need to error out - $reflector = new ReflectionMethod($func[0], $func[1]); - if (!$reflector->isStatic()) { - throw new Exception(' - HTML Purifier autoloader registrar is not compatible - with non-static object methods due to PHP Bug #44144; - Please do not use HTMLPurifier.autoload.php (or any - file that includes this file); instead, place the code: - spl_autoload_register(array(\'HTMLPurifier_Bootstrap\', \'autoload\')) - after your own autoloaders. - '); - } - // Suprisingly, spl_autoload_register supports the - // Class::staticMethod callback format, although call_user_func doesn't - if ($compat) $func = implode('::', $func); - } - spl_autoload_unregister($func); - } - spl_autoload_register($autoload); - foreach ($funcs as $func) spl_autoload_register($func); - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/CSSDefinition.php b/extlib/HTMLPurifier/HTMLPurifier/CSSDefinition.php deleted file mode 100644 index 6a2e6f56d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/CSSDefinition.php +++ /dev/null @@ -1,292 +0,0 @@ -info['text-align'] = new HTMLPurifier_AttrDef_Enum( - array('left', 'right', 'center', 'justify'), false); - - $border_style = - $this->info['border-bottom-style'] = - $this->info['border-right-style'] = - $this->info['border-left-style'] = - $this->info['border-top-style'] = new HTMLPurifier_AttrDef_Enum( - array('none', 'hidden', 'dotted', 'dashed', 'solid', 'double', - 'groove', 'ridge', 'inset', 'outset'), false); - - $this->info['border-style'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_style); - - $this->info['clear'] = new HTMLPurifier_AttrDef_Enum( - array('none', 'left', 'right', 'both'), false); - $this->info['float'] = new HTMLPurifier_AttrDef_Enum( - array('none', 'left', 'right'), false); - $this->info['font-style'] = new HTMLPurifier_AttrDef_Enum( - array('normal', 'italic', 'oblique'), false); - $this->info['font-variant'] = new HTMLPurifier_AttrDef_Enum( - array('normal', 'small-caps'), false); - - $uri_or_none = new HTMLPurifier_AttrDef_CSS_Composite( - array( - new HTMLPurifier_AttrDef_Enum(array('none')), - new HTMLPurifier_AttrDef_CSS_URI() - ) - ); - - $this->info['list-style-position'] = new HTMLPurifier_AttrDef_Enum( - array('inside', 'outside'), false); - $this->info['list-style-type'] = new HTMLPurifier_AttrDef_Enum( - array('disc', 'circle', 'square', 'decimal', 'lower-roman', - 'upper-roman', 'lower-alpha', 'upper-alpha', 'none'), false); - $this->info['list-style-image'] = $uri_or_none; - - $this->info['list-style'] = new HTMLPurifier_AttrDef_CSS_ListStyle($config); - - $this->info['text-transform'] = new HTMLPurifier_AttrDef_Enum( - array('capitalize', 'uppercase', 'lowercase', 'none'), false); - $this->info['color'] = new HTMLPurifier_AttrDef_CSS_Color(); - - $this->info['background-image'] = $uri_or_none; - $this->info['background-repeat'] = new HTMLPurifier_AttrDef_Enum( - array('repeat', 'repeat-x', 'repeat-y', 'no-repeat') - ); - $this->info['background-attachment'] = new HTMLPurifier_AttrDef_Enum( - array('scroll', 'fixed') - ); - $this->info['background-position'] = new HTMLPurifier_AttrDef_CSS_BackgroundPosition(); - - $border_color = - $this->info['border-top-color'] = - $this->info['border-bottom-color'] = - $this->info['border-left-color'] = - $this->info['border-right-color'] = - $this->info['background-color'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('transparent')), - new HTMLPurifier_AttrDef_CSS_Color() - )); - - $this->info['background'] = new HTMLPurifier_AttrDef_CSS_Background($config); - - $this->info['border-color'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_color); - - $border_width = - $this->info['border-top-width'] = - $this->info['border-bottom-width'] = - $this->info['border-left-width'] = - $this->info['border-right-width'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('thin', 'medium', 'thick')), - new HTMLPurifier_AttrDef_CSS_Length('0') //disallow negative - )); - - $this->info['border-width'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_width); - - $this->info['letter-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('normal')), - new HTMLPurifier_AttrDef_CSS_Length() - )); - - $this->info['word-spacing'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('normal')), - new HTMLPurifier_AttrDef_CSS_Length() - )); - - $this->info['font-size'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('xx-small', 'x-small', - 'small', 'medium', 'large', 'x-large', 'xx-large', - 'larger', 'smaller')), - new HTMLPurifier_AttrDef_CSS_Percentage(), - new HTMLPurifier_AttrDef_CSS_Length() - )); - - $this->info['line-height'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('normal')), - new HTMLPurifier_AttrDef_CSS_Number(true), // no negatives - new HTMLPurifier_AttrDef_CSS_Length('0'), - new HTMLPurifier_AttrDef_CSS_Percentage(true) - )); - - $margin = - $this->info['margin-top'] = - $this->info['margin-bottom'] = - $this->info['margin-left'] = - $this->info['margin-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage(), - new HTMLPurifier_AttrDef_Enum(array('auto')) - )); - - $this->info['margin'] = new HTMLPurifier_AttrDef_CSS_Multiple($margin); - - // non-negative - $padding = - $this->info['padding-top'] = - $this->info['padding-bottom'] = - $this->info['padding-left'] = - $this->info['padding-right'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length('0'), - new HTMLPurifier_AttrDef_CSS_Percentage(true) - )); - - $this->info['padding'] = new HTMLPurifier_AttrDef_CSS_Multiple($padding); - - $this->info['text-indent'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); - - $trusted_wh = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length('0'), - new HTMLPurifier_AttrDef_CSS_Percentage(true), - new HTMLPurifier_AttrDef_Enum(array('auto')) - )); - $max = $config->get('CSS.MaxImgLength'); - - $this->info['width'] = - $this->info['height'] = - $max === null ? - $trusted_wh : - new HTMLPurifier_AttrDef_Switch('img', - // For img tags: - new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_CSS_Length('0', $max), - new HTMLPurifier_AttrDef_Enum(array('auto')) - )), - // For everyone else: - $trusted_wh - ); - - $this->info['text-decoration'] = new HTMLPurifier_AttrDef_CSS_TextDecoration(); - - $this->info['font-family'] = new HTMLPurifier_AttrDef_CSS_FontFamily(); - - // this could use specialized code - $this->info['font-weight'] = new HTMLPurifier_AttrDef_Enum( - array('normal', 'bold', 'bolder', 'lighter', '100', '200', '300', - '400', '500', '600', '700', '800', '900'), false); - - // MUST be called after other font properties, as it references - // a CSSDefinition object - $this->info['font'] = new HTMLPurifier_AttrDef_CSS_Font($config); - - // same here - $this->info['border'] = - $this->info['border-bottom'] = - $this->info['border-top'] = - $this->info['border-left'] = - $this->info['border-right'] = new HTMLPurifier_AttrDef_CSS_Border($config); - - $this->info['border-collapse'] = new HTMLPurifier_AttrDef_Enum(array( - 'collapse', 'separate')); - - $this->info['caption-side'] = new HTMLPurifier_AttrDef_Enum(array( - 'top', 'bottom')); - - $this->info['table-layout'] = new HTMLPurifier_AttrDef_Enum(array( - 'auto', 'fixed')); - - $this->info['vertical-align'] = new HTMLPurifier_AttrDef_CSS_Composite(array( - new HTMLPurifier_AttrDef_Enum(array('baseline', 'sub', 'super', - 'top', 'text-top', 'middle', 'bottom', 'text-bottom')), - new HTMLPurifier_AttrDef_CSS_Length(), - new HTMLPurifier_AttrDef_CSS_Percentage() - )); - - $this->info['border-spacing'] = new HTMLPurifier_AttrDef_CSS_Multiple(new HTMLPurifier_AttrDef_CSS_Length(), 2); - - // partial support - $this->info['white-space'] = new HTMLPurifier_AttrDef_Enum(array('nowrap')); - - if ($config->get('CSS.Proprietary')) { - $this->doSetupProprietary($config); - } - - if ($config->get('CSS.AllowTricky')) { - $this->doSetupTricky($config); - } - - $allow_important = $config->get('CSS.AllowImportant'); - // wrap all attr-defs with decorator that handles !important - foreach ($this->info as $k => $v) { - $this->info[$k] = new HTMLPurifier_AttrDef_CSS_ImportantDecorator($v, $allow_important); - } - - $this->setupConfigStuff($config); - } - - protected function doSetupProprietary($config) { - // Internet Explorer only scrollbar colors - $this->info['scrollbar-arrow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-base-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-darkshadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-face-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-highlight-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - $this->info['scrollbar-shadow-color'] = new HTMLPurifier_AttrDef_CSS_Color(); - - // technically not proprietary, but CSS3, and no one supports it - $this->info['opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); - $this->info['-moz-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); - $this->info['-khtml-opacity'] = new HTMLPurifier_AttrDef_CSS_AlphaValue(); - - // only opacity, for now - $this->info['filter'] = new HTMLPurifier_AttrDef_CSS_Filter(); - - } - - protected function doSetupTricky($config) { - $this->info['display'] = new HTMLPurifier_AttrDef_Enum(array( - 'inline', 'block', 'list-item', 'run-in', 'compact', - 'marker', 'table', 'inline-table', 'table-row-group', - 'table-header-group', 'table-footer-group', 'table-row', - 'table-column-group', 'table-column', 'table-cell', 'table-caption', 'none' - )); - $this->info['visibility'] = new HTMLPurifier_AttrDef_Enum(array( - 'visible', 'hidden', 'collapse' - )); - $this->info['overflow'] = new HTMLPurifier_AttrDef_Enum(array('visible', 'hidden', 'auto', 'scroll')); - } - - - /** - * Performs extra config-based processing. Based off of - * HTMLPurifier_HTMLDefinition. - * @todo Refactor duplicate elements into common class (probably using - * composition, not inheritance). - */ - protected function setupConfigStuff($config) { - - // setup allowed elements - $support = "(for information on implementing this, see the ". - "support forums) "; - $allowed_attributes = $config->get('CSS.AllowedProperties'); - if ($allowed_attributes !== null) { - foreach ($this->info as $name => $d) { - if(!isset($allowed_attributes[$name])) unset($this->info[$name]); - unset($allowed_attributes[$name]); - } - // emit errors - foreach ($allowed_attributes as $name => $d) { - // :TODO: Is this htmlspecialchars() call really necessary? - $name = htmlspecialchars($name); - trigger_error("Style attribute '$name' is not supported $support", E_USER_WARNING); - } - } - - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef.php deleted file mode 100644 index c5d5216da..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef.php +++ /dev/null @@ -1,48 +0,0 @@ -elements; - } - - /** - * Validates nodes according to definition and returns modification. - * - * @param $tokens_of_children Array of HTMLPurifier_Token - * @param $config HTMLPurifier_Config object - * @param $context HTMLPurifier_Context object - * @return bool true to leave nodes as is - * @return bool false to remove parent node - * @return array of replacement child tokens - */ - abstract public function validateChildren($tokens_of_children, $config, $context); -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php deleted file mode 100644 index 15c364ee3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Chameleon.php +++ /dev/null @@ -1,48 +0,0 @@ -inline = new HTMLPurifier_ChildDef_Optional($inline); - $this->block = new HTMLPurifier_ChildDef_Optional($block); - $this->elements = $this->block->elements; - } - - public function validateChildren($tokens_of_children, $config, $context) { - if ($context->get('IsInline') === false) { - return $this->block->validateChildren( - $tokens_of_children, $config, $context); - } else { - return $this->inline->validateChildren( - $tokens_of_children, $config, $context); - } - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php deleted file mode 100644 index b68047b4b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Custom.php +++ /dev/null @@ -1,90 +0,0 @@ -dtd_regex = $dtd_regex; - $this->_compileRegex(); - } - /** - * Compiles the PCRE regex from a DTD regex ($dtd_regex to $_pcre_regex) - */ - protected function _compileRegex() { - $raw = str_replace(' ', '', $this->dtd_regex); - if ($raw{0} != '(') { - $raw = "($raw)"; - } - $el = '[#a-zA-Z0-9_.-]+'; - $reg = $raw; - - // COMPLICATED! AND MIGHT BE BUGGY! I HAVE NO CLUE WHAT I'M - // DOING! Seriously: if there's problems, please report them. - - // collect all elements into the $elements array - preg_match_all("/$el/", $reg, $matches); - foreach ($matches[0] as $match) { - $this->elements[$match] = true; - } - - // setup all elements as parentheticals with leading commas - $reg = preg_replace("/$el/", '(,\\0)', $reg); - - // remove commas when they were not solicited - $reg = preg_replace("/([^,(|]\(+),/", '\\1', $reg); - - // remove all non-paranthetical commas: they are handled by first regex - $reg = preg_replace("/,\(/", '(', $reg); - - $this->_pcre_regex = $reg; - } - public function validateChildren($tokens_of_children, $config, $context) { - $list_of_children = ''; - $nesting = 0; // depth into the nest - foreach ($tokens_of_children as $token) { - if (!empty($token->is_whitespace)) continue; - - $is_child = ($nesting == 0); // direct - - if ($token instanceof HTMLPurifier_Token_Start) { - $nesting++; - } elseif ($token instanceof HTMLPurifier_Token_End) { - $nesting--; - } - - if ($is_child) { - $list_of_children .= $token->name . ','; - } - } - // add leading comma to deal with stray comma declarations - $list_of_children = ',' . rtrim($list_of_children, ','); - $okay = - preg_match( - '/^,?'.$this->_pcre_regex.'$/', - $list_of_children - ); - - return (bool) $okay; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php deleted file mode 100644 index 13171f665..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Empty.php +++ /dev/null @@ -1,20 +0,0 @@ -whitespace) return $tokens_of_children; - else return array(); - } - return $result; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php deleted file mode 100644 index 4889f249b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Required.php +++ /dev/null @@ -1,117 +0,0 @@ - $x) { - $elements[$i] = true; - if (empty($i)) unset($elements[$i]); // remove blank - } - } - $this->elements = $elements; - } - public $allow_empty = false; - public $type = 'required'; - public function validateChildren($tokens_of_children, $config, $context) { - // Flag for subclasses - $this->whitespace = false; - - // if there are no tokens, delete parent node - if (empty($tokens_of_children)) return false; - - // the new set of children - $result = array(); - - // current depth into the nest - $nesting = 0; - - // whether or not we're deleting a node - $is_deleting = false; - - // whether or not parsed character data is allowed - // this controls whether or not we silently drop a tag - // or generate escaped HTML from it - $pcdata_allowed = isset($this->elements['#PCDATA']); - - // a little sanity check to make sure it's not ALL whitespace - $all_whitespace = true; - - // some configuration - $escape_invalid_children = $config->get('Core.EscapeInvalidChildren'); - - // generator - $gen = new HTMLPurifier_Generator($config, $context); - - foreach ($tokens_of_children as $token) { - if (!empty($token->is_whitespace)) { - $result[] = $token; - continue; - } - $all_whitespace = false; // phew, we're not talking about whitespace - - $is_child = ($nesting == 0); - - if ($token instanceof HTMLPurifier_Token_Start) { - $nesting++; - } elseif ($token instanceof HTMLPurifier_Token_End) { - $nesting--; - } - - if ($is_child) { - $is_deleting = false; - if (!isset($this->elements[$token->name])) { - $is_deleting = true; - if ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text) { - $result[] = $token; - } elseif ($pcdata_allowed && $escape_invalid_children) { - $result[] = new HTMLPurifier_Token_Text( - $gen->generateFromToken($token) - ); - } - continue; - } - } - if (!$is_deleting || ($pcdata_allowed && $token instanceof HTMLPurifier_Token_Text)) { - $result[] = $token; - } elseif ($pcdata_allowed && $escape_invalid_children) { - $result[] = - new HTMLPurifier_Token_Text( - $gen->generateFromToken($token) - ); - } else { - // drop silently - } - } - if (empty($result)) return false; - if ($all_whitespace) { - $this->whitespace = true; - return false; - } - if ($tokens_of_children == $result) return true; - return $result; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php deleted file mode 100644 index dfae8a6e5..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/StrictBlockquote.php +++ /dev/null @@ -1,88 +0,0 @@ -init($config); - return $this->fake_elements; - } - - public function validateChildren($tokens_of_children, $config, $context) { - - $this->init($config); - - // trick the parent class into thinking it allows more - $this->elements = $this->fake_elements; - $result = parent::validateChildren($tokens_of_children, $config, $context); - $this->elements = $this->real_elements; - - if ($result === false) return array(); - if ($result === true) $result = $tokens_of_children; - - $def = $config->getHTMLDefinition(); - $block_wrap_start = new HTMLPurifier_Token_Start($def->info_block_wrapper); - $block_wrap_end = new HTMLPurifier_Token_End( $def->info_block_wrapper); - $is_inline = false; - $depth = 0; - $ret = array(); - - // assuming that there are no comment tokens - foreach ($result as $i => $token) { - $token = $result[$i]; - // ifs are nested for readability - if (!$is_inline) { - if (!$depth) { - if ( - ($token instanceof HTMLPurifier_Token_Text && !$token->is_whitespace) || - (!$token instanceof HTMLPurifier_Token_Text && !isset($this->elements[$token->name])) - ) { - $is_inline = true; - $ret[] = $block_wrap_start; - } - } - } else { - if (!$depth) { - // starting tokens have been inline text / empty - if ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) { - if (isset($this->elements[$token->name])) { - // ended - $ret[] = $block_wrap_end; - $is_inline = false; - } - } - } - } - $ret[] = $token; - if ($token instanceof HTMLPurifier_Token_Start) $depth++; - if ($token instanceof HTMLPurifier_Token_End) $depth--; - } - if ($is_inline) $ret[] = $block_wrap_end; - return $ret; - } - - private function init($config) { - if (!$this->init) { - $def = $config->getHTMLDefinition(); - // allow all inline elements - $this->real_elements = $this->elements; - $this->fake_elements = $def->info_content_sets['Flow']; - $this->fake_elements['#PCDATA'] = true; - $this->init = true; - } - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php deleted file mode 100644 index 34f0227dd..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php +++ /dev/null @@ -1,142 +0,0 @@ - true, 'tbody' => true, 'thead' => true, - 'tfoot' => true, 'caption' => true, 'colgroup' => true, 'col' => true); - public function __construct() {} - public function validateChildren($tokens_of_children, $config, $context) { - if (empty($tokens_of_children)) return false; - - // this ensures that the loop gets run one last time before closing - // up. It's a little bit of a hack, but it works! Just make sure you - // get rid of the token later. - $tokens_of_children[] = false; - - // only one of these elements is allowed in a table - $caption = false; - $thead = false; - $tfoot = false; - - // as many of these as you want - $cols = array(); - $content = array(); - - $nesting = 0; // current depth so we can determine nodes - $is_collecting = false; // are we globbing together tokens to package - // into one of the collectors? - $collection = array(); // collected nodes - $tag_index = 0; // the first node might be whitespace, - // so this tells us where the start tag is - - foreach ($tokens_of_children as $token) { - $is_child = ($nesting == 0); - - if ($token === false) { - // terminating sequence started - } elseif ($token instanceof HTMLPurifier_Token_Start) { - $nesting++; - } elseif ($token instanceof HTMLPurifier_Token_End) { - $nesting--; - } - - // handle node collection - if ($is_collecting) { - if ($is_child) { - // okay, let's stash the tokens away - // first token tells us the type of the collection - switch ($collection[$tag_index]->name) { - case 'tr': - case 'tbody': - $content[] = $collection; - break; - case 'caption': - if ($caption !== false) break; - $caption = $collection; - break; - case 'thead': - case 'tfoot': - // access the appropriate variable, $thead or $tfoot - $var = $collection[$tag_index]->name; - if ($$var === false) { - $$var = $collection; - } else { - // transmutate the first and less entries into - // tbody tags, and then put into content - $collection[$tag_index]->name = 'tbody'; - $collection[count($collection)-1]->name = 'tbody'; - $content[] = $collection; - } - break; - case 'colgroup': - $cols[] = $collection; - break; - } - $collection = array(); - $is_collecting = false; - $tag_index = 0; - } else { - // add the node to the collection - $collection[] = $token; - } - } - - // terminate - if ($token === false) break; - - if ($is_child) { - // determine what we're dealing with - if ($token->name == 'col') { - // the only empty tag in the possie, we can handle it - // immediately - $cols[] = array_merge($collection, array($token)); - $collection = array(); - $tag_index = 0; - continue; - } - switch($token->name) { - case 'caption': - case 'colgroup': - case 'thead': - case 'tfoot': - case 'tbody': - case 'tr': - $is_collecting = true; - $collection[] = $token; - continue; - default: - if (!empty($token->is_whitespace)) { - $collection[] = $token; - $tag_index++; - } - continue; - } - } - } - - if (empty($content)) return false; - - $ret = array(); - if ($caption !== false) $ret = array_merge($ret, $caption); - if ($cols !== false) foreach ($cols as $token_array) $ret = array_merge($ret, $token_array); - if ($thead !== false) $ret = array_merge($ret, $thead); - if ($tfoot !== false) $ret = array_merge($ret, $tfoot); - foreach ($content as $token_array) $ret = array_merge($ret, $token_array); - if (!empty($collection) && $is_collecting == false){ - // grab the trailing space - $ret = array_merge($ret, $collection); - } - - array_pop($tokens_of_children); // remove phantom token - - return ($ret === $tokens_of_children) ? true : $ret; - - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Config.php b/extlib/HTMLPurifier/HTMLPurifier/Config.php deleted file mode 100644 index a01706043..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Config.php +++ /dev/null @@ -1,580 +0,0 @@ -defaultPlist; - $this->plist = new HTMLPurifier_PropertyList($parent); - $this->def = $definition; // keep a copy around for checking - $this->parser = new HTMLPurifier_VarParser_Flexible(); - } - - /** - * Convenience constructor that creates a config object based on a mixed var - * @param mixed $config Variable that defines the state of the config - * object. Can be: a HTMLPurifier_Config() object, - * an array of directives based on loadArray(), - * or a string filename of an ini file. - * @param HTMLPurifier_ConfigSchema Schema object - * @return Configured HTMLPurifier_Config object - */ - public static function create($config, $schema = null) { - if ($config instanceof HTMLPurifier_Config) { - // pass-through - return $config; - } - if (!$schema) { - $ret = HTMLPurifier_Config::createDefault(); - } else { - $ret = new HTMLPurifier_Config($schema); - } - if (is_string($config)) $ret->loadIni($config); - elseif (is_array($config)) $ret->loadArray($config); - return $ret; - } - - /** - * Creates a new config object that inherits from a previous one. - * @param HTMLPurifier_Config $config Configuration object to inherit - * from. - * @return HTMLPurifier_Config object with $config as its parent. - */ - public static function inherit(HTMLPurifier_Config $config) { - return new HTMLPurifier_Config($config->def, $config->plist); - } - - /** - * Convenience constructor that creates a default configuration object. - * @return Default HTMLPurifier_Config object. - */ - public static function createDefault() { - $definition = HTMLPurifier_ConfigSchema::instance(); - $config = new HTMLPurifier_Config($definition); - return $config; - } - - /** - * Retreives a value from the configuration. - * @param $key String key - */ - public function get($key, $a = null) { - if ($a !== null) { - $this->triggerError("Using deprecated API: use \$config->get('$key.$a') instead", E_USER_WARNING); - $key = "$key.$a"; - } - if (!$this->finalized) $this->autoFinalize(); - if (!isset($this->def->info[$key])) { - // can't add % due to SimpleTest bug - $this->triggerError('Cannot retrieve value of undefined directive ' . htmlspecialchars($key), - E_USER_WARNING); - return; - } - if (isset($this->def->info[$key]->isAlias)) { - $d = $this->def->info[$key]; - $this->triggerError('Cannot get value from aliased directive, use real name ' . $d->key, - E_USER_ERROR); - return; - } - if ($this->lock) { - list($ns) = explode('.', $key); - if ($ns !== $this->lock) { - $this->triggerError('Cannot get value of namespace ' . $ns . ' when lock for ' . $this->lock . ' is active, this probably indicates a Definition setup method is accessing directives that are not within its namespace', E_USER_ERROR); - return; - } - } - return $this->plist->get($key); - } - - /** - * Retreives an array of directives to values from a given namespace - * @param $namespace String namespace - */ - public function getBatch($namespace) { - if (!$this->finalized) $this->autoFinalize(); - $full = $this->getAll(); - if (!isset($full[$namespace])) { - $this->triggerError('Cannot retrieve undefined namespace ' . htmlspecialchars($namespace), - E_USER_WARNING); - return; - } - return $full[$namespace]; - } - - /** - * Returns a md5 signature of a segment of the configuration object - * that uniquely identifies that particular configuration - * @note Revision is handled specially and is removed from the batch - * before processing! - * @param $namespace Namespace to get serial for - */ - public function getBatchSerial($namespace) { - if (empty($this->serials[$namespace])) { - $batch = $this->getBatch($namespace); - unset($batch['DefinitionRev']); - $this->serials[$namespace] = md5(serialize($batch)); - } - return $this->serials[$namespace]; - } - - /** - * Returns a md5 signature for the entire configuration object - * that uniquely identifies that particular configuration - */ - public function getSerial() { - if (empty($this->serial)) { - $this->serial = md5(serialize($this->getAll())); - } - return $this->serial; - } - - /** - * Retrieves all directives, organized by namespace - * @warning This is a pretty inefficient function, avoid if you can - */ - public function getAll() { - if (!$this->finalized) $this->autoFinalize(); - $ret = array(); - foreach ($this->plist->squash() as $name => $value) { - list($ns, $key) = explode('.', $name, 2); - $ret[$ns][$key] = $value; - } - return $ret; - } - - /** - * Sets a value to configuration. - * @param $key String key - * @param $value Mixed value - */ - public function set($key, $value, $a = null) { - if (strpos($key, '.') === false) { - $namespace = $key; - $directive = $value; - $value = $a; - $key = "$key.$directive"; - $this->triggerError("Using deprecated API: use \$config->set('$key', ...) instead", E_USER_NOTICE); - } else { - list($namespace) = explode('.', $key); - } - if ($this->isFinalized('Cannot set directive after finalization')) return; - if (!isset($this->def->info[$key])) { - $this->triggerError('Cannot set undefined directive ' . htmlspecialchars($key) . ' to value', - E_USER_WARNING); - return; - } - $def = $this->def->info[$key]; - - if (isset($def->isAlias)) { - if ($this->aliasMode) { - $this->triggerError('Double-aliases not allowed, please fix '. - 'ConfigSchema bug with' . $key, E_USER_ERROR); - return; - } - $this->aliasMode = true; - $this->set($def->key, $value); - $this->aliasMode = false; - $this->triggerError("$key is an alias, preferred directive name is {$def->key}", E_USER_NOTICE); - return; - } - - // Raw type might be negative when using the fully optimized form - // of stdclass, which indicates allow_null == true - $rtype = is_int($def) ? $def : $def->type; - if ($rtype < 0) { - $type = -$rtype; - $allow_null = true; - } else { - $type = $rtype; - $allow_null = isset($def->allow_null); - } - - try { - $value = $this->parser->parse($value, $type, $allow_null); - } catch (HTMLPurifier_VarParserException $e) { - $this->triggerError('Value for ' . $key . ' is of invalid type, should be ' . HTMLPurifier_VarParser::getTypeName($type), E_USER_WARNING); - return; - } - if (is_string($value) && is_object($def)) { - // resolve value alias if defined - if (isset($def->aliases[$value])) { - $value = $def->aliases[$value]; - } - // check to see if the value is allowed - if (isset($def->allowed) && !isset($def->allowed[$value])) { - $this->triggerError('Value not supported, valid values are: ' . - $this->_listify($def->allowed), E_USER_WARNING); - return; - } - } - $this->plist->set($key, $value); - - // reset definitions if the directives they depend on changed - // this is a very costly process, so it's discouraged - // with finalization - if ($namespace == 'HTML' || $namespace == 'CSS' || $namespace == 'URI') { - $this->definitions[$namespace] = null; - } - - $this->serials[$namespace] = false; - } - - /** - * Convenience function for error reporting - */ - private function _listify($lookup) { - $list = array(); - foreach ($lookup as $name => $b) $list[] = $name; - return implode(', ', $list); - } - - /** - * Retrieves object reference to the HTML definition. - * @param $raw Return a copy that has not been setup yet. Must be - * called before it's been setup, otherwise won't work. - */ - public function getHTMLDefinition($raw = false) { - return $this->getDefinition('HTML', $raw); - } - - /** - * Retrieves object reference to the CSS definition - * @param $raw Return a copy that has not been setup yet. Must be - * called before it's been setup, otherwise won't work. - */ - public function getCSSDefinition($raw = false) { - return $this->getDefinition('CSS', $raw); - } - - /** - * Retrieves a definition - * @param $type Type of definition: HTML, CSS, etc - * @param $raw Whether or not definition should be returned raw - */ - public function getDefinition($type, $raw = false) { - if (!$this->finalized) $this->autoFinalize(); - // temporarily suspend locks, so we can handle recursive definition calls - $lock = $this->lock; - $this->lock = null; - $factory = HTMLPurifier_DefinitionCacheFactory::instance(); - $cache = $factory->create($type, $this); - $this->lock = $lock; - if (!$raw) { - // see if we can quickly supply a definition - if (!empty($this->definitions[$type])) { - if (!$this->definitions[$type]->setup) { - $this->definitions[$type]->setup($this); - $cache->set($this->definitions[$type], $this); - } - return $this->definitions[$type]; - } - // memory check missed, try cache - $this->definitions[$type] = $cache->get($this); - if ($this->definitions[$type]) { - // definition in cache, return it - return $this->definitions[$type]; - } - } elseif ( - !empty($this->definitions[$type]) && - !$this->definitions[$type]->setup - ) { - // raw requested, raw in memory, quick return - return $this->definitions[$type]; - } - // quick checks failed, let's create the object - if ($type == 'HTML') { - $this->definitions[$type] = new HTMLPurifier_HTMLDefinition(); - } elseif ($type == 'CSS') { - $this->definitions[$type] = new HTMLPurifier_CSSDefinition(); - } elseif ($type == 'URI') { - $this->definitions[$type] = new HTMLPurifier_URIDefinition(); - } else { - throw new HTMLPurifier_Exception("Definition of $type type not supported"); - } - // quick abort if raw - if ($raw) { - if (is_null($this->get($type . '.DefinitionID'))) { - // fatally error out if definition ID not set - throw new HTMLPurifier_Exception("Cannot retrieve raw version without specifying %$type.DefinitionID"); - } - return $this->definitions[$type]; - } - // set it up - $this->lock = $type; - $this->definitions[$type]->setup($this); - $this->lock = null; - // save in cache - $cache->set($this->definitions[$type], $this); - return $this->definitions[$type]; - } - - /** - * Loads configuration values from an array with the following structure: - * Namespace.Directive => Value - * @param $config_array Configuration associative array - */ - public function loadArray($config_array) { - if ($this->isFinalized('Cannot load directives after finalization')) return; - foreach ($config_array as $key => $value) { - $key = str_replace('_', '.', $key); - if (strpos($key, '.') !== false) { - $this->set($key, $value); - } else { - $namespace = $key; - $namespace_values = $value; - foreach ($namespace_values as $directive => $value) { - $this->set($namespace .'.'. $directive, $value); - } - } - } - } - - /** - * Returns a list of array(namespace, directive) for all directives - * that are allowed in a web-form context as per an allowed - * namespaces/directives list. - * @param $allowed List of allowed namespaces/directives - */ - public static function getAllowedDirectivesForForm($allowed, $schema = null) { - if (!$schema) { - $schema = HTMLPurifier_ConfigSchema::instance(); - } - if ($allowed !== true) { - if (is_string($allowed)) $allowed = array($allowed); - $allowed_ns = array(); - $allowed_directives = array(); - $blacklisted_directives = array(); - foreach ($allowed as $ns_or_directive) { - if (strpos($ns_or_directive, '.') !== false) { - // directive - if ($ns_or_directive[0] == '-') { - $blacklisted_directives[substr($ns_or_directive, 1)] = true; - } else { - $allowed_directives[$ns_or_directive] = true; - } - } else { - // namespace - $allowed_ns[$ns_or_directive] = true; - } - } - } - $ret = array(); - foreach ($schema->info as $key => $def) { - list($ns, $directive) = explode('.', $key, 2); - if ($allowed !== true) { - if (isset($blacklisted_directives["$ns.$directive"])) continue; - if (!isset($allowed_directives["$ns.$directive"]) && !isset($allowed_ns[$ns])) continue; - } - if (isset($def->isAlias)) continue; - if ($directive == 'DefinitionID' || $directive == 'DefinitionRev') continue; - $ret[] = array($ns, $directive); - } - return $ret; - } - - /** - * Loads configuration values from $_GET/$_POST that were posted - * via ConfigForm - * @param $array $_GET or $_POST array to import - * @param $index Index/name that the config variables are in - * @param $allowed List of allowed namespaces/directives - * @param $mq_fix Boolean whether or not to enable magic quotes fix - * @param $schema Instance of HTMLPurifier_ConfigSchema to use, if not global copy - */ - public static function loadArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) { - $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $schema); - $config = HTMLPurifier_Config::create($ret, $schema); - return $config; - } - - /** - * Merges in configuration values from $_GET/$_POST to object. NOT STATIC. - * @note Same parameters as loadArrayFromForm - */ - public function mergeArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true) { - $ret = HTMLPurifier_Config::prepareArrayFromForm($array, $index, $allowed, $mq_fix, $this->def); - $this->loadArray($ret); - } - - /** - * Prepares an array from a form into something usable for the more - * strict parts of HTMLPurifier_Config - */ - public static function prepareArrayFromForm($array, $index = false, $allowed = true, $mq_fix = true, $schema = null) { - if ($index !== false) $array = (isset($array[$index]) && is_array($array[$index])) ? $array[$index] : array(); - $mq = $mq_fix && function_exists('get_magic_quotes_gpc') && get_magic_quotes_gpc(); - - $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $schema); - $ret = array(); - foreach ($allowed as $key) { - list($ns, $directive) = $key; - $skey = "$ns.$directive"; - if (!empty($array["Null_$skey"])) { - $ret[$ns][$directive] = null; - continue; - } - if (!isset($array[$skey])) continue; - $value = $mq ? stripslashes($array[$skey]) : $array[$skey]; - $ret[$ns][$directive] = $value; - } - return $ret; - } - - /** - * Loads configuration values from an ini file - * @param $filename Name of ini file - */ - public function loadIni($filename) { - if ($this->isFinalized('Cannot load directives after finalization')) return; - $array = parse_ini_file($filename, true); - $this->loadArray($array); - } - - /** - * Checks whether or not the configuration object is finalized. - * @param $error String error message, or false for no error - */ - public function isFinalized($error = false) { - if ($this->finalized && $error) { - $this->triggerError($error, E_USER_ERROR); - } - return $this->finalized; - } - - /** - * Finalizes configuration only if auto finalize is on and not - * already finalized - */ - public function autoFinalize() { - if ($this->autoFinalize) { - $this->finalize(); - } else { - $this->plist->squash(true); - } - } - - /** - * Finalizes a configuration object, prohibiting further change - */ - public function finalize() { - $this->finalized = true; - unset($this->parser); - } - - /** - * Produces a nicely formatted error message by supplying the - * stack frame information from two levels up and OUTSIDE of - * HTMLPurifier_Config. - */ - protected function triggerError($msg, $no) { - // determine previous stack frame - $backtrace = debug_backtrace(); - if ($this->chatty && isset($backtrace[1])) { - $frame = $backtrace[1]; - $extra = " on line {$frame['line']} in file {$frame['file']}"; - } else { - $extra = ''; - } - trigger_error($msg . $extra, $no); - } - - /** - * Returns a serialized form of the configuration object that can - * be reconstituted. - */ - public function serialize() { - $this->getDefinition('HTML'); - $this->getDefinition('CSS'); - $this->getDefinition('URI'); - return serialize($this); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php deleted file mode 100644 index 67be5c71f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php +++ /dev/null @@ -1,158 +0,0 @@ - array( - * 'Directive' => new stdclass(), - * ) - * ) - * - * The stdclass may have the following properties: - * - * - If isAlias isn't set: - * - type: Integer type of directive, see HTMLPurifier_VarParser for definitions - * - allow_null: If set, this directive allows null values - * - aliases: If set, an associative array of value aliases to real values - * - allowed: If set, a lookup array of allowed (string) values - * - If isAlias is set: - * - namespace: Namespace this directive aliases to - * - name: Directive name this directive aliases to - * - * In certain degenerate cases, stdclass will actually be an integer. In - * that case, the value is equivalent to an stdclass with the type - * property set to the integer. If the integer is negative, type is - * equal to the absolute value of integer, and allow_null is true. - * - * This class is friendly with HTMLPurifier_Config. If you need introspection - * about the schema, you're better of using the ConfigSchema_Interchange, - * which uses more memory but has much richer information. - */ - public $info = array(); - - /** - * Application-wide singleton - */ - static protected $singleton; - - public function __construct() { - $this->defaultPlist = new HTMLPurifier_PropertyList(); - } - - /** - * Unserializes the default ConfigSchema. - */ - public static function makeFromSerial() { - return unserialize(file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema.ser')); - } - - /** - * Retrieves an instance of the application-wide configuration definition. - */ - public static function instance($prototype = null) { - if ($prototype !== null) { - HTMLPurifier_ConfigSchema::$singleton = $prototype; - } elseif (HTMLPurifier_ConfigSchema::$singleton === null || $prototype === true) { - HTMLPurifier_ConfigSchema::$singleton = HTMLPurifier_ConfigSchema::makeFromSerial(); - } - return HTMLPurifier_ConfigSchema::$singleton; - } - - /** - * Defines a directive for configuration - * @warning Will fail of directive's namespace is defined. - * @warning This method's signature is slightly different from the legacy - * define() static method! Beware! - * @param $namespace Namespace the directive is in - * @param $name Key of directive - * @param $default Default value of directive - * @param $type Allowed type of the directive. See - * HTMLPurifier_DirectiveDef::$type for allowed values - * @param $allow_null Whether or not to allow null values - */ - public function add($key, $default, $type, $allow_null) { - $obj = new stdclass(); - $obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type]; - if ($allow_null) $obj->allow_null = true; - $this->info[$key] = $obj; - $this->defaults[$key] = $default; - $this->defaultPlist->set($key, $default); - } - - /** - * Defines a directive value alias. - * - * Directive value aliases are convenient for developers because it lets - * them set a directive to several values and get the same result. - * @param $namespace Directive's namespace - * @param $name Name of Directive - * @param $aliases Hash of aliased values to the real alias - */ - public function addValueAliases($key, $aliases) { - if (!isset($this->info[$key]->aliases)) { - $this->info[$key]->aliases = array(); - } - foreach ($aliases as $alias => $real) { - $this->info[$key]->aliases[$alias] = $real; - } - } - - /** - * Defines a set of allowed values for a directive. - * @warning This is slightly different from the corresponding static - * method definition. - * @param $namespace Namespace of directive - * @param $name Name of directive - * @param $allowed Lookup array of allowed values - */ - public function addAllowedValues($key, $allowed) { - $this->info[$key]->allowed = $allowed; - } - - /** - * Defines a directive alias for backwards compatibility - * @param $namespace - * @param $name Directive that will be aliased - * @param $new_namespace - * @param $new_name Directive that the alias will be to - */ - public function addAlias($key, $new_key) { - $obj = new stdclass; - $obj->key = $new_key; - $obj->isAlias = true; - $this->info[$key] = $obj; - } - - /** - * Replaces any stdclass that only has the type property with type integer. - */ - public function postProcess() { - foreach ($this->info as $key => $v) { - if (count((array) $v) == 1) { - $this->info[$key] = $v->type; - } elseif (count((array) $v) == 2 && isset($v->allow_null)) { - $this->info[$key] = -$v->type; - } - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php deleted file mode 100644 index c05668a70..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/ConfigSchema.php +++ /dev/null @@ -1,44 +0,0 @@ -directives as $d) { - $schema->add( - $d->id->key, - $d->default, - $d->type, - $d->typeAllowsNull - ); - if ($d->allowed !== null) { - $schema->addAllowedValues( - $d->id->key, - $d->allowed - ); - } - foreach ($d->aliases as $alias) { - $schema->addAlias( - $alias->key, - $d->id->key - ); - } - if ($d->valueAliases !== null) { - $schema->addValueAliases( - $d->id->key, - $d->valueAliases - ); - } - } - $schema->postProcess(); - return $schema; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php deleted file mode 100644 index 244561a37..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Builder/Xml.php +++ /dev/null @@ -1,106 +0,0 @@ -startElement('div'); - - $purifier = HTMLPurifier::getInstance(); - $html = $purifier->purify($html); - $this->writeAttribute('xmlns', 'http://www.w3.org/1999/xhtml'); - $this->writeRaw($html); - - $this->endElement(); // div - } - - protected function export($var) { - if ($var === array()) return 'array()'; - return var_export($var, true); - } - - public function build($interchange) { - // global access, only use as last resort - $this->interchange = $interchange; - - $this->setIndent(true); - $this->startDocument('1.0', 'UTF-8'); - $this->startElement('configdoc'); - $this->writeElement('title', $interchange->name); - - foreach ($interchange->directives as $directive) { - $this->buildDirective($directive); - } - - if ($this->namespace) $this->endElement(); // namespace - - $this->endElement(); // configdoc - $this->flush(); - } - - public function buildDirective($directive) { - - // Kludge, although I suppose having a notion of a "root namespace" - // certainly makes things look nicer when documentation is built. - // Depends on things being sorted. - if (!$this->namespace || $this->namespace !== $directive->id->getRootNamespace()) { - if ($this->namespace) $this->endElement(); // namespace - $this->namespace = $directive->id->getRootNamespace(); - $this->startElement('namespace'); - $this->writeAttribute('id', $this->namespace); - $this->writeElement('name', $this->namespace); - } - - $this->startElement('directive'); - $this->writeAttribute('id', $directive->id->toString()); - - $this->writeElement('name', $directive->id->getDirective()); - - $this->startElement('aliases'); - foreach ($directive->aliases as $alias) $this->writeElement('alias', $alias->toString()); - $this->endElement(); // aliases - - $this->startElement('constraints'); - if ($directive->version) $this->writeElement('version', $directive->version); - $this->startElement('type'); - if ($directive->typeAllowsNull) $this->writeAttribute('allow-null', 'yes'); - $this->text($directive->type); - $this->endElement(); // type - if ($directive->allowed) { - $this->startElement('allowed'); - foreach ($directive->allowed as $value => $x) $this->writeElement('value', $value); - $this->endElement(); // allowed - } - $this->writeElement('default', $this->export($directive->default)); - $this->writeAttribute('xml:space', 'preserve'); - if ($directive->external) { - $this->startElement('external'); - foreach ($directive->external as $project) $this->writeElement('project', $project); - $this->endElement(); - } - $this->endElement(); // constraints - - if ($directive->deprecatedVersion) { - $this->startElement('deprecated'); - $this->writeElement('version', $directive->deprecatedVersion); - $this->writeElement('use', $directive->deprecatedUse->toString()); - $this->endElement(); // deprecated - } - - $this->startElement('description'); - $this->writeHTMLDiv($directive->description); - $this->endElement(); // description - - $this->endElement(); // directive - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php deleted file mode 100644 index 2671516c5..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Exception.php +++ /dev/null @@ -1,11 +0,0 @@ - array(directive info) - */ - public $directives = array(); - - /** - * Adds a directive array to $directives - */ - public function addDirective($directive) { - if (isset($this->directives[$i = $directive->id->toString()])) { - throw new HTMLPurifier_ConfigSchema_Exception("Cannot redefine directive '$i'"); - } - $this->directives[$i] = $directive; - } - - /** - * Convenience function to perform standard validation. Throws exception - * on failed validation. - */ - public function validate() { - $validator = new HTMLPurifier_ConfigSchema_Validator(); - return $validator->validate($this); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php deleted file mode 100644 index ac8be0d97..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Directive.php +++ /dev/null @@ -1,77 +0,0 @@ - true). - * Null if all values are allowed. - */ - public $allowed; - - /** - * List of aliases for the directive, - * e.g. array(new HTMLPurifier_ConfigSchema_Interchange_Id('Ns', 'Dir'))). - */ - public $aliases = array(); - - /** - * Hash of value aliases, e.g. array('alt' => 'real'). Null if value - * aliasing is disabled (necessary for non-scalar types). - */ - public $valueAliases; - - /** - * Version of HTML Purifier the directive was introduced, e.g. '1.3.1'. - * Null if the directive has always existed. - */ - public $version; - - /** - * ID of directive that supercedes this old directive, is an instance - * of HTMLPurifier_ConfigSchema_Interchange_Id. Null if not deprecated. - */ - public $deprecatedUse; - - /** - * Version of HTML Purifier this directive was deprecated. Null if not - * deprecated. - */ - public $deprecatedVersion; - - /** - * List of external projects this directive depends on, e.g. array('CSSTidy'). - */ - public $external = array(); - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php deleted file mode 100644 index b9b3c6f5c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Interchange/Id.php +++ /dev/null @@ -1,37 +0,0 @@ -key = $key; - } - - /** - * @warning This is NOT magic, to ensure that people don't abuse SPL and - * cause problems for PHP 5.0 support. - */ - public function toString() { - return $this->key; - } - - public function getRootNamespace() { - return substr($this->key, 0, strpos($this->key, ".")); - } - - public function getDirective() { - return substr($this->key, strpos($this->key, ".") + 1); - } - - public static function make($id) { - return new HTMLPurifier_ConfigSchema_Interchange_Id($id); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php deleted file mode 100644 index 785b72ce8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/InterchangeBuilder.php +++ /dev/null @@ -1,180 +0,0 @@ -varParser = $varParser ? $varParser : new HTMLPurifier_VarParser_Native(); - } - - public static function buildFromDirectory($dir = null) { - $builder = new HTMLPurifier_ConfigSchema_InterchangeBuilder(); - $interchange = new HTMLPurifier_ConfigSchema_Interchange(); - return $builder->buildDir($interchange, $dir); - } - - public function buildDir($interchange, $dir = null) { - if (!$dir) $dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier/ConfigSchema/schema'; - if (file_exists($dir . '/info.ini')) { - $info = parse_ini_file($dir . '/info.ini'); - $interchange->name = $info['name']; - } - - $files = array(); - $dh = opendir($dir); - while (false !== ($file = readdir($dh))) { - if (!$file || $file[0] == '.' || strrchr($file, '.') !== '.txt') { - continue; - } - $files[] = $file; - } - closedir($dh); - - sort($files); - foreach ($files as $file) { - $this->buildFile($interchange, $dir . '/' . $file); - } - - return $interchange; - } - - public function buildFile($interchange, $file) { - $parser = new HTMLPurifier_StringHashParser(); - $this->build( - $interchange, - new HTMLPurifier_StringHash( $parser->parseFile($file) ) - ); - } - - /** - * Builds an interchange object based on a hash. - * @param $interchange HTMLPurifier_ConfigSchema_Interchange object to build - * @param $hash HTMLPurifier_ConfigSchema_StringHash source data - */ - public function build($interchange, $hash) { - if (!$hash instanceof HTMLPurifier_StringHash) { - $hash = new HTMLPurifier_StringHash($hash); - } - if (!isset($hash['ID'])) { - throw new HTMLPurifier_ConfigSchema_Exception('Hash does not have any ID'); - } - if (strpos($hash['ID'], '.') === false) { - if (count($hash) == 2 && isset($hash['DESCRIPTION'])) { - $hash->offsetGet('DESCRIPTION'); // prevent complaining - } else { - throw new HTMLPurifier_ConfigSchema_Exception('All directives must have a namespace'); - } - } else { - $this->buildDirective($interchange, $hash); - } - $this->_findUnused($hash); - } - - public function buildDirective($interchange, $hash) { - $directive = new HTMLPurifier_ConfigSchema_Interchange_Directive(); - - // These are required elements: - $directive->id = $this->id($hash->offsetGet('ID')); - $id = $directive->id->toString(); // convenience - - if (isset($hash['TYPE'])) { - $type = explode('/', $hash->offsetGet('TYPE')); - if (isset($type[1])) $directive->typeAllowsNull = true; - $directive->type = $type[0]; - } else { - throw new HTMLPurifier_ConfigSchema_Exception("TYPE in directive hash '$id' not defined"); - } - - if (isset($hash['DEFAULT'])) { - try { - $directive->default = $this->varParser->parse($hash->offsetGet('DEFAULT'), $directive->type, $directive->typeAllowsNull); - } catch (HTMLPurifier_VarParserException $e) { - throw new HTMLPurifier_ConfigSchema_Exception($e->getMessage() . " in DEFAULT in directive hash '$id'"); - } - } - - if (isset($hash['DESCRIPTION'])) { - $directive->description = $hash->offsetGet('DESCRIPTION'); - } - - if (isset($hash['ALLOWED'])) { - $directive->allowed = $this->lookup($this->evalArray($hash->offsetGet('ALLOWED'))); - } - - if (isset($hash['VALUE-ALIASES'])) { - $directive->valueAliases = $this->evalArray($hash->offsetGet('VALUE-ALIASES')); - } - - if (isset($hash['ALIASES'])) { - $raw_aliases = trim($hash->offsetGet('ALIASES')); - $aliases = preg_split('/\s*,\s*/', $raw_aliases); - foreach ($aliases as $alias) { - $directive->aliases[] = $this->id($alias); - } - } - - if (isset($hash['VERSION'])) { - $directive->version = $hash->offsetGet('VERSION'); - } - - if (isset($hash['DEPRECATED-USE'])) { - $directive->deprecatedUse = $this->id($hash->offsetGet('DEPRECATED-USE')); - } - - if (isset($hash['DEPRECATED-VERSION'])) { - $directive->deprecatedVersion = $hash->offsetGet('DEPRECATED-VERSION'); - } - - if (isset($hash['EXTERNAL'])) { - $directive->external = preg_split('/\s*,\s*/', trim($hash->offsetGet('EXTERNAL'))); - } - - $interchange->addDirective($directive); - } - - /** - * Evaluates an array PHP code string without array() wrapper - */ - protected function evalArray($contents) { - return eval('return array('. $contents .');'); - } - - /** - * Converts an array list into a lookup array. - */ - protected function lookup($array) { - $ret = array(); - foreach ($array as $val) $ret[$val] = true; - return $ret; - } - - /** - * Convenience function that creates an HTMLPurifier_ConfigSchema_Interchange_Id - * object based on a string Id. - */ - protected function id($id) { - return HTMLPurifier_ConfigSchema_Interchange_Id::make($id); - } - - /** - * Triggers errors for any unused keys passed in the hash; such keys - * may indicate typos, missing values, etc. - * @param $hash Instance of ConfigSchema_StringHash to check. - */ - protected function _findUnused($hash) { - $accessed = $hash->getAccessed(); - foreach ($hash as $k => $v) { - if (!isset($accessed[$k])) { - trigger_error("String hash key '$k' not used by builder", E_USER_NOTICE); - } - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php deleted file mode 100644 index f374f6a02..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/Validator.php +++ /dev/null @@ -1,206 +0,0 @@ -parser = new HTMLPurifier_VarParser(); - } - - /** - * Validates a fully-formed interchange object. Throws an - * HTMLPurifier_ConfigSchema_Exception if there's a problem. - */ - public function validate($interchange) { - $this->interchange = $interchange; - $this->aliases = array(); - // PHP is a bit lax with integer <=> string conversions in - // arrays, so we don't use the identical !== comparison - foreach ($interchange->directives as $i => $directive) { - $id = $directive->id->toString(); - if ($i != $id) $this->error(false, "Integrity violation: key '$i' does not match internal id '$id'"); - $this->validateDirective($directive); - } - return true; - } - - /** - * Validates a HTMLPurifier_ConfigSchema_Interchange_Id object. - */ - public function validateId($id) { - $id_string = $id->toString(); - $this->context[] = "id '$id_string'"; - if (!$id instanceof HTMLPurifier_ConfigSchema_Interchange_Id) { - // handled by InterchangeBuilder - $this->error(false, 'is not an instance of HTMLPurifier_ConfigSchema_Interchange_Id'); - } - // keys are now unconstrained (we might want to narrow down to A-Za-z0-9.) - // we probably should check that it has at least one namespace - $this->with($id, 'key') - ->assertNotEmpty() - ->assertIsString(); // implicit assertIsString handled by InterchangeBuilder - array_pop($this->context); - } - - /** - * Validates a HTMLPurifier_ConfigSchema_Interchange_Directive object. - */ - public function validateDirective($d) { - $id = $d->id->toString(); - $this->context[] = "directive '$id'"; - $this->validateId($d->id); - - $this->with($d, 'description') - ->assertNotEmpty(); - - // BEGIN - handled by InterchangeBuilder - $this->with($d, 'type') - ->assertNotEmpty(); - $this->with($d, 'typeAllowsNull') - ->assertIsBool(); - try { - // This also tests validity of $d->type - $this->parser->parse($d->default, $d->type, $d->typeAllowsNull); - } catch (HTMLPurifier_VarParserException $e) { - $this->error('default', 'had error: ' . $e->getMessage()); - } - // END - handled by InterchangeBuilder - - if (!is_null($d->allowed) || !empty($d->valueAliases)) { - // allowed and valueAliases require that we be dealing with - // strings, so check for that early. - $d_int = HTMLPurifier_VarParser::$types[$d->type]; - if (!isset(HTMLPurifier_VarParser::$stringTypes[$d_int])) { - $this->error('type', 'must be a string type when used with allowed or value aliases'); - } - } - - $this->validateDirectiveAllowed($d); - $this->validateDirectiveValueAliases($d); - $this->validateDirectiveAliases($d); - - array_pop($this->context); - } - - /** - * Extra validation if $allowed member variable of - * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. - */ - public function validateDirectiveAllowed($d) { - if (is_null($d->allowed)) return; - $this->with($d, 'allowed') - ->assertNotEmpty() - ->assertIsLookup(); // handled by InterchangeBuilder - if (is_string($d->default) && !isset($d->allowed[$d->default])) { - $this->error('default', 'must be an allowed value'); - } - $this->context[] = 'allowed'; - foreach ($d->allowed as $val => $x) { - if (!is_string($val)) $this->error("value $val", 'must be a string'); - } - array_pop($this->context); - } - - /** - * Extra validation if $valueAliases member variable of - * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. - */ - public function validateDirectiveValueAliases($d) { - if (is_null($d->valueAliases)) return; - $this->with($d, 'valueAliases') - ->assertIsArray(); // handled by InterchangeBuilder - $this->context[] = 'valueAliases'; - foreach ($d->valueAliases as $alias => $real) { - if (!is_string($alias)) $this->error("alias $alias", 'must be a string'); - if (!is_string($real)) $this->error("alias target $real from alias '$alias'", 'must be a string'); - if ($alias === $real) { - $this->error("alias '$alias'", "must not be an alias to itself"); - } - } - if (!is_null($d->allowed)) { - foreach ($d->valueAliases as $alias => $real) { - if (isset($d->allowed[$alias])) { - $this->error("alias '$alias'", 'must not be an allowed value'); - } elseif (!isset($d->allowed[$real])) { - $this->error("alias '$alias'", 'must be an alias to an allowed value'); - } - } - } - array_pop($this->context); - } - - /** - * Extra validation if $aliases member variable of - * HTMLPurifier_ConfigSchema_Interchange_Directive is defined. - */ - public function validateDirectiveAliases($d) { - $this->with($d, 'aliases') - ->assertIsArray(); // handled by InterchangeBuilder - $this->context[] = 'aliases'; - foreach ($d->aliases as $alias) { - $this->validateId($alias); - $s = $alias->toString(); - if (isset($this->interchange->directives[$s])) { - $this->error("alias '$s'", 'collides with another directive'); - } - if (isset($this->aliases[$s])) { - $other_directive = $this->aliases[$s]; - $this->error("alias '$s'", "collides with alias for directive '$other_directive'"); - } - $this->aliases[$s] = $d->id->toString(); - } - array_pop($this->context); - } - - // protected helper functions - - /** - * Convenience function for generating HTMLPurifier_ConfigSchema_ValidatorAtom - * for validating simple member variables of objects. - */ - protected function with($obj, $member) { - return new HTMLPurifier_ConfigSchema_ValidatorAtom($this->getFormattedContext(), $obj, $member); - } - - /** - * Emits an error, providing helpful context. - */ - protected function error($target, $msg) { - if ($target !== false) $prefix = ucfirst($target) . ' in ' . $this->getFormattedContext(); - else $prefix = ucfirst($this->getFormattedContext()); - throw new HTMLPurifier_ConfigSchema_Exception(trim($prefix . ' ' . $msg)); - } - - /** - * Returns a formatted context string. - */ - protected function getFormattedContext() { - return implode(' in ', array_reverse($this->context)); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php deleted file mode 100644 index b95aea18c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/ValidatorAtom.php +++ /dev/null @@ -1,66 +0,0 @@ -context = $context; - $this->obj = $obj; - $this->member = $member; - $this->contents =& $obj->$member; - } - - public function assertIsString() { - if (!is_string($this->contents)) $this->error('must be a string'); - return $this; - } - - public function assertIsBool() { - if (!is_bool($this->contents)) $this->error('must be a boolean'); - return $this; - } - - public function assertIsArray() { - if (!is_array($this->contents)) $this->error('must be an array'); - return $this; - } - - public function assertNotNull() { - if ($this->contents === null) $this->error('must not be null'); - return $this; - } - - public function assertAlnum() { - $this->assertIsString(); - if (!ctype_alnum($this->contents)) $this->error('must be alphanumeric'); - return $this; - } - - public function assertNotEmpty() { - if (empty($this->contents)) $this->error('must not be empty'); - return $this; - } - - public function assertIsLookup() { - $this->assertIsArray(); - foreach ($this->contents as $v) { - if ($v !== true) $this->error('must be a lookup array'); - } - return $this; - } - - protected function error($msg) { - throw new HTMLPurifier_ConfigSchema_Exception(ucfirst($this->member) . ' in ' . $this->context . ' ' . $msg); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser deleted file mode 100644 index bbf12f9c3..000000000 Binary files a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser and /dev/null differ diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt deleted file mode 100644 index 0517fed0a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedClasses.txt +++ /dev/null @@ -1,8 +0,0 @@ -Attr.AllowedClasses -TYPE: lookup/null -VERSION: 4.0.0 -DEFAULT: null ---DESCRIPTION-- -List of allowed class values in the class attribute. By default, this is null, -which means all classes are allowed. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt deleted file mode 100644 index 249edd647..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedFrameTargets.txt +++ /dev/null @@ -1,12 +0,0 @@ -Attr.AllowedFrameTargets -TYPE: lookup -DEFAULT: array() ---DESCRIPTION-- -Lookup table of all allowed link frame targets. Some commonly used link -targets include _blank, _self, _parent and _top. Values should be -lowercase, as validation will be done in a case-sensitive manner despite -W3C's recommendation. XHTML 1.0 Strict does not permit the target attribute -so this directive will have no effect in that doctype. XHTML 1.1 does not -enable the Target module by default, you will have to manually enable it -(see the module documentation for more details.) ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt deleted file mode 100644 index 9a8fa6a2e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRel.txt +++ /dev/null @@ -1,9 +0,0 @@ -Attr.AllowedRel -TYPE: lookup -VERSION: 1.6.0 -DEFAULT: array() ---DESCRIPTION-- -List of allowed forward document relationships in the rel attribute. Common -values may be nofollow or print. By default, this is empty, meaning that no -document relationships are allowed. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt deleted file mode 100644 index b01788348..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.AllowedRev.txt +++ /dev/null @@ -1,9 +0,0 @@ -Attr.AllowedRev -TYPE: lookup -VERSION: 1.6.0 -DEFAULT: array() ---DESCRIPTION-- -List of allowed reverse document relationships in the rev attribute. This -attribute is a bit of an edge-case; if you don't know what it is for, stay -away. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt deleted file mode 100644 index e774b823b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ClassUseCDATA.txt +++ /dev/null @@ -1,19 +0,0 @@ -Attr.ClassUseCDATA -TYPE: bool/null -DEFAULT: null -VERSION: 4.0.0 ---DESCRIPTION-- -If null, class will auto-detect the doctype and, if matching XHTML 1.1 or -XHTML 2.0, will use the restrictive NMTOKENS specification of class. Otherwise, -it will use a relaxed CDATA definition. If true, the relaxed CDATA definition -is forced; if false, the NMTOKENS definition is forced. To get behavior -of HTML Purifier prior to 4.0.0, set this directive to false. - -Some rational behind the auto-detection: -in previous versions of HTML Purifier, it was assumed that the form of -class was NMTOKENS, as specified by the XHTML Modularization (representing -XHTML 1.1 and XHTML 2.0). The DTDs for HTML 4.01 and XHTML 1.0, however -specify class as CDATA. HTML 5 effectively defines it as CDATA, but -with the additional constraint that each name should be unique (this is not -explicitly outlined in previous specifications). ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt deleted file mode 100644 index 533165e17..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultImageAlt.txt +++ /dev/null @@ -1,11 +0,0 @@ -Attr.DefaultImageAlt -TYPE: string/null -DEFAULT: null -VERSION: 3.2.0 ---DESCRIPTION-- -This is the content of the alt tag of an image if the user had not -previously specified an alt attribute. This applies to all images without -a valid alt attribute, as opposed to %Attr.DefaultInvalidImageAlt, which -only applies to invalid images, and overrides in the case of an invalid image. -Default behavior with null is to use the basename of the src tag for the alt. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt deleted file mode 100644 index 9eb7e3846..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImage.txt +++ /dev/null @@ -1,9 +0,0 @@ -Attr.DefaultInvalidImage -TYPE: string -DEFAULT: '' ---DESCRIPTION-- -This is the default image an img tag will be pointed to if it does not have -a valid src attribute. In future versions, we may allow the image tag to -be removed completely, but due to design issues, this is not possible right -now. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt deleted file mode 100644 index 2f17bf477..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultInvalidImageAlt.txt +++ /dev/null @@ -1,8 +0,0 @@ -Attr.DefaultInvalidImageAlt -TYPE: string -DEFAULT: 'Invalid image' ---DESCRIPTION-- -This is the content of the alt tag of an invalid image if the user had not -previously specified an alt attribute. It has no effect when the image is -valid but there was no alt attribute present. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt deleted file mode 100644 index 52654b53a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.DefaultTextDir.txt +++ /dev/null @@ -1,10 +0,0 @@ -Attr.DefaultTextDir -TYPE: string -DEFAULT: 'ltr' ---DESCRIPTION-- -Defines the default text direction (ltr or rtl) of the document being -parsed. This generally is the same as the value of the dir attribute in -HTML, or ltr if that is not specified. ---ALLOWED-- -'ltr', 'rtl' ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt deleted file mode 100644 index 6440d2103..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.EnableID.txt +++ /dev/null @@ -1,16 +0,0 @@ -Attr.EnableID -TYPE: bool -DEFAULT: false -VERSION: 1.2.0 ---DESCRIPTION-- -Allows the ID attribute in HTML. This is disabled by default due to the -fact that without proper configuration user input can easily break the -validation of a webpage by specifying an ID that is already on the -surrounding HTML. If you don't mind throwing caution to the wind, enable -this directive, but I strongly recommend you also consider blacklisting IDs -you use (%Attr.IDBlacklist) or prefixing all user supplied IDs -(%Attr.IDPrefix). When set to true HTML Purifier reverts to the behavior of -pre-1.2.0 versions. ---ALIASES-- -HTML.EnableAttrID ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt deleted file mode 100644 index f31d226f5..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ForbiddenClasses.txt +++ /dev/null @@ -1,8 +0,0 @@ -Attr.ForbiddenClasses -TYPE: lookup -VERSION: 4.0.0 -DEFAULT: array() ---DESCRIPTION-- -List of forbidden class values in the class attribute. By default, this is -empty, which means that no classes are forbidden. See also %Attr.AllowedClasses. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt deleted file mode 100644 index 5f2b5e3d2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklist.txt +++ /dev/null @@ -1,5 +0,0 @@ -Attr.IDBlacklist -TYPE: list -DEFAULT: array() -DESCRIPTION: Array of IDs not allowed in the document. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt deleted file mode 100644 index 6f5824586..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDBlacklistRegexp.txt +++ /dev/null @@ -1,9 +0,0 @@ -Attr.IDBlacklistRegexp -TYPE: string/null -VERSION: 1.6.0 -DEFAULT: NULL ---DESCRIPTION-- -PCRE regular expression to be matched against all IDs. If the expression is -matches, the ID is rejected. Use this with care: may cause significant -degradation. ID matching is done after all other validation. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt deleted file mode 100644 index cc49d43fd..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefix.txt +++ /dev/null @@ -1,12 +0,0 @@ -Attr.IDPrefix -TYPE: string -VERSION: 1.2.0 -DEFAULT: '' ---DESCRIPTION-- -String to prefix to IDs. If you have no idea what IDs your pages may use, -you may opt to simply add a prefix to all user-submitted ID attributes so -that they are still usable, but will not conflict with core page IDs. -Example: setting the directive to 'user_' will result in a user submitted -'foo' to become 'user_foo' Be sure to set %HTML.EnableAttrID to true -before using this. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt deleted file mode 100644 index 2c5924a7a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.IDPrefixLocal.txt +++ /dev/null @@ -1,14 +0,0 @@ -Attr.IDPrefixLocal -TYPE: string -VERSION: 1.2.0 -DEFAULT: '' ---DESCRIPTION-- -Temporary prefix for IDs used in conjunction with %Attr.IDPrefix. If you -need to allow multiple sets of user content on web page, you may need to -have a seperate prefix that changes with each iteration. This way, -seperately submitted user content displayed on the same page doesn't -clobber each other. Ideal values are unique identifiers for the content it -represents (i.e. the id of the row in the database). Be sure to add a -seperator (like an underscore) at the end. Warning: this directive will -not work unless %Attr.IDPrefix is set to a non-empty value! ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt deleted file mode 100644 index d5caa1bb9..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.AutoParagraph.txt +++ /dev/null @@ -1,31 +0,0 @@ -AutoFormat.AutoParagraph -TYPE: bool -VERSION: 2.0.1 -DEFAULT: false ---DESCRIPTION-- - -

- This directive turns on auto-paragraphing, where double newlines are - converted in to paragraphs whenever possible. Auto-paragraphing: -

-
    -
  • Always applies to inline elements or text in the root node,
  • -
  • Applies to inline elements or text with double newlines in nodes - that allow paragraph tags,
  • -
  • Applies to double newlines in paragraph tags
  • -
-

- p tags must be allowed for this directive to take effect. - We do not use br tags for paragraphing, as that is - semantically incorrect. -

-

- To prevent auto-paragraphing as a content-producer, refrain from using - double-newlines except to specify a new paragraph or in contexts where - it has special meaning (whitespace usually has no meaning except in - tags like pre, so this should not be difficult.) To prevent - the paragraphing of inline text adjacent to block elements, wrap them - in div tags (the behavior is slightly different outside of - the root node.) -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt deleted file mode 100644 index 2a476481a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Custom.txt +++ /dev/null @@ -1,12 +0,0 @@ -AutoFormat.Custom -TYPE: list -VERSION: 2.0.1 -DEFAULT: array() ---DESCRIPTION-- - -

- This directive can be used to add custom auto-format injectors. - Specify an array of injector names (class name minus the prefix) - or concrete implementations. Injector class must exist. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt deleted file mode 100644 index 663064a34..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.DisplayLinkURI.txt +++ /dev/null @@ -1,11 +0,0 @@ -AutoFormat.DisplayLinkURI -TYPE: bool -VERSION: 3.2.0 -DEFAULT: false ---DESCRIPTION-- -

- This directive turns on the in-text display of URIs in <a> tags, and disables - those links. For example, example becomes - example (http://example.com). -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt deleted file mode 100644 index 3a48ba960..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.Linkify.txt +++ /dev/null @@ -1,12 +0,0 @@ -AutoFormat.Linkify -TYPE: bool -VERSION: 2.0.1 -DEFAULT: false ---DESCRIPTION-- - -

- This directive turns on linkification, auto-linking http, ftp and - https URLs. a tags with the href attribute - must be allowed. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt deleted file mode 100644 index db58b1346..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.DocURL.txt +++ /dev/null @@ -1,12 +0,0 @@ -AutoFormat.PurifierLinkify.DocURL -TYPE: string -VERSION: 2.0.1 -DEFAULT: '#%s' -ALIASES: AutoFormatParam.PurifierLinkifyDocURL ---DESCRIPTION-- -

- Location of configuration documentation to link to, let %s substitute - into the configuration's namespace and directive names sans the percent - sign. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt deleted file mode 100644 index 7996488be..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.PurifierLinkify.txt +++ /dev/null @@ -1,12 +0,0 @@ -AutoFormat.PurifierLinkify -TYPE: bool -VERSION: 2.0.1 -DEFAULT: false ---DESCRIPTION-- - -

- Internal auto-formatter that converts configuration directives in - syntax %Namespace.Directive to links. a tags - with the href attribute must be allowed. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt deleted file mode 100644 index 35c393b4e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions.txt +++ /dev/null @@ -1,11 +0,0 @@ -AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions -TYPE: lookup -VERSION: 4.0.0 -DEFAULT: array('td' => true, 'th' => true) ---DESCRIPTION-- -

- When %AutoFormat.RemoveEmpty and %AutoFormat.RemoveEmpty.RemoveNbsp - are enabled, this directive defines what HTML elements should not be - removede if they have only a non-breaking space in them. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt deleted file mode 100644 index ca17eb1dc..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.RemoveNbsp.txt +++ /dev/null @@ -1,15 +0,0 @@ -AutoFormat.RemoveEmpty.RemoveNbsp -TYPE: bool -VERSION: 4.0.0 -DEFAULT: false ---DESCRIPTION-- -

- When enabled, HTML Purifier will treat any elements that contain only - non-breaking spaces as well as regular whitespace as empty, and remove - them when %AutoForamt.RemoveEmpty is enabled. -

-

- See %AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions for a list of elements - that don't have this behavior applied to them. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt deleted file mode 100644 index 34657ba47..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/AutoFormat.RemoveEmpty.txt +++ /dev/null @@ -1,46 +0,0 @@ -AutoFormat.RemoveEmpty -TYPE: bool -VERSION: 3.2.0 -DEFAULT: false ---DESCRIPTION-- -

- When enabled, HTML Purifier will attempt to remove empty elements that - contribute no semantic information to the document. The following types - of nodes will be removed: -

-
  • - Tags with no attributes and no content, and that are not empty - elements (remove <a></a> but not - <br />), and -
  • -
  • - Tags with no content, except for:
      -
    • The colgroup element, or
    • -
    • - Elements with the id or name attribute, - when those attributes are permitted on those elements. -
    • -
  • -
-

- Please be very careful when using this functionality; while it may not - seem that empty elements contain useful information, they can alter the - layout of a document given appropriate styling. This directive is most - useful when you are processing machine-generated HTML, please avoid using - it on regular user HTML. -

-

- Elements that contain only whitespace will be treated as empty. Non-breaking - spaces, however, do not count as whitespace. See - %AutoFormat.RemoveEmpty.RemoveNbsp for alternate behavior. -

-

- This algorithm is not perfect; you may still notice some empty tags, - particularly if a node had elements, but those elements were later removed - because they were not permitted in that context, or tags that, after - being auto-closed by another tag, where empty. This is for safety reasons - to prevent clever code from breaking validation. The general rule of thumb: - if a tag looked empty on the way in, it will get removed; if HTML Purifier - made it empty, it will stay. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt deleted file mode 100644 index b324608f7..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowImportant.txt +++ /dev/null @@ -1,8 +0,0 @@ -CSS.AllowImportant -TYPE: bool -DEFAULT: false -VERSION: 3.1.0 ---DESCRIPTION-- -This parameter determines whether or not !important cascade modifiers should -be allowed in user CSS. If false, !important will stripped. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt deleted file mode 100644 index 748be0eec..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowTricky.txt +++ /dev/null @@ -1,11 +0,0 @@ -CSS.AllowTricky -TYPE: bool -DEFAULT: false -VERSION: 3.1.0 ---DESCRIPTION-- -This parameter determines whether or not to allow "tricky" CSS properties and -values. Tricky CSS properties/values can drastically modify page layout or -be used for deceptive practices but do not directly constitute a security risk. -For example, display:none; is considered a tricky property that -will only be allowed if this directive is set to true. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt deleted file mode 100644 index 460112ebe..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowedProperties.txt +++ /dev/null @@ -1,18 +0,0 @@ -CSS.AllowedProperties -TYPE: lookup/null -VERSION: 3.1.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- If HTML Purifier's style attributes set is unsatisfactory for your needs, - you can overload it with your own list of tags to allow. Note that this - method is subtractive: it does its job by taking away from HTML Purifier - usual feature set, so you cannot add an attribute that HTML Purifier never - supported in the first place. -

-

- Warning: If another directive conflicts with the - elements here, that directive will win and override. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt deleted file mode 100644 index 5cb7dda3b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.DefinitionRev.txt +++ /dev/null @@ -1,11 +0,0 @@ -CSS.DefinitionRev -TYPE: int -VERSION: 2.0.0 -DEFAULT: 1 ---DESCRIPTION-- - -

- Revision identifier for your custom definition. See - %HTML.DefinitionRev for details. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt deleted file mode 100644 index 7a3291470..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.MaxImgLength.txt +++ /dev/null @@ -1,16 +0,0 @@ -CSS.MaxImgLength -TYPE: string/null -DEFAULT: '1200px' -VERSION: 3.1.1 ---DESCRIPTION-- -

- This parameter sets the maximum allowed length on img tags, - effectively the width and height properties. - Only absolute units of measurement (in, pt, pc, mm, cm) and pixels (px) are allowed. This is - in place to prevent imagecrash attacks, disable with null at your own risk. - This directive is similar to %HTML.MaxImgLength, and both should be - concurrently edited, although there are - subtle differences in the input format (the CSS max is a number with - a unit). -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt deleted file mode 100644 index 148eedb8b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.Proprietary.txt +++ /dev/null @@ -1,10 +0,0 @@ -CSS.Proprietary -TYPE: bool -VERSION: 3.0.0 -DEFAULT: false ---DESCRIPTION-- - -

- Whether or not to allow safe, proprietary CSS values. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt deleted file mode 100644 index c486724c8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.DefinitionImpl.txt +++ /dev/null @@ -1,14 +0,0 @@ -Cache.DefinitionImpl -TYPE: string/null -VERSION: 2.0.0 -DEFAULT: 'Serializer' ---DESCRIPTION-- - -This directive defines which method to use when caching definitions, -the complex data-type that makes HTML Purifier tick. Set to null -to disable caching (not recommended, as you will see a definite -performance degradation). - ---ALIASES-- -Core.DefinitionCache ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt deleted file mode 100644 index 54036507d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPath.txt +++ /dev/null @@ -1,13 +0,0 @@ -Cache.SerializerPath -TYPE: string/null -VERSION: 2.0.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- Absolute path with no trailing slash to store serialized definitions in. - Default is within the - HTML Purifier library inside DefinitionCache/Serializer. This - path must be writable by the webserver. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt deleted file mode 100644 index 568cbf3b3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.AggressivelyFixLt.txt +++ /dev/null @@ -1,18 +0,0 @@ -Core.AggressivelyFixLt -TYPE: bool -VERSION: 2.1.0 -DEFAULT: true ---DESCRIPTION-- -

- This directive enables aggressive pre-filter fixes HTML Purifier can - perform in order to ensure that open angled-brackets do not get killed - during parsing stage. Enabling this will result in two preg_replace_callback - calls and at least two preg_replace calls for every HTML document parsed; - if your users make very well-formed HTML, you can set this directive false. - This has no effect when DirectLex is used. -

-

- Notice: This directive's default turned from false to true - in HTML Purifier 3.2.0. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt deleted file mode 100644 index d7317911f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.CollectErrors.txt +++ /dev/null @@ -1,12 +0,0 @@ -Core.CollectErrors -TYPE: bool -VERSION: 2.0.0 -DEFAULT: false ---DESCRIPTION-- - -Whether or not to collect errors found while filtering the document. This -is a useful way to give feedback to your users. Warning: -Currently this feature is very patchy and experimental, with lots of -possible error messages not yet implemented. It will not cause any -problems, but it may not help your users either. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt deleted file mode 100644 index 08b381d34..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ColorKeywords.txt +++ /dev/null @@ -1,28 +0,0 @@ -Core.ColorKeywords -TYPE: hash -VERSION: 2.0.0 ---DEFAULT-- -array ( - 'maroon' => '#800000', - 'red' => '#FF0000', - 'orange' => '#FFA500', - 'yellow' => '#FFFF00', - 'olive' => '#808000', - 'purple' => '#800080', - 'fuchsia' => '#FF00FF', - 'white' => '#FFFFFF', - 'lime' => '#00FF00', - 'green' => '#008000', - 'navy' => '#000080', - 'blue' => '#0000FF', - 'aqua' => '#00FFFF', - 'teal' => '#008080', - 'black' => '#000000', - 'silver' => '#C0C0C0', - 'gray' => '#808080', -) ---DESCRIPTION-- - -Lookup array of color names to six digit hexadecimal number corresponding -to color, with preceding hash mark. Used when parsing colors. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt deleted file mode 100644 index 64b114fce..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.ConvertDocumentToFragment.txt +++ /dev/null @@ -1,14 +0,0 @@ -Core.ConvertDocumentToFragment -TYPE: bool -DEFAULT: true ---DESCRIPTION-- - -This parameter determines whether or not the filter should convert -input that is a full document with html and body tags to a fragment -of just the contents of a body tag. This parameter is simply something -HTML Purifier can do during an edge-case: for most inputs, this -processing is not necessary. - ---ALIASES-- -Core.AcceptFullDocuments ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt deleted file mode 100644 index 36f16e07e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.DirectLexLineNumberSyncInterval.txt +++ /dev/null @@ -1,17 +0,0 @@ -Core.DirectLexLineNumberSyncInterval -TYPE: int -VERSION: 2.0.0 -DEFAULT: 0 ---DESCRIPTION-- - -

- Specifies the number of tokens the DirectLex line number tracking - implementations should process before attempting to resyncronize the - current line count by manually counting all previous new-lines. When - at 0, this functionality is disabled. Lower values will decrease - performance, and this is only strictly necessary if the counting - algorithm is buggy (in which case you should report it as a bug). - This has no effect when %Core.MaintainLineNumbers is disabled or DirectLex is - not being used. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt deleted file mode 100644 index 8bfb47c3a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Encoding.txt +++ /dev/null @@ -1,15 +0,0 @@ -Core.Encoding -TYPE: istring -DEFAULT: 'utf-8' ---DESCRIPTION-- -If for some reason you are unable to convert all webpages to UTF-8, you can -use this directive as a stop-gap compatibility change to let HTML Purifier -deal with non UTF-8 input. This technique has notable deficiencies: -absolutely no characters outside of the selected character encoding will be -preserved, not even the ones that have been ampersand escaped (this is due -to a UTF-8 specific feature that automatically resolves all -entities), making it pretty useless for anything except the most I18N-blind -applications, although %Core.EscapeNonASCIICharacters offers fixes this -trouble with another tradeoff. This directive only accepts ISO-8859-1 if -iconv is not enabled. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt deleted file mode 100644 index 4d5b5055c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidChildren.txt +++ /dev/null @@ -1,10 +0,0 @@ -Core.EscapeInvalidChildren -TYPE: bool -DEFAULT: false ---DESCRIPTION-- -When true, a child is found that is not allowed in the context of the -parent element will be transformed into text as if it were ASCII. When -false, that element and all internal tags will be dropped, though text will -be preserved. There is no option for dropping the element but preserving -child nodes. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt deleted file mode 100644 index a7a5b249b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeInvalidTags.txt +++ /dev/null @@ -1,7 +0,0 @@ -Core.EscapeInvalidTags -TYPE: bool -DEFAULT: false ---DESCRIPTION-- -When true, invalid tags will be written back to the document as plain text. -Otherwise, they are silently dropped. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt deleted file mode 100644 index abb499948..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.EscapeNonASCIICharacters.txt +++ /dev/null @@ -1,13 +0,0 @@ -Core.EscapeNonASCIICharacters -TYPE: bool -VERSION: 1.4.0 -DEFAULT: false ---DESCRIPTION-- -This directive overcomes a deficiency in %Core.Encoding by blindly -converting all non-ASCII characters into decimal numeric entities before -converting it to its native encoding. This means that even characters that -can be expressed in the non-UTF-8 encoding will be entity-ized, which can -be a real downer for encodings like Big5. It also assumes that the ASCII -repetoire is available, although this is the case for almost all encodings. -Anyway, use UTF-8! ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt deleted file mode 100644 index 915391edb..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.HiddenElements.txt +++ /dev/null @@ -1,19 +0,0 @@ -Core.HiddenElements -TYPE: lookup ---DEFAULT-- -array ( - 'script' => true, - 'style' => true, -) ---DESCRIPTION-- - -

- This directive is a lookup array of elements which should have their - contents removed when they are not allowed by the HTML definition. - For example, the contents of a script tag are not - normally shown in a document, so if script tags are to be removed, - their contents should be removed to. This is opposed to a b - tag, which defines some presentational changes but does not hide its - contents. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt deleted file mode 100644 index 233fca14f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.Language.txt +++ /dev/null @@ -1,10 +0,0 @@ -Core.Language -TYPE: string -VERSION: 2.0.0 -DEFAULT: 'en' ---DESCRIPTION-- - -ISO 639 language code for localizable things in HTML Purifier to use, -which is mainly error reporting. There is currently only an English (en) -translation, so this directive is currently useless. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt deleted file mode 100644 index 8983e2cca..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.LexerImpl.txt +++ /dev/null @@ -1,34 +0,0 @@ -Core.LexerImpl -TYPE: mixed/null -VERSION: 2.0.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- This parameter determines what lexer implementation can be used. The - valid values are: -

-
-
null
-
- Recommended, the lexer implementation will be auto-detected based on - your PHP-version and configuration. -
-
string lexer identifier
-
- This is a slim way of manually overridding the implementation. - Currently recognized values are: DOMLex (the default PHP5 -implementation) - and DirectLex (the default PHP4 implementation). Only use this if - you know what you are doing: usually, the auto-detection will - manage things for cases you aren't even aware of. -
-
object lexer instance
-
- Super-advanced: you can specify your own, custom, implementation that - implements the interface defined by HTMLPurifier_Lexer. - I may remove this option simply because I don't expect anyone - to use it. -
-
---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt deleted file mode 100644 index eb841a759..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.MaintainLineNumbers.txt +++ /dev/null @@ -1,16 +0,0 @@ -Core.MaintainLineNumbers -TYPE: bool/null -VERSION: 2.0.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- If true, HTML Purifier will add line number information to all tokens. - This is useful when error reporting is turned on, but can result in - significant performance degradation and should not be used when - unnecessary. This directive must be used with the DirectLex lexer, - as the DOMLex lexer does not (yet) support this functionality. - If the value is null, an appropriate value will be selected based - on other configuration. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt deleted file mode 100644 index 4070c2a0d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveInvalidImg.txt +++ /dev/null @@ -1,12 +0,0 @@ -Core.RemoveInvalidImg -TYPE: bool -DEFAULT: true -VERSION: 1.3.0 ---DESCRIPTION-- - -

- This directive enables pre-emptive URI checking in img - tags, as the attribute validation strategy is not authorized to - remove elements from the document. Revert to pre-1.3.0 behavior by setting to false. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt deleted file mode 100644 index a4cd966df..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Core.RemoveScriptContents.txt +++ /dev/null @@ -1,12 +0,0 @@ -Core.RemoveScriptContents -TYPE: bool/null -DEFAULT: NULL -VERSION: 2.0.0 -DEPRECATED-VERSION: 2.1.0 -DEPRECATED-USE: Core.HiddenElements ---DESCRIPTION-- -

- This directive enables HTML Purifier to remove not only script tags - but all of their contents. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt deleted file mode 100644 index 3db50ef20..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.Custom.txt +++ /dev/null @@ -1,11 +0,0 @@ -Filter.Custom -TYPE: list -VERSION: 3.1.0 -DEFAULT: array() ---DESCRIPTION-- -

- This directive can be used to add custom filters; it is nearly the - equivalent of the now deprecated HTMLPurifier->addFilter() - method. Specify an array of concrete implementations. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt deleted file mode 100644 index 16829bcda..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Escaping.txt +++ /dev/null @@ -1,14 +0,0 @@ -Filter.ExtractStyleBlocks.Escaping -TYPE: bool -VERSION: 3.0.0 -DEFAULT: true -ALIASES: Filter.ExtractStyleBlocksEscaping, FilterParam.ExtractStyleBlocksEscaping ---DESCRIPTION-- - -

- Whether or not to escape the dangerous characters <, > and & - as \3C, \3E and \26, respectively. This is can be safely set to false - if the contents of StyleBlocks will be placed in an external stylesheet, - where there is no risk of it being interpreted as HTML. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt deleted file mode 100644 index 7f95f54d1..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.Scope.txt +++ /dev/null @@ -1,29 +0,0 @@ -Filter.ExtractStyleBlocks.Scope -TYPE: string/null -VERSION: 3.0.0 -DEFAULT: NULL -ALIASES: Filter.ExtractStyleBlocksScope, FilterParam.ExtractStyleBlocksScope ---DESCRIPTION-- - -

- If you would like users to be able to define external stylesheets, but - only allow them to specify CSS declarations for a specific node and - prevent them from fiddling with other elements, use this directive. - It accepts any valid CSS selector, and will prepend this to any - CSS declaration extracted from the document. For example, if this - directive is set to #user-content and a user uses the - selector a:hover, the final selector will be - #user-content a:hover. -

-

- The comma shorthand may be used; consider the above example, with - #user-content, #user-content2, the final selector will - be #user-content a:hover, #user-content2 a:hover. -

-

- Warning: It is possible for users to bypass this measure - using a naughty + selector. This is a bug in CSS Tidy 1.3, not HTML - Purifier, and I am working to get it fixed. Until then, HTML Purifier - performs a basic check to prevent this. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt deleted file mode 100644 index 6c231b2d7..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.TidyImpl.txt +++ /dev/null @@ -1,16 +0,0 @@ -Filter.ExtractStyleBlocks.TidyImpl -TYPE: mixed/null -VERSION: 3.1.0 -DEFAULT: NULL -ALIASES: FilterParam.ExtractStyleBlocksTidyImpl ---DESCRIPTION-- -

- If left NULL, HTML Purifier will attempt to instantiate a csstidy - class to use for internal cleaning. This will usually be good enough. -

-

- However, for trusted user input, you can set this to false to - disable cleaning. In addition, you can supply your own concrete implementation - of Tidy's interface to use, although I don't know why you'd want to do that. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt deleted file mode 100644 index 078d08741..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.ExtractStyleBlocks.txt +++ /dev/null @@ -1,74 +0,0 @@ -Filter.ExtractStyleBlocks -TYPE: bool -VERSION: 3.1.0 -DEFAULT: false -EXTERNAL: CSSTidy ---DESCRIPTION-- -

- This directive turns on the style block extraction filter, which removes - style blocks from input HTML, cleans them up with CSSTidy, - and places them in the StyleBlocks context variable, for further - use by you, usually to be placed in an external stylesheet, or a - style block in the head of your document. -

-

- Sample usage: -

-
';
-?>
-
-
-
-  Filter.ExtractStyleBlocks
-body {color:#F00;} Some text';
-
-    $config = HTMLPurifier_Config::createDefault();
-    $config->set('Filter', 'ExtractStyleBlocks', true);
-    $purifier = new HTMLPurifier($config);
-
-    $html = $purifier->purify($dirty);
-
-    // This implementation writes the stylesheets to the styles/ directory.
-    // You can also echo the styles inside the document, but it's a bit
-    // more difficult to make sure they get interpreted properly by
-    // browsers; try the usual CSS armoring techniques.
-    $styles = $purifier->context->get('StyleBlocks');
-    $dir = 'styles/';
-    if (!is_dir($dir)) mkdir($dir);
-    $hash = sha1($_GET['html']);
-    foreach ($styles as $i => $style) {
-        file_put_contents($name = $dir . $hash . "_$i");
-        echo '';
-    }
-?>
-
-
-  
- -
- - -]]>
-

- Warning: It is possible for a user to mount an - imagecrash attack using this CSS. Counter-measures are difficult; - it is not simply enough to limit the range of CSS lengths (using - relative lengths with many nesting levels allows for large values - to be attained without actually specifying them in the stylesheet), - and the flexible nature of selectors makes it difficult to selectively - disable lengths on image tags (HTML Purifier, however, does disable - CSS width and height in inline styling). There are probably two effective - counter measures: an explicit width and height set to auto in all - images in your document (unlikely) or the disabling of width and - height (somewhat reasonable). Whether or not these measures should be - used is left to the reader. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt deleted file mode 100644 index 7fa6536b2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Filter.YouTube.txt +++ /dev/null @@ -1,11 +0,0 @@ -Filter.YouTube -TYPE: bool -VERSION: 3.1.0 -DEFAULT: false ---DESCRIPTION-- -

- This directive enables YouTube video embedding in HTML Purifier. Check - this document - on embedding videos for more information on what this filter does. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt deleted file mode 100644 index 3e231d2d1..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Allowed.txt +++ /dev/null @@ -1,22 +0,0 @@ -HTML.Allowed -TYPE: itext/null -VERSION: 2.0.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- This is a convenience directive that rolls the functionality of - %HTML.AllowedElements and %HTML.AllowedAttributes into one directive. - Specify elements and attributes that are allowed using: - element1[attr1|attr2],element2.... You can also use - newlines instead of commas to separate elements. -

-

- Warning: - All of the constraints on the component directives are still enforced. - The syntax is a subset of TinyMCE's valid_elements - whitelist: directly copy-pasting it here will probably result in - broken whitelists. If %HTML.AllowedElements or %HTML.AllowedAttributes - are set, this directive has no effect. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt deleted file mode 100644 index fcf093f17..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedAttributes.txt +++ /dev/null @@ -1,19 +0,0 @@ -HTML.AllowedAttributes -TYPE: lookup/null -VERSION: 1.3.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- If HTML Purifier's attribute set is unsatisfactory, overload it! - The syntax is "tag.attr" or "*.attr" for the global attributes - (style, id, class, dir, lang, xml:lang). -

-

- Warning: If another directive conflicts with the - elements here, that directive will win and override. For - example, %HTML.EnableAttrID will take precedence over *.id in this - directive. You must set that directive to true before you can use - IDs at all. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt deleted file mode 100644 index 888d55819..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedElements.txt +++ /dev/null @@ -1,18 +0,0 @@ -HTML.AllowedElements -TYPE: lookup/null -VERSION: 1.3.0 -DEFAULT: NULL ---DESCRIPTION-- -

- If HTML Purifier's tag set is unsatisfactory for your needs, you - can overload it with your own list of tags to allow. Note that this - method is subtractive: it does its job by taking away from HTML Purifier - usual feature set, so you cannot add a tag that HTML Purifier never - supported in the first place (like embed, form or head). If you - change this, you probably also want to change %HTML.AllowedAttributes. -

-

- Warning: If another directive conflicts with the - elements here, that directive will win and override. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt deleted file mode 100644 index 5a59a55c0..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.AllowedModules.txt +++ /dev/null @@ -1,20 +0,0 @@ -HTML.AllowedModules -TYPE: lookup/null -VERSION: 2.0.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- A doctype comes with a set of usual modules to use. Without having - to mucking about with the doctypes, you can quickly activate or - disable these modules by specifying which modules you wish to allow - with this directive. This is most useful for unit testing specific - modules, although end users may find it useful for their own ends. -

-

- If you specify a module that does not exist, the manager will silently - fail to use it, so be careful! User-defined modules are not affected - by this directive. Modules defined in %HTML.CoreModules are not - affected by this directive. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt deleted file mode 100644 index 151fb7b82..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Attr.Name.UseCDATA.txt +++ /dev/null @@ -1,11 +0,0 @@ -HTML.Attr.Name.UseCDATA -TYPE: bool -DEFAULT: false -VERSION: 4.0.0 ---DESCRIPTION-- -The W3C specification DTD defines the name attribute to be CDATA, not ID, due -to limitations of DTD. In certain documents, this relaxed behavior is desired, -whether it is to specify duplicate names, or to specify names that would be -illegal IDs (for example, names that begin with a digit.) Set this configuration -directive to true to use the relaxed parsing rules. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt deleted file mode 100644 index 45ae469ec..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.BlockWrapper.txt +++ /dev/null @@ -1,18 +0,0 @@ -HTML.BlockWrapper -TYPE: string -VERSION: 1.3.0 -DEFAULT: 'p' ---DESCRIPTION-- - -

- String name of element to wrap inline elements that are inside a block - context. This only occurs in the children of blockquote in strict mode. -

-

- Example: by default value, - <blockquote>Foo</blockquote> would become - <blockquote><p>Foo</p></blockquote>. - The <p> tags can be replaced with whatever you desire, - as long as it is a block level element. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt deleted file mode 100644 index 524618879..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CoreModules.txt +++ /dev/null @@ -1,23 +0,0 @@ -HTML.CoreModules -TYPE: lookup -VERSION: 2.0.0 ---DEFAULT-- -array ( - 'Structure' => true, - 'Text' => true, - 'Hypertext' => true, - 'List' => true, - 'NonXMLCommonAttributes' => true, - 'XMLCommonAttributes' => true, - 'CommonAttributes' => true, -) ---DESCRIPTION-- - -

- Certain modularized doctypes (XHTML, namely), have certain modules - that must be included for the doctype to be an conforming document - type: put those modules here. By default, XHTML's core modules - are used. You can set this to a blank array to disable core module - protection, but this is not recommended. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt deleted file mode 100644 index a64e3d7c3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.CustomDoctype.txt +++ /dev/null @@ -1,9 +0,0 @@ -HTML.CustomDoctype -TYPE: string/null -VERSION: 2.0.1 -DEFAULT: NULL ---DESCRIPTION-- - -A custom doctype for power-users who defined there own document -type. This directive only applies when %HTML.Doctype is blank. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt deleted file mode 100644 index 103db754a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionID.txt +++ /dev/null @@ -1,33 +0,0 @@ -HTML.DefinitionID -TYPE: string/null -DEFAULT: NULL -VERSION: 2.0.0 ---DESCRIPTION-- - -

- Unique identifier for a custom-built HTML definition. If you edit - the raw version of the HTMLDefinition, introducing changes that the - configuration object does not reflect, you must specify this variable. - If you change your custom edits, you should change this directive, or - clear your cache. Example: -

-
-$config = HTMLPurifier_Config::createDefault();
-$config->set('HTML', 'DefinitionID', '1');
-$def = $config->getHTMLDefinition();
-$def->addAttribute('a', 'tabindex', 'Number');
-
-

- In the above example, the configuration is still at the defaults, but - using the advanced API, an extra attribute has been added. The - configuration object normally has no way of knowing that this change - has taken place, so it needs an extra directive: %HTML.DefinitionID. - If someone else attempts to use the default configuration, these two - pieces of code will not clobber each other in the cache, since one has - an extra directive attached to it. -

-

- You must specify a value to this directive to use the - advanced API features. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt deleted file mode 100644 index 229ae0267..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.DefinitionRev.txt +++ /dev/null @@ -1,16 +0,0 @@ -HTML.DefinitionRev -TYPE: int -VERSION: 2.0.0 -DEFAULT: 1 ---DESCRIPTION-- - -

- Revision identifier for your custom definition specified in - %HTML.DefinitionID. This serves the same purpose: uniquely identifying - your custom definition, but this one does so in a chronological - context: revision 3 is more up-to-date then revision 2. Thus, when - this gets incremented, the cache handling is smart enough to clean - up any older revisions of your definition as well as flush the - cache. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt deleted file mode 100644 index 9dab497f2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Doctype.txt +++ /dev/null @@ -1,11 +0,0 @@ -HTML.Doctype -TYPE: string/null -DEFAULT: NULL ---DESCRIPTION-- -Doctype to use during filtering. Technically speaking this is not actually -a doctype (as it does not identify a corresponding DTD), but we are using -this name for sake of simplicity. When non-blank, this will override any -older directives like %HTML.XHTML or %HTML.Strict. ---ALLOWED-- -'HTML 4.01 Transitional', 'HTML 4.01 Strict', 'XHTML 1.0 Transitional', 'XHTML 1.0 Strict', 'XHTML 1.1' ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt deleted file mode 100644 index 57358f9ba..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenAttributes.txt +++ /dev/null @@ -1,21 +0,0 @@ -HTML.ForbiddenAttributes -TYPE: lookup -VERSION: 3.1.0 -DEFAULT: array() ---DESCRIPTION-- -

- While this directive is similar to %HTML.AllowedAttributes, for - forwards-compatibility with XML, this attribute has a different syntax. Instead of - tag.attr, use tag@attr. To disallow href - attributes in a tags, set this directive to - a@href. You can also disallow an attribute globally with - attr or *@attr (either syntax is fine; the latter - is provided for consistency with %HTML.AllowedAttributes). -

-

- Warning: This directive complements %HTML.ForbiddenElements, - accordingly, check - out that directive for a discussion of why you - should think twice before using this directive. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt deleted file mode 100644 index 93a53e14f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.ForbiddenElements.txt +++ /dev/null @@ -1,20 +0,0 @@ -HTML.ForbiddenElements -TYPE: lookup -VERSION: 3.1.0 -DEFAULT: array() ---DESCRIPTION-- -

- This was, perhaps, the most requested feature ever in HTML - Purifier. Please don't abuse it! This is the logical inverse of - %HTML.AllowedElements, and it will override that directive, or any - other directive. -

-

- If possible, %HTML.Allowed is recommended over this directive, because it - can sometimes be difficult to tell whether or not you've forbidden all of - the behavior you would like to disallow. If you forbid img - with the expectation of preventing images on your site, you'll be in for - a nasty surprise when people start using the background-image - CSS property. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt deleted file mode 100644 index e424c386e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.MaxImgLength.txt +++ /dev/null @@ -1,14 +0,0 @@ -HTML.MaxImgLength -TYPE: int/null -DEFAULT: 1200 -VERSION: 3.1.1 ---DESCRIPTION-- -

- This directive controls the maximum number of pixels in the width and - height attributes in img tags. This is - in place to prevent imagecrash attacks, disable with null at your own risk. - This directive is similar to %CSS.MaxImgLength, and both should be - concurrently edited, although there are - subtle differences in the input format (the HTML max is an integer). -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt deleted file mode 100644 index 62e8e160c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Parent.txt +++ /dev/null @@ -1,12 +0,0 @@ -HTML.Parent -TYPE: string -VERSION: 1.3.0 -DEFAULT: 'div' ---DESCRIPTION-- - -

- String name of element that HTML fragment passed to library will be - inserted in. An interesting variation would be using span as the - parent element, meaning that only inline tags would be allowed. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt deleted file mode 100644 index dfb720496..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Proprietary.txt +++ /dev/null @@ -1,12 +0,0 @@ -HTML.Proprietary -TYPE: bool -VERSION: 3.1.0 -DEFAULT: false ---DESCRIPTION-- -

- Whether or not to allow proprietary elements and attributes in your - documents, as per HTMLPurifier_HTMLModule_Proprietary. - Warning: This can cause your documents to stop - validating! -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt deleted file mode 100644 index f635a6854..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeEmbed.txt +++ /dev/null @@ -1,14 +0,0 @@ -HTML.SafeEmbed -TYPE: bool -VERSION: 3.1.1 -DEFAULT: false ---DESCRIPTION-- -

- Whether or not to permit embed tags in documents, with a number of extra - security features added to prevent script execution. This is similar to - what websites like MySpace do to embed tags. Embed is a proprietary - element and will cause your website to stop validating. You probably want - to enable this with %HTML.SafeObject. - Highly experimental. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt deleted file mode 100644 index 32967b88f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.SafeObject.txt +++ /dev/null @@ -1,14 +0,0 @@ -HTML.SafeObject -TYPE: bool -VERSION: 3.1.1 -DEFAULT: false ---DESCRIPTION-- -

- Whether or not to permit object tags in documents, with a number of extra - security features added to prevent script execution. This is similar to - what websites like MySpace do to object tags. You may also want to - enable %HTML.SafeEmbed for maximum interoperability with Internet Explorer, - although embed tags will cause your website to stop validating. - Highly experimental. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt deleted file mode 100644 index a8b1de56b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Strict.txt +++ /dev/null @@ -1,9 +0,0 @@ -HTML.Strict -TYPE: bool -VERSION: 1.3.0 -DEFAULT: false -DEPRECATED-VERSION: 1.7.0 -DEPRECATED-USE: HTML.Doctype ---DESCRIPTION-- -Determines whether or not to use Transitional (loose) or Strict rulesets. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt deleted file mode 100644 index b4c271b7f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyAdd.txt +++ /dev/null @@ -1,8 +0,0 @@ -HTML.TidyAdd -TYPE: lookup -VERSION: 2.0.0 -DEFAULT: array() ---DESCRIPTION-- - -Fixes to add to the default set of Tidy fixes as per your level. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt deleted file mode 100644 index 4186ccd0d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyLevel.txt +++ /dev/null @@ -1,24 +0,0 @@ -HTML.TidyLevel -TYPE: string -VERSION: 2.0.0 -DEFAULT: 'medium' ---DESCRIPTION-- - -

General level of cleanliness the Tidy module should enforce. -There are four allowed values:

-
-
none
-
No extra tidying should be done
-
light
-
Only fix elements that would be discarded otherwise due to - lack of support in doctype
-
medium
-
Enforce best practices
-
heavy
-
Transform all deprecated elements and attributes to standards - compliant equivalents
-
- ---ALLOWED-- -'none', 'light', 'medium', 'heavy' ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt deleted file mode 100644 index 996762bd1..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TidyRemove.txt +++ /dev/null @@ -1,8 +0,0 @@ -HTML.TidyRemove -TYPE: lookup -VERSION: 2.0.0 -DEFAULT: array() ---DESCRIPTION-- - -Fixes to remove from the default set of Tidy fixes as per your level. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt deleted file mode 100644 index 89133b1a3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.Trusted.txt +++ /dev/null @@ -1,8 +0,0 @@ -HTML.Trusted -TYPE: bool -VERSION: 2.0.0 -DEFAULT: false ---DESCRIPTION-- -Indicates whether or not the user input is trusted or not. If the input is -trusted, a more expansive set of allowed tags and attributes will be used. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt deleted file mode 100644 index 2a47e384f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.XHTML.txt +++ /dev/null @@ -1,11 +0,0 @@ -HTML.XHTML -TYPE: bool -DEFAULT: true -VERSION: 1.1.0 -DEPRECATED-VERSION: 1.7.0 -DEPRECATED-USE: HTML.Doctype ---DESCRIPTION-- -Determines whether or not output is XHTML 1.0 or HTML 4.01 flavor. ---ALIASES-- -Core.XHTML ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt deleted file mode 100644 index 08921fde7..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.CommentScriptContents.txt +++ /dev/null @@ -1,10 +0,0 @@ -Output.CommentScriptContents -TYPE: bool -VERSION: 2.0.0 -DEFAULT: true ---DESCRIPTION-- -Determines whether or not HTML Purifier should attempt to fix up the -contents of script tags for legacy browsers with comments. ---ALIASES-- -Core.CommentScriptContents ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt deleted file mode 100644 index 79f8ad82c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.Newline.txt +++ /dev/null @@ -1,13 +0,0 @@ -Output.Newline -TYPE: string/null -VERSION: 2.0.1 -DEFAULT: NULL ---DESCRIPTION-- - -

- Newline string to format final output with. If left null, HTML Purifier - will auto-detect the default newline type of the system and use that; - you can manually override it here. Remember, \r\n is Windows, \r - is Mac, and \n is Unix. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt deleted file mode 100644 index 232b02362..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.SortAttr.txt +++ /dev/null @@ -1,14 +0,0 @@ -Output.SortAttr -TYPE: bool -VERSION: 3.2.0 -DEFAULT: false ---DESCRIPTION-- -

- If true, HTML Purifier will sort attributes by name before writing them back - to the document, converting a tag like: <el b="" a="" c="" /> - to <el a="" b="" c="" />. This is a workaround for - a bug in FCKeditor which causes it to swap attributes order, adding noise - to text diffs. If you're not seeing this bug, chances are, you don't need - this directive. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt deleted file mode 100644 index 06bab00a0..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Output.TidyFormat.txt +++ /dev/null @@ -1,25 +0,0 @@ -Output.TidyFormat -TYPE: bool -VERSION: 1.1.1 -DEFAULT: false ---DESCRIPTION-- -

- Determines whether or not to run Tidy on the final output for pretty - formatting reasons, such as indentation and wrap. -

-

- This can greatly improve readability for editors who are hand-editing - the HTML, but is by no means necessary as HTML Purifier has already - fixed all major errors the HTML may have had. Tidy is a non-default - extension, and this directive will silently fail if Tidy is not - available. -

-

- If you are looking to make the overall look of your page's source - better, I recommend running Tidy on the entire page rather than just - user-content (after all, the indentation relative to the containing - blocks will be incorrect). -

---ALIASES-- -Core.TidyFormat ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt deleted file mode 100644 index 071bc0295..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Test.ForceNoIconv.txt +++ /dev/null @@ -1,7 +0,0 @@ -Test.ForceNoIconv -TYPE: bool -DEFAULT: false ---DESCRIPTION-- -When set to true, HTMLPurifier_Encoder will act as if iconv does not exist -and use only pure PHP implementations. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt deleted file mode 100644 index 98fdfe922..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt +++ /dev/null @@ -1,15 +0,0 @@ -URI.AllowedSchemes -TYPE: lookup ---DEFAULT-- -array ( - 'http' => true, - 'https' => true, - 'mailto' => true, - 'ftp' => true, - 'nntp' => true, - 'news' => true, -) ---DESCRIPTION-- -Whitelist that defines the schemes that a URI is allowed to have. This -prevents XSS attacks from using pseudo-schemes like javascript or mocha. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt deleted file mode 100644 index 876f0680c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Base.txt +++ /dev/null @@ -1,17 +0,0 @@ -URI.Base -TYPE: string/null -VERSION: 2.1.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- The base URI is the URI of the document this purified HTML will be - inserted into. This information is important if HTML Purifier needs - to calculate absolute URIs from relative URIs, such as when %URI.MakeAbsolute - is on. You may use a non-absolute URI for this value, but behavior - may vary (%URI.MakeAbsolute deals nicely with both absolute and - relative paths, but forwards-compatibility is not guaranteed). - Warning: If set, the scheme on this URI - overrides the one specified by %URI.DefaultScheme. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt deleted file mode 100644 index 728e378cb..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt +++ /dev/null @@ -1,10 +0,0 @@ -URI.DefaultScheme -TYPE: string -DEFAULT: 'http' ---DESCRIPTION-- - -

- Defines through what scheme the output will be served, in order to - select the proper object validator when no scheme information is present. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt deleted file mode 100644 index f05312ba8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionID.txt +++ /dev/null @@ -1,11 +0,0 @@ -URI.DefinitionID -TYPE: string/null -VERSION: 2.1.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- Unique identifier for a custom-built URI definition. If you want - to add custom URIFilters, you must specify this value. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt deleted file mode 100644 index 80cfea93f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefinitionRev.txt +++ /dev/null @@ -1,11 +0,0 @@ -URI.DefinitionRev -TYPE: int -VERSION: 2.1.0 -DEFAULT: 1 ---DESCRIPTION-- - -

- Revision identifier for your custom definition. See - %HTML.DefinitionRev for details. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt deleted file mode 100644 index 71ce025a2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Disable.txt +++ /dev/null @@ -1,14 +0,0 @@ -URI.Disable -TYPE: bool -VERSION: 1.3.0 -DEFAULT: false ---DESCRIPTION-- - -

- Disables all URIs in all forms. Not sure why you'd want to do that - (after all, the Internet's founded on the notion of a hyperlink). -

- ---ALIASES-- -Attr.DisableURI ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt deleted file mode 100644 index 13c122c8c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternal.txt +++ /dev/null @@ -1,11 +0,0 @@ -URI.DisableExternal -TYPE: bool -VERSION: 1.2.0 -DEFAULT: false ---DESCRIPTION-- -Disables links to external websites. This is a highly effective anti-spam -and anti-pagerank-leech measure, but comes at a hefty price: nolinks or -images outside of your domain will be allowed. Non-linkified URIs will -still be preserved. If you want to be able to link to subdomains or use -absolute URIs, specify %URI.Host for your website. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt deleted file mode 100644 index abcc1efd6..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableExternalResources.txt +++ /dev/null @@ -1,13 +0,0 @@ -URI.DisableExternalResources -TYPE: bool -VERSION: 1.3.0 -DEFAULT: false ---DESCRIPTION-- -Disables the embedding of external resources, preventing users from -embedding things like images from other hosts. This prevents access -tracking (good for email viewers), bandwidth leeching, cross-site request -forging, goatse.cx posting, and other nasties, but also results in a loss -of end-user functionality (they can't directly post a pic they posted from -Flickr anymore). Use it if you don't have a robust user-content moderation -team. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt deleted file mode 100644 index 51e6ea91f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DisableResources.txt +++ /dev/null @@ -1,12 +0,0 @@ -URI.DisableResources -TYPE: bool -VERSION: 1.3.0 -DEFAULT: false ---DESCRIPTION-- - -

- Disables embedding resources, essentially meaning no pictures. You can - still link to them though. See %URI.DisableExternalResources for why - this might be a good idea. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt deleted file mode 100644 index ee83b121d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Host.txt +++ /dev/null @@ -1,19 +0,0 @@ -URI.Host -TYPE: string/null -VERSION: 1.2.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- Defines the domain name of the server, so we can determine whether or - an absolute URI is from your website or not. Not strictly necessary, - as users should be using relative URIs to reference resources on your - website. It will, however, let you use absolute URIs to link to - subdomains of the domain you post here: i.e. example.com will allow - sub.example.com. However, higher up domains will still be excluded: - if you set %URI.Host to sub.example.com, example.com will be blocked. - Note: This directive overrides %URI.Base because - a given page may be on a sub-domain, but you wish HTML Purifier to be - more relaxed and allow some of the parent domains too. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt deleted file mode 100644 index 0b6df7625..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.HostBlacklist.txt +++ /dev/null @@ -1,9 +0,0 @@ -URI.HostBlacklist -TYPE: list -VERSION: 1.3.0 -DEFAULT: array() ---DESCRIPTION-- -List of strings that are forbidden in the host of any URI. Use it to kill -domain names of spam, etc. Note that it will catch anything in the domain, -so moo.com will catch moo.com.example.com. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt deleted file mode 100644 index 4214900a5..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MakeAbsolute.txt +++ /dev/null @@ -1,13 +0,0 @@ -URI.MakeAbsolute -TYPE: bool -VERSION: 2.1.0 -DEFAULT: false ---DESCRIPTION-- - -

- Converts all URIs into absolute forms. This is useful when the HTML - being filtered assumes a specific base path, but will actually be - viewed in a different context (and setting an alternate base URI is - not possible). %URI.Base must be set for this directive to work. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt deleted file mode 100644 index 58c81dcc4..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt +++ /dev/null @@ -1,83 +0,0 @@ -URI.Munge -TYPE: string/null -VERSION: 1.3.0 -DEFAULT: NULL ---DESCRIPTION-- - -

- Munges all browsable (usually http, https and ftp) - absolute URIs into another URI, usually a URI redirection service. - This directive accepts a URI, formatted with a %s where - the url-encoded original URI should be inserted (sample: - http://www.google.com/url?q=%s). -

-

- Uses for this directive: -

-
    -
  • - Prevent PageRank leaks, while being fairly transparent - to users (you may also want to add some client side JavaScript to - override the text in the statusbar). Notice: - Many security experts believe that this form of protection does not deter spam-bots. -
  • -
  • - Redirect users to a splash page telling them they are leaving your - website. While this is poor usability practice, it is often mandated - in corporate environments. -
  • -
-

- Prior to HTML Purifier 3.1.1, this directive also enabled the munging - of browsable external resources, which could break things if your redirection - script was a splash page or used meta tags. To revert to - previous behavior, please use %URI.MungeResources. -

-

- You may want to also use %URI.MungeSecretKey along with this directive - in order to enforce what URIs your redirector script allows. Open - redirector scripts can be a security risk and negatively affect the - reputation of your domain name. -

-

- Starting with HTML Purifier 3.1.1, there is also these substitutions: -

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
KeyDescriptionExample <a href="">
%r1 - The URI embeds a resource
(blank) - The URI is merely a link
%nThe name of the tag this URI came froma
%mThe name of the attribute this URI came fromhref
%pThe name of the CSS property this URI came from, or blank if irrelevant
-

- 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 - 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 - property. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt deleted file mode 100644 index 6fce0fdc3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeResources.txt +++ /dev/null @@ -1,17 +0,0 @@ -URI.MungeResources -TYPE: bool -VERSION: 3.1.1 -DEFAULT: false ---DESCRIPTION-- -

- If true, any URI munging directives like %URI.Munge - will also apply to embedded resources, such as <img src="">. - Be careful enabling this directive if you have a redirector script - that does not use the Location HTTP header; all of your images - and other embedded resources will break. -

-

- Warning: It is strongly advised you use this in conjunction - %URI.MungeSecretKey to mitigate the security risk of an open redirector. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt deleted file mode 100644 index 0d00f62ea..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.MungeSecretKey.txt +++ /dev/null @@ -1,30 +0,0 @@ -URI.MungeSecretKey -TYPE: string/null -VERSION: 3.1.1 -DEFAULT: NULL ---DESCRIPTION-- -

- This directive enables secure checksum generation along with %URI.Munge. - It should be set to a secure key that is not shared with anyone else. - The checksum can be placed in the URI using %t. Use of this checksum - affords an additional level of protection by allowing a redirector - to check if a URI has passed through HTML Purifier with this line: -

- -
$checksum === sha1($secret_key . ':' . $url)
- -

- If the output is TRUE, the redirector script should accept the URI. -

- -

- Please note that it would still be possible for an attacker to procure - secure hashes en-mass by abusing your website's Preview feature or the - like, but this service affords an additional level of protection - that should be combined with website blacklisting. -

- -

- Remember this has no effect if %URI.Munge is not on. -

---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt deleted file mode 100644 index 23331a4e7..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.OverrideAllowedSchemes.txt +++ /dev/null @@ -1,9 +0,0 @@ -URI.OverrideAllowedSchemes -TYPE: bool -DEFAULT: true ---DESCRIPTION-- -If this is set to true (which it is by default), you can override -%URI.AllowedSchemes by simply registering a HTMLPurifier_URIScheme to the -registry. If false, you will also have to update that directive in order -to add more schemes. ---# vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini deleted file mode 100644 index 5de4505e1..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/info.ini +++ /dev/null @@ -1,3 +0,0 @@ -name = "HTML Purifier" - -; vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ContentSets.php b/extlib/HTMLPurifier/HTMLPurifier/ContentSets.php deleted file mode 100644 index 3b6e96f5f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ContentSets.php +++ /dev/null @@ -1,155 +0,0 @@ - true) indexed by name. - * @note This is in HTMLPurifier_HTMLDefinition->info_content_sets - */ - public $lookup = array(); - - /** - * Synchronized list of defined content sets (keys of info) - */ - protected $keys = array(); - /** - * Synchronized list of defined content values (values of info) - */ - protected $values = array(); - - /** - * Merges in module's content sets, expands identifiers in the content - * sets and populates the keys, values and lookup member variables. - * @param $modules List of HTMLPurifier_HTMLModule - */ - public function __construct($modules) { - if (!is_array($modules)) $modules = array($modules); - // populate content_sets based on module hints - // sorry, no way of overloading - foreach ($modules as $module_i => $module) { - foreach ($module->content_sets as $key => $value) { - $temp = $this->convertToLookup($value); - if (isset($this->lookup[$key])) { - // add it into the existing content set - $this->lookup[$key] = array_merge($this->lookup[$key], $temp); - } else { - $this->lookup[$key] = $temp; - } - } - } - $old_lookup = false; - while ($old_lookup !== $this->lookup) { - $old_lookup = $this->lookup; - foreach ($this->lookup as $i => $set) { - $add = array(); - foreach ($set as $element => $x) { - if (isset($this->lookup[$element])) { - $add += $this->lookup[$element]; - unset($this->lookup[$i][$element]); - } - } - $this->lookup[$i] += $add; - } - } - - foreach ($this->lookup as $key => $lookup) { - $this->info[$key] = implode(' | ', array_keys($lookup)); - } - $this->keys = array_keys($this->info); - $this->values = array_values($this->info); - } - - /** - * Accepts a definition; generates and assigns a ChildDef for it - * @param $def HTMLPurifier_ElementDef reference - * @param $module Module that defined the ElementDef - */ - public function generateChildDef(&$def, $module) { - if (!empty($def->child)) return; // already done! - $content_model = $def->content_model; - if (is_string($content_model)) { - // Assume that $this->keys is alphanumeric - $def->content_model = preg_replace_callback( - '/\b(' . implode('|', $this->keys) . ')\b/', - array($this, 'generateChildDefCallback'), - $content_model - ); - //$def->content_model = str_replace( - // $this->keys, $this->values, $content_model); - } - $def->child = $this->getChildDef($def, $module); - } - - public function generateChildDefCallback($matches) { - return $this->info[$matches[0]]; - } - - /** - * Instantiates a ChildDef based on content_model and content_model_type - * member variables in HTMLPurifier_ElementDef - * @note This will also defer to modules for custom HTMLPurifier_ChildDef - * subclasses that need content set expansion - * @param $def HTMLPurifier_ElementDef to have ChildDef extracted - * @return HTMLPurifier_ChildDef corresponding to ElementDef - */ - public function getChildDef($def, $module) { - $value = $def->content_model; - if (is_object($value)) { - trigger_error( - 'Literal object child definitions should be stored in '. - 'ElementDef->child not ElementDef->content_model', - E_USER_NOTICE - ); - return $value; - } - switch ($def->content_model_type) { - case 'required': - return new HTMLPurifier_ChildDef_Required($value); - case 'optional': - return new HTMLPurifier_ChildDef_Optional($value); - case 'empty': - return new HTMLPurifier_ChildDef_Empty(); - case 'custom': - return new HTMLPurifier_ChildDef_Custom($value); - } - // defer to its module - $return = false; - if ($module->defines_child_def) { // save a func call - $return = $module->getChildDef($def); - } - if ($return !== false) return $return; - // error-out - trigger_error( - 'Could not determine which ChildDef class to instantiate', - E_USER_ERROR - ); - return false; - } - - /** - * Converts a string list of elements separated by pipes into - * a lookup array. - * @param $string List of elements - * @return Lookup array of elements - */ - protected function convertToLookup($string) { - $array = explode('|', str_replace(' ', '', $string)); - $ret = array(); - foreach ($array as $i => $k) { - $ret[$k] = true; - } - return $ret; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Context.php b/extlib/HTMLPurifier/HTMLPurifier/Context.php deleted file mode 100644 index 9ddf0c547..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Context.php +++ /dev/null @@ -1,82 +0,0 @@ -_storage[$name])) { - trigger_error("Name $name produces collision, cannot re-register", - E_USER_ERROR); - return; - } - $this->_storage[$name] =& $ref; - } - - /** - * Retrieves a variable reference from the context. - * @param $name String name - * @param $ignore_error Boolean whether or not to ignore error - */ - public function &get($name, $ignore_error = false) { - if (!isset($this->_storage[$name])) { - if (!$ignore_error) { - trigger_error("Attempted to retrieve non-existent variable $name", - E_USER_ERROR); - } - $var = null; // so we can return by reference - return $var; - } - return $this->_storage[$name]; - } - - /** - * Destorys a variable in the context. - * @param $name String name - */ - public function destroy($name) { - if (!isset($this->_storage[$name])) { - trigger_error("Attempted to destroy non-existent variable $name", - E_USER_ERROR); - return; - } - unset($this->_storage[$name]); - } - - /** - * Checks whether or not the variable exists. - * @param $name String name - */ - public function exists($name) { - return isset($this->_storage[$name]); - } - - /** - * Loads a series of variables from an associative array - * @param $context_array Assoc array of variables to load - */ - public function loadArray($context_array) { - foreach ($context_array as $key => $discard) { - $this->register($key, $context_array[$key]); - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Definition.php b/extlib/HTMLPurifier/HTMLPurifier/Definition.php deleted file mode 100644 index a7408c974..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Definition.php +++ /dev/null @@ -1,39 +0,0 @@ -setup) return; - $this->setup = true; - $this->doSetup($config); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php deleted file mode 100644 index c6e1e388c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php +++ /dev/null @@ -1,108 +0,0 @@ -type = $type; - } - - /** - * Generates a unique identifier for a particular configuration - * @param Instance of HTMLPurifier_Config - */ - public function generateKey($config) { - return $config->version . ',' . // possibly replace with function calls - $config->getBatchSerial($this->type) . ',' . - $config->get($this->type . '.DefinitionRev'); - } - - /** - * Tests whether or not a key is old with respect to the configuration's - * version and revision number. - * @param $key Key to test - * @param $config Instance of HTMLPurifier_Config to test against - */ - public function isOld($key, $config) { - if (substr_count($key, ',') < 2) return true; - list($version, $hash, $revision) = explode(',', $key, 3); - $compare = version_compare($version, $config->version); - // version mismatch, is always old - if ($compare != 0) return true; - // versions match, ids match, check revision number - if ( - $hash == $config->getBatchSerial($this->type) && - $revision < $config->get($this->type . '.DefinitionRev') - ) return true; - return false; - } - - /** - * Checks if a definition's type jives with the cache's type - * @note Throws an error on failure - * @param $def Definition object to check - * @return Boolean true if good, false if not - */ - public function checkDefType($def) { - if ($def->type !== $this->type) { - trigger_error("Cannot use definition of type {$def->type} in cache for {$this->type}"); - return false; - } - return true; - } - - /** - * Adds a definition object to the cache - */ - abstract public function add($def, $config); - - /** - * Unconditionally saves a definition object to the cache - */ - abstract public function set($def, $config); - - /** - * Replace an object in the cache - */ - abstract public function replace($def, $config); - - /** - * Retrieves a definition object from the cache - */ - abstract public function get($config); - - /** - * Removes a definition object to the cache - */ - abstract public function remove($config); - - /** - * Clears all objects from cache - */ - abstract public function flush($config); - - /** - * Clears all expired (older version or revision) objects from cache - * @note Be carefuly implementing this method as flush. Flush must - * not interfere with other Definition types, and cleanup() - * should not be repeatedly called by userland code. - */ - abstract public function cleanup($config); - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php deleted file mode 100644 index b0fb6d0cd..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator.php +++ /dev/null @@ -1,62 +0,0 @@ -copy(); - // reference is necessary for mocks in PHP 4 - $decorator->cache =& $cache; - $decorator->type = $cache->type; - return $decorator; - } - - /** - * Cross-compatible clone substitute - */ - public function copy() { - return new HTMLPurifier_DefinitionCache_Decorator(); - } - - public function add($def, $config) { - return $this->cache->add($def, $config); - } - - public function set($def, $config) { - return $this->cache->set($def, $config); - } - - public function replace($def, $config) { - return $this->cache->replace($def, $config); - } - - public function get($config) { - return $this->cache->get($config); - } - - public function remove($config) { - return $this->cache->remove($config); - } - - public function flush($config) { - return $this->cache->flush($config); - } - - public function cleanup($config) { - return $this->cache->cleanup($config); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php deleted file mode 100644 index d4cc35c4b..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Cleanup.php +++ /dev/null @@ -1,43 +0,0 @@ -definitions[$this->generateKey($config)] = $def; - return $status; - } - - public function set($def, $config) { - $status = parent::set($def, $config); - if ($status) $this->definitions[$this->generateKey($config)] = $def; - return $status; - } - - public function replace($def, $config) { - $status = parent::replace($def, $config); - if ($status) $this->definitions[$this->generateKey($config)] = $def; - return $status; - } - - public function get($config) { - $key = $this->generateKey($config); - if (isset($this->definitions[$key])) return $this->definitions[$key]; - $this->definitions[$key] = parent::get($config); - return $this->definitions[$key]; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in deleted file mode 100644 index 21a8fcfda..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Decorator/Template.php.in +++ /dev/null @@ -1,47 +0,0 @@ -checkDefType($def)) return; - $file = $this->generateFilePath($config); - if (file_exists($file)) return false; - if (!$this->_prepareDir($config)) return false; - return $this->_write($file, serialize($def)); - } - - public function set($def, $config) { - if (!$this->checkDefType($def)) return; - $file = $this->generateFilePath($config); - if (!$this->_prepareDir($config)) return false; - return $this->_write($file, serialize($def)); - } - - public function replace($def, $config) { - if (!$this->checkDefType($def)) return; - $file = $this->generateFilePath($config); - if (!file_exists($file)) return false; - if (!$this->_prepareDir($config)) return false; - return $this->_write($file, serialize($def)); - } - - public function get($config) { - $file = $this->generateFilePath($config); - if (!file_exists($file)) return false; - return unserialize(file_get_contents($file)); - } - - public function remove($config) { - $file = $this->generateFilePath($config); - if (!file_exists($file)) return false; - return unlink($file); - } - - public function flush($config) { - if (!$this->_prepareDir($config)) return false; - $dir = $this->generateDirectoryPath($config); - $dh = opendir($dir); - while (false !== ($filename = readdir($dh))) { - if (empty($filename)) continue; - if ($filename[0] === '.') continue; - unlink($dir . '/' . $filename); - } - } - - public function cleanup($config) { - if (!$this->_prepareDir($config)) return false; - $dir = $this->generateDirectoryPath($config); - $dh = opendir($dir); - while (false !== ($filename = readdir($dh))) { - if (empty($filename)) continue; - if ($filename[0] === '.') continue; - $key = substr($filename, 0, strlen($filename) - 4); - if ($this->isOld($key, $config)) unlink($dir . '/' . $filename); - } - } - - /** - * Generates the file path to the serial file corresponding to - * the configuration and definition name - * @todo Make protected - */ - public function generateFilePath($config) { - $key = $this->generateKey($config); - return $this->generateDirectoryPath($config) . '/' . $key . '.ser'; - } - - /** - * Generates the path to the directory contain this cache's serial files - * @note No trailing slash - * @todo Make protected - */ - public function generateDirectoryPath($config) { - $base = $this->generateBaseDirectoryPath($config); - return $base . '/' . $this->type; - } - - /** - * Generates path to base directory that contains all definition type - * serials - * @todo Make protected - */ - public function generateBaseDirectoryPath($config) { - $base = $config->get('Cache.SerializerPath'); - $base = is_null($base) ? HTMLPURIFIER_PREFIX . '/HTMLPurifier/DefinitionCache/Serializer' : $base; - return $base; - } - - /** - * Convenience wrapper function for file_put_contents - * @param $file File name to write to - * @param $data Data to write into file - * @return Number of bytes written if success, or false if failure. - */ - private function _write($file, $data) { - return file_put_contents($file, $data); - } - - /** - * Prepares the directory that this type stores the serials in - * @return True if successful - */ - private function _prepareDir($config) { - $directory = $this->generateDirectoryPath($config); - if (!is_dir($directory)) { - $base = $this->generateBaseDirectoryPath($config); - if (!is_dir($base)) { - trigger_error('Base directory '.$base.' does not exist, - please create or change using %Cache.SerializerPath', - E_USER_WARNING); - return false; - } elseif (!$this->_testPermissions($base)) { - return false; - } - $old = umask(0022); // disable group and world writes - mkdir($directory); - umask($old); - } elseif (!$this->_testPermissions($directory)) { - return false; - } - return true; - } - - /** - * Tests permissions on a directory and throws out friendly - * error messages and attempts to chmod it itself if possible - */ - private function _testPermissions($dir) { - // early abort, if it is writable, everything is hunky-dory - if (is_writable($dir)) return true; - if (!is_dir($dir)) { - // generally, you'll want to handle this beforehand - // so a more specific error message can be given - trigger_error('Directory '.$dir.' does not exist', - E_USER_WARNING); - return false; - } - if (function_exists('posix_getuid')) { - // POSIX system, we can give more specific advice - if (fileowner($dir) === posix_getuid()) { - // we can chmod it ourselves - chmod($dir, 0755); - return true; - } elseif (filegroup($dir) === posix_getgid()) { - $chmod = '775'; - } else { - // PHP's probably running as nobody, so we'll - // need to give global permissions - $chmod = '777'; - } - trigger_error('Directory '.$dir.' not writable, '. - 'please chmod to ' . $chmod, - E_USER_WARNING); - } else { - // generic error message - trigger_error('Directory '.$dir.' not writable, '. - 'please alter file permissions', - E_USER_WARNING); - } - return false; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README deleted file mode 100755 index 2e35c1c3d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer/README +++ /dev/null @@ -1,3 +0,0 @@ -This is a dummy file to prevent Git from ignoring this empty directory. - - vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php deleted file mode 100644 index a6ead6281..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCacheFactory.php +++ /dev/null @@ -1,91 +0,0 @@ - array()); - protected $implementations = array(); - protected $decorators = array(); - - /** - * Initialize default decorators - */ - public function setup() { - $this->addDecorator('Cleanup'); - } - - /** - * Retrieves an instance of global definition cache factory. - */ - public static function instance($prototype = null) { - static $instance; - if ($prototype !== null) { - $instance = $prototype; - } elseif ($instance === null || $prototype === true) { - $instance = new HTMLPurifier_DefinitionCacheFactory(); - $instance->setup(); - } - return $instance; - } - - /** - * Registers a new definition cache object - * @param $short Short name of cache object, for reference - * @param $long Full class name of cache object, for construction - */ - public function register($short, $long) { - $this->implementations[$short] = $long; - } - - /** - * Factory method that creates a cache object based on configuration - * @param $name Name of definitions handled by cache - * @param $config Instance of HTMLPurifier_Config - */ - public function create($type, $config) { - $method = $config->get('Cache.DefinitionImpl'); - if ($method === null) { - return new HTMLPurifier_DefinitionCache_Null($type); - } - if (!empty($this->caches[$method][$type])) { - return $this->caches[$method][$type]; - } - if ( - isset($this->implementations[$method]) && - class_exists($class = $this->implementations[$method], false) - ) { - $cache = new $class($type); - } else { - if ($method != 'Serializer') { - trigger_error("Unrecognized DefinitionCache $method, using Serializer instead", E_USER_WARNING); - } - $cache = new HTMLPurifier_DefinitionCache_Serializer($type); - } - foreach ($this->decorators as $decorator) { - $new_cache = $decorator->decorate($cache); - // prevent infinite recursion in PHP 4 - unset($cache); - $cache = $new_cache; - } - $this->caches[$method][$type] = $cache; - return $this->caches[$method][$type]; - } - - /** - * Registers a decorator to add to all new cache objects - * @param - */ - public function addDecorator($decorator) { - if (is_string($decorator)) { - $class = "HTMLPurifier_DefinitionCache_Decorator_$decorator"; - $decorator = new $class; - } - $this->decorators[$decorator->name] = $decorator; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Doctype.php b/extlib/HTMLPurifier/HTMLPurifier/Doctype.php deleted file mode 100644 index 1e3c574c0..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Doctype.php +++ /dev/null @@ -1,60 +0,0 @@ -renderDoctype. - * If structure changes, please update that function. - */ -class HTMLPurifier_Doctype -{ - /** - * Full name of doctype - */ - public $name; - - /** - * List of standard modules (string identifiers or literal objects) - * that this doctype uses - */ - public $modules = array(); - - /** - * List of modules to use for tidying up code - */ - public $tidyModules = array(); - - /** - * Is the language derived from XML (i.e. XHTML)? - */ - public $xml = true; - - /** - * List of aliases for this doctype - */ - public $aliases = array(); - - /** - * Public DTD identifier - */ - public $dtdPublic; - - /** - * System DTD identifier - */ - public $dtdSystem; - - public function __construct($name = null, $xml = true, $modules = array(), - $tidyModules = array(), $aliases = array(), $dtd_public = null, $dtd_system = null - ) { - $this->name = $name; - $this->xml = $xml; - $this->modules = $modules; - $this->tidyModules = $tidyModules; - $this->aliases = $aliases; - $this->dtdPublic = $dtd_public; - $this->dtdSystem = $dtd_system; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php b/extlib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php deleted file mode 100644 index 86049e939..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/DoctypeRegistry.php +++ /dev/null @@ -1,103 +0,0 @@ -doctypes[$doctype->name] = $doctype; - $name = $doctype->name; - // hookup aliases - foreach ($doctype->aliases as $alias) { - if (isset($this->doctypes[$alias])) continue; - $this->aliases[$alias] = $name; - } - // remove old aliases - if (isset($this->aliases[$name])) unset($this->aliases[$name]); - return $doctype; - } - - /** - * Retrieves reference to a doctype of a certain name - * @note This function resolves aliases - * @note When possible, use the more fully-featured make() - * @param $doctype Name of doctype - * @return Editable doctype object - */ - public function get($doctype) { - if (isset($this->aliases[$doctype])) $doctype = $this->aliases[$doctype]; - if (!isset($this->doctypes[$doctype])) { - trigger_error('Doctype ' . htmlspecialchars($doctype) . ' does not exist', E_USER_ERROR); - $anon = new HTMLPurifier_Doctype($doctype); - return $anon; - } - return $this->doctypes[$doctype]; - } - - /** - * Creates a doctype based on a configuration object, - * will perform initialization on the doctype - * @note Use this function to get a copy of doctype that config - * can hold on to (this is necessary in order to tell - * Generator whether or not the current document is XML - * based or not). - */ - public function make($config) { - return clone $this->get($this->getDoctypeFromConfig($config)); - } - - /** - * Retrieves the doctype from the configuration object - */ - public function getDoctypeFromConfig($config) { - // recommended test - $doctype = $config->get('HTML.Doctype'); - if (!empty($doctype)) return $doctype; - $doctype = $config->get('HTML.CustomDoctype'); - if (!empty($doctype)) return $doctype; - // backwards-compatibility - if ($config->get('HTML.XHTML')) { - $doctype = 'XHTML 1.0'; - } else { - $doctype = 'HTML 4.01'; - } - if ($config->get('HTML.Strict')) { - $doctype .= ' Strict'; - } else { - $doctype .= ' Transitional'; - } - return $doctype; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ElementDef.php b/extlib/HTMLPurifier/HTMLPurifier/ElementDef.php deleted file mode 100644 index aede2c3bb..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ElementDef.php +++ /dev/null @@ -1,176 +0,0 @@ -setup(), this array may also - * contain an array at index 0 that indicates which attribute - * collections to load into the full array. It may also - * contain string indentifiers in lieu of HTMLPurifier_AttrDef, - * see HTMLPurifier_AttrTypes on how they are expanded during - * HTMLPurifier_HTMLDefinition->setup() processing. - */ - public $attr = array(); - - /** - * Indexed list of tag's HTMLPurifier_AttrTransform to be done before validation - */ - public $attr_transform_pre = array(); - - /** - * Indexed list of tag's HTMLPurifier_AttrTransform to be done after validation - */ - public $attr_transform_post = array(); - - /** - * HTMLPurifier_ChildDef of this tag. - */ - public $child; - - /** - * Abstract string representation of internal ChildDef rules. See - * HTMLPurifier_ContentSets for how this is parsed and then transformed - * into an HTMLPurifier_ChildDef. - * @warning This is a temporary variable that is not available after - * being processed by HTMLDefinition - */ - public $content_model; - - /** - * Value of $child->type, used to determine which ChildDef to use, - * used in combination with $content_model. - * @warning This must be lowercase - * @warning This is a temporary variable that is not available after - * being processed by HTMLDefinition - */ - public $content_model_type; - - - - /** - * Does the element have a content model (#PCDATA | Inline)*? This - * is important for chameleon ins and del processing in - * HTMLPurifier_ChildDef_Chameleon. Dynamically set: modules don't - * have to worry about this one. - */ - public $descendants_are_inline = false; - - /** - * List of the names of required attributes this element has. Dynamically - * populated by HTMLPurifier_HTMLDefinition::getElement - */ - public $required_attr = array(); - - /** - * Lookup table of tags excluded from all descendants of this tag. - * @note SGML permits exclusions for all descendants, but this is - * not possible with DTDs or XML Schemas. W3C has elected to - * use complicated compositions of content_models to simulate - * exclusion for children, but we go the simpler, SGML-style - * route of flat-out exclusions, which correctly apply to - * all descendants and not just children. Note that the XHTML - * Modularization Abstract Modules are blithely unaware of such - * distinctions. - */ - public $excludes = array(); - - /** - * This tag is explicitly auto-closed by the following tags. - */ - public $autoclose = array(); - - /** - * Whether or not this is a formatting element affected by the - * "Active Formatting Elements" algorithm. - */ - public $formatting; - - /** - * Low-level factory constructor for creating new standalone element defs - */ - public static function create($content_model, $content_model_type, $attr) { - $def = new HTMLPurifier_ElementDef(); - $def->content_model = $content_model; - $def->content_model_type = $content_model_type; - $def->attr = $attr; - return $def; - } - - /** - * Merges the values of another element definition into this one. - * Values from the new element def take precedence if a value is - * not mergeable. - */ - public function mergeIn($def) { - - // later keys takes precedence - foreach($def->attr as $k => $v) { - if ($k === 0) { - // merge in the includes - // sorry, no way to override an include - foreach ($v as $v2) { - $this->attr[0][] = $v2; - } - continue; - } - if ($v === false) { - if (isset($this->attr[$k])) unset($this->attr[$k]); - continue; - } - $this->attr[$k] = $v; - } - $this->_mergeAssocArray($this->attr_transform_pre, $def->attr_transform_pre); - $this->_mergeAssocArray($this->attr_transform_post, $def->attr_transform_post); - $this->_mergeAssocArray($this->excludes, $def->excludes); - - if(!empty($def->content_model)) { - $this->content_model = - str_replace("#SUPER", $this->content_model, $def->content_model); - $this->child = false; - } - if(!empty($def->content_model_type)) { - $this->content_model_type = $def->content_model_type; - $this->child = false; - } - if(!is_null($def->child)) $this->child = $def->child; - if(!is_null($def->formatting)) $this->formatting = $def->formatting; - if($def->descendants_are_inline) $this->descendants_are_inline = $def->descendants_are_inline; - - } - - /** - * Merges one array into another, removes values which equal false - * @param $a1 Array by reference that is merged into - * @param $a2 Array that merges into $a1 - */ - private function _mergeAssocArray(&$a1, $a2) { - foreach ($a2 as $k => $v) { - if ($v === false) { - if (isset($a1[$k])) unset($a1[$k]); - continue; - } - $a1[$k] = $v; - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Encoder.php b/extlib/HTMLPurifier/HTMLPurifier/Encoder.php deleted file mode 100644 index 2b3140caa..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Encoder.php +++ /dev/null @@ -1,426 +0,0 @@ - under the - * LGPL license. Notes on what changed are inside, but in general, - * the original code transformed UTF-8 text into an array of integer - * Unicode codepoints. Understandably, transforming that back to - * a string would be somewhat expensive, so the function was modded to - * directly operate on the string. However, this discourages code - * reuse, and the logic enumerated here would be useful for any - * function that needs to be able to understand UTF-8 characters. - * As of right now, only smart lossless character encoding converters - * 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) { - - // UTF-8 validity is checked since PHP 4.3.5 - // 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. - // The regexp matches the XML char production, as well as well as excluding - // non-SGML codepoints U+007F to U+009F - 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', $str)) { - return $str; - } - - $mState = 0; // cached expected number of octets after the current octet - // until the beginning of the next UTF8 character sequence - $mUcs4 = 0; // cached Unicode character - $mBytes = 1; // cached expected number of octets in the current sequence - - // original code involved an $out that was an array of Unicode - // codepoints. Instead of having to convert back into UTF-8, we've - // decided to directly append valid UTF-8 characters onto a string - // $out once they're done. $char accumulates raw bytes, while $mUcs4 - // turns into the Unicode code point, so there's some redundancy. - - $out = ''; - $char = ''; - - $len = strlen($str); - for($i = 0; $i < $len; $i++) { - $in = ord($str{$i}); - $char .= $str[$i]; // append byte to char - if (0 == $mState) { - // When mState is zero we expect either a US-ASCII character - // or a multi-octet sequence. - if (0 == (0x80 & ($in))) { - // US-ASCII, pass straight through. - if (($in <= 31 || $in == 127) && - !($in == 9 || $in == 13 || $in == 10) // save \r\t\n - ) { - // control characters, remove - } else { - $out .= $char; - } - // reset - $char = ''; - $mBytes = 1; - } elseif (0xC0 == (0xE0 & ($in))) { - // First octet of 2 octet sequence - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x1F) << 6; - $mState = 1; - $mBytes = 2; - } elseif (0xE0 == (0xF0 & ($in))) { - // First octet of 3 octet sequence - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x0F) << 12; - $mState = 2; - $mBytes = 3; - } elseif (0xF0 == (0xF8 & ($in))) { - // First octet of 4 octet sequence - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x07) << 18; - $mState = 3; - $mBytes = 4; - } elseif (0xF8 == (0xFC & ($in))) { - // First octet of 5 octet sequence. - // - // This is illegal because the encoded codepoint must be - // either: - // (a) not the shortest form or - // (b) outside the Unicode range of 0-0x10FFFF. - // Rather than trying to resynchronize, we will carry on - // until the end of the sequence and let the later error - // handling code catch it. - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 0x03) << 24; - $mState = 4; - $mBytes = 5; - } elseif (0xFC == (0xFE & ($in))) { - // First octet of 6 octet sequence, see comments for 5 - // octet sequence. - $mUcs4 = ($in); - $mUcs4 = ($mUcs4 & 1) << 30; - $mState = 5; - $mBytes = 6; - } else { - // Current octet is neither in the US-ASCII range nor a - // legal first octet of a multi-octet sequence. - $mState = 0; - $mUcs4 = 0; - $mBytes = 1; - $char = ''; - } - } else { - // When mState is non-zero, we expect a continuation of the - // multi-octet sequence - if (0x80 == (0xC0 & ($in))) { - // Legal continuation. - $shift = ($mState - 1) * 6; - $tmp = $in; - $tmp = ($tmp & 0x0000003F) << $shift; - $mUcs4 |= $tmp; - - if (0 == --$mState) { - // End of the multi-octet sequence. mUcs4 now contains - // the final Unicode codepoint to be output - - // Check for illegal sequences and codepoints. - - // From Unicode 3.1, non-shortest form is illegal - if (((2 == $mBytes) && ($mUcs4 < 0x0080)) || - ((3 == $mBytes) && ($mUcs4 < 0x0800)) || - ((4 == $mBytes) && ($mUcs4 < 0x10000)) || - (4 < $mBytes) || - // From Unicode 3.2, surrogate characters = illegal - (($mUcs4 & 0xFFFFF800) == 0xD800) || - // Codepoints outside the Unicode range are illegal - ($mUcs4 > 0x10FFFF) - ) { - - } elseif (0xFEFF != $mUcs4 && // omit BOM - // check for valid Char unicode codepoints - ( - 0x9 == $mUcs4 || - 0xA == $mUcs4 || - 0xD == $mUcs4 || - (0x20 <= $mUcs4 && 0x7E >= $mUcs4) || - // 7F-9F is not strictly prohibited by XML, - // but it is non-SGML, and thus we don't allow it - (0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) || - (0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4) - ) - ) { - $out .= $char; - } - // initialize UTF8 cache (reset) - $mState = 0; - $mUcs4 = 0; - $mBytes = 1; - $char = ''; - } - } else { - // ((0xC0 & (*in) != 0x80) && (mState != 0)) - // Incomplete multi-octet sequence. - // used to result in complete fail, but we'll reset - $mState = 0; - $mUcs4 = 0; - $mBytes = 1; - $char =''; - } - } - } - return $out; - } - - /** - * Translates a Unicode codepoint into its corresponding UTF-8 character. - * @note Based on Feyd's function at - * , - * which is in public domain. - * @note While we're going to do code point parsing anyway, a good - * optimization would be to refuse to translate code points that - * are non-SGML characters. However, this could lead to duplication. - * @note This is very similar to the unichr function in - * maintenance/generate-entity-file.php (although this is superior, - * due to its sanity checks). - */ - - // +----------+----------+----------+----------+ - // | 33222222 | 22221111 | 111111 | | - // | 10987654 | 32109876 | 54321098 | 76543210 | bit - // +----------+----------+----------+----------+ - // | | | | 0xxxxxxx | 1 byte 0x00000000..0x0000007F - // | | | 110yyyyy | 10xxxxxx | 2 byte 0x00000080..0x000007FF - // | | 1110zzzz | 10yyyyyy | 10xxxxxx | 3 byte 0x00000800..0x0000FFFF - // | 11110www | 10wwzzzz | 10yyyyyy | 10xxxxxx | 4 byte 0x00010000..0x0010FFFF - // +----------+----------+----------+----------+ - // | 00000000 | 00011111 | 11111111 | 11111111 | Theoretical upper limit of legal scalars: 2097151 (0x001FFFFF) - // | 00000000 | 00010000 | 11111111 | 11111111 | Defined upper limit of legal scalar codes - // +----------+----------+----------+----------+ - - public static function unichr($code) { - if($code > 1114111 or $code < 0 or - ($code >= 55296 and $code <= 57343) ) { - // bits are set outside the "valid" range as defined - // by UNICODE 4.1.0 - return ''; - } - - $x = $y = $z = $w = 0; - if ($code < 128) { - // regular ASCII character - $x = $code; - } else { - // set up bits for UTF-8 - $x = ($code & 63) | 128; - if ($code < 2048) { - $y = (($code & 2047) >> 6) | 192; - } else { - $y = (($code & 4032) >> 6) | 128; - if($code < 65536) { - $z = (($code >> 12) & 15) | 224; - } else { - $z = (($code >> 12) & 63) | 128; - $w = (($code >> 18) & 7) | 240; - } - } - } - // set up the actual character - $ret = ''; - if($w) $ret .= chr($w); - if($z) $ret .= chr($z); - if($y) $ret .= chr($y); - $ret .= chr($x); - - return $ret; - } - - /** - * Converts a string to UTF-8 based on configuration. - */ - public static function convertToUTF8($str, $config, $context) { - $encoding = $config->get('Core.Encoding'); - if ($encoding === 'utf-8') return $str; - static $iconv = null; - if ($iconv === null) $iconv = function_exists('iconv'); - set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); - if ($iconv && !$config->get('Test.ForceNoIconv')) { - $str = iconv($encoding, 'utf-8//IGNORE', $str); - if ($str === false) { - // $encoding is not a valid encoding - restore_error_handler(); - trigger_error('Invalid encoding ' . $encoding, E_USER_ERROR); - return ''; - } - // If the string is bjorked by Shift_JIS or a similar encoding - // that doesn't support all of ASCII, convert the naughty - // characters to their true byte-wise ASCII/UTF-8 equivalents. - $str = strtr($str, HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding)); - restore_error_handler(); - return $str; - } elseif ($encoding === 'iso-8859-1') { - $str = utf8_encode($str); - restore_error_handler(); - return $str; - } - trigger_error('Encoding not supported, please install iconv', E_USER_ERROR); - } - - /** - * Converts a string from UTF-8 based on configuration. - * @note Currently, this is a lossy conversion, with unexpressable - * characters being omitted. - */ - public static function convertFromUTF8($str, $config, $context) { - $encoding = $config->get('Core.Encoding'); - if ($encoding === 'utf-8') return $str; - static $iconv = null; - if ($iconv === null) $iconv = function_exists('iconv'); - if ($escape = $config->get('Core.EscapeNonASCIICharacters')) { - $str = HTMLPurifier_Encoder::convertToASCIIDumbLossless($str); - } - set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); - if ($iconv && !$config->get('Test.ForceNoIconv')) { - // Undo our previous fix in convertToUTF8, otherwise iconv will barf - $ascii_fix = HTMLPurifier_Encoder::testEncodingSupportsASCII($encoding); - if (!$escape && !empty($ascii_fix)) { - $clear_fix = array(); - foreach ($ascii_fix as $utf8 => $native) $clear_fix[$utf8] = ''; - $str = strtr($str, $clear_fix); - } - $str = strtr($str, array_flip($ascii_fix)); - // Normal stuff - $str = iconv('utf-8', $encoding . '//IGNORE', $str); - restore_error_handler(); - return $str; - } elseif ($encoding === 'iso-8859-1') { - $str = utf8_decode($str); - restore_error_handler(); - return $str; - } - trigger_error('Encoding not supported', E_USER_ERROR); - } - - /** - * Lossless (character-wise) conversion of HTML to ASCII - * @param $str UTF-8 string to be converted to ASCII - * @returns ASCII encoded string with non-ASCII character entity-ized - * @warning Adapted from MediaWiki, claiming fair use: this is a common - * algorithm. If you disagree with this license fudgery, - * implement it yourself. - * @note Uses decimal numeric entities since they are best supported. - * @note This is a DUMB function: it has no concept of keeping - * character entities that the projected character encoding - * can allow. We could possibly implement a smart version - * but that would require it to also know which Unicode - * codepoints the charset supported (not an easy task). - * @note Sort of with cleanUTF8() but it assumes that $str is - * well-formed UTF-8 - */ - public static function convertToASCIIDumbLossless($str) { - $bytesleft = 0; - $result = ''; - $working = 0; - $len = strlen($str); - for( $i = 0; $i < $len; $i++ ) { - $bytevalue = ord( $str[$i] ); - if( $bytevalue <= 0x7F ) { //0xxx xxxx - $result .= chr( $bytevalue ); - $bytesleft = 0; - } elseif( $bytevalue <= 0xBF ) { //10xx xxxx - $working = $working << 6; - $working += ($bytevalue & 0x3F); - $bytesleft--; - if( $bytesleft <= 0 ) { - $result .= "&#" . $working . ";"; - } - } elseif( $bytevalue <= 0xDF ) { //110x xxxx - $working = $bytevalue & 0x1F; - $bytesleft = 1; - } elseif( $bytevalue <= 0xEF ) { //1110 xxxx - $working = $bytevalue & 0x0F; - $bytesleft = 2; - } else { //1111 0xxx - $working = $bytevalue & 0x07; - $bytesleft = 3; - } - } - return $result; - } - - /** - * This expensive function tests whether or not a given character - * encoding supports ASCII. 7/8-bit encodings like Shift_JIS will - * fail this test, and require special processing. Variable width - * encodings shouldn't ever fail. - * - * @param string $encoding Encoding name to test, as per iconv format - * @param bool $bypass Whether or not to bypass the precompiled arrays. - * @return Array of UTF-8 characters to their corresponding ASCII, - * which can be used to "undo" any overzealous iconv action. - */ - public static function testEncodingSupportsASCII($encoding, $bypass = false) { - static $encodings = array(); - if (!$bypass) { - if (isset($encodings[$encoding])) return $encodings[$encoding]; - $lenc = strtolower($encoding); - switch ($lenc) { - case 'shift_jis': - return array("\xC2\xA5" => '\\', "\xE2\x80\xBE" => '~'); - case 'johab': - return array("\xE2\x82\xA9" => '\\'); - } - if (strpos($lenc, 'iso-8859-') === 0) return array(); - } - $ret = array(); - set_error_handler(array('HTMLPurifier_Encoder', 'muteErrorHandler')); - if (iconv('UTF-8', $encoding, 'a') === false) return false; - for ($i = 0x20; $i <= 0x7E; $i++) { // all printable ASCII chars - $c = chr($i); // UTF-8 char - $r = iconv('UTF-8', "$encoding//IGNORE", $c); // initial conversion - if ( - $r === '' || - // This line is needed for iconv implementations that do not - // omit characters that do not exist in the target character set - ($r === $c && iconv($encoding, 'UTF-8//IGNORE', $r) !== $c) - ) { - // Reverse engineer: what's the UTF-8 equiv of this byte - // sequence? This assumes that there's no variable width - // encoding that doesn't support ASCII. - $ret[iconv($encoding, 'UTF-8//IGNORE', $c)] = $c; - } - } - restore_error_handler(); - $encodings[$encoding] = $ret; - return $ret; - } - - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/EntityLookup.php b/extlib/HTMLPurifier/HTMLPurifier/EntityLookup.php deleted file mode 100644 index b4dfce94c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/EntityLookup.php +++ /dev/null @@ -1,44 +0,0 @@ -table = unserialize(file_get_contents($file)); - } - - /** - * Retrieves sole instance of the object. - * @param Optional prototype of custom lookup table to overload with. - */ - public static function instance($prototype = false) { - // no references, since PHP doesn't copy unless modified - static $instance = null; - if ($prototype) { - $instance = $prototype; - } elseif (!$instance) { - $instance = new HTMLPurifier_EntityLookup(); - $instance->setup(); - } - return $instance; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser b/extlib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser deleted file mode 100644 index f2b8b8f2d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/EntityLookup/entities.ser +++ /dev/null @@ -1 +0,0 @@ -a:246:{s:4:"nbsp";s:2:" ";s:5:"iexcl";s:2:"¡";s:4:"cent";s:2:"¢";s:5:"pound";s:2:"£";s:6:"curren";s:2:"¤";s:3:"yen";s:2:"¥";s:6:"brvbar";s:2:"¦";s:4:"sect";s:2:"§";s:3:"uml";s:2:"¨";s:4:"copy";s:2:"©";s:4:"ordf";s:2:"ª";s:5:"laquo";s:2:"«";s:3:"not";s:2:"¬";s:3:"shy";s:2:"­";s:3:"reg";s:2:"®";s:4:"macr";s:2:"¯";s:3:"deg";s:2:"°";s:6:"plusmn";s:2:"±";s:5:"acute";s:2:"´";s:5:"micro";s:2:"µ";s:4:"para";s:2:"¶";s:6:"middot";s:2:"·";s:5:"cedil";s:2:"¸";s:4:"ordm";s:2:"º";s:5:"raquo";s:2:"»";s:6:"iquest";s:2:"¿";s:6:"Agrave";s:2:"À";s:6:"Aacute";s:2:"Á";s:5:"Acirc";s:2:"Â";s:6:"Atilde";s:2:"Ã";s:4:"Auml";s:2:"Ä";s:5:"Aring";s:2:"Å";s:5:"AElig";s:2:"Æ";s:6:"Ccedil";s:2:"Ç";s:6:"Egrave";s:2:"È";s:6:"Eacute";s:2:"É";s:5:"Ecirc";s:2:"Ê";s:4:"Euml";s:2:"Ë";s:6:"Igrave";s:2:"Ì";s:6:"Iacute";s:2:"Í";s:5:"Icirc";s:2:"Î";s:4:"Iuml";s:2:"Ï";s:3:"ETH";s:2:"Ð";s:6:"Ntilde";s:2:"Ñ";s:6:"Ograve";s:2:"Ò";s:6:"Oacute";s:2:"Ó";s:5:"Ocirc";s:2:"Ô";s:6:"Otilde";s:2:"Õ";s:4:"Ouml";s:2:"Ö";s:5:"times";s:2:"×";s:6:"Oslash";s:2:"Ø";s:6:"Ugrave";s:2:"Ù";s:6:"Uacute";s:2:"Ú";s:5:"Ucirc";s:2:"Û";s:4:"Uuml";s:2:"Ü";s:6:"Yacute";s:2:"Ý";s:5:"THORN";s:2:"Þ";s:5:"szlig";s:2:"ß";s:6:"agrave";s:2:"à";s:6:"aacute";s:2:"á";s:5:"acirc";s:2:"â";s:6:"atilde";s:2:"ã";s:4:"auml";s:2:"ä";s:5:"aring";s:2:"å";s:5:"aelig";s:2:"æ";s:6:"ccedil";s:2:"ç";s:6:"egrave";s:2:"è";s:6:"eacute";s:2:"é";s:5:"ecirc";s:2:"ê";s:4:"euml";s:2:"ë";s:6:"igrave";s:2:"ì";s:6:"iacute";s:2:"í";s:5:"icirc";s:2:"î";s:4:"iuml";s:2:"ï";s:3:"eth";s:2:"ð";s:6:"ntilde";s:2:"ñ";s:6:"ograve";s:2:"ò";s:6:"oacute";s:2:"ó";s:5:"ocirc";s:2:"ô";s:6:"otilde";s:2:"õ";s:4:"ouml";s:2:"ö";s:6:"divide";s:2:"÷";s:6:"oslash";s:2:"ø";s:6:"ugrave";s:2:"ù";s:6:"uacute";s:2:"ú";s:5:"ucirc";s:2:"û";s:4:"uuml";s:2:"ü";s:6:"yacute";s:2:"ý";s:5:"thorn";s:2:"þ";s:4:"yuml";s:2:"ÿ";s:4:"quot";s:1:""";s:3:"amp";s:1:"&";s:2:"lt";s:1:"<";s:2:"gt";s:1:">";s:4:"apos";s:1:"'";s:5:"OElig";s:2:"Œ";s:5:"oelig";s:2:"œ";s:6:"Scaron";s:2:"Š";s:6:"scaron";s:2:"š";s:4:"Yuml";s:2:"Ÿ";s:4:"circ";s:2:"ˆ";s:5:"tilde";s:2:"˜";s:4:"ensp";s:3:" ";s:4:"emsp";s:3:" ";s:6:"thinsp";s:3:" ";s:4:"zwnj";s:3:"‌";s:3:"zwj";s:3:"‍";s:3:"lrm";s:3:"‎";s:3:"rlm";s:3:"‏";s:5:"ndash";s:3:"–";s:5:"mdash";s:3:"—";s:5:"lsquo";s:3:"‘";s:5:"rsquo";s:3:"’";s:5:"sbquo";s:3:"‚";s:5:"ldquo";s:3:"“";s:5:"rdquo";s:3:"”";s:5:"bdquo";s:3:"„";s:6:"dagger";s:3:"†";s:6:"Dagger";s:3:"‡";s:6:"permil";s:3:"‰";s:6:"lsaquo";s:3:"‹";s:6:"rsaquo";s:3:"›";s:4:"euro";s:3:"€";s:4:"fnof";s:2:"ƒ";s:5:"Alpha";s:2:"Α";s:4:"Beta";s:2:"Β";s:5:"Gamma";s:2:"Γ";s:5:"Delta";s:2:"Δ";s:7:"Epsilon";s:2:"Ε";s:4:"Zeta";s:2:"Ζ";s:3:"Eta";s:2:"Η";s:5:"Theta";s:2:"Θ";s:4:"Iota";s:2:"Ι";s:5:"Kappa";s:2:"Κ";s:6:"Lambda";s:2:"Λ";s:2:"Mu";s:2:"Μ";s:2:"Nu";s:2:"Ν";s:2:"Xi";s:2:"Ξ";s:7:"Omicron";s:2:"Ο";s:2:"Pi";s:2:"Π";s:3:"Rho";s:2:"Ρ";s:5:"Sigma";s:2:"Σ";s:3:"Tau";s:2:"Τ";s:7:"Upsilon";s:2:"Υ";s:3:"Phi";s:2:"Φ";s:3:"Chi";s:2:"Χ";s:3:"Psi";s:2:"Ψ";s:5:"Omega";s:2:"Ω";s:5:"alpha";s:2:"α";s:4:"beta";s:2:"β";s:5:"gamma";s:2:"γ";s:5:"delta";s:2:"δ";s:7:"epsilon";s:2:"ε";s:4:"zeta";s:2:"ζ";s:3:"eta";s:2:"η";s:5:"theta";s:2:"θ";s:4:"iota";s:2:"ι";s:5:"kappa";s:2:"κ";s:6:"lambda";s:2:"λ";s:2:"mu";s:2:"μ";s:2:"nu";s:2:"ν";s:2:"xi";s:2:"ξ";s:7:"omicron";s:2:"ο";s:2:"pi";s:2:"π";s:3:"rho";s:2:"ρ";s:6:"sigmaf";s:2:"ς";s:5:"sigma";s:2:"σ";s:3:"tau";s:2:"τ";s:7:"upsilon";s:2:"υ";s:3:"phi";s:2:"φ";s:3:"chi";s:2:"χ";s:3:"psi";s:2:"ψ";s:5:"omega";s:2:"ω";s:8:"thetasym";s:2:"ϑ";s:5:"upsih";s:2:"ϒ";s:3:"piv";s:2:"ϖ";s:4:"bull";s:3:"•";s:6:"hellip";s:3:"…";s:5:"prime";s:3:"′";s:5:"Prime";s:3:"″";s:5:"oline";s:3:"‾";s:5:"frasl";s:3:"⁄";s:6:"weierp";s:3:"℘";s:5:"image";s:3:"ℑ";s:4:"real";s:3:"ℜ";s:5:"trade";s:3:"™";s:7:"alefsym";s:3:"ℵ";s:4:"larr";s:3:"←";s:4:"uarr";s:3:"↑";s:4:"rarr";s:3:"→";s:4:"darr";s:3:"↓";s:4:"harr";s:3:"↔";s:5:"crarr";s:3:"↵";s:4:"lArr";s:3:"⇐";s:4:"uArr";s:3:"⇑";s:4:"rArr";s:3:"⇒";s:4:"dArr";s:3:"⇓";s:4:"hArr";s:3:"⇔";s:6:"forall";s:3:"∀";s:4:"part";s:3:"∂";s:5:"exist";s:3:"∃";s:5:"empty";s:3:"∅";s:5:"nabla";s:3:"∇";s:4:"isin";s:3:"∈";s:5:"notin";s:3:"∉";s:2:"ni";s:3:"∋";s:4:"prod";s:3:"∏";s:3:"sum";s:3:"∑";s:5:"minus";s:3:"−";s:6:"lowast";s:3:"∗";s:5:"radic";s:3:"√";s:4:"prop";s:3:"∝";s:5:"infin";s:3:"∞";s:3:"ang";s:3:"∠";s:3:"and";s:3:"∧";s:2:"or";s:3:"∨";s:3:"cap";s:3:"∩";s:3:"cup";s:3:"∪";s:3:"int";s:3:"∫";s:3:"sim";s:3:"∼";s:4:"cong";s:3:"≅";s:5:"asymp";s:3:"≈";s:2:"ne";s:3:"≠";s:5:"equiv";s:3:"≡";s:2:"le";s:3:"≤";s:2:"ge";s:3:"≥";s:3:"sub";s:3:"⊂";s:3:"sup";s:3:"⊃";s:4:"nsub";s:3:"⊄";s:4:"sube";s:3:"⊆";s:4:"supe";s:3:"⊇";s:5:"oplus";s:3:"⊕";s:6:"otimes";s:3:"⊗";s:4:"perp";s:3:"⊥";s:4:"sdot";s:3:"⋅";s:5:"lceil";s:3:"⌈";s:5:"rceil";s:3:"⌉";s:6:"lfloor";s:3:"⌊";s:6:"rfloor";s:3:"⌋";s:4:"lang";s:3:"〈";s:4:"rang";s:3:"〉";s:3:"loz";s:3:"◊";s:6:"spades";s:3:"♠";s:5:"clubs";s:3:"♣";s:6:"hearts";s:3:"♥";s:5:"diams";s:3:"♦";} \ No newline at end of file diff --git a/extlib/HTMLPurifier/HTMLPurifier/EntityParser.php b/extlib/HTMLPurifier/HTMLPurifier/EntityParser.php deleted file mode 100644 index 8c384472d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/EntityParser.php +++ /dev/null @@ -1,144 +0,0 @@ - '"', - 38 => '&', - 39 => "'", - 60 => '<', - 62 => '>' - ); - - /** - * Stripped entity names to decimal conversion table for special entities. - */ - protected $_special_ent2dec = - array( - 'quot' => 34, - 'amp' => 38, - 'lt' => 60, - 'gt' => 62 - ); - - /** - * Substitutes non-special entities with their parsed equivalents. Since - * running this whenever you have parsed character is t3h 5uck, we run - * it before everything else. - * - * @param $string String to have non-special entities parsed. - * @returns Parsed string. - */ - public function substituteNonSpecialEntities($string) { - // it will try to detect missing semicolons, but don't rely on it - return preg_replace_callback( - $this->_substituteEntitiesRegex, - array($this, 'nonSpecialEntityCallback'), - $string - ); - } - - /** - * Callback function for substituteNonSpecialEntities() that does the work. - * - * @param $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). - * @returns Replacement string. - */ - - protected function nonSpecialEntityCallback($matches) { - // replaces all but big five - $entity = $matches[0]; - $is_num = (@$matches[0][1] === '#'); - if ($is_num) { - $is_hex = (@$entity[2] === 'x'); - $code = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; - - // abort for special characters - if (isset($this->_special_dec2str[$code])) return $entity; - - return HTMLPurifier_Encoder::unichr($code); - } else { - if (isset($this->_special_ent2dec[$matches[3]])) return $entity; - if (!$this->_entity_lookup) { - $this->_entity_lookup = HTMLPurifier_EntityLookup::instance(); - } - if (isset($this->_entity_lookup->table[$matches[3]])) { - return $this->_entity_lookup->table[$matches[3]]; - } else { - return $entity; - } - } - } - - /** - * Substitutes only special entities with their parsed equivalents. - * - * @notice We try to avoid calling this function because otherwise, it - * would have to be called a lot (for every parsed section). - * - * @param $string String to have non-special entities parsed. - * @returns Parsed string. - */ - public function substituteSpecialEntities($string) { - return preg_replace_callback( - $this->_substituteEntitiesRegex, - array($this, 'specialEntityCallback'), - $string); - } - - /** - * Callback function for substituteSpecialEntities() that does the work. - * - * This callback has same syntax as nonSpecialEntityCallback(). - * - * @param $matches PCRE-style matches array, with 0 the entire match, and - * either index 1, 2 or 3 set with a hex value, dec value, - * or string (respectively). - * @returns Replacement string. - */ - protected function specialEntityCallback($matches) { - $entity = $matches[0]; - $is_num = (@$matches[0][1] === '#'); - if ($is_num) { - $is_hex = (@$entity[2] === 'x'); - $int = $is_hex ? hexdec($matches[1]) : (int) $matches[2]; - return isset($this->_special_dec2str[$int]) ? - $this->_special_dec2str[$int] : - $entity; - } else { - return isset($this->_special_ent2dec[$matches[3]]) ? - $this->_special_ent2dec[$matches[3]] : - $entity; - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ErrorCollector.php b/extlib/HTMLPurifier/HTMLPurifier/ErrorCollector.php deleted file mode 100644 index 6713eaf77..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ErrorCollector.php +++ /dev/null @@ -1,209 +0,0 @@ -locale =& $context->get('Locale'); - $this->context = $context; - $this->_current =& $this->_stacks[0]; - $this->errors =& $this->_stacks[0]; - } - - /** - * Sends an error message to the collector for later use - * @param $severity int Error severity, PHP error style (don't use E_USER_) - * @param $msg string Error message text - * @param $subst1 string First substitution for $msg - * @param $subst2 string ... - */ - public function send($severity, $msg) { - - $args = array(); - if (func_num_args() > 2) { - $args = func_get_args(); - array_shift($args); - unset($args[0]); - } - - $token = $this->context->get('CurrentToken', true); - $line = $token ? $token->line : $this->context->get('CurrentLine', true); - $col = $token ? $token->col : $this->context->get('CurrentCol', true); - $attr = $this->context->get('CurrentAttr', true); - - // perform special substitutions, also add custom parameters - $subst = array(); - if (!is_null($token)) { - $args['CurrentToken'] = $token; - } - if (!is_null($attr)) { - $subst['$CurrentAttr.Name'] = $attr; - if (isset($token->attr[$attr])) $subst['$CurrentAttr.Value'] = $token->attr[$attr]; - } - - if (empty($args)) { - $msg = $this->locale->getMessage($msg); - } else { - $msg = $this->locale->formatMessage($msg, $args); - } - - if (!empty($subst)) $msg = strtr($msg, $subst); - - // (numerically indexed) - $error = array( - self::LINENO => $line, - self::SEVERITY => $severity, - self::MESSAGE => $msg, - self::CHILDREN => array() - ); - $this->_current[] = $error; - - - // NEW CODE BELOW ... - - $struct = null; - // Top-level errors are either: - // TOKEN type, if $value is set appropriately, or - // "syntax" type, if $value is null - $new_struct = new HTMLPurifier_ErrorStruct(); - $new_struct->type = HTMLPurifier_ErrorStruct::TOKEN; - if ($token) $new_struct->value = clone $token; - if (is_int($line) && is_int($col)) { - if (isset($this->lines[$line][$col])) { - $struct = $this->lines[$line][$col]; - } else { - $struct = $this->lines[$line][$col] = $new_struct; - } - // These ksorts may present a performance problem - ksort($this->lines[$line], SORT_NUMERIC); - } else { - if (isset($this->lines[-1])) { - $struct = $this->lines[-1]; - } else { - $struct = $this->lines[-1] = $new_struct; - } - } - ksort($this->lines, SORT_NUMERIC); - - // Now, check if we need to operate on a lower structure - if (!empty($attr)) { - $struct = $struct->getChild(HTMLPurifier_ErrorStruct::ATTR, $attr); - if (!$struct->value) { - $struct->value = array($attr, 'PUT VALUE HERE'); - } - } - if (!empty($cssprop)) { - $struct = $struct->getChild(HTMLPurifier_ErrorStruct::CSSPROP, $cssprop); - if (!$struct->value) { - // if we tokenize CSS this might be a little more difficult to do - $struct->value = array($cssprop, 'PUT VALUE HERE'); - } - } - - // Ok, structs are all setup, now time to register the error - $struct->addError($severity, $msg); - } - - /** - * Retrieves raw error data for custom formatter to use - * @param List of arrays in format of array(line of error, - * error severity, error message, - * recursive sub-errors array) - */ - public function getRaw() { - return $this->errors; - } - - /** - * Default HTML formatting implementation for error messages - * @param $config Configuration array, vital for HTML output nature - * @param $errors Errors array to display; used for recursion. - */ - public function getHTMLFormatted($config, $errors = null) { - $ret = array(); - - $this->generator = new HTMLPurifier_Generator($config, $this->context); - if ($errors === null) $errors = $this->errors; - - // 'At line' message needs to be removed - - // generation code for new structure goes here. It needs to be recursive. - foreach ($this->lines as $line => $col_array) { - if ($line == -1) continue; - foreach ($col_array as $col => $struct) { - $this->_renderStruct($ret, $struct, $line, $col); - } - } - if (isset($this->lines[-1])) { - $this->_renderStruct($ret, $this->lines[-1]); - } - - if (empty($errors)) { - return '

' . $this->locale->getMessage('ErrorCollector: No errors') . '

'; - } else { - return '
  • ' . implode('
  • ', $ret) . '
'; - } - - } - - private function _renderStruct(&$ret, $struct, $line = null, $col = null) { - $stack = array($struct); - $context_stack = array(array()); - while ($current = array_pop($stack)) { - $context = array_pop($context_stack); - foreach ($current->errors as $error) { - list($severity, $msg) = $error; - $string = ''; - $string .= '
'; - // W3C uses an icon to indicate the severity of the error. - $error = $this->locale->getErrorName($severity); - $string .= "$error "; - if (!is_null($line) && !is_null($col)) { - $string .= "Line $line, Column $col: "; - } else { - $string .= 'End of Document: '; - } - $string .= '' . $this->generator->escape($msg) . ' '; - $string .= '
'; - // Here, have a marker for the character on the column appropriate. - // Be sure to clip extremely long lines. - //$string .= '
';
-                //$string .= '';
-                //$string .= '
'; - $ret[] = $string; - } - foreach ($current->children as $type => $array) { - $context[] = $current; - $stack = array_merge($stack, array_reverse($array, true)); - for ($i = count($array); $i > 0; $i--) { - $context_stack[] = $context; - } - } - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/ErrorStruct.php b/extlib/HTMLPurifier/HTMLPurifier/ErrorStruct.php deleted file mode 100644 index 9bc8996ec..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/ErrorStruct.php +++ /dev/null @@ -1,60 +0,0 @@ -children[$type][$id])) { - $this->children[$type][$id] = new HTMLPurifier_ErrorStruct(); - $this->children[$type][$id]->type = $type; - } - return $this->children[$type][$id]; - } - - public function addError($severity, $message) { - $this->errors[] = array($severity, $message); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Exception.php b/extlib/HTMLPurifier/HTMLPurifier/Exception.php deleted file mode 100644 index be85b4c56..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Exception.php +++ /dev/null @@ -1,12 +0,0 @@ -preFilter, - * 2->preFilter, 3->preFilter, purify, 3->postFilter, 2->postFilter, - * 1->postFilter. - * - * @note Methods are not declared abstract as it is perfectly legitimate - * for an implementation not to want anything to happen on a step - */ - -class HTMLPurifier_Filter -{ - - /** - * Name of the filter for identification purposes - */ - public $name; - - /** - * Pre-processor function, handles HTML before HTML Purifier - */ - public function preFilter($html, $config, $context) { - return $html; - } - - /** - * Post-processor function, handles HTML after HTML Purifier - */ - public function postFilter($html, $config, $context) { - return $html; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php b/extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php deleted file mode 100644 index bbf78a663..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php +++ /dev/null @@ -1,135 +0,0 @@ - blocks from input HTML, cleans them up - * using CSSTidy, and then places them in $purifier->context->get('StyleBlocks') - * so they can be used elsewhere in the document. - * - * @note - * See tests/HTMLPurifier/Filter/ExtractStyleBlocksTest.php for - * sample usage. - * - * @note - * This filter can also be used on stylesheets not included in the - * document--something purists would probably prefer. Just directly - * call HTMLPurifier_Filter_ExtractStyleBlocks->cleanCSS() - */ -class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter -{ - - public $name = 'ExtractStyleBlocks'; - private $_styleMatches = array(); - private $_tidy; - - public function __construct() { - $this->_tidy = new csstidy(); - } - - /** - * Save the contents of CSS blocks to style matches - * @param $matches preg_replace style $matches array - */ - protected function styleCallback($matches) { - $this->_styleMatches[] = $matches[1]; - } - - /** - * Removes inline #isU', array($this, 'styleCallback'), $html); - $style_blocks = $this->_styleMatches; - $this->_styleMatches = array(); // reset - $context->register('StyleBlocks', $style_blocks); // $context must not be reused - if ($this->_tidy) { - foreach ($style_blocks as &$style) { - $style = $this->cleanCSS($style, $config, $context); - } - } - return $html; - } - - /** - * Takes CSS (the stuff found in in a font-family prop). - if ($config->get('Filter.ExtractStyleBlocks.Escaping')) { - $css = str_replace( - array('<', '>', '&'), - array('\3C ', '\3E ', '\26 '), - $css - ); - } - return $css; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php b/extlib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php deleted file mode 100644 index aca972f6c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Filter/YouTube.php +++ /dev/null @@ -1,39 +0,0 @@ -]+>.+?'. - 'http://www.youtube.com/v/([A-Za-z0-9\-_]+).+?#s'; - $pre_replace = '\1'; - return preg_replace($pre_regex, $pre_replace, $html); - } - - public function postFilter($html, $config, $context) { - $post_regex = '#([A-Za-z0-9\-_]+)#'; - return preg_replace_callback($post_regex, array($this, 'postFilterCallback'), $html); - } - - protected function armorUrl($url) { - return str_replace('--', '--', $url); - } - - protected function postFilterCallback($matches) { - $url = $this->armorUrl($matches[1]); - return ''. - ''. - ''. - ''; - - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Generator.php b/extlib/HTMLPurifier/HTMLPurifier/Generator.php deleted file mode 100644 index 24bd8a54e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Generator.php +++ /dev/null @@ -1,183 +0,0 @@ - tags - */ - private $_scriptFix = false; - - /** - * Cache of HTMLDefinition during HTML output to determine whether or - * not attributes should be minimized. - */ - private $_def; - - /** - * Cache of %Output.SortAttr - */ - private $_sortAttr; - - /** - * Configuration for the generator - */ - protected $config; - - /** - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context - */ - public function __construct($config, $context) { - $this->config = $config; - $this->_scriptFix = $config->get('Output.CommentScriptContents'); - $this->_sortAttr = $config->get('Output.SortAttr'); - $this->_def = $config->getHTMLDefinition(); - $this->_xhtml = $this->_def->doctype->xml; - } - - /** - * Generates HTML from an array of tokens. - * @param $tokens Array of HTMLPurifier_Token - * @param $config HTMLPurifier_Config object - * @return Generated HTML - */ - public function generateFromTokens($tokens) { - if (!$tokens) return ''; - - // Basic algorithm - $html = ''; - for ($i = 0, $size = count($tokens); $i < $size; $i++) { - if ($this->_scriptFix && $tokens[$i]->name === 'script' - && $i + 2 < $size && $tokens[$i+2] instanceof HTMLPurifier_Token_End) { - // script special case - // the contents of the script block must be ONE token - // for this to work. - $html .= $this->generateFromToken($tokens[$i++]); - $html .= $this->generateScriptFromToken($tokens[$i++]); - } - $html .= $this->generateFromToken($tokens[$i]); - } - - // Tidy cleanup - if (extension_loaded('tidy') && $this->config->get('Output.TidyFormat')) { - $tidy = new Tidy; - $tidy->parseString($html, array( - 'indent'=> true, - 'output-xhtml' => $this->_xhtml, - 'show-body-only' => true, - 'indent-spaces' => 2, - 'wrap' => 68, - ), 'utf8'); - $tidy->cleanRepair(); - $html = (string) $tidy; // explicit cast necessary - } - - // Normalize newlines to system defined value - $nl = $this->config->get('Output.Newline'); - if ($nl === null) $nl = PHP_EOL; - if ($nl !== "\n") $html = str_replace("\n", $nl, $html); - return $html; - } - - /** - * Generates HTML from a single token. - * @param $token HTMLPurifier_Token object. - * @return Generated HTML - */ - public function generateFromToken($token) { - if (!$token instanceof HTMLPurifier_Token) { - trigger_error('Cannot generate HTML from non-HTMLPurifier_Token object', E_USER_WARNING); - return ''; - - } elseif ($token instanceof HTMLPurifier_Token_Start) { - $attr = $this->generateAttributes($token->attr, $token->name); - return '<' . $token->name . ($attr ? ' ' : '') . $attr . '>'; - - } elseif ($token instanceof HTMLPurifier_Token_End) { - return 'name . '>'; - - } elseif ($token instanceof HTMLPurifier_Token_Empty) { - $attr = $this->generateAttributes($token->attr, $token->name); - return '<' . $token->name . ($attr ? ' ' : '') . $attr . - ( $this->_xhtml ? ' /': '' ) //
v.
- . '>'; - - } elseif ($token instanceof HTMLPurifier_Token_Text) { - return $this->escape($token->data, ENT_NOQUOTES); - - } elseif ($token instanceof HTMLPurifier_Token_Comment) { - return ''; - } else { - return ''; - - } - } - - /** - * Special case processor for the contents of script tags - * @warning This runs into problems if there's already a literal - * --> somewhere inside the script contents. - */ - public function generateScriptFromToken($token) { - if (!$token instanceof HTMLPurifier_Token_Text) return $this->generateFromToken($token); - // Thanks - $data = preg_replace('#//\s*$#', '', $token->data); - return ''; - } - - /** - * Generates attribute declarations from attribute array. - * @note This does not include the leading or trailing space. - * @param $assoc_array_of_attributes Attribute array - * @param $element Name of element attributes are for, used to check - * attribute minimization. - * @return Generate HTML fragment for insertion. - */ - public function generateAttributes($assoc_array_of_attributes, $element = false) { - $html = ''; - if ($this->_sortAttr) ksort($assoc_array_of_attributes); - foreach ($assoc_array_of_attributes as $key => $value) { - if (!$this->_xhtml) { - // Remove namespaced attributes - if (strpos($key, ':') !== false) continue; - // Check if we should minimize the attribute: val="val" -> val - if ($element && !empty($this->_def->info[$element]->attr[$key]->minimized)) { - $html .= $key . ' '; - continue; - } - } - $html .= $key.'="'.$this->escape($value).'" '; - } - return rtrim($html); - } - - /** - * Escapes raw text data. - * @todo This really ought to be protected, but until we have a facility - * for properly generating HTML here w/o using tokens, it stays - * public. - * @param $string String data to escape for HTML. - * @param $quote Quoting style, like htmlspecialchars. ENT_NOQUOTES is - * permissible for non-attribute output. - * @return String escaped data. - */ - public function escape($string, $quote = ENT_COMPAT) { - return htmlspecialchars($string, $quote, 'UTF-8'); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php deleted file mode 100644 index c99ac11eb..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLDefinition.php +++ /dev/null @@ -1,420 +0,0 @@ -getAnonymousModule(); - if (!isset($module->info[$element_name])) { - $element = $module->addBlankElement($element_name); - } else { - $element = $module->info[$element_name]; - } - $element->attr[$attr_name] = $def; - } - - /** - * Adds a custom element to your HTML definition - * @note See HTMLPurifier_HTMLModule::addElement for detailed - * parameter and return value descriptions. - */ - public function addElement($element_name, $type, $contents, $attr_collections, $attributes = array()) { - $module = $this->getAnonymousModule(); - // assume that if the user is calling this, the element - // is safe. This may not be a good idea - $element = $module->addElement($element_name, $type, $contents, $attr_collections, $attributes); - return $element; - } - - /** - * Adds a blank element to your HTML definition, for overriding - * existing behavior - * @note See HTMLPurifier_HTMLModule::addBlankElement for detailed - * parameter and return value descriptions. - */ - public function addBlankElement($element_name) { - $module = $this->getAnonymousModule(); - $element = $module->addBlankElement($element_name); - return $element; - } - - /** - * Retrieves a reference to the anonymous module, so you can - * bust out advanced features without having to make your own - * module. - */ - public function getAnonymousModule() { - if (!$this->_anonModule) { - $this->_anonModule = new HTMLPurifier_HTMLModule(); - $this->_anonModule->name = 'Anonymous'; - } - return $this->_anonModule; - } - - private $_anonModule; - - - // PUBLIC BUT INTERNAL VARIABLES -------------------------------------- - - public $type = 'HTML'; - public $manager; /**< Instance of HTMLPurifier_HTMLModuleManager */ - - /** - * Performs low-cost, preliminary initialization. - */ - public function __construct() { - $this->manager = new HTMLPurifier_HTMLModuleManager(); - } - - protected function doSetup($config) { - $this->processModules($config); - $this->setupConfigStuff($config); - unset($this->manager); - - // cleanup some of the element definitions - foreach ($this->info as $k => $v) { - unset($this->info[$k]->content_model); - unset($this->info[$k]->content_model_type); - } - } - - /** - * Extract out the information from the manager - */ - protected function processModules($config) { - - if ($this->_anonModule) { - // for user specific changes - // this is late-loaded so we don't have to deal with PHP4 - // reference wonky-ness - $this->manager->addModule($this->_anonModule); - unset($this->_anonModule); - } - - $this->manager->setup($config); - $this->doctype = $this->manager->doctype; - - foreach ($this->manager->modules as $module) { - foreach($module->info_tag_transform as $k => $v) { - if ($v === false) unset($this->info_tag_transform[$k]); - else $this->info_tag_transform[$k] = $v; - } - foreach($module->info_attr_transform_pre as $k => $v) { - if ($v === false) unset($this->info_attr_transform_pre[$k]); - else $this->info_attr_transform_pre[$k] = $v; - } - foreach($module->info_attr_transform_post as $k => $v) { - if ($v === false) unset($this->info_attr_transform_post[$k]); - else $this->info_attr_transform_post[$k] = $v; - } - foreach ($module->info_injector as $k => $v) { - if ($v === false) unset($this->info_injector[$k]); - else $this->info_injector[$k] = $v; - } - } - - $this->info = $this->manager->getElements(); - $this->info_content_sets = $this->manager->contentSets->lookup; - - } - - /** - * Sets up stuff based on config. We need a better way of doing this. - */ - protected function setupConfigStuff($config) { - - $block_wrapper = $config->get('HTML.BlockWrapper'); - if (isset($this->info_content_sets['Block'][$block_wrapper])) { - $this->info_block_wrapper = $block_wrapper; - } else { - trigger_error('Cannot use non-block element as block wrapper', - E_USER_ERROR); - } - - $parent = $config->get('HTML.Parent'); - $def = $this->manager->getElement($parent, true); - if ($def) { - $this->info_parent = $parent; - $this->info_parent_def = $def; - } else { - trigger_error('Cannot use unrecognized element as parent', - E_USER_ERROR); - $this->info_parent_def = $this->manager->getElement($this->info_parent, true); - } - - // support template text - $support = "(for information on implementing this, see the ". - "support forums) "; - - // setup allowed elements ----------------------------------------- - - $allowed_elements = $config->get('HTML.AllowedElements'); - $allowed_attributes = $config->get('HTML.AllowedAttributes'); // retrieve early - - if (!is_array($allowed_elements) && !is_array($allowed_attributes)) { - $allowed = $config->get('HTML.Allowed'); - if (is_string($allowed)) { - list($allowed_elements, $allowed_attributes) = $this->parseTinyMCEAllowedList($allowed); - } - } - - if (is_array($allowed_elements)) { - foreach ($this->info as $name => $d) { - if(!isset($allowed_elements[$name])) unset($this->info[$name]); - unset($allowed_elements[$name]); - } - // emit errors - foreach ($allowed_elements as $element => $d) { - $element = htmlspecialchars($element); // PHP doesn't escape errors, be careful! - trigger_error("Element '$element' is not supported $support", E_USER_WARNING); - } - } - - // setup allowed attributes --------------------------------------- - - $allowed_attributes_mutable = $allowed_attributes; // by copy! - if (is_array($allowed_attributes)) { - - // This actually doesn't do anything, since we went away from - // global attributes. It's possible that userland code uses - // it, but HTMLModuleManager doesn't! - foreach ($this->info_global_attr as $attr => $x) { - $keys = array($attr, "*@$attr", "*.$attr"); - $delete = true; - foreach ($keys as $key) { - if ($delete && isset($allowed_attributes[$key])) { - $delete = false; - } - if (isset($allowed_attributes_mutable[$key])) { - unset($allowed_attributes_mutable[$key]); - } - } - if ($delete) unset($this->info_global_attr[$attr]); - } - - foreach ($this->info as $tag => $info) { - foreach ($info->attr as $attr => $x) { - $keys = array("$tag@$attr", $attr, "*@$attr", "$tag.$attr", "*.$attr"); - $delete = true; - foreach ($keys as $key) { - if ($delete && isset($allowed_attributes[$key])) { - $delete = false; - } - if (isset($allowed_attributes_mutable[$key])) { - unset($allowed_attributes_mutable[$key]); - } - } - if ($delete) unset($this->info[$tag]->attr[$attr]); - } - } - // emit errors - foreach ($allowed_attributes_mutable as $elattr => $d) { - $bits = preg_split('/[.@]/', $elattr, 2); - $c = count($bits); - switch ($c) { - case 2: - if ($bits[0] !== '*') { - $element = htmlspecialchars($bits[0]); - $attribute = htmlspecialchars($bits[1]); - if (!isset($this->info[$element])) { - trigger_error("Cannot allow attribute '$attribute' if element '$element' is not allowed/supported $support"); - } else { - trigger_error("Attribute '$attribute' in element '$element' not supported $support", - E_USER_WARNING); - } - break; - } - // otherwise fall through - case 1: - $attribute = htmlspecialchars($bits[0]); - trigger_error("Global attribute '$attribute' is not ". - "supported in any elements $support", - E_USER_WARNING); - break; - } - } - - } - - // setup forbidden elements --------------------------------------- - - $forbidden_elements = $config->get('HTML.ForbiddenElements'); - $forbidden_attributes = $config->get('HTML.ForbiddenAttributes'); - - foreach ($this->info as $tag => $info) { - if (isset($forbidden_elements[$tag])) { - unset($this->info[$tag]); - continue; - } - foreach ($info->attr as $attr => $x) { - if ( - isset($forbidden_attributes["$tag@$attr"]) || - isset($forbidden_attributes["*@$attr"]) || - isset($forbidden_attributes[$attr]) - ) { - unset($this->info[$tag]->attr[$attr]); - continue; - } // this segment might get removed eventually - elseif (isset($forbidden_attributes["$tag.$attr"])) { - // $tag.$attr are not user supplied, so no worries! - trigger_error("Error with $tag.$attr: tag.attr syntax not supported for HTML.ForbiddenAttributes; use tag@attr instead", E_USER_WARNING); - } - } - } - foreach ($forbidden_attributes as $key => $v) { - if (strlen($key) < 2) continue; - if ($key[0] != '*') continue; - if ($key[1] == '.') { - trigger_error("Error with $key: *.attr syntax not supported for HTML.ForbiddenAttributes; use attr instead", E_USER_WARNING); - } - } - - // setup injectors ----------------------------------------------------- - foreach ($this->info_injector as $i => $injector) { - if ($injector->checkNeeded($config) !== false) { - // remove injector that does not have it's required - // elements/attributes present, and is thus not needed. - unset($this->info_injector[$i]); - } - } - } - - /** - * Parses a TinyMCE-flavored Allowed Elements and Attributes list into - * separate lists for processing. Format is element[attr1|attr2],element2... - * @warning Although it's largely drawn from TinyMCE's implementation, - * it is different, and you'll probably have to modify your lists - * @param $list String list to parse - * @param array($allowed_elements, $allowed_attributes) - * @todo Give this its own class, probably static interface - */ - public function parseTinyMCEAllowedList($list) { - - $list = str_replace(array(' ', "\t"), '', $list); - - $elements = array(); - $attributes = array(); - - $chunks = preg_split('/(,|[\n\r]+)/', $list); - foreach ($chunks as $chunk) { - if (empty($chunk)) continue; - // remove TinyMCE element control characters - if (!strpos($chunk, '[')) { - $element = $chunk; - $attr = false; - } else { - list($element, $attr) = explode('[', $chunk); - } - if ($element !== '*') $elements[$element] = true; - if (!$attr) continue; - $attr = substr($attr, 0, strlen($attr) - 1); // remove trailing ] - $attr = explode('|', $attr); - foreach ($attr as $key) { - $attributes["$element.$key"] = true; - } - } - - return array($elements, $attributes); - - } - - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule.php deleted file mode 100644 index 072cf6808..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule.php +++ /dev/null @@ -1,244 +0,0 @@ -info, since the object's data is only info, - * with extra behavior associated with it. - */ - public $attr_collections = array(); - - /** - * Associative array of deprecated tag name to HTMLPurifier_TagTransform - */ - public $info_tag_transform = array(); - - /** - * List of HTMLPurifier_AttrTransform to be performed before validation. - */ - public $info_attr_transform_pre = array(); - - /** - * List of HTMLPurifier_AttrTransform to be performed after validation. - */ - public $info_attr_transform_post = array(); - - /** - * List of HTMLPurifier_Injector to be performed during well-formedness fixing. - * An injector will only be invoked if all of it's pre-requisites are met; - * if an injector fails setup, there will be no error; it will simply be - * silently disabled. - */ - public $info_injector = array(); - - /** - * Boolean flag that indicates whether or not getChildDef is implemented. - * For optimization reasons: may save a call to a function. Be sure - * to set it if you do implement getChildDef(), otherwise it will have - * no effect! - */ - public $defines_child_def = false; - - /** - * Boolean flag whether or not this module is safe. If it is not safe, all - * of its members are unsafe. Modules are safe by default (this might be - * slightly dangerous, but it doesn't make much sense to force HTML Purifier, - * which is based off of safe HTML, to explicitly say, "This is safe," even - * though there are modules which are "unsafe") - * - * @note Previously, safety could be applied at an element level granularity. - * We've removed this ability, so in order to add "unsafe" elements - * or attributes, a dedicated module with this property set to false - * must be used. - */ - public $safe = true; - - /** - * Retrieves a proper HTMLPurifier_ChildDef subclass based on - * content_model and content_model_type member variables of - * the HTMLPurifier_ElementDef class. There is a similar function - * in HTMLPurifier_HTMLDefinition. - * @param $def HTMLPurifier_ElementDef instance - * @return HTMLPurifier_ChildDef subclass - */ - public function getChildDef($def) {return false;} - - // -- Convenience ----------------------------------------------------- - - /** - * Convenience function that sets up a new element - * @param $element Name of element to add - * @param $type What content set should element be registered to? - * Set as false to skip this step. - * @param $contents Allowed children in form of: - * "$content_model_type: $content_model" - * @param $attr_includes What attribute collections to register to - * element? - * @param $attr What unique attributes does the element define? - * @note See ElementDef for in-depth descriptions of these parameters. - * @return Created element definition object, so you - * can set advanced parameters - */ - public function addElement($element, $type, $contents, $attr_includes = array(), $attr = array()) { - $this->elements[] = $element; - // parse content_model - list($content_model_type, $content_model) = $this->parseContents($contents); - // merge in attribute inclusions - $this->mergeInAttrIncludes($attr, $attr_includes); - // add element to content sets - if ($type) $this->addElementToContentSet($element, $type); - // create element - $this->info[$element] = HTMLPurifier_ElementDef::create( - $content_model, $content_model_type, $attr - ); - // literal object $contents means direct child manipulation - if (!is_string($contents)) $this->info[$element]->child = $contents; - return $this->info[$element]; - } - - /** - * Convenience function that creates a totally blank, non-standalone - * element. - * @param $element Name of element to create - * @return Created element - */ - public function addBlankElement($element) { - if (!isset($this->info[$element])) { - $this->elements[] = $element; - $this->info[$element] = new HTMLPurifier_ElementDef(); - $this->info[$element]->standalone = false; - } else { - trigger_error("Definition for $element already exists in module, cannot redefine"); - } - return $this->info[$element]; - } - - /** - * Convenience function that registers an element to a content set - * @param Element to register - * @param Name content set (warning: case sensitive, usually upper-case - * first letter) - */ - public function addElementToContentSet($element, $type) { - if (!isset($this->content_sets[$type])) $this->content_sets[$type] = ''; - else $this->content_sets[$type] .= ' | '; - $this->content_sets[$type] .= $element; - } - - /** - * Convenience function that transforms single-string contents - * into separate content model and content model type - * @param $contents Allowed children in form of: - * "$content_model_type: $content_model" - * @note If contents is an object, an array of two nulls will be - * returned, and the callee needs to take the original $contents - * and use it directly. - */ - public function parseContents($contents) { - if (!is_string($contents)) return array(null, null); // defer - switch ($contents) { - // check for shorthand content model forms - case 'Empty': - return array('empty', ''); - case 'Inline': - return array('optional', 'Inline | #PCDATA'); - case 'Flow': - return array('optional', 'Flow | #PCDATA'); - } - list($content_model_type, $content_model) = explode(':', $contents); - $content_model_type = strtolower(trim($content_model_type)); - $content_model = trim($content_model); - return array($content_model_type, $content_model); - } - - /** - * Convenience function that merges a list of attribute includes into - * an attribute array. - * @param $attr Reference to attr array to modify - * @param $attr_includes Array of includes / string include to merge in - */ - public function mergeInAttrIncludes(&$attr, $attr_includes) { - if (!is_array($attr_includes)) { - if (empty($attr_includes)) $attr_includes = array(); - else $attr_includes = array($attr_includes); - } - $attr[0] = $attr_includes; - } - - /** - * Convenience function that generates a lookup table with boolean - * true as value. - * @param $list List of values to turn into a lookup - * @note You can also pass an arbitrary number of arguments in - * place of the regular argument - * @return Lookup array equivalent of list - */ - public function makeLookup($list) { - if (is_string($list)) $list = func_get_args(); - $ret = array(); - foreach ($list as $value) { - if (is_null($value)) continue; - $ret[$value] = true; - } - return $ret; - } - - /** - * Lazy load construction of the module after determining whether - * or not it's needed, and also when a finalized configuration object - * is available. - * @param $config Instance of HTMLPurifier_Config - */ - public function setup($config) {} - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php deleted file mode 100644 index 3d66f1b4e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Bdo.php +++ /dev/null @@ -1,31 +0,0 @@ - array('dir' => false) - ); - - public function setup($config) { - $bdo = $this->addElement( - 'bdo', 'Inline', 'Inline', array('Core', 'Lang'), - array( - 'dir' => 'Enum#ltr,rtl', // required - // The Abstract Module specification has the attribute - // inclusions wrong for bdo: bdo allows Lang - ) - ); - $bdo->attr_transform_post['required-dir'] = new HTMLPurifier_AttrTransform_BdoDir(); - - $this->attr_collections['I18N']['dir'] = 'Enum#ltr,rtl'; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php deleted file mode 100644 index 7c15da84f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/CommonAttributes.php +++ /dev/null @@ -1,26 +0,0 @@ - array( - 0 => array('Style'), - // 'xml:space' => false, - 'class' => 'Class', - 'id' => 'ID', - 'title' => 'CDATA', - ), - 'Lang' => array(), - 'I18N' => array( - 0 => array('Lang'), // proprietary, for xml:lang/lang - ), - 'Common' => array( - 0 => array('Core', 'I18N') - ) - ); - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php deleted file mode 100644 index ff9369055..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Edit.php +++ /dev/null @@ -1,38 +0,0 @@ - 'URI', - // 'datetime' => 'Datetime', // not implemented - ); - $this->addElement('del', 'Inline', $contents, 'Common', $attr); - $this->addElement('ins', 'Inline', $contents, 'Common', $attr); - } - - // HTML 4.01 specifies that ins/del must not contain block - // elements when used in an inline context, chameleon is - // a complicated workaround to acheive this effect - - // Inline context ! Block context (exclamation mark is - // separator, see getChildDef for parsing) - - public $defines_child_def = true; - public function getChildDef($def) { - if ($def->content_model_type != 'chameleon') return false; - $value = explode('!', $def->content_model); - return new HTMLPurifier_ChildDef_Chameleon($value[0], $value[1]); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php deleted file mode 100644 index 44c22f6f8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Forms.php +++ /dev/null @@ -1,118 +0,0 @@ - 'Form', - 'Inline' => 'Formctrl', - ); - - public function setup($config) { - $form = $this->addElement('form', 'Form', - 'Required: Heading | List | Block | fieldset', 'Common', array( - 'accept' => 'ContentTypes', - 'accept-charset' => 'Charsets', - 'action*' => 'URI', - 'method' => 'Enum#get,post', - // really ContentType, but these two are the only ones used today - 'enctype' => 'Enum#application/x-www-form-urlencoded,multipart/form-data', - )); - $form->excludes = array('form' => true); - - $input = $this->addElement('input', 'Formctrl', 'Empty', 'Common', array( - 'accept' => 'ContentTypes', - 'accesskey' => 'Character', - 'alt' => 'Text', - 'checked' => 'Bool#checked', - 'disabled' => 'Bool#disabled', - 'maxlength' => 'Number', - 'name' => 'CDATA', - 'readonly' => 'Bool#readonly', - 'size' => 'Number', - 'src' => 'URI#embeds', - 'tabindex' => 'Number', - 'type' => 'Enum#text,password,checkbox,button,radio,submit,reset,file,hidden,image', - 'value' => 'CDATA', - )); - $input->attr_transform_post[] = new HTMLPurifier_AttrTransform_Input(); - - $this->addElement('select', 'Formctrl', 'Required: optgroup | option', 'Common', array( - 'disabled' => 'Bool#disabled', - 'multiple' => 'Bool#multiple', - 'name' => 'CDATA', - 'size' => 'Number', - 'tabindex' => 'Number', - )); - - $this->addElement('option', false, 'Optional: #PCDATA', 'Common', array( - 'disabled' => 'Bool#disabled', - 'label' => 'Text', - 'selected' => 'Bool#selected', - 'value' => 'CDATA', - )); - // It's illegal for there to be more than one selected, but not - // be multiple. Also, no selected means undefined behavior. This might - // be difficult to implement; perhaps an injector, or a context variable. - - $textarea = $this->addElement('textarea', 'Formctrl', 'Optional: #PCDATA', 'Common', array( - 'accesskey' => 'Character', - 'cols*' => 'Number', - 'disabled' => 'Bool#disabled', - 'name' => 'CDATA', - 'readonly' => 'Bool#readonly', - 'rows*' => 'Number', - 'tabindex' => 'Number', - )); - $textarea->attr_transform_pre[] = new HTMLPurifier_AttrTransform_Textarea(); - - $button = $this->addElement('button', 'Formctrl', 'Optional: #PCDATA | Heading | List | Block | Inline', 'Common', array( - 'accesskey' => 'Character', - 'disabled' => 'Bool#disabled', - 'name' => 'CDATA', - 'tabindex' => 'Number', - 'type' => 'Enum#button,submit,reset', - 'value' => 'CDATA', - )); - - // For exclusions, ideally we'd specify content sets, not literal elements - $button->excludes = $this->makeLookup( - 'form', 'fieldset', // Form - 'input', 'select', 'textarea', 'label', 'button', // Formctrl - 'a' // as per HTML 4.01 spec, this is omitted by modularization - ); - - // Extra exclusion: img usemap="" is not permitted within this element. - // We'll omit this for now, since we don't have any good way of - // indicating it yet. - - // This is HIGHLY user-unfriendly; we need a custom child-def for this - $this->addElement('fieldset', 'Form', 'Custom: (#WS?,legend,(Flow|#PCDATA)*)', 'Common'); - - $label = $this->addElement('label', 'Formctrl', 'Optional: #PCDATA | Inline', 'Common', array( - 'accesskey' => 'Character', - // 'for' => 'IDREF', // IDREF not implemented, cannot allow - )); - $label->excludes = array('label' => true); - - $this->addElement('legend', false, 'Optional: #PCDATA | Inline', 'Common', array( - 'accesskey' => 'Character', - )); - - $this->addElement('optgroup', false, 'Required: option', 'Common', array( - 'disabled' => 'Bool#disabled', - 'label*' => 'Text', - )); - - // Don't forget an injector for . This one's a little complex - // because it maps to multiple elements. - - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php deleted file mode 100644 index d7e9bdd27..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Hypertext.php +++ /dev/null @@ -1,31 +0,0 @@ -addElement( - 'a', 'Inline', 'Inline', 'Common', - array( - // 'accesskey' => 'Character', - // 'charset' => 'Charset', - 'href' => 'URI', - // 'hreflang' => 'LanguageCode', - 'rel' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rel'), - 'rev' => new HTMLPurifier_AttrDef_HTML_LinkTypes('rev'), - // 'tabindex' => 'Number', - // 'type' => 'ContentType', - ) - ); - $a->formatting = true; - $a->excludes = array('a' => true); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php deleted file mode 100644 index 948d435bc..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Image.php +++ /dev/null @@ -1,40 +0,0 @@ -get('HTML.MaxImgLength'); - $img = $this->addElement( - 'img', 'Inline', 'Empty', 'Common', - array( - 'alt*' => 'Text', - // According to the spec, it's Length, but percents can - // be abused, so we allow only Pixels. - 'height' => 'Pixels#' . $max, - 'width' => 'Pixels#' . $max, - 'longdesc' => 'URI', - 'src*' => new HTMLPurifier_AttrDef_URI(true), // embedded - ) - ); - if ($max === null || $config->get('HTML.Trusted')) { - $img->attr['height'] = - $img->attr['width'] = 'Length'; - } - - // kind of strange, but splitting things up would be inefficient - $img->attr_transform_pre[] = - $img->attr_transform_post[] = - new HTMLPurifier_AttrTransform_ImgRequired(); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php deleted file mode 100644 index df33927ba..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Legacy.php +++ /dev/null @@ -1,143 +0,0 @@ -addElement('basefont', 'Inline', 'Empty', false, array( - 'color' => 'Color', - 'face' => 'Text', // extremely broad, we should - 'size' => 'Text', // tighten it - 'id' => 'ID' - )); - $this->addElement('center', 'Block', 'Flow', 'Common'); - $this->addElement('dir', 'Block', 'Required: li', 'Common', array( - 'compact' => 'Bool#compact' - )); - $this->addElement('font', 'Inline', 'Inline', array('Core', 'I18N'), array( - 'color' => 'Color', - 'face' => 'Text', // extremely broad, we should - 'size' => 'Text', // tighten it - )); - $this->addElement('menu', 'Block', 'Required: li', 'Common', array( - 'compact' => 'Bool#compact' - )); - - $s = $this->addElement('s', 'Inline', 'Inline', 'Common'); - $s->formatting = true; - - $strike = $this->addElement('strike', 'Inline', 'Inline', 'Common'); - $strike->formatting = true; - - $u = $this->addElement('u', 'Inline', 'Inline', 'Common'); - $u->formatting = true; - - // setup modifications to old elements - - $align = 'Enum#left,right,center,justify'; - - $address = $this->addBlankElement('address'); - $address->content_model = 'Inline | #PCDATA | p'; - $address->content_model_type = 'optional'; - $address->child = false; - - $blockquote = $this->addBlankElement('blockquote'); - $blockquote->content_model = 'Flow | #PCDATA'; - $blockquote->content_model_type = 'optional'; - $blockquote->child = false; - - $br = $this->addBlankElement('br'); - $br->attr['clear'] = 'Enum#left,all,right,none'; - - $caption = $this->addBlankElement('caption'); - $caption->attr['align'] = 'Enum#top,bottom,left,right'; - - $div = $this->addBlankElement('div'); - $div->attr['align'] = $align; - - $dl = $this->addBlankElement('dl'); - $dl->attr['compact'] = 'Bool#compact'; - - for ($i = 1; $i <= 6; $i++) { - $h = $this->addBlankElement("h$i"); - $h->attr['align'] = $align; - } - - $hr = $this->addBlankElement('hr'); - $hr->attr['align'] = $align; - $hr->attr['noshade'] = 'Bool#noshade'; - $hr->attr['size'] = 'Pixels'; - $hr->attr['width'] = 'Length'; - - $img = $this->addBlankElement('img'); - $img->attr['align'] = 'Enum#top,middle,bottom,left,right'; - $img->attr['border'] = 'Pixels'; - $img->attr['hspace'] = 'Pixels'; - $img->attr['vspace'] = 'Pixels'; - - // figure out this integer business - - $li = $this->addBlankElement('li'); - $li->attr['value'] = new HTMLPurifier_AttrDef_Integer(); - $li->attr['type'] = 'Enum#s:1,i,I,a,A,disc,square,circle'; - - $ol = $this->addBlankElement('ol'); - $ol->attr['compact'] = 'Bool#compact'; - $ol->attr['start'] = new HTMLPurifier_AttrDef_Integer(); - $ol->attr['type'] = 'Enum#s:1,i,I,a,A'; - - $p = $this->addBlankElement('p'); - $p->attr['align'] = $align; - - $pre = $this->addBlankElement('pre'); - $pre->attr['width'] = 'Number'; - - // script omitted - - $table = $this->addBlankElement('table'); - $table->attr['align'] = 'Enum#left,center,right'; - $table->attr['bgcolor'] = 'Color'; - - $tr = $this->addBlankElement('tr'); - $tr->attr['bgcolor'] = 'Color'; - - $th = $this->addBlankElement('th'); - $th->attr['bgcolor'] = 'Color'; - $th->attr['height'] = 'Length'; - $th->attr['nowrap'] = 'Bool#nowrap'; - $th->attr['width'] = 'Length'; - - $td = $this->addBlankElement('td'); - $td->attr['bgcolor'] = 'Color'; - $td->attr['height'] = 'Length'; - $td->attr['nowrap'] = 'Bool#nowrap'; - $td->attr['width'] = 'Length'; - - $ul = $this->addBlankElement('ul'); - $ul->attr['compact'] = 'Bool#compact'; - $ul->attr['type'] = 'Enum#square,disc,circle'; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php deleted file mode 100644 index 1d15f2729..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/List.php +++ /dev/null @@ -1,35 +0,0 @@ - 'List'); - - public function setup($config) { - $this->addElement('ol', 'List', 'Required: li', 'Common'); - $this->addElement('ul', 'List', 'Required: li', 'Common'); - $this->addElement('dl', 'List', 'Required: dt | dd', 'Common'); - - $this->addElement('li', false, 'Flow', 'Common'); - - $this->addElement('dd', false, 'Flow', 'Common'); - $this->addElement('dt', false, 'Inline', 'Common'); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php deleted file mode 100644 index 05694b450..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Name.php +++ /dev/null @@ -1,21 +0,0 @@ -addBlankElement($name); - $element->attr['name'] = 'CDATA'; - if (!$config->get('HTML.Attr.Name.UseCDATA')) { - $element->attr_transform_post['NameSync'] = new HTMLPurifier_AttrTransform_NameSync(); - } - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php deleted file mode 100644 index 5f1b14abb..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/NonXMLCommonAttributes.php +++ /dev/null @@ -1,14 +0,0 @@ - array( - 'lang' => 'LanguageCode', - ) - ); -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php deleted file mode 100644 index 193c1011f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Object.php +++ /dev/null @@ -1,47 +0,0 @@ - to cater to legacy browsers: this - * module does not allow this sort of behavior - */ -class HTMLPurifier_HTMLModule_Object extends HTMLPurifier_HTMLModule -{ - - public $name = 'Object'; - public $safe = false; - - public function setup($config) { - - $this->addElement('object', 'Inline', 'Optional: #PCDATA | Flow | param', 'Common', - array( - 'archive' => 'URI', - 'classid' => 'URI', - 'codebase' => 'URI', - 'codetype' => 'Text', - 'data' => 'URI', - 'declare' => 'Bool#declare', - 'height' => 'Length', - 'name' => 'CDATA', - 'standby' => 'Text', - 'tabindex' => 'Number', - 'type' => 'ContentType', - 'width' => 'Length' - ) - ); - - $this->addElement('param', false, 'Empty', false, - array( - 'id' => 'ID', - 'name*' => 'Text', - 'type' => 'Text', - 'value' => 'Text', - 'valuetype' => 'Enum#data,ref,object' - ) - ); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php deleted file mode 100644 index 8ff0b5ed7..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Presentation.php +++ /dev/null @@ -1,36 +0,0 @@ -addElement('hr', 'Block', 'Empty', 'Common'); - $this->addElement('sub', 'Inline', 'Inline', 'Common'); - $this->addElement('sup', 'Inline', 'Inline', 'Common'); - $b = $this->addElement('b', 'Inline', 'Inline', 'Common'); - $b->formatting = true; - $big = $this->addElement('big', 'Inline', 'Inline', 'Common'); - $big->formatting = true; - $i = $this->addElement('i', 'Inline', 'Inline', 'Common'); - $i->formatting = true; - $small = $this->addElement('small', 'Inline', 'Inline', 'Common'); - $small->formatting = true; - $tt = $this->addElement('tt', 'Inline', 'Inline', 'Common'); - $tt->formatting = true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php deleted file mode 100644 index dd36a3de0..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Proprietary.php +++ /dev/null @@ -1,33 +0,0 @@ -addElement('marquee', 'Inline', 'Flow', 'Common', - array( - 'direction' => 'Enum#left,right,up,down', - 'behavior' => 'Enum#alternate', - 'width' => 'Length', - 'height' => 'Length', - 'scrolldelay' => 'Number', - 'scrollamount' => 'Number', - 'loop' => 'Number', - 'bgcolor' => 'Color', - 'hspace' => 'Pixels', - 'vspace' => 'Pixels', - ) - ); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php deleted file mode 100644 index b26a0a30a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Ruby.php +++ /dev/null @@ -1,27 +0,0 @@ -addElement('ruby', 'Inline', - 'Custom: ((rb, (rt | (rp, rt, rp))) | (rbc, rtc, rtc?))', - 'Common'); - $this->addElement('rbc', false, 'Required: rb', 'Common'); - $this->addElement('rtc', false, 'Required: rt', 'Common'); - $rb = $this->addElement('rb', false, 'Inline', 'Common'); - $rb->excludes = array('ruby' => true); - $rt = $this->addElement('rt', false, 'Inline', 'Common', array('rbspan' => 'Number')); - $rt->excludes = array('ruby' => true); - $this->addElement('rp', false, 'Optional: #PCDATA', 'Common'); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php deleted file mode 100644 index 8fc03cb1c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeEmbed.php +++ /dev/null @@ -1,33 +0,0 @@ -get('HTML.MaxImgLength'); - $embed = $this->addElement( - 'embed', 'Inline', 'Empty', 'Common', - array( - 'src*' => 'URI#embedded', - 'type' => 'Enum#application/x-shockwave-flash', - 'width' => 'Pixels#' . $max, - 'height' => 'Pixels#' . $max, - 'allowscriptaccess' => 'Enum#never', - 'allownetworking' => 'Enum#internal', - 'wmode' => 'Enum#window', - 'name' => 'ID', - ) - ); - $embed->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeEmbed(); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php deleted file mode 100644 index 33bac00cf..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/SafeObject.php +++ /dev/null @@ -1,50 +0,0 @@ -get('HTML.MaxImgLength'); - $object = $this->addElement( - 'object', - 'Inline', - 'Optional: param | Flow | #PCDATA', - 'Common', - array( - // While technically not required by the spec, we're forcing - // it to this value. - 'type' => 'Enum#application/x-shockwave-flash', - 'width' => 'Pixels#' . $max, - 'height' => 'Pixels#' . $max, - 'data' => 'URI#embedded' - ) - ); - $object->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeObject(); - - $param = $this->addElement('param', false, 'Empty', false, - array( - 'id' => 'ID', - 'name*' => 'Text', - 'value' => 'Text' - ) - ); - $param->attr_transform_post[] = new HTMLPurifier_AttrTransform_SafeParam(); - $this->info_injector[] = 'SafeObject'; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php deleted file mode 100644 index cecdea6c3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Scripting.php +++ /dev/null @@ -1,54 +0,0 @@ - 'script | noscript', 'Inline' => 'script | noscript'); - public $safe = false; - - public function setup($config) { - // TODO: create custom child-definition for noscript that - // auto-wraps stray #PCDATA in a similar manner to - // blockquote's custom definition (we would use it but - // blockquote's contents are optional while noscript's contents - // are required) - - // TODO: convert this to new syntax, main problem is getting - // both content sets working - - // In theory, this could be safe, but I don't see any reason to - // allow it. - $this->info['noscript'] = new HTMLPurifier_ElementDef(); - $this->info['noscript']->attr = array( 0 => array('Common') ); - $this->info['noscript']->content_model = 'Heading | List | Block'; - $this->info['noscript']->content_model_type = 'required'; - - $this->info['script'] = new HTMLPurifier_ElementDef(); - $this->info['script']->attr = array( - 'defer' => new HTMLPurifier_AttrDef_Enum(array('defer')), - 'src' => new HTMLPurifier_AttrDef_URI(true), - 'type' => new HTMLPurifier_AttrDef_Enum(array('text/javascript')) - ); - $this->info['script']->content_model = '#PCDATA'; - $this->info['script']->content_model_type = 'optional'; - $this->info['script']->attr_transform_pre['type'] = - $this->info['script']->attr_transform_post['type'] = - new HTMLPurifier_AttrTransform_ScriptRequired(); - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php deleted file mode 100644 index eb78464cc..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/StyleAttribute.php +++ /dev/null @@ -1,24 +0,0 @@ - array('style' => false), // see constructor - 'Core' => array(0 => array('Style')) - ); - - public function setup($config) { - $this->attr_collections['Style']['style'] = new HTMLPurifier_AttrDef_CSS(); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php deleted file mode 100644 index f314ced3f..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tables.php +++ /dev/null @@ -1,66 +0,0 @@ -addElement('caption', false, 'Inline', 'Common'); - - $this->addElement('table', 'Block', - new HTMLPurifier_ChildDef_Table(), 'Common', - array( - 'border' => 'Pixels', - 'cellpadding' => 'Length', - 'cellspacing' => 'Length', - 'frame' => 'Enum#void,above,below,hsides,lhs,rhs,vsides,box,border', - 'rules' => 'Enum#none,groups,rows,cols,all', - 'summary' => 'Text', - 'width' => 'Length' - ) - ); - - // common attributes - $cell_align = array( - 'align' => 'Enum#left,center,right,justify,char', - 'charoff' => 'Length', - 'valign' => 'Enum#top,middle,bottom,baseline', - ); - - $cell_t = array_merge( - array( - 'abbr' => 'Text', - 'colspan' => 'Number', - 'rowspan' => 'Number', - ), - $cell_align - ); - $this->addElement('td', false, 'Flow', 'Common', $cell_t); - $this->addElement('th', false, 'Flow', 'Common', $cell_t); - - $this->addElement('tr', false, 'Required: td | th', 'Common', $cell_align); - - $cell_col = array_merge( - array( - 'span' => 'Number', - 'width' => 'MultiLength', - ), - $cell_align - ); - $this->addElement('col', false, 'Empty', 'Common', $cell_col); - $this->addElement('colgroup', false, 'Optional: col', 'Common', $cell_col); - - $this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align); - $this->addElement('thead', false, 'Required: tr', 'Common', $cell_align); - $this->addElement('tfoot', false, 'Required: tr', 'Common', $cell_align); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php deleted file mode 100644 index 2b844ecc4..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Target.php +++ /dev/null @@ -1,23 +0,0 @@ -addBlankElement($name); - $e->attr = array( - 'target' => new HTMLPurifier_AttrDef_HTML_FrameTarget() - ); - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php deleted file mode 100644 index ae77c7188..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Text.php +++ /dev/null @@ -1,71 +0,0 @@ - 'Heading | Block | Inline' - ); - - public function setup($config) { - - // Inline Phrasal ------------------------------------------------- - $this->addElement('abbr', 'Inline', 'Inline', 'Common'); - $this->addElement('acronym', 'Inline', 'Inline', 'Common'); - $this->addElement('cite', 'Inline', 'Inline', 'Common'); - $this->addElement('dfn', 'Inline', 'Inline', 'Common'); - $this->addElement('kbd', 'Inline', 'Inline', 'Common'); - $this->addElement('q', 'Inline', 'Inline', 'Common', array('cite' => 'URI')); - $this->addElement('samp', 'Inline', 'Inline', 'Common'); - $this->addElement('var', 'Inline', 'Inline', 'Common'); - - $em = $this->addElement('em', 'Inline', 'Inline', 'Common'); - $em->formatting = true; - - $strong = $this->addElement('strong', 'Inline', 'Inline', 'Common'); - $strong->formatting = true; - - $code = $this->addElement('code', 'Inline', 'Inline', 'Common'); - $code->formatting = true; - - // Inline Structural ---------------------------------------------- - $this->addElement('span', 'Inline', 'Inline', 'Common'); - $this->addElement('br', 'Inline', 'Empty', 'Core'); - - // Block Phrasal -------------------------------------------------- - $this->addElement('address', 'Block', 'Inline', 'Common'); - $this->addElement('blockquote', 'Block', 'Optional: Heading | Block | List', 'Common', array('cite' => 'URI') ); - $pre = $this->addElement('pre', 'Block', 'Inline', 'Common'); - $pre->excludes = $this->makeLookup( - 'img', 'big', 'small', 'object', 'applet', 'font', 'basefont' ); - $this->addElement('h1', 'Heading', 'Inline', 'Common'); - $this->addElement('h2', 'Heading', 'Inline', 'Common'); - $this->addElement('h3', 'Heading', 'Inline', 'Common'); - $this->addElement('h4', 'Heading', 'Inline', 'Common'); - $this->addElement('h5', 'Heading', 'Inline', 'Common'); - $this->addElement('h6', 'Heading', 'Inline', 'Common'); - - // Block Structural ----------------------------------------------- - $p = $this->addElement('p', 'Block', 'Inline', 'Common'); - $p->autoclose = array_flip(array("address", "blockquote", "center", "dir", "div", "dl", "fieldset", "ol", "p", "ul")); - - $this->addElement('div', 'Block', 'Flow', 'Common'); - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php deleted file mode 100644 index 21783f18e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy.php +++ /dev/null @@ -1,207 +0,0 @@ - 'none', 'light', 'medium', 'heavy'); - - /** - * Default level to place all fixes in. Disabled by default - */ - public $defaultLevel = null; - - /** - * Lists of fixes used by getFixesForLevel(). Format is: - * HTMLModule_Tidy->fixesForLevel[$level] = array('fix-1', 'fix-2'); - */ - public $fixesForLevel = array( - 'light' => array(), - 'medium' => array(), - 'heavy' => array() - ); - - /** - * Lazy load constructs the module by determining the necessary - * fixes to create and then delegating to the populate() function. - * @todo Wildcard matching and error reporting when an added or - * subtracted fix has no effect. - */ - public function setup($config) { - - // create fixes, initialize fixesForLevel - $fixes = $this->makeFixes(); - $this->makeFixesForLevel($fixes); - - // figure out which fixes to use - $level = $config->get('HTML.TidyLevel'); - $fixes_lookup = $this->getFixesForLevel($level); - - // get custom fix declarations: these need namespace processing - $add_fixes = $config->get('HTML.TidyAdd'); - $remove_fixes = $config->get('HTML.TidyRemove'); - - foreach ($fixes as $name => $fix) { - // needs to be refactored a little to implement globbing - if ( - isset($remove_fixes[$name]) || - (!isset($add_fixes[$name]) && !isset($fixes_lookup[$name])) - ) { - unset($fixes[$name]); - } - } - - // populate this module with necessary fixes - $this->populate($fixes); - - } - - /** - * Retrieves all fixes per a level, returning fixes for that specific - * level as well as all levels below it. - * @param $level String level identifier, see $levels for valid values - * @return Lookup up table of fixes - */ - public function getFixesForLevel($level) { - if ($level == $this->levels[0]) { - return array(); - } - $activated_levels = array(); - for ($i = 1, $c = count($this->levels); $i < $c; $i++) { - $activated_levels[] = $this->levels[$i]; - if ($this->levels[$i] == $level) break; - } - if ($i == $c) { - trigger_error( - 'Tidy level ' . htmlspecialchars($level) . ' not recognized', - E_USER_WARNING - ); - return array(); - } - $ret = array(); - foreach ($activated_levels as $level) { - foreach ($this->fixesForLevel[$level] as $fix) { - $ret[$fix] = true; - } - } - return $ret; - } - - /** - * Dynamically populates the $fixesForLevel member variable using - * the fixes array. It may be custom overloaded, used in conjunction - * with $defaultLevel, or not used at all. - */ - public function makeFixesForLevel($fixes) { - if (!isset($this->defaultLevel)) return; - if (!isset($this->fixesForLevel[$this->defaultLevel])) { - trigger_error( - 'Default level ' . $this->defaultLevel . ' does not exist', - E_USER_ERROR - ); - return; - } - $this->fixesForLevel[$this->defaultLevel] = array_keys($fixes); - } - - /** - * Populates the module with transforms and other special-case code - * based on a list of fixes passed to it - * @param $lookup Lookup table of fixes to activate - */ - public function populate($fixes) { - foreach ($fixes as $name => $fix) { - // determine what the fix is for - list($type, $params) = $this->getFixType($name); - switch ($type) { - case 'attr_transform_pre': - case 'attr_transform_post': - $attr = $params['attr']; - if (isset($params['element'])) { - $element = $params['element']; - if (empty($this->info[$element])) { - $e = $this->addBlankElement($element); - } else { - $e = $this->info[$element]; - } - } else { - $type = "info_$type"; - $e = $this; - } - // PHP does some weird parsing when I do - // $e->$type[$attr], so I have to assign a ref. - $f =& $e->$type; - $f[$attr] = $fix; - break; - case 'tag_transform': - $this->info_tag_transform[$params['element']] = $fix; - break; - case 'child': - case 'content_model_type': - $element = $params['element']; - if (empty($this->info[$element])) { - $e = $this->addBlankElement($element); - } else { - $e = $this->info[$element]; - } - $e->$type = $fix; - break; - default: - trigger_error("Fix type $type not supported", E_USER_ERROR); - break; - } - } - } - - /** - * Parses a fix name and determines what kind of fix it is, as well - * as other information defined by the fix - * @param $name String name of fix - * @return array(string $fix_type, array $fix_parameters) - * @note $fix_parameters is type dependant, see populate() for usage - * of these parameters - */ - public function getFixType($name) { - // parse it - $property = $attr = null; - if (strpos($name, '#') !== false) list($name, $property) = explode('#', $name); - if (strpos($name, '@') !== false) list($name, $attr) = explode('@', $name); - - // figure out the parameters - $params = array(); - if ($name !== '') $params['element'] = $name; - if (!is_null($attr)) $params['attr'] = $attr; - - // special case: attribute transform - if (!is_null($attr)) { - if (is_null($property)) $property = 'pre'; - $type = 'attr_transform_' . $property; - return array($type, $params); - } - - // special case: tag transform - if (is_null($property)) { - return array('tag_transform', $params); - } - - return array($property, $params); - - } - - /** - * Defines all fixes the module will perform in a compact - * associative array of fix name to fix implementation. - */ - public function makeFixes() {} - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php deleted file mode 100644 index 61ff85ce2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Name.php +++ /dev/null @@ -1,24 +0,0 @@ -content_model_type != 'strictblockquote') return parent::getChildDef($def); - return new HTMLPurifier_ChildDef_StrictBlockquote($def->content_model); - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php deleted file mode 100644 index 9960b1dd1..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/Tidy/Transitional.php +++ /dev/null @@ -1,9 +0,0 @@ - 'text-align:left;', - 'right' => 'text-align:right;', - 'top' => 'caption-side:top;', - 'bottom' => 'caption-side:bottom;' // not supported by IE - )); - - // @align for img ------------------------------------------------- - $r['img@align'] = - new HTMLPurifier_AttrTransform_EnumToCSS('align', array( - 'left' => 'float:left;', - 'right' => 'float:right;', - 'top' => 'vertical-align:top;', - 'middle' => 'vertical-align:middle;', - 'bottom' => 'vertical-align:baseline;', - )); - - // @align for table ----------------------------------------------- - $r['table@align'] = - new HTMLPurifier_AttrTransform_EnumToCSS('align', array( - 'left' => 'float:left;', - 'center' => 'margin-left:auto;margin-right:auto;', - 'right' => 'float:right;' - )); - - // @align for hr ----------------------------------------------- - $r['hr@align'] = - new HTMLPurifier_AttrTransform_EnumToCSS('align', array( - // we use both text-align and margin because these work - // for different browsers (IE and Firefox, respectively) - // and the melange makes for a pretty cross-compatible - // solution - 'left' => 'margin-left:0;margin-right:auto;text-align:left;', - 'center' => 'margin-left:auto;margin-right:auto;text-align:center;', - 'right' => 'margin-left:auto;margin-right:0;text-align:right;' - )); - - // @align for h1, h2, h3, h4, h5, h6, p, div ---------------------- - // {{{ - $align_lookup = array(); - $align_values = array('left', 'right', 'center', 'justify'); - foreach ($align_values as $v) $align_lookup[$v] = "text-align:$v;"; - // }}} - $r['h1@align'] = - $r['h2@align'] = - $r['h3@align'] = - $r['h4@align'] = - $r['h5@align'] = - $r['h6@align'] = - $r['p@align'] = - $r['div@align'] = - new HTMLPurifier_AttrTransform_EnumToCSS('align', $align_lookup); - - // @bgcolor for table, tr, td, th --------------------------------- - $r['table@bgcolor'] = - $r['td@bgcolor'] = - $r['th@bgcolor'] = - new HTMLPurifier_AttrTransform_BgColor(); - - // @border for img ------------------------------------------------ - $r['img@border'] = new HTMLPurifier_AttrTransform_Border(); - - // @clear for br -------------------------------------------------- - $r['br@clear'] = - new HTMLPurifier_AttrTransform_EnumToCSS('clear', array( - 'left' => 'clear:left;', - 'right' => 'clear:right;', - 'all' => 'clear:both;', - 'none' => 'clear:none;', - )); - - // @height for td, th --------------------------------------------- - $r['td@height'] = - $r['th@height'] = - new HTMLPurifier_AttrTransform_Length('height'); - - // @hspace for img ------------------------------------------------ - $r['img@hspace'] = new HTMLPurifier_AttrTransform_ImgSpace('hspace'); - - // @noshade for hr ------------------------------------------------ - // this transformation is not precise but often good enough. - // different browsers use different styles to designate noshade - $r['hr@noshade'] = - new HTMLPurifier_AttrTransform_BoolToCSS( - 'noshade', - 'color:#808080;background-color:#808080;border:0;' - ); - - // @nowrap for td, th --------------------------------------------- - $r['td@nowrap'] = - $r['th@nowrap'] = - new HTMLPurifier_AttrTransform_BoolToCSS( - 'nowrap', - 'white-space:nowrap;' - ); - - // @size for hr -------------------------------------------------- - $r['hr@size'] = new HTMLPurifier_AttrTransform_Length('size', 'height'); - - // @type for li, ol, ul ------------------------------------------- - // {{{ - $ul_types = array( - 'disc' => 'list-style-type:disc;', - 'square' => 'list-style-type:square;', - 'circle' => 'list-style-type:circle;' - ); - $ol_types = array( - '1' => 'list-style-type:decimal;', - 'i' => 'list-style-type:lower-roman;', - 'I' => 'list-style-type:upper-roman;', - 'a' => 'list-style-type:lower-alpha;', - 'A' => 'list-style-type:upper-alpha;' - ); - $li_types = $ul_types + $ol_types; - // }}} - - $r['ul@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ul_types); - $r['ol@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $ol_types, true); - $r['li@type'] = new HTMLPurifier_AttrTransform_EnumToCSS('type', $li_types, true); - - // @vspace for img ------------------------------------------------ - $r['img@vspace'] = new HTMLPurifier_AttrTransform_ImgSpace('vspace'); - - // @width for hr, td, th ------------------------------------------ - $r['td@width'] = - $r['th@width'] = - $r['hr@width'] = new HTMLPurifier_AttrTransform_Length('width'); - - return $r; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php deleted file mode 100644 index 9c0e03198..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModule/XMLCommonAttributes.php +++ /dev/null @@ -1,14 +0,0 @@ - array( - 'xml:lang' => 'LanguageCode', - ) - ); -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php b/extlib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php deleted file mode 100644 index f5c4a1d2c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/HTMLModuleManager.php +++ /dev/null @@ -1,403 +0,0 @@ -attrTypes = new HTMLPurifier_AttrTypes(); - $this->doctypes = new HTMLPurifier_DoctypeRegistry(); - - // setup basic modules - $common = array( - 'CommonAttributes', 'Text', 'Hypertext', 'List', - 'Presentation', 'Edit', 'Bdo', 'Tables', 'Image', - 'StyleAttribute', - // Unsafe: - 'Scripting', 'Object', 'Forms', - // Sorta legacy, but present in strict: - 'Name', - ); - $transitional = array('Legacy', 'Target'); - $xml = array('XMLCommonAttributes'); - $non_xml = array('NonXMLCommonAttributes'); - - // setup basic doctypes - $this->doctypes->register( - 'HTML 4.01 Transitional', false, - array_merge($common, $transitional, $non_xml), - array('Tidy_Transitional', 'Tidy_Proprietary'), - array(), - '-//W3C//DTD HTML 4.01 Transitional//EN', - 'http://www.w3.org/TR/html4/loose.dtd' - ); - - $this->doctypes->register( - 'HTML 4.01 Strict', false, - array_merge($common, $non_xml), - array('Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), - array(), - '-//W3C//DTD HTML 4.01//EN', - 'http://www.w3.org/TR/html4/strict.dtd' - ); - - $this->doctypes->register( - 'XHTML 1.0 Transitional', true, - array_merge($common, $transitional, $xml, $non_xml), - array('Tidy_Transitional', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Name'), - array(), - '-//W3C//DTD XHTML 1.0 Transitional//EN', - 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd' - ); - - $this->doctypes->register( - 'XHTML 1.0 Strict', true, - array_merge($common, $xml, $non_xml), - array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Strict', 'Tidy_Proprietary', 'Tidy_Name'), - array(), - '-//W3C//DTD XHTML 1.0 Strict//EN', - 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd' - ); - - $this->doctypes->register( - 'XHTML 1.1', true, - array_merge($common, $xml, array('Ruby')), - array('Tidy_Strict', 'Tidy_XHTML', 'Tidy_Proprietary', 'Tidy_Strict', 'Tidy_Name'), // Tidy_XHTML1_1 - array(), - '-//W3C//DTD XHTML 1.1//EN', - 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd' - ); - - } - - /** - * Registers a module to the recognized module list, useful for - * overloading pre-existing modules. - * @param $module Mixed: string module name, with or without - * HTMLPurifier_HTMLModule prefix, or instance of - * subclass of HTMLPurifier_HTMLModule. - * @param $overload Boolean whether or not to overload previous modules. - * If this is not set, and you do overload a module, - * HTML Purifier will complain with a warning. - * @note This function will not call autoload, you must instantiate - * (and thus invoke) autoload outside the method. - * @note If a string is passed as a module name, different variants - * will be tested in this order: - * - Check for HTMLPurifier_HTMLModule_$name - * - Check all prefixes with $name in order they were added - * - Check for literal object name - * - Throw fatal error - * If your object name collides with an internal class, specify - * your module manually. All modules must have been included - * externally: registerModule will not perform inclusions for you! - */ - public function registerModule($module, $overload = false) { - if (is_string($module)) { - // attempt to load the module - $original_module = $module; - $ok = false; - foreach ($this->prefixes as $prefix) { - $module = $prefix . $original_module; - if (class_exists($module)) { - $ok = true; - break; - } - } - if (!$ok) { - $module = $original_module; - if (!class_exists($module)) { - trigger_error($original_module . ' module does not exist', - E_USER_ERROR); - return; - } - } - $module = new $module(); - } - if (empty($module->name)) { - trigger_error('Module instance of ' . get_class($module) . ' must have name'); - return; - } - if (!$overload && isset($this->registeredModules[$module->name])) { - trigger_error('Overloading ' . $module->name . ' without explicit overload parameter', E_USER_WARNING); - } - $this->registeredModules[$module->name] = $module; - } - - /** - * Adds a module to the current doctype by first registering it, - * and then tacking it on to the active doctype - */ - public function addModule($module) { - $this->registerModule($module); - if (is_object($module)) $module = $module->name; - $this->userModules[] = $module; - } - - /** - * Adds a class prefix that registerModule() will use to resolve a - * string name to a concrete class - */ - public function addPrefix($prefix) { - $this->prefixes[] = $prefix; - } - - /** - * Performs processing on modules, after being called you may - * use getElement() and getElements() - * @param $config Instance of HTMLPurifier_Config - */ - public function setup($config) { - - $this->trusted = $config->get('HTML.Trusted'); - - // generate - $this->doctype = $this->doctypes->make($config); - $modules = $this->doctype->modules; - - // take out the default modules that aren't allowed - $lookup = $config->get('HTML.AllowedModules'); - $special_cases = $config->get('HTML.CoreModules'); - - if (is_array($lookup)) { - foreach ($modules as $k => $m) { - if (isset($special_cases[$m])) continue; - if (!isset($lookup[$m])) unset($modules[$k]); - } - } - - // add proprietary module (this gets special treatment because - // it is completely removed from doctypes, etc.) - if ($config->get('HTML.Proprietary')) { - $modules[] = 'Proprietary'; - } - - // add SafeObject/Safeembed modules - if ($config->get('HTML.SafeObject')) { - $modules[] = 'SafeObject'; - } - if ($config->get('HTML.SafeEmbed')) { - $modules[] = 'SafeEmbed'; - } - - // merge in custom modules - $modules = array_merge($modules, $this->userModules); - - foreach ($modules as $module) { - $this->processModule($module); - $this->modules[$module]->setup($config); - } - - foreach ($this->doctype->tidyModules as $module) { - $this->processModule($module); - $this->modules[$module]->setup($config); - } - - // prepare any injectors - foreach ($this->modules as $module) { - $n = array(); - foreach ($module->info_injector as $i => $injector) { - if (!is_object($injector)) { - $class = "HTMLPurifier_Injector_$injector"; - $injector = new $class; - } - $n[$injector->name] = $injector; - } - $module->info_injector = $n; - } - - // setup lookup table based on all valid modules - foreach ($this->modules as $module) { - foreach ($module->info as $name => $def) { - if (!isset($this->elementLookup[$name])) { - $this->elementLookup[$name] = array(); - } - $this->elementLookup[$name][] = $module->name; - } - } - - // note the different choice - $this->contentSets = new HTMLPurifier_ContentSets( - // content set assembly deals with all possible modules, - // not just ones deemed to be "safe" - $this->modules - ); - $this->attrCollections = new HTMLPurifier_AttrCollections( - $this->attrTypes, - // there is no way to directly disable a global attribute, - // but using AllowedAttributes or simply not including - // the module in your custom doctype should be sufficient - $this->modules - ); - } - - /** - * Takes a module and adds it to the active module collection, - * registering it if necessary. - */ - public function processModule($module) { - if (!isset($this->registeredModules[$module]) || is_object($module)) { - $this->registerModule($module); - } - $this->modules[$module] = $this->registeredModules[$module]; - } - - /** - * Retrieves merged element definitions. - * @return Array of HTMLPurifier_ElementDef - */ - public function getElements() { - - $elements = array(); - foreach ($this->modules as $module) { - if (!$this->trusted && !$module->safe) continue; - foreach ($module->info as $name => $v) { - if (isset($elements[$name])) continue; - $elements[$name] = $this->getElement($name); - } - } - - // remove dud elements, this happens when an element that - // appeared to be safe actually wasn't - foreach ($elements as $n => $v) { - if ($v === false) unset($elements[$n]); - } - - return $elements; - - } - - /** - * Retrieves a single merged element definition - * @param $name Name of element - * @param $trusted Boolean trusted overriding parameter: set to true - * if you want the full version of an element - * @return Merged HTMLPurifier_ElementDef - * @note You may notice that modules are getting iterated over twice (once - * in getElements() and once here). This - * is because - */ - public function getElement($name, $trusted = null) { - - if (!isset($this->elementLookup[$name])) { - return false; - } - - // setup global state variables - $def = false; - if ($trusted === null) $trusted = $this->trusted; - - // iterate through each module that has registered itself to this - // element - foreach($this->elementLookup[$name] as $module_name) { - - $module = $this->modules[$module_name]; - - // refuse to create/merge from a module that is deemed unsafe-- - // pretend the module doesn't exist--when trusted mode is not on. - if (!$trusted && !$module->safe) { - continue; - } - - // clone is used because, ideally speaking, the original - // definition should not be modified. Usually, this will - // make no difference, but for consistency's sake - $new_def = clone $module->info[$name]; - - if (!$def && $new_def->standalone) { - $def = $new_def; - } elseif ($def) { - // This will occur even if $new_def is standalone. In practice, - // this will usually result in a full replacement. - $def->mergeIn($new_def); - } else { - // :TODO: - // non-standalone definitions that don't have a standalone - // to merge into could be deferred to the end - continue; - } - - // attribute value expansions - $this->attrCollections->performInclusions($def->attr); - $this->attrCollections->expandIdentifiers($def->attr, $this->attrTypes); - - // descendants_are_inline, for ChildDef_Chameleon - if (is_string($def->content_model) && - strpos($def->content_model, 'Inline') !== false) { - if ($name != 'del' && $name != 'ins') { - // this is for you, ins/del - $def->descendants_are_inline = true; - } - } - - $this->contentSets->generateChildDef($def, $module); - } - - // This can occur if there is a blank definition, but no base to - // mix it in with - if (!$def) return false; - - // add information on required attributes - foreach ($def->attr as $attr_name => $attr_def) { - if ($attr_def->required) { - $def->required_attr[] = $attr_name; - } - } - - return $def; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/IDAccumulator.php b/extlib/HTMLPurifier/HTMLPurifier/IDAccumulator.php deleted file mode 100644 index 73215295a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/IDAccumulator.php +++ /dev/null @@ -1,53 +0,0 @@ -load($config->get('Attr.IDBlacklist')); - return $id_accumulator; - } - - /** - * Add an ID to the lookup table. - * @param $id ID to be added. - * @return Bool status, true if success, false if there's a dupe - */ - public function add($id) { - if (isset($this->ids[$id])) return false; - return $this->ids[$id] = true; - } - - /** - * Load a list of IDs into the lookup table - * @param $array_of_ids Array of IDs to load - * @note This function doesn't care about duplicates - */ - public function load($array_of_ids) { - foreach ($array_of_ids as $id) { - $this->ids[$id] = true; - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Injector.php b/extlib/HTMLPurifier/HTMLPurifier/Injector.php deleted file mode 100644 index 5922f8130..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Injector.php +++ /dev/null @@ -1,239 +0,0 @@ -processToken() - * documentation. - * - * @todo Allow injectors to request a re-run on their output. This - * would help if an operation is recursive. - */ -abstract class HTMLPurifier_Injector -{ - - /** - * Advisory name of injector, this is for friendly error messages - */ - public $name; - - /** - * Instance of HTMLPurifier_HTMLDefinition - */ - protected $htmlDefinition; - - /** - * Reference to CurrentNesting variable in Context. This is an array - * list of tokens that we are currently "inside" - */ - protected $currentNesting; - - /** - * Reference to InputTokens variable in Context. This is an array - * list of the input tokens that are being processed. - */ - protected $inputTokens; - - /** - * Reference to InputIndex variable in Context. This is an integer - * array index for $this->inputTokens that indicates what token - * is currently being processed. - */ - protected $inputIndex; - - /** - * Array of elements and attributes this injector creates and therefore - * need to be allowed by the definition. Takes form of - * array('element' => array('attr', 'attr2'), 'element2') - */ - public $needed = array(); - - /** - * Index of inputTokens to rewind to. - */ - protected $rewind = false; - - /** - * Rewind to a spot to re-perform processing. This is useful if you - * deleted a node, and now need to see if this change affected any - * earlier nodes. Rewinding does not affect other injectors, and can - * result in infinite loops if not used carefully. - * @warning HTML Purifier will prevent you from fast-forwarding with this - * function. - */ - public function rewind($index) { - $this->rewind = $index; - } - - /** - * Retrieves rewind, and then unsets it. - */ - public function getRewind() { - $r = $this->rewind; - $this->rewind = false; - return $r; - } - - /** - * Prepares the injector by giving it the config and context objects: - * this allows references to important variables to be made within - * the injector. This function also checks if the HTML environment - * will work with the Injector (see checkNeeded()). - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context - * @return Boolean false if success, string of missing needed element/attribute if failure - */ - public function prepare($config, $context) { - $this->htmlDefinition = $config->getHTMLDefinition(); - // Even though this might fail, some unit tests ignore this and - // still test checkNeeded, so be careful. Maybe get rid of that - // dependency. - $result = $this->checkNeeded($config); - if ($result !== false) return $result; - $this->currentNesting =& $context->get('CurrentNesting'); - $this->inputTokens =& $context->get('InputTokens'); - $this->inputIndex =& $context->get('InputIndex'); - return false; - } - - /** - * This function checks if the HTML environment - * will work with the Injector: if p tags are not allowed, the - * Auto-Paragraphing injector should not be enabled. - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context - * @return Boolean false if success, string of missing needed element/attribute if failure - */ - public function checkNeeded($config) { - $def = $config->getHTMLDefinition(); - foreach ($this->needed as $element => $attributes) { - if (is_int($element)) $element = $attributes; - if (!isset($def->info[$element])) return $element; - if (!is_array($attributes)) continue; - foreach ($attributes as $name) { - if (!isset($def->info[$element]->attr[$name])) return "$element.$name"; - } - } - return false; - } - - /** - * Tests if the context node allows a certain element - * @param $name Name of element to test for - * @return True if element is allowed, false if it is not - */ - public function allowsElement($name) { - if (!empty($this->currentNesting)) { - $parent_token = array_pop($this->currentNesting); - $this->currentNesting[] = $parent_token; - $parent = $this->htmlDefinition->info[$parent_token->name]; - } else { - $parent = $this->htmlDefinition->info_parent_def; - } - if (!isset($parent->child->elements[$name]) || isset($parent->excludes[$name])) { - return false; - } - // check for exclusion - for ($i = count($this->currentNesting) - 2; $i >= 0; $i--) { - $node = $this->currentNesting[$i]; - $def = $this->htmlDefinition->info[$node->name]; - if (isset($def->excludes[$name])) return false; - } - return true; - } - - /** - * Iterator function, which starts with the next token and continues until - * you reach the end of the input tokens. - * @warning Please prevent previous references from interfering with this - * functions by setting $i = null beforehand! - * @param &$i Current integer index variable for inputTokens - * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference - */ - protected function forward(&$i, &$current) { - if ($i === null) $i = $this->inputIndex + 1; - else $i++; - if (!isset($this->inputTokens[$i])) return false; - $current = $this->inputTokens[$i]; - return true; - } - - /** - * Similar to _forward, but accepts a third parameter $nesting (which - * should be initialized at 0) and stops when we hit the end tag - * for the node $this->inputIndex starts in. - */ - protected function forwardUntilEndToken(&$i, &$current, &$nesting) { - $result = $this->forward($i, $current); - if (!$result) return false; - if ($nesting === null) $nesting = 0; - if ($current instanceof HTMLPurifier_Token_Start) $nesting++; - elseif ($current instanceof HTMLPurifier_Token_End) { - if ($nesting <= 0) return false; - $nesting--; - } - return true; - } - - /** - * Iterator function, starts with the previous token and continues until - * you reach the beginning of input tokens. - * @warning Please prevent previous references from interfering with this - * functions by setting $i = null beforehand! - * @param &$i Current integer index variable for inputTokens - * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference - */ - protected function backward(&$i, &$current) { - if ($i === null) $i = $this->inputIndex - 1; - else $i--; - if ($i < 0) return false; - $current = $this->inputTokens[$i]; - return true; - } - - /** - * Initializes the iterator at the current position. Use in a do {} while; - * loop to force the _forward and _backward functions to start at the - * current location. - * @warning Please prevent previous references from interfering with this - * functions by setting $i = null beforehand! - * @param &$i Current integer index variable for inputTokens - * @param &$current Current token variable. Do NOT use $token, as that variable is also a reference - */ - protected function current(&$i, &$current) { - if ($i === null) $i = $this->inputIndex; - $current = $this->inputTokens[$i]; - } - - /** - * Handler that is called when a text token is processed - */ - public function handleText(&$token) {} - - /** - * Handler that is called when a start or empty token is processed - */ - public function handleElement(&$token) {} - - /** - * Handler that is called when an end token is processed - */ - public function handleEnd(&$token) { - $this->notifyEnd($token); - } - - /** - * Notifier that is called when an end token is processed - * @note This differs from handlers in that the token is read-only - * @deprecated - */ - public function notifyEnd($token) {} - - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php b/extlib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php deleted file mode 100644 index 8cc952549..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Injector/AutoParagraph.php +++ /dev/null @@ -1,340 +0,0 @@ -armor['MakeWellFormed_TagClosedError'] = true; - return $par; - } - - public function handleText(&$token) { - $text = $token->data; - // Does the current parent allow

tags? - if ($this->allowsElement('p')) { - if (empty($this->currentNesting) || strpos($text, "\n\n") !== false) { - // Note that we have differing behavior when dealing with text - // in the anonymous root node, or a node inside the document. - // If the text as a double-newline, the treatment is the same; - // if it doesn't, see the next if-block if you're in the document. - - $i = $nesting = null; - if (!$this->forwardUntilEndToken($i, $current, $nesting) && $token->is_whitespace) { - // State 1.1: ... ^ (whitespace, then document end) - // ---- - // This is a degenerate case - } else { - // State 1.2: PAR1 - // ---- - - // State 1.3: PAR1\n\nPAR2 - // ------------ - - // State 1.4:

PAR1\n\nPAR2 (see State 2) - // ------------ - $token = array($this->_pStart()); - $this->_splitText($text, $token); - } - } else { - // State 2:
PAR1... (similar to 1.4) - // ---- - - // We're in an element that allows paragraph tags, but we're not - // sure if we're going to need them. - if ($this->_pLookAhead()) { - // State 2.1:
PAR1PAR1\n\nPAR2 - // ---- - // Note: This will always be the first child, since any - // previous inline element would have triggered this very - // same routine, and found the double newline. One possible - // exception would be a comment. - $token = array($this->_pStart(), $token); - } else { - // State 2.2.1:
PAR1
- // ---- - - // State 2.2.2:
PAR1PAR1
- // ---- - } - } - // Is the current parent a

tag? - } elseif ( - !empty($this->currentNesting) && - $this->currentNesting[count($this->currentNesting)-1]->name == 'p' - ) { - // State 3.1: ...

PAR1 - // ---- - - // State 3.2: ...

PAR1\n\nPAR2 - // ------------ - $token = array(); - $this->_splitText($text, $token); - // Abort! - } else { - // State 4.1: ...PAR1 - // ---- - - // State 4.2: ...PAR1\n\nPAR2 - // ------------ - } - } - - public function handleElement(&$token) { - // We don't have to check if we're already in a

tag for block - // tokens, because the tag would have been autoclosed by MakeWellFormed. - if ($this->allowsElement('p')) { - if (!empty($this->currentNesting)) { - if ($this->_isInline($token)) { - // State 1:

... - // --- - - // Check if this token is adjacent to the parent token - // (seek backwards until token isn't whitespace) - $i = null; - $this->backward($i, $prev); - - if (!$prev instanceof HTMLPurifier_Token_Start) { - // Token wasn't adjacent - - if ( - $prev instanceof HTMLPurifier_Token_Text && - substr($prev->data, -2) === "\n\n" - ) { - // State 1.1.4:

PAR1

\n\n - // --- - - // Quite frankly, this should be handled by splitText - $token = array($this->_pStart(), $token); - } else { - // State 1.1.1:

PAR1

- // --- - - // State 1.1.2:

- // --- - - // State 1.1.3:
PAR - // --- - } - - } else { - // State 1.2.1:
- // --- - - // Lookahead to see if

is needed. - if ($this->_pLookAhead()) { - // State 1.3.1:

PAR1\n\nPAR2 - // --- - $token = array($this->_pStart(), $token); - } else { - // State 1.3.2:
PAR1
- // --- - - // State 1.3.3:
PAR1
\n\n
- // --- - } - } - } else { - // State 2.3: ...
- // ----- - } - } else { - if ($this->_isInline($token)) { - // State 3.1: - // --- - // This is where the {p} tag is inserted, not reflected in - // inputTokens yet, however. - $token = array($this->_pStart(), $token); - } else { - // State 3.2:
- // ----- - } - - $i = null; - if ($this->backward($i, $prev)) { - if ( - !$prev instanceof HTMLPurifier_Token_Text - ) { - // State 3.1.1: ...

{p} - // --- - - // State 3.2.1: ...

- // ----- - - if (!is_array($token)) $token = array($token); - array_unshift($token, new HTMLPurifier_Token_Text("\n\n")); - } else { - // State 3.1.2: ...

\n\n{p} - // --- - - // State 3.2.2: ...

\n\n
- // ----- - - // Note: PAR cannot occur because PAR would have been - // wrapped in

tags. - } - } - } - } else { - // State 2.2:

  • - // ---- - - // State 2.4:

    - // --- - } - } - - /** - * Splits up a text in paragraph tokens and appends them - * to the result stream that will replace the original - * @param $data String text data that will be processed - * into paragraphs - * @param $result Reference to array of tokens that the - * tags will be appended onto - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context - */ - private function _splitText($data, &$result) { - $raw_paragraphs = explode("\n\n", $data); - $paragraphs = array(); // without empty paragraphs - $needs_start = false; - $needs_end = false; - - $c = count($raw_paragraphs); - if ($c == 1) { - // There were no double-newlines, abort quickly. In theory this - // should never happen. - $result[] = new HTMLPurifier_Token_Text($data); - return; - } - for ($i = 0; $i < $c; $i++) { - $par = $raw_paragraphs[$i]; - if (trim($par) !== '') { - $paragraphs[] = $par; - } else { - if ($i == 0) { - // Double newline at the front - if (empty($result)) { - // The empty result indicates that the AutoParagraph - // injector did not add any start paragraph tokens. - // This means that we have been in a paragraph for - // a while, and the newline means we should start a new one. - $result[] = new HTMLPurifier_Token_End('p'); - $result[] = new HTMLPurifier_Token_Text("\n\n"); - // However, the start token should only be added if - // there is more processing to be done (i.e. there are - // real paragraphs in here). If there are none, the - // next start paragraph tag will be handled by the - // next call to the injector - $needs_start = true; - } else { - // We just started a new paragraph! - // Reinstate a double-newline for presentation's sake, since - // it was in the source code. - array_unshift($result, new HTMLPurifier_Token_Text("\n\n")); - } - } elseif ($i + 1 == $c) { - // Double newline at the end - // There should be a trailing

    when we're finally done. - $needs_end = true; - } - } - } - - // Check if this was just a giant blob of whitespace. Move this earlier, - // perhaps? - if (empty($paragraphs)) { - return; - } - - // Add the start tag indicated by \n\n at the beginning of $data - if ($needs_start) { - $result[] = $this->_pStart(); - } - - // Append the paragraphs onto the result - foreach ($paragraphs as $par) { - $result[] = new HTMLPurifier_Token_Text($par); - $result[] = new HTMLPurifier_Token_End('p'); - $result[] = new HTMLPurifier_Token_Text("\n\n"); - $result[] = $this->_pStart(); - } - - // Remove trailing start token; Injector will handle this later if - // it was indeed needed. This prevents from needing to do a lookahead, - // at the cost of a lookbehind later. - array_pop($result); - - // If there is no need for an end tag, remove all of it and let - // MakeWellFormed close it later. - if (!$needs_end) { - array_pop($result); // removes \n\n - array_pop($result); // removes

    - } - - } - - /** - * Returns true if passed token is inline (and, ergo, allowed in - * paragraph tags) - */ - private function _isInline($token) { - return isset($this->htmlDefinition->info['p']->child->elements[$token->name]); - } - - /** - * Looks ahead in the token list and determines whether or not we need - * to insert a

    tag. - */ - private function _pLookAhead() { - $this->current($i, $current); - if ($current instanceof HTMLPurifier_Token_Start) $nesting = 1; - else $nesting = 0; - $ok = false; - while ($this->forwardUntilEndToken($i, $current, $nesting)) { - $result = $this->_checkNeedsP($current); - if ($result !== null) { - $ok = $result; - break; - } - } - return $ok; - } - - /** - * Determines if a particular token requires an earlier inline token - * to get a paragraph. This should be used with _forwardUntilEndToken - */ - private function _checkNeedsP($current) { - if ($current instanceof HTMLPurifier_Token_Start){ - if (!$this->_isInline($current)) { - //

    PAR1
    - // ---- - // Terminate early, since we hit a block element - return false; - } - } elseif ($current instanceof HTMLPurifier_Token_Text) { - if (strpos($current->data, "\n\n") !== false) { - //
    PAR1PAR1\n\nPAR2 - // ---- - return true; - } else { - //
    PAR1PAR1... - // ---- - } - } - return null; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php b/extlib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php deleted file mode 100644 index 9dce9bd08..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Injector/DisplayLinkURI.php +++ /dev/null @@ -1,26 +0,0 @@ -start->attr['href'])){ - $url = $token->start->attr['href']; - unset($token->start->attr['href']); - $token = array($token, new HTMLPurifier_Token_Text(" ($url)")); - } else { - // nothing to display - } - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php b/extlib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php deleted file mode 100644 index 296dac282..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Injector/Linkify.php +++ /dev/null @@ -1,46 +0,0 @@ - array('href')); - - public function handleText(&$token) { - if (!$this->allowsElement('a')) return; - - if (strpos($token->data, '://') === false) { - // our really quick heuristic failed, abort - // this may not work so well if we want to match things like - // "google.com", but then again, most people don't - return; - } - - // there is/are URL(s). Let's split the string: - // Note: this regex is extremely permissive - $bits = preg_split('#((?:https?|ftp)://[^\s\'"<>()]+)#S', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); - - $token = array(); - - // $i = index - // $c = count - // $l = is link - for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) { - if (!$l) { - if ($bits[$i] === '') continue; - $token[] = new HTMLPurifier_Token_Text($bits[$i]); - } else { - $token[] = new HTMLPurifier_Token_Start('a', array('href' => $bits[$i])); - $token[] = new HTMLPurifier_Token_Text($bits[$i]); - $token[] = new HTMLPurifier_Token_End('a'); - } - } - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php b/extlib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php deleted file mode 100644 index ad2455a91..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Injector/PurifierLinkify.php +++ /dev/null @@ -1,45 +0,0 @@ - array('href')); - - public function prepare($config, $context) { - $this->docURL = $config->get('AutoFormat.PurifierLinkify.DocURL'); - return parent::prepare($config, $context); - } - - public function handleText(&$token) { - if (!$this->allowsElement('a')) return; - if (strpos($token->data, '%') === false) return; - - $bits = preg_split('#%([a-z0-9]+\.[a-z0-9]+)#Si', $token->data, -1, PREG_SPLIT_DELIM_CAPTURE); - $token = array(); - - // $i = index - // $c = count - // $l = is link - for ($i = 0, $c = count($bits), $l = false; $i < $c; $i++, $l = !$l) { - if (!$l) { - if ($bits[$i] === '') continue; - $token[] = new HTMLPurifier_Token_Text($bits[$i]); - } else { - $token[] = new HTMLPurifier_Token_Start('a', - array('href' => str_replace('%s', $bits[$i], $this->docURL))); - $token[] = new HTMLPurifier_Token_Text('%' . $bits[$i]); - $token[] = new HTMLPurifier_Token_End('a'); - } - } - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php b/extlib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php deleted file mode 100644 index 638bfca03..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Injector/RemoveEmpty.php +++ /dev/null @@ -1,51 +0,0 @@ -config = $config; - $this->context = $context; - $this->removeNbsp = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp'); - $this->removeNbspExceptions = $config->get('AutoFormat.RemoveEmpty.RemoveNbsp.Exceptions'); - $this->attrValidator = new HTMLPurifier_AttrValidator(); - } - - public function handleElement(&$token) { - if (!$token instanceof HTMLPurifier_Token_Start) return; - $next = false; - for ($i = $this->inputIndex + 1, $c = count($this->inputTokens); $i < $c; $i++) { - $next = $this->inputTokens[$i]; - if ($next instanceof HTMLPurifier_Token_Text) { - if ($next->is_whitespace) continue; - if ($this->removeNbsp && !isset($this->removeNbspExceptions[$token->name])) { - $plain = str_replace("\xC2\xA0", "", $next->data); - $isWsOrNbsp = $plain === '' || ctype_space($plain); - if ($isWsOrNbsp) continue; - } - } - break; - } - if (!$next || ($next instanceof HTMLPurifier_Token_End && $next->name == $token->name)) { - if ($token->name == 'colgroup') return; - $this->attrValidator->validateToken($token, $this->config, $this->context); - $token->armor['ValidateAttributes'] = true; - if (isset($token->attr['id']) || isset($token->attr['name'])) return; - $token = $i - $this->inputIndex + 1; - for ($b = $this->inputIndex - 1; $b > 0; $b--) { - $prev = $this->inputTokens[$b]; - if ($prev instanceof HTMLPurifier_Token_Text && $prev->is_whitespace) continue; - break; - } - // This is safe because we removed the token that triggered this. - $this->rewind($b - 1); - return; - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php b/extlib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php deleted file mode 100644 index 341582868..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Injector/SafeObject.php +++ /dev/null @@ -1,87 +0,0 @@ - 'never', - 'allowNetworking' => 'internal', - ); - protected $allowedParam = array( - 'wmode' => true, - 'movie' => true, - ); - - public function prepare($config, $context) { - parent::prepare($config, $context); - } - - public function handleElement(&$token) { - if ($token->name == 'object') { - $this->objectStack[] = $token; - $this->paramStack[] = array(); - $new = array($token); - foreach ($this->addParam as $name => $value) { - $new[] = new HTMLPurifier_Token_Empty('param', array('name' => $name, 'value' => $value)); - } - $token = $new; - } elseif ($token->name == 'param') { - $nest = count($this->currentNesting) - 1; - if ($nest >= 0 && $this->currentNesting[$nest]->name === 'object') { - $i = count($this->objectStack) - 1; - if (!isset($token->attr['name'])) { - $token = false; - return; - } - $n = $token->attr['name']; - // We need this fix because YouTube doesn't supply a data - // attribute, which we need if a type is specified. This is - // *very* Flash specific. - if (!isset($this->objectStack[$i]->attr['data']) && $token->attr['name'] == 'movie') { - $this->objectStack[$i]->attr['data'] = $token->attr['value']; - } - // Check if the parameter is the correct value but has not - // already been added - if ( - !isset($this->paramStack[$i][$n]) && - isset($this->addParam[$n]) && - $token->attr['name'] === $this->addParam[$n] - ) { - // keep token, and add to param stack - $this->paramStack[$i][$n] = true; - } elseif (isset($this->allowedParam[$n])) { - // keep token, don't do anything to it - // (could possibly check for duplicates here) - } else { - $token = false; - } - } else { - // not directly inside an object, DENY! - $token = false; - } - } - } - - public function handleEnd(&$token) { - // This is the WRONG way of handling the object and param stacks; - // we should be inserting them directly on the relevant object tokens - // so that the global stack handling handles it. - if ($token->name == 'object') { - array_pop($this->objectStack); - array_pop($this->paramStack); - } - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Language.php b/extlib/HTMLPurifier/HTMLPurifier/Language.php deleted file mode 100644 index 3e2be03b5..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Language.php +++ /dev/null @@ -1,163 +0,0 @@ -config = $config; - $this->context = $context; - } - - /** - * Loads language object with necessary info from factory cache - * @note This is a lazy loader - */ - public function load() { - if ($this->_loaded) return; - $factory = HTMLPurifier_LanguageFactory::instance(); - $factory->loadLanguage($this->code); - foreach ($factory->keys as $key) { - $this->$key = $factory->cache[$this->code][$key]; - } - $this->_loaded = true; - } - - /** - * Retrieves a localised message. - * @param $key string identifier of message - * @return string localised message - */ - public function getMessage($key) { - if (!$this->_loaded) $this->load(); - if (!isset($this->messages[$key])) return "[$key]"; - return $this->messages[$key]; - } - - /** - * Retrieves a localised error name. - * @param $int integer error number, corresponding to PHP's error - * reporting - * @return string localised message - */ - public function getErrorName($int) { - if (!$this->_loaded) $this->load(); - if (!isset($this->errorNames[$int])) return "[Error: $int]"; - return $this->errorNames[$int]; - } - - /** - * Converts an array list into a string readable representation - */ - public function listify($array) { - $sep = $this->getMessage('Item separator'); - $sep_last = $this->getMessage('Item separator last'); - $ret = ''; - for ($i = 0, $c = count($array); $i < $c; $i++) { - if ($i == 0) { - } elseif ($i + 1 < $c) { - $ret .= $sep; - } else { - $ret .= $sep_last; - } - $ret .= $array[$i]; - } - return $ret; - } - - /** - * Formats a localised message with passed parameters - * @param $key string identifier of message - * @param $args Parameters to substitute in - * @return string localised message - * @todo Implement conditionals? Right now, some messages make - * reference to line numbers, but those aren't always available - */ - public function formatMessage($key, $args = array()) { - if (!$this->_loaded) $this->load(); - if (!isset($this->messages[$key])) return "[$key]"; - $raw = $this->messages[$key]; - $subst = array(); - $generator = false; - foreach ($args as $i => $value) { - if (is_object($value)) { - if ($value instanceof HTMLPurifier_Token) { - // factor this out some time - if (!$generator) $generator = $this->context->get('Generator'); - if (isset($value->name)) $subst['$'.$i.'.Name'] = $value->name; - if (isset($value->data)) $subst['$'.$i.'.Data'] = $value->data; - $subst['$'.$i.'.Compact'] = - $subst['$'.$i.'.Serialized'] = $generator->generateFromToken($value); - // a more complex algorithm for compact representation - // could be introduced for all types of tokens. This - // may need to be factored out into a dedicated class - if (!empty($value->attr)) { - $stripped_token = clone $value; - $stripped_token->attr = array(); - $subst['$'.$i.'.Compact'] = $generator->generateFromToken($stripped_token); - } - $subst['$'.$i.'.Line'] = $value->line ? $value->line : 'unknown'; - } - continue; - } elseif (is_array($value)) { - $keys = array_keys($value); - if (array_keys($keys) === $keys) { - // list - $subst['$'.$i] = $this->listify($value); - } else { - // associative array - // no $i implementation yet, sorry - $subst['$'.$i.'.Keys'] = $this->listify($keys); - $subst['$'.$i.'.Values'] = $this->listify(array_values($value)); - } - continue; - } - $subst['$' . $i] = $value; - } - return strtr($raw, $subst); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php b/extlib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php deleted file mode 100644 index d52fcb7ac..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Language/classes/en-x-test.php +++ /dev/null @@ -1,12 +0,0 @@ - 'HTML Purifier X' -); - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php b/extlib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php deleted file mode 100644 index 806c83fbf..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Language/messages/en-x-testmini.php +++ /dev/null @@ -1,12 +0,0 @@ - 'HTML Purifier XNone' -); - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Language/messages/en.php b/extlib/HTMLPurifier/HTMLPurifier/Language/messages/en.php deleted file mode 100644 index aab2e52eb..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Language/messages/en.php +++ /dev/null @@ -1,62 +0,0 @@ - 'HTML Purifier', - -// for unit testing purposes -'LanguageFactoryTest: Pizza' => 'Pizza', -'LanguageTest: List' => '$1', -'LanguageTest: Hash' => '$1.Keys; $1.Values', - -'Item separator' => ', ', -'Item separator last' => ' and ', // non-Harvard style - -'ErrorCollector: No errors' => 'No errors detected. However, because error reporting is still incomplete, there may have been errors that the error collector was not notified of; please inspect the output HTML carefully.', -'ErrorCollector: At line' => ' at line $line', -'ErrorCollector: Incidental errors' => 'Incidental errors', - -'Lexer: Unclosed comment' => 'Unclosed comment', -'Lexer: Unescaped lt' => 'Unescaped less-than sign (<) should be <', -'Lexer: Missing gt' => 'Missing greater-than sign (>), previous less-than sign (<) should be escaped', -'Lexer: Missing attribute key' => 'Attribute declaration has no key', -'Lexer: Missing end quote' => 'Attribute declaration has no end quote', - -'Strategy_RemoveForeignElements: Tag transform' => '<$1> element transformed into $CurrentToken.Serialized', -'Strategy_RemoveForeignElements: Missing required attribute' => '$CurrentToken.Compact element missing required attribute $1', -'Strategy_RemoveForeignElements: Foreign element to text' => 'Unrecognized $CurrentToken.Serialized tag converted to text', -'Strategy_RemoveForeignElements: Foreign element removed' => 'Unrecognized $CurrentToken.Serialized tag removed', -'Strategy_RemoveForeignElements: Comment removed' => 'Comment containing "$CurrentToken.Data" removed', -'Strategy_RemoveForeignElements: Foreign meta element removed' => 'Unrecognized $CurrentToken.Serialized meta tag and all descendants removed', -'Strategy_RemoveForeignElements: Token removed to end' => 'Tags and text starting from $1 element where removed to end', -'Strategy_RemoveForeignElements: Trailing hyphen in comment removed' => 'Trailing hyphen(s) in comment removed', -'Strategy_RemoveForeignElements: Hyphens in comment collapsed' => 'Double hyphens in comments are not allowed, and were collapsed into single hyphens', - -'Strategy_MakeWellFormed: Unnecessary end tag removed' => 'Unnecessary $CurrentToken.Serialized tag removed', -'Strategy_MakeWellFormed: Unnecessary end tag to text' => 'Unnecessary $CurrentToken.Serialized tag converted to text', -'Strategy_MakeWellFormed: Tag auto closed' => '$1.Compact started on line $1.Line auto-closed by $CurrentToken.Compact', -'Strategy_MakeWellFormed: Tag carryover' => '$1.Compact started on line $1.Line auto-continued into $CurrentToken.Compact', -'Strategy_MakeWellFormed: Stray end tag removed' => 'Stray $CurrentToken.Serialized tag removed', -'Strategy_MakeWellFormed: Stray end tag to text' => 'Stray $CurrentToken.Serialized tag converted to text', -'Strategy_MakeWellFormed: Tag closed by element end' => '$1.Compact tag started on line $1.Line closed by end of $CurrentToken.Serialized', -'Strategy_MakeWellFormed: Tag closed by document end' => '$1.Compact tag started on line $1.Line closed by end of document', - -'Strategy_FixNesting: Node removed' => '$CurrentToken.Compact node removed', -'Strategy_FixNesting: Node excluded' => '$CurrentToken.Compact node removed due to descendant exclusion by ancestor element', -'Strategy_FixNesting: Node reorganized' => 'Contents of $CurrentToken.Compact node reorganized to enforce its content model', -'Strategy_FixNesting: Node contents removed' => 'Contents of $CurrentToken.Compact node removed', - -'AttrValidator: Attributes transformed' => 'Attributes on $CurrentToken.Compact transformed from $1.Keys to $2.Keys', -'AttrValidator: Attribute removed' => '$CurrentAttr.Name attribute on $CurrentToken.Compact removed', - -); - -$errorNames = array( - E_ERROR => 'Error', - E_WARNING => 'Warning', - E_NOTICE => 'Notice' -); - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/LanguageFactory.php b/extlib/HTMLPurifier/HTMLPurifier/LanguageFactory.php deleted file mode 100644 index 134ef8c74..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/LanguageFactory.php +++ /dev/null @@ -1,198 +0,0 @@ -cache[$language_code][$key] = $value - * @value array map - */ - public $cache; - - /** - * Valid keys in the HTMLPurifier_Language object. Designates which - * variables to slurp out of a message file. - * @value array list - */ - public $keys = array('fallback', 'messages', 'errorNames'); - - /** - * Instance of HTMLPurifier_AttrDef_Lang to validate language codes - * @value object HTMLPurifier_AttrDef_Lang - */ - protected $validator; - - /** - * Cached copy of dirname(__FILE__), directory of current file without - * trailing slash - * @value string filename - */ - protected $dir; - - /** - * Keys whose contents are a hash map and can be merged - * @value array lookup - */ - protected $mergeable_keys_map = array('messages' => true, 'errorNames' => true); - - /** - * Keys whose contents are a list and can be merged - * @value array lookup - */ - protected $mergeable_keys_list = array(); - - /** - * Retrieve sole instance of the factory. - * @param $prototype Optional prototype to overload sole instance with, - * or bool true to reset to default factory. - */ - public static function instance($prototype = null) { - static $instance = null; - if ($prototype !== null) { - $instance = $prototype; - } elseif ($instance === null || $prototype == true) { - $instance = new HTMLPurifier_LanguageFactory(); - $instance->setup(); - } - return $instance; - } - - /** - * Sets up the singleton, much like a constructor - * @note Prevents people from getting this outside of the singleton - */ - public function setup() { - $this->validator = new HTMLPurifier_AttrDef_Lang(); - $this->dir = HTMLPURIFIER_PREFIX . '/HTMLPurifier'; - } - - /** - * Creates a language object, handles class fallbacks - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context - * @param $code Code to override configuration with. Private parameter. - */ - public function create($config, $context, $code = false) { - - // validate language code - if ($code === false) { - $code = $this->validator->validate( - $config->get('Core.Language'), $config, $context - ); - } else { - $code = $this->validator->validate($code, $config, $context); - } - if ($code === false) $code = 'en'; // malformed code becomes English - - $pcode = str_replace('-', '_', $code); // make valid PHP classname - static $depth = 0; // recursion protection - - if ($code == 'en') { - $lang = new HTMLPurifier_Language($config, $context); - } else { - $class = 'HTMLPurifier_Language_' . $pcode; - $file = $this->dir . '/Language/classes/' . $code . '.php'; - if (file_exists($file) || class_exists($class, false)) { - $lang = new $class($config, $context); - } else { - // Go fallback - $raw_fallback = $this->getFallbackFor($code); - $fallback = $raw_fallback ? $raw_fallback : 'en'; - $depth++; - $lang = $this->create($config, $context, $fallback); - if (!$raw_fallback) { - $lang->error = true; - } - $depth--; - } - } - - $lang->code = $code; - - return $lang; - - } - - /** - * Returns the fallback language for language - * @note Loads the original language into cache - * @param $code string language code - */ - public function getFallbackFor($code) { - $this->loadLanguage($code); - return $this->cache[$code]['fallback']; - } - - /** - * Loads language into the cache, handles message file and fallbacks - * @param $code string language code - */ - public function loadLanguage($code) { - static $languages_seen = array(); // recursion guard - - // abort if we've already loaded it - if (isset($this->cache[$code])) return; - - // generate filename - $filename = $this->dir . '/Language/messages/' . $code . '.php'; - - // default fallback : may be overwritten by the ensuing include - $fallback = ($code != 'en') ? 'en' : false; - - // load primary localisation - if (!file_exists($filename)) { - // skip the include: will rely solely on fallback - $filename = $this->dir . '/Language/messages/en.php'; - $cache = array(); - } else { - include $filename; - $cache = compact($this->keys); - } - - // load fallback localisation - if (!empty($fallback)) { - - // infinite recursion guard - if (isset($languages_seen[$code])) { - trigger_error('Circular fallback reference in language ' . - $code, E_USER_ERROR); - $fallback = 'en'; - } - $language_seen[$code] = true; - - // load the fallback recursively - $this->loadLanguage($fallback); - $fallback_cache = $this->cache[$fallback]; - - // merge fallback with current language - foreach ( $this->keys as $key ) { - if (isset($cache[$key]) && isset($fallback_cache[$key])) { - if (isset($this->mergeable_keys_map[$key])) { - $cache[$key] = $cache[$key] + $fallback_cache[$key]; - } elseif (isset($this->mergeable_keys_list[$key])) { - $cache[$key] = array_merge( $fallback_cache[$key], $cache[$key] ); - } - } else { - $cache[$key] = $fallback_cache[$key]; - } - } - - } - - // save to cache for later retrieval - $this->cache[$code] = $cache; - - return; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Length.php b/extlib/HTMLPurifier/HTMLPurifier/Length.php deleted file mode 100644 index 8d2a46b7d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Length.php +++ /dev/null @@ -1,115 +0,0 @@ - true, 'ex' => true, 'px' => true, 'in' => true, - 'cm' => true, 'mm' => true, 'pt' => true, 'pc' => true - ); - - /** - * @param number $n Magnitude - * @param string $u Unit - */ - public function __construct($n = '0', $u = false) { - $this->n = (string) $n; - $this->unit = $u !== false ? (string) $u : false; - } - - /** - * @param string $s Unit string, like '2em' or '3.4in' - * @warning Does not perform validation. - */ - static public function make($s) { - if ($s instanceof HTMLPurifier_Length) return $s; - $n_length = strspn($s, '1234567890.+-'); - $n = substr($s, 0, $n_length); - $unit = substr($s, $n_length); - if ($unit === '') $unit = false; - return new HTMLPurifier_Length($n, $unit); - } - - /** - * Validates the number and unit. - */ - protected function validate() { - // Special case: - if ($this->n === '+0' || $this->n === '-0') $this->n = '0'; - if ($this->n === '0' && $this->unit === false) return true; - if (!ctype_lower($this->unit)) $this->unit = strtolower($this->unit); - if (!isset(HTMLPurifier_Length::$allowedUnits[$this->unit])) return false; - // Hack: - $def = new HTMLPurifier_AttrDef_CSS_Number(); - $result = $def->validate($this->n, false, false); - if ($result === false) return false; - $this->n = $result; - return true; - } - - /** - * Returns string representation of number. - */ - public function toString() { - if (!$this->isValid()) return false; - return $this->n . $this->unit; - } - - /** - * Retrieves string numeric magnitude. - */ - public function getN() {return $this->n;} - - /** - * Retrieves string unit. - */ - public function getUnit() {return $this->unit;} - - /** - * Returns true if this length unit is valid. - */ - public function isValid() { - if ($this->isValid === null) $this->isValid = $this->validate(); - return $this->isValid; - } - - /** - * Compares two lengths, and returns 1 if greater, -1 if less and 0 if equal. - * @warning If both values are too large or small, this calculation will - * not work properly - */ - public function compareTo($l) { - if ($l === false) return false; - if ($l->unit !== $this->unit) { - $converter = new HTMLPurifier_UnitConverter(); - $l = $converter->convert($l, $this->unit); - if ($l === false) return false; - } - return $this->n - $l->n; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Lexer.php b/extlib/HTMLPurifier/HTMLPurifier/Lexer.php deleted file mode 100644 index 8cce008d3..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Lexer.php +++ /dev/null @@ -1,298 +0,0 @@ -get('Core.LexerImpl'); - } - - $needs_tracking = - $config->get('Core.MaintainLineNumbers') || - $config->get('Core.CollectErrors'); - - $inst = null; - if (is_object($lexer)) { - $inst = $lexer; - } else { - - if (is_null($lexer)) { do { - // auto-detection algorithm - - if ($needs_tracking) { - $lexer = 'DirectLex'; - break; - } - - if ( - class_exists('DOMDocument') && - method_exists('DOMDocument', 'loadHTML') && - !extension_loaded('domxml') - ) { - // check for DOM support, because while it's part of the - // core, it can be disabled compile time. Also, the PECL - // domxml extension overrides the default DOM, and is evil - // and nasty and we shan't bother to support it - $lexer = 'DOMLex'; - } else { - $lexer = 'DirectLex'; - } - - } while(0); } // do..while so we can break - - // instantiate recognized string names - switch ($lexer) { - case 'DOMLex': - $inst = new HTMLPurifier_Lexer_DOMLex(); - break; - case 'DirectLex': - $inst = new HTMLPurifier_Lexer_DirectLex(); - break; - case 'PH5P': - $inst = new HTMLPurifier_Lexer_PH5P(); - break; - default: - throw new HTMLPurifier_Exception("Cannot instantiate unrecognized Lexer type " . htmlspecialchars($lexer)); - } - } - - if (!$inst) throw new HTMLPurifier_Exception('No lexer was instantiated'); - - // once PHP DOM implements native line numbers, or we - // hack out something using XSLT, remove this stipulation - if ($needs_tracking && !$inst->tracksLineNumbers) { - throw new HTMLPurifier_Exception('Cannot use lexer that does not support line numbers with Core.MaintainLineNumbers or Core.CollectErrors (use DirectLex instead)'); - } - - return $inst; - - } - - // -- CONVENIENCE MEMBERS --------------------------------------------- - - public function __construct() { - $this->_entity_parser = new HTMLPurifier_EntityParser(); - } - - /** - * Most common entity to raw value conversion table for special entities. - */ - protected $_special_entity2str = - array( - '"' => '"', - '&' => '&', - '<' => '<', - '>' => '>', - ''' => "'", - ''' => "'", - ''' => "'" - ); - - /** - * Parses special entities into the proper characters. - * - * This string will translate escaped versions of the special characters - * 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 character data to be parsed. - * @returns Parsed character data. - */ - public function parseData($string) { - - // following functions require at least one character - if ($string === '') return ''; - - // subtracts amps that cannot possibly be escaped - $num_amp = substr_count($string, '&') - substr_count($string, '& ') - - ($string[strlen($string)-1] === '&' ? 1 : 0); - - if (!$num_amp) return $string; // abort if no entities - $num_esc_amp = substr_count($string, '&'); - $string = strtr($string, $this->_special_entity2str); - - // code duplication for sake of optimization, see above - $num_amp_2 = substr_count($string, '&') - substr_count($string, '& ') - - ($string[strlen($string)-1] === '&' ? 1 : 0); - - if ($num_amp_2 <= $num_esc_amp) return $string; - - // hmm... now we have some uncommon entities. Use the callback. - $string = $this->_entity_parser->substituteSpecialEntities($string); - return $string; - } - - /** - * Lexes an HTML string into tokens. - * - * @param $string String HTML. - * @return HTMLPurifier_Token array representation of HTML. - */ - public function tokenizeHTML($string, $config, $context) { - trigger_error('Call to abstract class', E_USER_ERROR); - } - - /** - * Translates CDATA sections into regular sections (through escaping). - * - * @param $string HTML string to process. - * @returns HTML with CDATA sections escaped. - */ - protected static function escapeCDATA($string) { - return preg_replace_callback( - '//s', - array('HTMLPurifier_Lexer', 'CDATACallback'), - $string - ); - } - - /** - * Special CDATA case that is especially convoluted for )#si', - array($this, 'scriptCallback'), $html); - } - - $html = $this->normalize($html, $config, $context); - - $cursor = 0; // our location in the text - $inside_tag = false; // whether or not we're parsing the inside of a tag - $array = array(); // result array - - // This is also treated to mean maintain *column* numbers too - $maintain_line_numbers = $config->get('Core.MaintainLineNumbers'); - - if ($maintain_line_numbers === null) { - // automatically determine line numbering by checking - // if error collection is on - $maintain_line_numbers = $config->get('Core.CollectErrors'); - } - - if ($maintain_line_numbers) { - $current_line = 1; - $current_col = 0; - $length = strlen($html); - } else { - $current_line = false; - $current_col = false; - $length = false; - } - $context->register('CurrentLine', $current_line); - $context->register('CurrentCol', $current_col); - $nl = "\n"; - // how often to manually recalculate. This will ALWAYS be right, - // but it's pretty wasteful. Set to 0 to turn off - $synchronize_interval = $config->get('Core.DirectLexLineNumberSyncInterval'); - - $e = false; - if ($config->get('Core.CollectErrors')) { - $e =& $context->get('ErrorCollector'); - } - - // for testing synchronization - $loops = 0; - - while(++$loops) { - - // $cursor is either at the start of a token, or inside of - // a tag (i.e. there was a < immediately before it), as indicated - // by $inside_tag - - if ($maintain_line_numbers) { - - // $rcursor, however, is always at the start of a token. - $rcursor = $cursor - (int) $inside_tag; - - // Column number is cheap, so we calculate it every round. - // We're interested at the *end* of the newline string, so - // we need to add strlen($nl) == 1 to $nl_pos before subtracting it - // from our "rcursor" position. - $nl_pos = strrpos($html, $nl, $rcursor - $length); - $current_col = $rcursor - (is_bool($nl_pos) ? 0 : $nl_pos + 1); - - // recalculate lines - if ( - $synchronize_interval && // synchronization is on - $cursor > 0 && // cursor is further than zero - $loops % $synchronize_interval === 0 // time to synchronize! - ) { - $current_line = 1 + $this->substrCount($html, $nl, 0, $cursor); - } - - } - - $position_next_lt = strpos($html, '<', $cursor); - $position_next_gt = strpos($html, '>', $cursor); - - // triggers on "asdf" but not "asdf " - // special case to set up context - if ($position_next_lt === $cursor) { - $inside_tag = true; - $cursor++; - } - - if (!$inside_tag && $position_next_lt !== false) { - // We are not inside tag and there still is another tag to parse - $token = new - HTMLPurifier_Token_Text( - $this->parseData( - substr( - $html, $cursor, $position_next_lt - $cursor - ) - ) - ); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_lt - $cursor); - } - $array[] = $token; - $cursor = $position_next_lt + 1; - $inside_tag = true; - continue; - } elseif (!$inside_tag) { - // We are not inside tag but there are no more tags - // If we're already at the end, break - if ($cursor === strlen($html)) break; - // Create Text of rest of string - $token = new - HTMLPurifier_Token_Text( - $this->parseData( - substr( - $html, $cursor - ) - ) - ); - if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); - $array[] = $token; - break; - } elseif ($inside_tag && $position_next_gt !== false) { - // We are in tag and it is well formed - // Grab the internals of the tag - $strlen_segment = $position_next_gt - $cursor; - - if ($strlen_segment < 1) { - // there's nothing to process! - $token = new HTMLPurifier_Token_Text('<'); - $cursor++; - continue; - } - - $segment = substr($html, $cursor, $strlen_segment); - - if ($segment === false) { - // somehow, we attempted to access beyond the end of - // the string, defense-in-depth, reported by Nate Abele - break; - } - - // Check if it's a comment - if ( - substr($segment, 0, 3) === '!--' - ) { - // re-determine segment length, looking for --> - $position_comment_end = strpos($html, '-->', $cursor); - if ($position_comment_end === false) { - // uh oh, we have a comment that extends to - // infinity. Can't be helped: set comment - // end position to end of string - if ($e) $e->send(E_WARNING, 'Lexer: Unclosed comment'); - $position_comment_end = strlen($html); - $end = true; - } else { - $end = false; - } - $strlen_segment = $position_comment_end - $cursor; - $segment = substr($html, $cursor, $strlen_segment); - $token = new - HTMLPurifier_Token_Comment( - substr( - $segment, 3, $strlen_segment - 3 - ) - ); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $strlen_segment); - } - $array[] = $token; - $cursor = $end ? $position_comment_end : $position_comment_end + 3; - $inside_tag = false; - continue; - } - - // Check if it's an end tag - $is_end_tag = (strpos($segment,'/') === 0); - if ($is_end_tag) { - $type = substr($segment, 1); - $token = new HTMLPurifier_Token_End($type); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $inside_tag = false; - $cursor = $position_next_gt + 1; - continue; - } - - // Check leading character is alnum, if not, we may - // have accidently grabbed an emoticon. Translate into - // text and go our merry way - if (!ctype_alpha($segment[0])) { - // XML: $segment[0] !== '_' && $segment[0] !== ':' - if ($e) $e->send(E_NOTICE, 'Lexer: Unescaped lt'); - $token = new HTMLPurifier_Token_Text('<'); - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $inside_tag = false; - continue; - } - - // Check if it is explicitly self closing, if so, remove - // trailing slash. Remember, we could have a tag like
    , so - // any later token processing scripts must convert improperly - // classified EmptyTags from StartTags. - $is_self_closing = (strrpos($segment,'/') === $strlen_segment-1); - if ($is_self_closing) { - $strlen_segment--; - $segment = substr($segment, 0, $strlen_segment); - } - - // Check if there are any attributes - $position_first_space = strcspn($segment, $this->_whitespace); - - if ($position_first_space >= $strlen_segment) { - if ($is_self_closing) { - $token = new HTMLPurifier_Token_Empty($segment); - } else { - $token = new HTMLPurifier_Token_Start($segment); - } - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $inside_tag = false; - $cursor = $position_next_gt + 1; - continue; - } - - // Grab out all the data - $type = substr($segment, 0, $position_first_space); - $attribute_string = - trim( - substr( - $segment, $position_first_space - ) - ); - if ($attribute_string) { - $attr = $this->parseAttributeString( - $attribute_string - , $config, $context - ); - } else { - $attr = array(); - } - - if ($is_self_closing) { - $token = new HTMLPurifier_Token_Empty($type, $attr); - } else { - $token = new HTMLPurifier_Token_Start($type, $attr); - } - if ($maintain_line_numbers) { - $token->rawPosition($current_line, $current_col); - $current_line += $this->substrCount($html, $nl, $cursor, $position_next_gt - $cursor); - } - $array[] = $token; - $cursor = $position_next_gt + 1; - $inside_tag = false; - continue; - } else { - // inside tag, but there's no ending > sign - if ($e) $e->send(E_WARNING, 'Lexer: Missing gt'); - $token = new - HTMLPurifier_Token_Text( - '<' . - $this->parseData( - substr($html, $cursor) - ) - ); - if ($maintain_line_numbers) $token->rawPosition($current_line, $current_col); - // no cursor scroll? Hmm... - $array[] = $token; - break; - } - break; - } - - $context->destroy('CurrentLine'); - $context->destroy('CurrentCol'); - return $array; - } - - /** - * PHP 5.0.x compatible substr_count that implements offset and length - */ - protected function substrCount($haystack, $needle, $offset, $length) { - static $oldVersion; - if ($oldVersion === null) { - $oldVersion = version_compare(PHP_VERSION, '5.1', '<'); - } - if ($oldVersion) { - $haystack = substr($haystack, $offset, $length); - return substr_count($haystack, $needle); - } else { - return substr_count($haystack, $needle, $offset, $length); - } - } - - /** - * Takes the inside of an HTML tag and makes an assoc array of attributes. - * - * @param $string Inside of tag excluding name. - * @returns Assoc array of attributes. - */ - public function parseAttributeString($string, $config, $context) { - $string = (string) $string; // quick typecast - - if ($string == '') return array(); // no attributes - - $e = false; - if ($config->get('Core.CollectErrors')) { - $e =& $context->get('ErrorCollector'); - } - - // let's see if we can abort as quickly as possible - // one equal sign, no spaces => one attribute - $num_equal = substr_count($string, '='); - $has_space = strpos($string, ' '); - if ($num_equal === 0 && !$has_space) { - // bool attribute - return array($string => $string); - } elseif ($num_equal === 1 && !$has_space) { - // only one attribute - list($key, $quoted_value) = explode('=', $string); - $quoted_value = trim($quoted_value); - if (!$key) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); - return array(); - } - if (!$quoted_value) return array($key => ''); - $first_char = @$quoted_value[0]; - $last_char = @$quoted_value[strlen($quoted_value)-1]; - - $same_quote = ($first_char == $last_char); - $open_quote = ($first_char == '"' || $first_char == "'"); - - if ( $same_quote && $open_quote) { - // well behaved - $value = substr($quoted_value, 1, strlen($quoted_value) - 2); - } else { - // not well behaved - if ($open_quote) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing end quote'); - $value = substr($quoted_value, 1); - } else { - $value = $quoted_value; - } - } - if ($value === false) $value = ''; - return array($key => $value); - } - - // setup loop environment - $array = array(); // return assoc array of attributes - $cursor = 0; // current position in string (moves forward) - $size = strlen($string); // size of the string (stays the same) - - // if we have unquoted attributes, the parser expects a terminating - // space, so let's guarantee that there's always a terminating space. - $string .= ' '; - - while(true) { - - if ($cursor >= $size) { - break; - } - - $cursor += ($value = strspn($string, $this->_whitespace, $cursor)); - // grab the key - - $key_begin = $cursor; //we're currently at the start of the key - - // scroll past all characters that are the key (not whitespace or =) - $cursor += strcspn($string, $this->_whitespace . '=', $cursor); - - $key_end = $cursor; // now at the end of the key - - $key = substr($string, $key_begin, $key_end - $key_begin); - - if (!$key) { - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); - $cursor += strcspn($string, $this->_whitespace, $cursor + 1); // prevent infinite loop - continue; // empty key - } - - // scroll past all whitespace - $cursor += strspn($string, $this->_whitespace, $cursor); - - if ($cursor >= $size) { - $array[$key] = $key; - break; - } - - // if the next character is an equal sign, we've got a regular - // pair, otherwise, it's a bool attribute - $first_char = @$string[$cursor]; - - if ($first_char == '=') { - // key="value" - - $cursor++; - $cursor += strspn($string, $this->_whitespace, $cursor); - - if ($cursor === false) { - $array[$key] = ''; - break; - } - - // we might be in front of a quote right now - - $char = @$string[$cursor]; - - if ($char == '"' || $char == "'") { - // it's quoted, end bound is $char - $cursor++; - $value_begin = $cursor; - $cursor = strpos($string, $char, $cursor); - $value_end = $cursor; - } else { - // it's not quoted, end bound is whitespace - $value_begin = $cursor; - $cursor += strcspn($string, $this->_whitespace, $cursor); - $value_end = $cursor; - } - - // we reached a premature end - if ($cursor === false) { - $cursor = $size; - $value_end = $cursor; - } - - $value = substr($string, $value_begin, $value_end - $value_begin); - if ($value === false) $value = ''; - $array[$key] = $this->parseData($value); - $cursor++; - - } else { - // boolattr - if ($key !== '') { - $array[$key] = $key; - } else { - // purely theoretical - if ($e) $e->send(E_ERROR, 'Lexer: Missing attribute key'); - } - - } - } - return $array; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php b/extlib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php deleted file mode 100644 index 57cffa82a..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Lexer/PEARSax3.php +++ /dev/null @@ -1,106 +0,0 @@ -tokens = array(); - - $string = $this->normalize($string, $config, $context); - - $parser = new XML_HTMLSax3(); - $parser->set_object($this); - $parser->set_element_handler('openHandler','closeHandler'); - $parser->set_data_handler('dataHandler'); - $parser->set_escape_handler('escapeHandler'); - - // doesn't seem to work correctly for attributes - $parser->set_option('XML_OPTION_ENTITIES_PARSED', 1); - - $parser->parse($string); - - return $this->tokens; - - } - - /** - * Open tag event handler, interface is defined by PEAR package. - */ - public function openHandler(&$parser, $name, $attrs, $closed) { - // entities are not resolved in attrs - foreach ($attrs as $key => $attr) { - $attrs[$key] = $this->parseData($attr); - } - if ($closed) { - $this->tokens[] = new HTMLPurifier_Token_Empty($name, $attrs); - } else { - $this->tokens[] = new HTMLPurifier_Token_Start($name, $attrs); - } - return true; - } - - /** - * Close tag event handler, interface is defined by PEAR package. - */ - public function closeHandler(&$parser, $name) { - // HTMLSax3 seems to always send empty tags an extra close tag - // check and ignore if you see it: - // [TESTME] to make sure it doesn't overreach - if ($this->tokens[count($this->tokens)-1] instanceof HTMLPurifier_Token_Empty) { - return true; - } - $this->tokens[] = new HTMLPurifier_Token_End($name); - return true; - } - - /** - * Data event handler, interface is defined by PEAR package. - */ - public function dataHandler(&$parser, $data) { - $this->tokens[] = new HTMLPurifier_Token_Text($data); - return true; - } - - /** - * Escaped text handler, interface is defined by PEAR package. - */ - public function escapeHandler(&$parser, $data) { - if (strpos($data, '--') === 0) { - $this->tokens[] = new HTMLPurifier_Token_Comment($data); - } - // CDATA is handled elsewhere, but if it was handled here: - //if (strpos($data, '[CDATA[') === 0) { - // $this->tokens[] = new HTMLPurifier_Token_Text( - // substr($data, 7, strlen($data) - 9) ); - //} - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php b/extlib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php deleted file mode 100644 index fa1bf973e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Lexer/PH5P.php +++ /dev/null @@ -1,3906 +0,0 @@ -normalize($html, $config, $context); - $new_html = $this->wrapHTML($new_html, $config, $context); - try { - $parser = new HTML5($new_html); - $doc = $parser->save(); - } catch (DOMException $e) { - // Uh oh, it failed. Punt to DirectLex. - $lexer = new HTMLPurifier_Lexer_DirectLex(); - $context->register('PH5PError', $e); // save the error, so we can detect it - return $lexer->tokenizeHTML($html, $config, $context); // use original HTML - } - $tokens = array(); - $this->tokenizeDOM( - $doc->getElementsByTagName('html')->item(0)-> // - getElementsByTagName('body')->item(0)-> // - getElementsByTagName('div')->item(0) //
    - , $tokens); - return $tokens; - } - -} - -/* - -Copyright 2007 Jeroen van der Meer - -Permission is hereby granted, free of charge, to any person obtaining a -copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -*/ - -class HTML5 { - private $data; - private $char; - private $EOF; - private $state; - private $tree; - private $token; - private $content_model; - private $escape = false; - private $entities = array('AElig;','AElig','AMP;','AMP','Aacute;','Aacute', - 'Acirc;','Acirc','Agrave;','Agrave','Alpha;','Aring;','Aring','Atilde;', - 'Atilde','Auml;','Auml','Beta;','COPY;','COPY','Ccedil;','Ccedil','Chi;', - 'Dagger;','Delta;','ETH;','ETH','Eacute;','Eacute','Ecirc;','Ecirc','Egrave;', - 'Egrave','Epsilon;','Eta;','Euml;','Euml','GT;','GT','Gamma;','Iacute;', - 'Iacute','Icirc;','Icirc','Igrave;','Igrave','Iota;','Iuml;','Iuml','Kappa;', - 'LT;','LT','Lambda;','Mu;','Ntilde;','Ntilde','Nu;','OElig;','Oacute;', - 'Oacute','Ocirc;','Ocirc','Ograve;','Ograve','Omega;','Omicron;','Oslash;', - 'Oslash','Otilde;','Otilde','Ouml;','Ouml','Phi;','Pi;','Prime;','Psi;', - 'QUOT;','QUOT','REG;','REG','Rho;','Scaron;','Sigma;','THORN;','THORN', - 'TRADE;','Tau;','Theta;','Uacute;','Uacute','Ucirc;','Ucirc','Ugrave;', - 'Ugrave','Upsilon;','Uuml;','Uuml','Xi;','Yacute;','Yacute','Yuml;','Zeta;', - 'aacute;','aacute','acirc;','acirc','acute;','acute','aelig;','aelig', - 'agrave;','agrave','alefsym;','alpha;','amp;','amp','and;','ang;','apos;', - 'aring;','aring','asymp;','atilde;','atilde','auml;','auml','bdquo;','beta;', - 'brvbar;','brvbar','bull;','cap;','ccedil;','ccedil','cedil;','cedil', - 'cent;','cent','chi;','circ;','clubs;','cong;','copy;','copy','crarr;', - 'cup;','curren;','curren','dArr;','dagger;','darr;','deg;','deg','delta;', - 'diams;','divide;','divide','eacute;','eacute','ecirc;','ecirc','egrave;', - 'egrave','empty;','emsp;','ensp;','epsilon;','equiv;','eta;','eth;','eth', - 'euml;','euml','euro;','exist;','fnof;','forall;','frac12;','frac12', - 'frac14;','frac14','frac34;','frac34','frasl;','gamma;','ge;','gt;','gt', - 'hArr;','harr;','hearts;','hellip;','iacute;','iacute','icirc;','icirc', - 'iexcl;','iexcl','igrave;','igrave','image;','infin;','int;','iota;', - 'iquest;','iquest','isin;','iuml;','iuml','kappa;','lArr;','lambda;','lang;', - 'laquo;','laquo','larr;','lceil;','ldquo;','le;','lfloor;','lowast;','loz;', - 'lrm;','lsaquo;','lsquo;','lt;','lt','macr;','macr','mdash;','micro;','micro', - 'middot;','middot','minus;','mu;','nabla;','nbsp;','nbsp','ndash;','ne;', - 'ni;','not;','not','notin;','nsub;','ntilde;','ntilde','nu;','oacute;', - 'oacute','ocirc;','ocirc','oelig;','ograve;','ograve','oline;','omega;', - 'omicron;','oplus;','or;','ordf;','ordf','ordm;','ordm','oslash;','oslash', - 'otilde;','otilde','otimes;','ouml;','ouml','para;','para','part;','permil;', - 'perp;','phi;','pi;','piv;','plusmn;','plusmn','pound;','pound','prime;', - 'prod;','prop;','psi;','quot;','quot','rArr;','radic;','rang;','raquo;', - 'raquo','rarr;','rceil;','rdquo;','real;','reg;','reg','rfloor;','rho;', - 'rlm;','rsaquo;','rsquo;','sbquo;','scaron;','sdot;','sect;','sect','shy;', - 'shy','sigma;','sigmaf;','sim;','spades;','sub;','sube;','sum;','sup1;', - 'sup1','sup2;','sup2','sup3;','sup3','sup;','supe;','szlig;','szlig','tau;', - 'there4;','theta;','thetasym;','thinsp;','thorn;','thorn','tilde;','times;', - 'times','trade;','uArr;','uacute;','uacute','uarr;','ucirc;','ucirc', - 'ugrave;','ugrave','uml;','uml','upsih;','upsilon;','uuml;','uuml','weierp;', - 'xi;','yacute;','yacute','yen;','yen','yuml;','yuml','zeta;','zwj;','zwnj;'); - - const PCDATA = 0; - const RCDATA = 1; - const CDATA = 2; - const PLAINTEXT = 3; - - const DOCTYPE = 0; - const STARTTAG = 1; - const ENDTAG = 2; - const COMMENT = 3; - const CHARACTR = 4; - const EOF = 5; - - public function __construct($data) { - $data = str_replace("\r\n", "\n", $data); - $data = str_replace("\r", null, $data); - - $this->data = $data; - $this->char = -1; - $this->EOF = strlen($data); - $this->tree = new HTML5TreeConstructer; - $this->content_model = self::PCDATA; - - $this->state = 'data'; - - while($this->state !== null) { - $this->{$this->state.'State'}(); - } - } - - public function save() { - return $this->tree->save(); - } - - private function char() { - return ($this->char < $this->EOF) - ? $this->data[$this->char] - : false; - } - - private function character($s, $l = 0) { - if($s + $l < $this->EOF) { - if($l === 0) { - return $this->data[$s]; - } else { - return substr($this->data, $s, $l); - } - } - } - - private function characters($char_class, $start) { - return preg_replace('#^(['.$char_class.']+).*#s', '\\1', substr($this->data, $start)); - } - - private function dataState() { - // Consume the next input character - $this->char++; - $char = $this->char(); - - if($char === '&' && ($this->content_model === self::PCDATA || $this->content_model === self::RCDATA)) { - /* U+0026 AMPERSAND (&) - When the content model flag is set to one of the PCDATA or RCDATA - states: switch to the entity data state. Otherwise: treat it as per - the "anything else" entry below. */ - $this->state = 'entityData'; - - } elseif($char === '-') { - /* If the content model flag is set to either the RCDATA state or - the CDATA state, and the escape flag is false, and there are at - least three characters before this one in the input stream, and the - last four characters in the input stream, including this one, are - U+003C LESS-THAN SIGN, U+0021 EXCLAMATION MARK, U+002D HYPHEN-MINUS, - and U+002D HYPHEN-MINUS (""), - set the escape flag to false. */ - if(($this->content_model === self::RCDATA || - $this->content_model === self::CDATA) && $this->escape === true && - $this->character($this->char, 3) === '-->') { - $this->escape = false; - } - - /* In any case, emit the input character as a character token. - Stay in the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => $char - )); - - } elseif($this->char === $this->EOF) { - /* EOF - Emit an end-of-file token. */ - $this->EOF(); - - } elseif($this->content_model === self::PLAINTEXT) { - /* When the content model flag is set to the PLAINTEXT state - THIS DIFFERS GREATLY FROM THE SPEC: Get the remaining characters of - the text and emit it as a character token. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => substr($this->data, $this->char) - )); - - $this->EOF(); - - } else { - /* Anything else - THIS DIFFERS GREATLY FROM THE SPEC: Get as many character that - otherwise would also be treated as a character token and emit it - as a single character token. Stay in the data state. */ - $len = strcspn($this->data, '<&', $this->char); - $char = substr($this->data, $this->char, $len); - $this->char += $len - 1; - - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => $char - )); - - $this->state = 'data'; - } - } - - private function entityDataState() { - // Attempt to consume an entity. - $entity = $this->entity(); - - // If nothing is returned, emit a U+0026 AMPERSAND character token. - // Otherwise, emit the character token that was returned. - $char = (!$entity) ? '&' : $entity; - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => $char - )); - - // Finally, switch to the data state. - $this->state = 'data'; - } - - private function tagOpenState() { - switch($this->content_model) { - case self::RCDATA: - case self::CDATA: - /* If the next input character is a U+002F SOLIDUS (/) character, - consume it and switch to the close tag open state. If the next - input character is not a U+002F SOLIDUS (/) character, emit a - U+003C LESS-THAN SIGN character token and switch to the data - state to process the next input character. */ - if($this->character($this->char + 1) === '/') { - $this->char++; - $this->state = 'closeTagOpen'; - - } else { - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => '<' - )); - - $this->state = 'data'; - } - break; - - case self::PCDATA: - // If the content model flag is set to the PCDATA state - // Consume the next input character: - $this->char++; - $char = $this->char(); - - if($char === '!') { - /* U+0021 EXCLAMATION MARK (!) - Switch to the markup declaration open state. */ - $this->state = 'markupDeclarationOpen'; - - } elseif($char === '/') { - /* U+002F SOLIDUS (/) - Switch to the close tag open state. */ - $this->state = 'closeTagOpen'; - - } elseif(preg_match('/^[A-Za-z]$/', $char)) { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new start tag token, set its tag name to the lowercase - version of the input character (add 0x0020 to the character's code - point), then switch to the tag name state. (Don't emit the token - yet; further details will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::STARTTAG, - 'attr' => array() - ); - - $this->state = 'tagName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Emit a U+003C LESS-THAN SIGN character token and a - U+003E GREATER-THAN SIGN character token. Switch to the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => '<>' - )); - - $this->state = 'data'; - - } elseif($char === '?') { - /* U+003F QUESTION MARK (?) - Parse error. Switch to the bogus comment state. */ - $this->state = 'bogusComment'; - - } else { - /* Anything else - Parse error. Emit a U+003C LESS-THAN SIGN character token and - reconsume the current input character in the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => '<' - )); - - $this->char--; - $this->state = 'data'; - } - break; - } - } - - private function closeTagOpenState() { - $next_node = strtolower($this->characters('A-Za-z', $this->char + 1)); - $the_same = count($this->tree->stack) > 0 && $next_node === end($this->tree->stack)->nodeName; - - if(($this->content_model === self::RCDATA || $this->content_model === self::CDATA) && - (!$the_same || ($the_same && (!preg_match('/[\t\n\x0b\x0c >\/]/', - $this->character($this->char + 1 + strlen($next_node))) || $this->EOF === $this->char)))) { - /* If the content model flag is set to the RCDATA or CDATA states then - examine the next few characters. If they do not match the tag name of - the last start tag token emitted (case insensitively), or if they do but - they are not immediately followed by one of the following characters: - * U+0009 CHARACTER TABULATION - * U+000A LINE FEED (LF) - * U+000B LINE TABULATION - * U+000C FORM FEED (FF) - * U+0020 SPACE - * U+003E GREATER-THAN SIGN (>) - * U+002F SOLIDUS (/) - * EOF - ...then there is a parse error. Emit a U+003C LESS-THAN SIGN character - token, a U+002F SOLIDUS character token, and switch to the data state - to process the next input character. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => 'state = 'data'; - - } else { - /* Otherwise, if the content model flag is set to the PCDATA state, - or if the next few characters do match that tag name, consume the - next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[A-Za-z]$/', $char)) { - /* U+0041 LATIN LETTER A through to U+005A LATIN LETTER Z - Create a new end tag token, set its tag name to the lowercase version - of the input character (add 0x0020 to the character's code point), then - switch to the tag name state. (Don't emit the token yet; further details - will be filled in before it is emitted.) */ - $this->token = array( - 'name' => strtolower($char), - 'type' => self::ENDTAG - ); - - $this->state = 'tagName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Parse error. Switch to the data state. */ - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit a U+003C LESS-THAN SIGN character token and a U+002F - SOLIDUS character token. Reconsume the EOF character in the data state. */ - $this->emitToken(array( - 'type' => self::CHARACTR, - 'data' => 'char--; - $this->state = 'data'; - - } else { - /* Parse error. Switch to the bogus comment state. */ - $this->state = 'bogusComment'; - } - } - } - - private function tagNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } elseif($char === '/') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } else { - /* Anything else - Append the current input character to the current tag token's tag name. - Stay in the tag name state. */ - $this->token['name'] .= strtolower($char); - $this->state = 'tagName'; - } - } - - private function beforeAttributeNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '/') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Stay in the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => null - ); - - $this->state = 'attributeName'; - } - } - - private function attributeNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute name state. */ - $this->state = 'afterAttributeName'; - - } elseif($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '/' && $this->character($this->char + 1) !== '>') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the before - attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's name. - Stay in the attribute name state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['name'] .= strtolower($char); - - $this->state = 'attributeName'; - } - } - - private function afterAttributeNameState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the after attribute name state. */ - $this->state = 'afterAttributeName'; - - } elseif($char === '=') { - /* U+003D EQUALS SIGN (=) - Switch to the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '/' && $this->character($this->char + 1) !== '>') { - /* U+002F SOLIDUS (/) - Parse error unless this is a permitted slash. Switch to the - before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the EOF - character in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Start a new attribute in the current tag token. Set that attribute's - name to the current input character, and its value to the empty string. - Switch to the attribute name state. */ - $this->token['attr'][] = array( - 'name' => strtolower($char), - 'value' => null - ); - - $this->state = 'attributeName'; - } - } - - private function beforeAttributeValueState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Stay in the before attribute value state. */ - $this->state = 'beforeAttributeValue'; - - } elseif($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the attribute value (double-quoted) state. */ - $this->state = 'attributeValueDoubleQuoted'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the attribute value (unquoted) state and reconsume - this input character. */ - $this->char--; - $this->state = 'attributeValueUnquoted'; - - } elseif($char === '\'') { - /* U+0027 APOSTROPHE (') - Switch to the attribute value (single-quoted) state. */ - $this->state = 'attributeValueSingleQuoted'; - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Switch to the attribute value (unquoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueUnquoted'; - } - } - - private function attributeValueDoubleQuotedState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if($char === '"') { - /* U+0022 QUOTATION MARK (") - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState('double'); - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the character - in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (double-quoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueDoubleQuoted'; - } - } - - private function attributeValueSingleQuotedState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if($char === '\'') { - /* U+0022 QUOTATION MARK (') - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState('single'); - - } elseif($this->char === $this->EOF) { - /* EOF - Parse error. Emit the current tag token. Reconsume the character - in the data state. */ - $this->emitToken($this->token); - - $this->char--; - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (single-quoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueSingleQuoted'; - } - } - - private function attributeValueUnquotedState() { - // Consume the next input character: - $this->char++; - $char = $this->character($this->char); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - /* U+0009 CHARACTER TABULATION - U+000A LINE FEED (LF) - U+000B LINE TABULATION - U+000C FORM FEED (FF) - U+0020 SPACE - Switch to the before attribute name state. */ - $this->state = 'beforeAttributeName'; - - } elseif($char === '&') { - /* U+0026 AMPERSAND (&) - Switch to the entity in attribute value state. */ - $this->entityInAttributeValueState(); - - } elseif($char === '>') { - /* U+003E GREATER-THAN SIGN (>) - Emit the current tag token. Switch to the data state. */ - $this->emitToken($this->token); - $this->state = 'data'; - - } else { - /* Anything else - Append the current input character to the current attribute's value. - Stay in the attribute value (unquoted) state. */ - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - - $this->state = 'attributeValueUnquoted'; - } - } - - private function entityInAttributeValueState() { - // Attempt to consume an entity. - $entity = $this->entity(); - - // If nothing is returned, append a U+0026 AMPERSAND character to the - // current attribute's value. Otherwise, emit the character token that - // was returned. - $char = (!$entity) - ? '&' - : $entity; - - $last = count($this->token['attr']) - 1; - $this->token['attr'][$last]['value'] .= $char; - } - - private function bogusCommentState() { - /* Consume every character up to the first U+003E GREATER-THAN SIGN - character (>) or the end of the file (EOF), whichever comes first. Emit - a comment token whose data is the concatenation of all the characters - starting from and including the character that caused the state machine - to switch into the bogus comment state, up to and including the last - consumed character before the U+003E character, if any, or up to the - end of the file otherwise. (If the comment was started by the end of - the file (EOF), the token is empty.) */ - $data = $this->characters('^>', $this->char); - $this->emitToken(array( - 'data' => $data, - 'type' => self::COMMENT - )); - - $this->char += strlen($data); - - /* Switch to the data state. */ - $this->state = 'data'; - - /* If the end of the file was reached, reconsume the EOF character. */ - if($this->char === $this->EOF) { - $this->char = $this->EOF - 1; - } - } - - private function markupDeclarationOpenState() { - /* If the next two characters are both U+002D HYPHEN-MINUS (-) - characters, consume those two characters, create a comment token whose - data is the empty string, and switch to the comment state. */ - if($this->character($this->char + 1, 2) === '--') { - $this->char += 2; - $this->state = 'comment'; - $this->token = array( - 'data' => null, - 'type' => self::COMMENT - ); - - /* Otherwise if the next seven chacacters are a case-insensitive match - for the word "DOCTYPE", then consume those characters and switch to the - DOCTYPE state. */ - } elseif(strtolower($this->character($this->char + 1, 7)) === 'doctype') { - $this->char += 7; - $this->state = 'doctype'; - - /* Otherwise, is is a parse error. Switch to the bogus comment state. - The next character that is consumed, if any, is the first character - that will be in the comment. */ - } else { - $this->char++; - $this->state = 'bogusComment'; - } - } - - private function commentState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - /* U+002D HYPHEN-MINUS (-) */ - if($char === '-') { - /* Switch to the comment dash state */ - $this->state = 'commentDash'; - - /* EOF */ - } elseif($this->char === $this->EOF) { - /* Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - /* Anything else */ - } else { - /* Append the input character to the comment token's data. Stay in - the comment state. */ - $this->token['data'] .= $char; - } - } - - private function commentDashState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - /* U+002D HYPHEN-MINUS (-) */ - if($char === '-') { - /* Switch to the comment end state */ - $this->state = 'commentEnd'; - - /* EOF */ - } elseif($this->char === $this->EOF) { - /* Parse error. Emit the comment token. Reconsume the EOF character - in the data state. */ - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - /* Anything else */ - } else { - /* Append a U+002D HYPHEN-MINUS (-) character and the input - character to the comment token's data. Switch to the comment state. */ - $this->token['data'] .= '-'.$char; - $this->state = 'comment'; - } - } - - private function commentEndState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($char === '-') { - $this->token['data'] .= '-'; - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['data'] .= '--'.$char; - $this->state = 'comment'; - } - } - - private function doctypeState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - $this->state = 'beforeDoctypeName'; - - } else { - $this->char--; - $this->state = 'beforeDoctypeName'; - } - } - - private function beforeDoctypeNameState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - // Stay in the before DOCTYPE name state. - - } elseif(preg_match('/^[a-z]$/', $char)) { - $this->token = array( - 'name' => strtoupper($char), - 'type' => self::DOCTYPE, - 'error' => true - ); - - $this->state = 'doctypeName'; - - } elseif($char === '>') { - $this->emitToken(array( - 'name' => null, - 'type' => self::DOCTYPE, - 'error' => true - )); - - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - $this->emitToken(array( - 'name' => null, - 'type' => self::DOCTYPE, - 'error' => true - )); - - $this->char--; - $this->state = 'data'; - - } else { - $this->token = array( - 'name' => $char, - 'type' => self::DOCTYPE, - 'error' => true - ); - - $this->state = 'doctypeName'; - } - } - - private function doctypeNameState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - $this->state = 'AfterDoctypeName'; - - } elseif($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif(preg_match('/^[a-z]$/', $char)) { - $this->token['name'] .= strtoupper($char); - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['name'] .= $char; - } - - $this->token['error'] = ($this->token['name'] === 'HTML') - ? false - : true; - } - - private function afterDoctypeNameState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if(preg_match('/^[\t\n\x0b\x0c ]$/', $char)) { - // Stay in the DOCTYPE name state. - - } elseif($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - $this->token['error'] = true; - $this->state = 'bogusDoctype'; - } - } - - private function bogusDoctypeState() { - /* Consume the next input character: */ - $this->char++; - $char = $this->char(); - - if($char === '>') { - $this->emitToken($this->token); - $this->state = 'data'; - - } elseif($this->char === $this->EOF) { - $this->emitToken($this->token); - $this->char--; - $this->state = 'data'; - - } else { - // Stay in the bogus DOCTYPE state. - } - } - - private function entity() { - $start = $this->char; - - // This section defines how to consume an entity. This definition is - // used when parsing entities in text and in attributes. - - // The behaviour depends on the identity of the next character (the - // one immediately after the U+0026 AMPERSAND character): - - switch($this->character($this->char + 1)) { - // U+0023 NUMBER SIGN (#) - case '#': - - // The behaviour further depends on the character after the - // U+0023 NUMBER SIGN: - switch($this->character($this->char + 1)) { - // U+0078 LATIN SMALL LETTER X - // U+0058 LATIN CAPITAL LETTER X - case 'x': - case 'X': - // Follow the steps below, but using the range of - // characters U+0030 DIGIT ZERO through to U+0039 DIGIT - // NINE, U+0061 LATIN SMALL LETTER A through to U+0066 - // LATIN SMALL LETTER F, and U+0041 LATIN CAPITAL LETTER - // A, through to U+0046 LATIN CAPITAL LETTER F (in other - // words, 0-9, A-F, a-f). - $char = 1; - $char_class = '0-9A-Fa-f'; - break; - - // Anything else - default: - // Follow the steps below, but using the range of - // characters U+0030 DIGIT ZERO through to U+0039 DIGIT - // NINE (i.e. just 0-9). - $char = 0; - $char_class = '0-9'; - break; - } - - // Consume as many characters as match the range of characters - // given above. - $this->char++; - $e_name = $this->characters($char_class, $this->char + $char + 1); - $entity = $this->character($start, $this->char); - $cond = strlen($e_name) > 0; - - // The rest of the parsing happens bellow. - break; - - // Anything else - default: - // Consume the maximum number of characters possible, with the - // consumed characters case-sensitively matching one of the - // identifiers in the first column of the entities table. - $e_name = $this->characters('0-9A-Za-z;', $this->char + 1); - $len = strlen($e_name); - - for($c = 1; $c <= $len; $c++) { - $id = substr($e_name, 0, $c); - $this->char++; - - if(in_array($id, $this->entities)) { - if ($e_name[$c-1] !== ';') { - if ($c < $len && $e_name[$c] == ';') { - $this->char++; // consume extra semicolon - } - } - $entity = $id; - break; - } - } - - $cond = isset($entity); - // The rest of the parsing happens bellow. - break; - } - - if(!$cond) { - // If no match can be made, then this is a parse error. No - // characters are consumed, and nothing is returned. - $this->char = $start; - return false; - } - - // Return a character token for the character corresponding to the - // entity name (as given by the second column of the entities table). - return html_entity_decode('&'.$entity.';', ENT_QUOTES, 'UTF-8'); - } - - private function emitToken($token) { - $emit = $this->tree->emitToken($token); - - if(is_int($emit)) { - $this->content_model = $emit; - - } elseif($token['type'] === self::ENDTAG) { - $this->content_model = self::PCDATA; - } - } - - private function EOF() { - $this->state = null; - $this->tree->emitToken(array( - 'type' => self::EOF - )); - } -} - -class HTML5TreeConstructer { - public $stack = array(); - - private $phase; - private $mode; - private $dom; - private $foster_parent = null; - private $a_formatting = array(); - - private $head_pointer = null; - private $form_pointer = null; - - private $scoping = array('button','caption','html','marquee','object','table','td','th'); - private $formatting = array('a','b','big','em','font','i','nobr','s','small','strike','strong','tt','u'); - private $special = array('address','area','base','basefont','bgsound', - 'blockquote','body','br','center','col','colgroup','dd','dir','div','dl', - 'dt','embed','fieldset','form','frame','frameset','h1','h2','h3','h4','h5', - 'h6','head','hr','iframe','image','img','input','isindex','li','link', - 'listing','menu','meta','noembed','noframes','noscript','ol','optgroup', - 'option','p','param','plaintext','pre','script','select','spacer','style', - 'tbody','textarea','tfoot','thead','title','tr','ul','wbr'); - - // The different phases. - const INIT_PHASE = 0; - const ROOT_PHASE = 1; - const MAIN_PHASE = 2; - const END_PHASE = 3; - - // The different insertion modes for the main phase. - const BEFOR_HEAD = 0; - const IN_HEAD = 1; - const AFTER_HEAD = 2; - const IN_BODY = 3; - const IN_TABLE = 4; - const IN_CAPTION = 5; - const IN_CGROUP = 6; - const IN_TBODY = 7; - const IN_ROW = 8; - const IN_CELL = 9; - const IN_SELECT = 10; - const AFTER_BODY = 11; - const IN_FRAME = 12; - const AFTR_FRAME = 13; - - // The different types of elements. - const SPECIAL = 0; - const SCOPING = 1; - const FORMATTING = 2; - const PHRASING = 3; - - const MARKER = 0; - - public function __construct() { - $this->phase = self::INIT_PHASE; - $this->mode = self::BEFOR_HEAD; - $this->dom = new DOMDocument; - - $this->dom->encoding = 'UTF-8'; - $this->dom->preserveWhiteSpace = true; - $this->dom->substituteEntities = true; - $this->dom->strictErrorChecking = false; - } - - // Process tag tokens - public function emitToken($token) { - switch($this->phase) { - case self::INIT_PHASE: return $this->initPhase($token); break; - case self::ROOT_PHASE: return $this->rootElementPhase($token); break; - case self::MAIN_PHASE: return $this->mainPhase($token); break; - case self::END_PHASE : return $this->trailingEndPhase($token); break; - } - } - - private function initPhase($token) { - /* Initially, the tree construction stage must handle each token - emitted from the tokenisation stage as follows: */ - - /* A DOCTYPE token that is marked as being in error - A comment token - A start tag token - An end tag token - A character token that is not one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE - An end-of-file token */ - if((isset($token['error']) && $token['error']) || - $token['type'] === HTML5::COMMENT || - $token['type'] === HTML5::STARTTAG || - $token['type'] === HTML5::ENDTAG || - $token['type'] === HTML5::EOF || - ($token['type'] === HTML5::CHARACTR && isset($token['data']) && - !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data']))) { - /* This specification does not define how to handle this case. In - particular, user agents may ignore the entirety of this specification - altogether for such documents, and instead invoke special parse modes - with a greater emphasis on backwards compatibility. */ - - $this->phase = self::ROOT_PHASE; - return $this->rootElementPhase($token); - - /* A DOCTYPE token marked as being correct */ - } elseif(isset($token['error']) && !$token['error']) { - /* Append a DocumentType node to the Document node, with the name - attribute set to the name given in the DOCTYPE token (which will be - "HTML"), and the other attributes specific to DocumentType objects - set to null, empty lists, or the empty string as appropriate. */ - $doctype = new DOMDocumentType(null, null, 'HTML'); - - /* Then, switch to the root element phase of the tree construction - stage. */ - $this->phase = self::ROOT_PHASE; - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif(isset($token['data']) && preg_match('/^[\t\n\x0b\x0c ]+$/', - $token['data'])) { - /* Append that character to the Document node. */ - $text = $this->dom->createTextNode($token['data']); - $this->dom->appendChild($text); - } - } - - private function rootElementPhase($token) { - /* After the initial phase, as each token is emitted from the tokenisation - stage, it must be processed as described in this section. */ - - /* A DOCTYPE token */ - if($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append that character to the Document node. */ - $text = $this->dom->createTextNode($token['data']); - $this->dom->appendChild($text); - - /* A character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED - (FF), or U+0020 SPACE - A start tag token - An end tag token - An end-of-file token */ - } elseif(($token['type'] === HTML5::CHARACTR && - !preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || - $token['type'] === HTML5::STARTTAG || - $token['type'] === HTML5::ENDTAG || - $token['type'] === HTML5::EOF) { - /* Create an HTMLElement node with the tag name html, in the HTML - namespace. Append it to the Document object. Switch to the main - phase and reprocess the current token. */ - $html = $this->dom->createElement('html'); - $this->dom->appendChild($html); - $this->stack[] = $html; - - $this->phase = self::MAIN_PHASE; - return $this->mainPhase($token); - } - } - - private function mainPhase($token) { - /* Tokens in the main phase must be handled as follows: */ - - /* A DOCTYPE token */ - if($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A start tag token with the tag name "html" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'html') { - /* If this start tag token was not the first start tag token, then - it is a parse error. */ - - /* For each attribute on the token, check to see if the attribute - is already present on the top element of the stack of open elements. - If it is not, add the attribute and its corresponding value to that - element. */ - foreach($token['attr'] as $attr) { - if(!$this->stack[0]->hasAttribute($attr['name'])) { - $this->stack[0]->setAttribute($attr['name'], $attr['value']); - } - } - - /* An end-of-file token */ - } elseif($token['type'] === HTML5::EOF) { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Anything else. */ - } else { - /* Depends on the insertion mode: */ - switch($this->mode) { - case self::BEFOR_HEAD: return $this->beforeHead($token); break; - case self::IN_HEAD: return $this->inHead($token); break; - case self::AFTER_HEAD: return $this->afterHead($token); break; - case self::IN_BODY: return $this->inBody($token); break; - case self::IN_TABLE: return $this->inTable($token); break; - case self::IN_CAPTION: return $this->inCaption($token); break; - case self::IN_CGROUP: return $this->inColumnGroup($token); break; - case self::IN_TBODY: return $this->inTableBody($token); break; - case self::IN_ROW: return $this->inRow($token); break; - case self::IN_CELL: return $this->inCell($token); break; - case self::IN_SELECT: return $this->inSelect($token); break; - case self::AFTER_BODY: return $this->afterBody($token); break; - case self::IN_FRAME: return $this->inFrameset($token); break; - case self::AFTR_FRAME: return $this->afterFrameset($token); break; - case self::END_PHASE: return $this->trailingEndPhase($token); break; - } - } - } - - private function beforeHead($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token with the tag name "head" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') { - /* Create an element for the token, append the new element to the - current node and push it onto the stack of open elements. */ - $element = $this->insertElement($token); - - /* Set the head element pointer to this new element node. */ - $this->head_pointer = $element; - - /* Change the insertion mode to "in head". */ - $this->mode = self::IN_HEAD; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title". Or an end tag with the tag name "html". - Or a character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. Or any other start tag token */ - } elseif($token['type'] === HTML5::STARTTAG || - ($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') || - ($token['type'] === HTML5::CHARACTR && !preg_match('/^[\t\n\x0b\x0c ]$/', - $token['data']))) { - /* Act as if a start tag token with the tag name "head" and no - attributes had been seen, then reprocess the current token. */ - $this->beforeHead(array( - 'name' => 'head', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inHead($token); - - /* Any other end tag */ - } elseif($token['type'] === HTML5::ENDTAG) { - /* Parse error. Ignore the token. */ - } - } - - private function inHead($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. - - THIS DIFFERS FROM THE SPEC: If the current node is either a title, style - or script element, append the character to the current node regardless - of its content. */ - if(($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || ( - $token['type'] === HTML5::CHARACTR && in_array(end($this->stack)->nodeName, - array('title', 'style', 'script')))) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - } elseif($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('title', 'style', 'script'))) { - array_pop($this->stack); - return HTML5::PCDATA; - - /* A start tag with the tag name "title" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'title') { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - } else { - $element = $this->insertElement($token); - } - - /* Switch the tokeniser's content model flag to the RCDATA state. */ - return HTML5::RCDATA; - - /* A start tag with the tag name "style" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'style') { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - } else { - $this->insertElement($token); - } - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - - /* A start tag with the tag name "script" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'script') { - /* Create an element for the token. */ - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - - /* A start tag with the tag name "base", "link", or "meta" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('base', 'link', 'meta'))) { - /* Create an element for the token and append the new element to the - node pointed to by the head element pointer, or, if that is null - (innerHTML case), to the current node. */ - if($this->head_pointer !== null) { - $element = $this->insertElement($token, false); - $this->head_pointer->appendChild($element); - array_pop($this->stack); - - } else { - $this->insertElement($token); - } - - /* An end tag with the tag name "head" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'head') { - /* If the current node is a head element, pop the current node off - the stack of open elements. */ - if($this->head_pointer->isSameNode(end($this->stack))) { - array_pop($this->stack); - - /* Otherwise, this is a parse error. */ - } else { - // k - } - - /* Change the insertion mode to "after head". */ - $this->mode = self::AFTER_HEAD; - - /* A start tag with the tag name "head" or an end tag except "html". */ - } elseif(($token['type'] === HTML5::STARTTAG && $token['name'] === 'head') || - ($token['type'] === HTML5::ENDTAG && $token['name'] !== 'html')) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* If the current node is a head element, act as if an end tag - token with the tag name "head" had been seen. */ - if($this->head_pointer->isSameNode(end($this->stack))) { - $this->inHead(array( - 'name' => 'head', - 'type' => HTML5::ENDTAG - )); - - /* Otherwise, change the insertion mode to "after head". */ - } else { - $this->mode = self::AFTER_HEAD; - } - - /* Then, reprocess the current token. */ - return $this->afterHead($token); - } - } - - private function afterHead($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data attribute - set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token with the tag name "body" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'body') { - /* Insert a body element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in body". */ - $this->mode = self::IN_BODY; - - /* A start tag token with the tag name "frameset" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'frameset') { - /* Insert a frameset element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in frameset". */ - $this->mode = self::IN_FRAME; - - /* A start tag token whose tag name is one of: "base", "link", "meta", - "script", "style", "title" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('base', 'link', 'meta', 'script', 'style', 'title'))) { - /* Parse error. Switch the insertion mode back to "in head" and - reprocess the token. */ - $this->mode = self::IN_HEAD; - return $this->inHead($token); - - /* Anything else */ - } else { - /* Act as if a start tag token with the tag name "body" and no - attributes had been seen, and then reprocess the current token. */ - $this->afterHead(array( - 'name' => 'body', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inBody($token); - } - } - - private function inBody($token) { - /* Handle the token as follows: */ - - switch($token['type']) { - /* A character token */ - case HTML5::CHARACTR: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - break; - - /* A comment token */ - case HTML5::COMMENT: - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - break; - - case HTML5::STARTTAG: - switch($token['name']) { - /* A start tag token whose tag name is one of: "script", - "style" */ - case 'script': case 'style': - /* Process the token as if the insertion mode had been "in - head". */ - return $this->inHead($token); - break; - - /* A start tag token whose tag name is one of: "base", "link", - "meta", "title" */ - case 'base': case 'link': case 'meta': case 'title': - /* Parse error. Process the token as if the insertion mode - had been "in head". */ - return $this->inHead($token); - break; - - /* A start tag token with the tag name "body" */ - case 'body': - /* Parse error. If the second element on the stack of open - elements is not a body element, or, if the stack of open - elements has only one node on it, then ignore the token. - (innerHTML case) */ - if(count($this->stack) === 1 || $this->stack[1]->nodeName !== 'body') { - // Ignore - - /* Otherwise, for each attribute on the token, check to see - if the attribute is already present on the body element (the - second element) on the stack of open elements. If it is not, - add the attribute and its corresponding value to that - element. */ - } else { - foreach($token['attr'] as $attr) { - if(!$this->stack[1]->hasAttribute($attr['name'])) { - $this->stack[1]->setAttribute($attr['name'], $attr['value']); - } - } - } - break; - - /* A start tag whose tag name is one of: "address", - "blockquote", "center", "dir", "div", "dl", "fieldset", - "listing", "menu", "ol", "p", "ul" */ - case 'address': case 'blockquote': case 'center': case 'dir': - case 'div': case 'dl': case 'fieldset': case 'listing': - case 'menu': case 'ol': case 'p': case 'ul': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is "form" */ - case 'form': - /* If the form element pointer is not null, ignore the - token with a parse error. */ - if($this->form_pointer !== null) { - // Ignore. - - /* Otherwise: */ - } else { - /* If the stack of open elements has a p element in - scope, then act as if an end tag with the tag name p - had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token, and set the - form element pointer to point to the element created. */ - $element = $this->insertElement($token); - $this->form_pointer = $element; - } - break; - - /* A start tag whose tag name is "li", "dd" or "dt" */ - case 'li': case 'dd': case 'dt': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - $stack_length = count($this->stack) - 1; - - for($n = $stack_length; 0 <= $n; $n--) { - /* 1. Initialise node to be the current node (the - bottommost node of the stack). */ - $stop = false; - $node = $this->stack[$n]; - $cat = $this->getElementCategory($node->tagName); - - /* 2. If node is an li, dd or dt element, then pop all - the nodes from the current node up to node, including - node, then stop this algorithm. */ - if($token['name'] === $node->tagName || ($token['name'] !== 'li' - && ($node->tagName === 'dd' || $node->tagName === 'dt'))) { - for($x = $stack_length; $x >= $n ; $x--) { - array_pop($this->stack); - } - - break; - } - - /* 3. If node is not in the formatting category, and is - not in the phrasing category, and is not an address or - div element, then stop this algorithm. */ - if($cat !== self::FORMATTING && $cat !== self::PHRASING && - $node->tagName !== 'address' && $node->tagName !== 'div') { - break; - } - } - - /* Finally, insert an HTML element with the same tag - name as the token's. */ - $this->insertElement($token); - break; - - /* A start tag token whose tag name is "plaintext" */ - case 'plaintext': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been - seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - return HTML5::PLAINTEXT; - break; - - /* A start tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* If the stack of open elements has in scope an element whose - tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then - this is a parse error; pop elements from the stack until an - element with one of those tag names has been popped from the - stack. */ - while($this->elementInScope(array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'))) { - array_pop($this->stack); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - break; - - /* A start tag whose tag name is "a" */ - case 'a': - /* If the list of active formatting elements contains - an element whose tag name is "a" between the end of the - list and the last marker on the list (or the start of - the list if there is no marker on the list), then this - is a parse error; act as if an end tag with the tag name - "a" had been seen, then remove that element from the list - of active formatting elements and the stack of open - elements if the end tag didn't already remove it (it - might not have if the element is not in table scope). */ - $leng = count($this->a_formatting); - - for($n = $leng - 1; $n >= 0; $n--) { - if($this->a_formatting[$n] === self::MARKER) { - break; - - } elseif($this->a_formatting[$n]->nodeName === 'a') { - $this->emitToken(array( - 'name' => 'a', - 'type' => HTML5::ENDTAG - )); - break; - } - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - /* A start tag whose tag name is one of: "b", "big", "em", "font", - "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ - case 'b': case 'big': case 'em': case 'font': case 'i': - case 'nobr': case 's': case 'small': case 'strike': - case 'strong': case 'tt': case 'u': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $el = $this->insertElement($token); - - /* Add that element to the list of active formatting - elements. */ - $this->a_formatting[] = $el; - break; - - /* A start tag token whose tag name is "button" */ - case 'button': - /* If the stack of open elements has a button element in scope, - then this is a parse error; act as if an end tag with the tag - name "button" had been seen, then reprocess the token. (We don't - do that. Unnecessary.) */ - if($this->elementInScope('button')) { - $this->inBody(array( - 'name' => 'button', - 'type' => HTML5::ENDTAG - )); - } - - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - break; - - /* A start tag token whose tag name is one of: "marquee", "object" */ - case 'marquee': case 'object': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - break; - - /* A start tag token whose tag name is "xmp" */ - case 'xmp': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Switch the content model flag to the CDATA state. */ - return HTML5::CDATA; - break; - - /* A start tag whose tag name is "table" */ - case 'table': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - break; - - /* A start tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "img", "param", "spacer", "wbr" */ - case 'area': case 'basefont': case 'bgsound': case 'br': - case 'embed': case 'img': case 'param': case 'spacer': - case 'wbr': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "hr" */ - case 'hr': - /* If the stack of open elements has a p element in scope, - then act as if an end tag with the tag name p had been seen. */ - if($this->elementInScope('p')) { - $this->emitToken(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "image" */ - case 'image': - /* Parse error. Change the token's tag name to "img" and - reprocess it. (Don't ask.) */ - $token['name'] = 'img'; - return $this->inBody($token); - break; - - /* A start tag whose tag name is "input" */ - case 'input': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an input element for the token. */ - $element = $this->insertElement($token, false); - - /* If the form element pointer is not null, then associate the - input element with the form element pointed to by the form - element pointer. */ - $this->form_pointer !== null - ? $this->form_pointer->appendChild($element) - : end($this->stack)->appendChild($element); - - /* Pop that input element off the stack of open elements. */ - array_pop($this->stack); - break; - - /* A start tag whose tag name is "isindex" */ - case 'isindex': - /* Parse error. */ - // w/e - - /* If the form element pointer is not null, - then ignore the token. */ - if($this->form_pointer === null) { - /* Act as if a start tag token with the tag name "form" had - been seen. */ - $this->inBody(array( - 'name' => 'body', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->inBody(array( - 'name' => 'hr', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "p" had - been seen. */ - $this->inBody(array( - 'name' => 'p', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a start tag token with the tag name "label" - had been seen. */ - $this->inBody(array( - 'name' => 'label', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - /* Act as if a stream of character tokens had been seen. */ - $this->insertText('This is a searchable index. '. - 'Insert your search keywords here: '); - - /* Act as if a start tag token with the tag name "input" - had been seen, with all the attributes from the "isindex" - token, except with the "name" attribute set to the value - "isindex" (ignoring any explicit "name" attribute). */ - $attr = $token['attr']; - $attr[] = array('name' => 'name', 'value' => 'isindex'); - - $this->inBody(array( - 'name' => 'input', - 'type' => HTML5::STARTTAG, - 'attr' => $attr - )); - - /* Act as if a stream of character tokens had been seen - (see below for what they should say). */ - $this->insertText('This is a searchable index. '. - 'Insert your search keywords here: '); - - /* Act as if an end tag token with the tag name "label" - had been seen. */ - $this->inBody(array( - 'name' => 'label', - 'type' => HTML5::ENDTAG - )); - - /* Act as if an end tag token with the tag name "p" had - been seen. */ - $this->inBody(array( - 'name' => 'p', - 'type' => HTML5::ENDTAG - )); - - /* Act as if a start tag token with the tag name "hr" had - been seen. */ - $this->inBody(array( - 'name' => 'hr', - 'type' => HTML5::ENDTAG - )); - - /* Act as if an end tag token with the tag name "form" had - been seen. */ - $this->inBody(array( - 'name' => 'form', - 'type' => HTML5::ENDTAG - )); - } - break; - - /* A start tag whose tag name is "textarea" */ - case 'textarea': - $this->insertElement($token); - - /* Switch the tokeniser's content model flag to the - RCDATA state. */ - return HTML5::RCDATA; - break; - - /* A start tag whose tag name is one of: "iframe", "noembed", - "noframes" */ - case 'iframe': case 'noembed': case 'noframes': - $this->insertElement($token); - - /* Switch the tokeniser's content model flag to the CDATA state. */ - return HTML5::CDATA; - break; - - /* A start tag whose tag name is "select" */ - case 'select': - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Change the insertion mode to "in select". */ - $this->mode = self::IN_SELECT; - break; - - /* A start or end tag whose tag name is one of: "caption", "col", - "colgroup", "frame", "frameset", "head", "option", "optgroup", - "tbody", "td", "tfoot", "th", "thead", "tr". */ - case 'caption': case 'col': case 'colgroup': case 'frame': - case 'frameset': case 'head': case 'option': case 'optgroup': - case 'tbody': case 'td': case 'tfoot': case 'th': case 'thead': - case 'tr': - // Parse error. Ignore the token. - break; - - /* A start or end tag whose tag name is one of: "event-source", - "section", "nav", "article", "aside", "header", "footer", - "datagrid", "command" */ - case 'event-source': case 'section': case 'nav': case 'article': - case 'aside': case 'header': case 'footer': case 'datagrid': - case 'command': - // Work in progress! - break; - - /* A start tag token not covered by the previous entries */ - default: - /* Reconstruct the active formatting elements, if any. */ - $this->reconstructActiveFormattingElements(); - - $this->insertElement($token, true, true); - break; - } - break; - - case HTML5::ENDTAG: - switch($token['name']) { - /* An end tag with the tag name "body" */ - case 'body': - /* If the second element in the stack of open elements is - not a body element, this is a parse error. Ignore the token. - (innerHTML case) */ - if(count($this->stack) < 2 || $this->stack[1]->nodeName !== 'body') { - // Ignore. - - /* If the current node is not the body element, then this - is a parse error. */ - } elseif(end($this->stack)->nodeName !== 'body') { - // Parse error. - } - - /* Change the insertion mode to "after body". */ - $this->mode = self::AFTER_BODY; - break; - - /* An end tag with the tag name "html" */ - case 'html': - /* Act as if an end tag with tag name "body" had been seen, - then, if that token wasn't ignored, reprocess the current - token. */ - $this->inBody(array( - 'name' => 'body', - 'type' => HTML5::ENDTAG - )); - - return $this->afterBody($token); - break; - - /* An end tag whose tag name is one of: "address", "blockquote", - "center", "dir", "div", "dl", "fieldset", "listing", "menu", - "ol", "pre", "ul" */ - case 'address': case 'blockquote': case 'center': case 'dir': - case 'div': case 'dl': case 'fieldset': case 'listing': - case 'menu': case 'ol': case 'pre': case 'ul': - /* If the stack of open elements has an element in scope - with the same tag name as that of the token, then generate - implied end tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with - the same tag name as that of the token, then this - is a parse error. */ - // w/e - - /* If the stack of open elements has an element in - scope with the same tag name as that of the token, - then pop elements from this stack until an element - with that tag name has been popped from the stack. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is "form" */ - case 'form': - /* If the stack of open elements has an element in scope - with the same tag name as that of the token, then generate - implied end tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - } - - if(end($this->stack)->nodeName !== $token['name']) { - /* Now, if the current node is not an element with the - same tag name as that of the token, then this is a parse - error. */ - // w/e - - } else { - /* Otherwise, if the current node is an element with - the same tag name as that of the token pop that element - from the stack. */ - array_pop($this->stack); - } - - /* In any case, set the form element pointer to null. */ - $this->form_pointer = null; - break; - - /* An end tag whose tag name is "p" */ - case 'p': - /* If the stack of open elements has a p element in scope, - then generate implied end tags, except for p elements. */ - if($this->elementInScope('p')) { - $this->generateImpliedEndTags(array('p')); - - /* If the current node is not a p element, then this is - a parse error. */ - // k - - /* If the stack of open elements has a p element in - scope, then pop elements from this stack until the stack - no longer has a p element in scope. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->elementInScope('p')) { - array_pop($this->stack); - - } else { - break; - } - } - } - break; - - /* An end tag whose tag name is "dd", "dt", or "li" */ - case 'dd': case 'dt': case 'li': - /* If the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then - generate implied end tags, except for elements with the - same tag name as the token. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(array($token['name'])); - - /* If the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // w/e - - /* If the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then - pop elements from this stack until an element with that - tag name has been popped from the stack. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is one of: "h1", "h2", "h3", "h4", - "h5", "h6" */ - case 'h1': case 'h2': case 'h3': case 'h4': case 'h5': case 'h6': - $elements = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6'); - - /* If the stack of open elements has in scope an element whose - tag name is one of "h1", "h2", "h3", "h4", "h5", or "h6", then - generate implied end tags. */ - if($this->elementInScope($elements)) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as that of the token, then this is a parse error. */ - // w/e - - /* If the stack of open elements has in scope an element - whose tag name is one of "h1", "h2", "h3", "h4", "h5", or - "h6", then pop elements from the stack until an element - with one of those tag names has been popped from the stack. */ - while($this->elementInScope($elements)) { - array_pop($this->stack); - } - } - break; - - /* An end tag whose tag name is one of: "a", "b", "big", "em", - "font", "i", "nobr", "s", "small", "strike", "strong", "tt", "u" */ - case 'a': case 'b': case 'big': case 'em': case 'font': - case 'i': case 'nobr': case 's': case 'small': case 'strike': - case 'strong': case 'tt': case 'u': - /* 1. Let the formatting element be the last element in - the list of active formatting elements that: - * is between the end of the list and the last scope - marker in the list, if any, or the start of the list - otherwise, and - * has the same tag name as the token. - */ - while(true) { - for($a = count($this->a_formatting) - 1; $a >= 0; $a--) { - if($this->a_formatting[$a] === self::MARKER) { - break; - - } elseif($this->a_formatting[$a]->tagName === $token['name']) { - $formatting_element = $this->a_formatting[$a]; - $in_stack = in_array($formatting_element, $this->stack, true); - $fe_af_pos = $a; - break; - } - } - - /* If there is no such node, or, if that node is - also in the stack of open elements but the element - is not in scope, then this is a parse error. Abort - these steps. The token is ignored. */ - if(!isset($formatting_element) || ($in_stack && - !$this->elementInScope($token['name']))) { - break; - - /* Otherwise, if there is such a node, but that node - is not in the stack of open elements, then this is a - parse error; remove the element from the list, and - abort these steps. */ - } elseif(isset($formatting_element) && !$in_stack) { - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* 2. Let the furthest block be the topmost node in the - stack of open elements that is lower in the stack - than the formatting element, and is not an element in - the phrasing or formatting categories. There might - not be one. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - $length = count($this->stack); - - for($s = $fe_s_pos + 1; $s < $length; $s++) { - $category = $this->getElementCategory($this->stack[$s]->nodeName); - - if($category !== self::PHRASING && $category !== self::FORMATTING) { - $furthest_block = $this->stack[$s]; - } - } - - /* 3. If there is no furthest block, then the UA must - skip the subsequent steps and instead just pop all - the nodes from the bottom of the stack of open - elements, from the current node up to the formatting - element, and remove the formatting element from the - list of active formatting elements. */ - if(!isset($furthest_block)) { - for($n = $length - 1; $n >= $fe_s_pos; $n--) { - array_pop($this->stack); - } - - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - break; - } - - /* 4. Let the common ancestor be the element - immediately above the formatting element in the stack - of open elements. */ - $common_ancestor = $this->stack[$fe_s_pos - 1]; - - /* 5. If the furthest block has a parent node, then - remove the furthest block from its parent node. */ - if($furthest_block->parentNode !== null) { - $furthest_block->parentNode->removeChild($furthest_block); - } - - /* 6. Let a bookmark note the position of the - formatting element in the list of active formatting - elements relative to the elements on either side - of it in the list. */ - $bookmark = $fe_af_pos; - - /* 7. Let node and last node be the furthest block. - Follow these steps: */ - $node = $furthest_block; - $last_node = $furthest_block; - - while(true) { - for($n = array_search($node, $this->stack, true) - 1; $n >= 0; $n--) { - /* 7.1 Let node be the element immediately - prior to node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 7.2 If node is not in the list of active - formatting elements, then remove node from - the stack of open elements and then go back - to step 1. */ - if(!in_array($node, $this->a_formatting, true)) { - unset($this->stack[$n]); - $this->stack = array_merge($this->stack); - - } else { - break; - } - } - - /* 7.3 Otherwise, if node is the formatting - element, then go to the next step in the overall - algorithm. */ - if($node === $formatting_element) { - break; - - /* 7.4 Otherwise, if last node is the furthest - block, then move the aforementioned bookmark to - be immediately after the node in the list of - active formatting elements. */ - } elseif($last_node === $furthest_block) { - $bookmark = array_search($node, $this->a_formatting, true) + 1; - } - - /* 7.5 If node has any children, perform a - shallow clone of node, replace the entry for - node in the list of active formatting elements - with an entry for the clone, replace the entry - for node in the stack of open elements with an - entry for the clone, and let node be the clone. */ - if($node->hasChildNodes()) { - $clone = $node->cloneNode(); - $s_pos = array_search($node, $this->stack, true); - $a_pos = array_search($node, $this->a_formatting, true); - - $this->stack[$s_pos] = $clone; - $this->a_formatting[$a_pos] = $clone; - $node = $clone; - } - - /* 7.6 Insert last node into node, first removing - it from its previous parent node if any. */ - if($last_node->parentNode !== null) { - $last_node->parentNode->removeChild($last_node); - } - - $node->appendChild($last_node); - - /* 7.7 Let last node be node. */ - $last_node = $node; - } - - /* 8. Insert whatever last node ended up being in - the previous step into the common ancestor node, - first removing it from its previous parent node if - any. */ - if($last_node->parentNode !== null) { - $last_node->parentNode->removeChild($last_node); - } - - $common_ancestor->appendChild($last_node); - - /* 9. Perform a shallow clone of the formatting - element. */ - $clone = $formatting_element->cloneNode(); - - /* 10. Take all of the child nodes of the furthest - block and append them to the clone created in the - last step. */ - while($furthest_block->hasChildNodes()) { - $child = $furthest_block->firstChild; - $furthest_block->removeChild($child); - $clone->appendChild($child); - } - - /* 11. Append that clone to the furthest block. */ - $furthest_block->appendChild($clone); - - /* 12. Remove the formatting element from the list - of active formatting elements, and insert the clone - into the list of active formatting elements at the - position of the aforementioned bookmark. */ - $fe_af_pos = array_search($formatting_element, $this->a_formatting, true); - unset($this->a_formatting[$fe_af_pos]); - $this->a_formatting = array_merge($this->a_formatting); - - $af_part1 = array_slice($this->a_formatting, 0, $bookmark - 1); - $af_part2 = array_slice($this->a_formatting, $bookmark, count($this->a_formatting)); - $this->a_formatting = array_merge($af_part1, array($clone), $af_part2); - - /* 13. Remove the formatting element from the stack - of open elements, and insert the clone into the stack - of open elements immediately after (i.e. in a more - deeply nested position than) the position of the - furthest block in that stack. */ - $fe_s_pos = array_search($formatting_element, $this->stack, true); - $fb_s_pos = array_search($furthest_block, $this->stack, true); - unset($this->stack[$fe_s_pos]); - - $s_part1 = array_slice($this->stack, 0, $fb_s_pos); - $s_part2 = array_slice($this->stack, $fb_s_pos + 1, count($this->stack)); - $this->stack = array_merge($s_part1, array($clone), $s_part2); - - /* 14. Jump back to step 1 in this series of steps. */ - unset($formatting_element, $fe_af_pos, $fe_s_pos, $furthest_block); - } - break; - - /* An end tag token whose tag name is one of: "button", - "marquee", "object" */ - case 'button': case 'marquee': case 'object': - /* If the stack of open elements has an element in scope whose - tag name matches the tag name of the token, then generate implied - tags. */ - if($this->elementInScope($token['name'])) { - $this->generateImpliedEndTags(); - - /* Now, if the current node is not an element with the same - tag name as the token, then this is a parse error. */ - // k - - /* Now, if the stack of open elements has an element in scope - whose tag name matches the tag name of the token, then pop - elements from the stack until that element has been popped from - the stack, and clear the list of active formatting elements up - to the last marker. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === $token['name']) { - $n = -1; - } - - array_pop($this->stack); - } - - $marker = end(array_keys($this->a_formatting, self::MARKER, true)); - - for($n = count($this->a_formatting) - 1; $n > $marker; $n--) { - array_pop($this->a_formatting); - } - } - break; - - /* Or an end tag whose tag name is one of: "area", "basefont", - "bgsound", "br", "embed", "hr", "iframe", "image", "img", - "input", "isindex", "noembed", "noframes", "param", "select", - "spacer", "table", "textarea", "wbr" */ - case 'area': case 'basefont': case 'bgsound': case 'br': - case 'embed': case 'hr': case 'iframe': case 'image': - case 'img': case 'input': case 'isindex': case 'noembed': - case 'noframes': case 'param': case 'select': case 'spacer': - case 'table': case 'textarea': case 'wbr': - // Parse error. Ignore the token. - break; - - /* An end tag token not covered by the previous entries */ - default: - for($n = count($this->stack) - 1; $n >= 0; $n--) { - /* Initialise node to be the current node (the bottommost - node of the stack). */ - $node = end($this->stack); - - /* If node has the same tag name as the end tag token, - then: */ - if($token['name'] === $node->nodeName) { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* If the tag name of the end tag token does not - match the tag name of the current node, this is a - parse error. */ - // k - - /* Pop all the nodes from the current node up to - node, including node, then stop this algorithm. */ - for($x = count($this->stack) - $n; $x >= $n; $x--) { - array_pop($this->stack); - } - - } else { - $category = $this->getElementCategory($node); - - if($category !== self::SPECIAL && $category !== self::SCOPING) { - /* Otherwise, if node is in neither the formatting - category nor the phrasing category, then this is a - parse error. Stop this algorithm. The end tag token - is ignored. */ - return false; - } - } - } - break; - } - break; - } - } - - private function inTable($token) { - $clear = array('html', 'table'); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $text = $this->dom->createTextNode($token['data']); - end($this->stack)->appendChild($text); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - end($this->stack)->appendChild($comment); - - /* A start tag whose tag name is "caption" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'caption') { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert a marker at the end of the list of active - formatting elements. */ - $this->a_formatting[] = self::MARKER; - - /* Insert an HTML element for the token, then switch the - insertion mode to "in caption". */ - $this->insertElement($token); - $this->mode = self::IN_CAPTION; - - /* A start tag whose tag name is "colgroup" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'colgroup') { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the - insertion mode to "in column group". */ - $this->insertElement($token); - $this->mode = self::IN_CGROUP; - - /* A start tag whose tag name is "col" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'col') { - $this->inTable(array( - 'name' => 'colgroup', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - $this->inColumnGroup($token); - - /* A start tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('tbody', 'tfoot', 'thead'))) { - /* Clear the stack back to a table context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in table body". */ - $this->insertElement($token); - $this->mode = self::IN_TBODY; - - /* A start tag whose tag name is one of: "td", "th", "tr" */ - } elseif($token['type'] === HTML5::STARTTAG && - in_array($token['name'], array('td', 'th', 'tr'))) { - /* Act as if a start tag token with the tag name "tbody" had been - seen, then reprocess the current token. */ - $this->inTable(array( - 'name' => 'tbody', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inTableBody($token); - - /* A start tag whose tag name is "table" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'table') { - /* Parse error. Act as if an end tag token with the tag name "table" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->inTable(array( - 'name' => 'table', - 'type' => HTML5::ENDTAG - )); - - return $this->mainPhase($token); - - /* An end tag whose tag name is "table" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'table') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - return false; - - /* Otherwise: */ - } else { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Now, if the current node is not a table element, then this - is a parse error. */ - // w/e - - /* Pop elements from this stack until a table element has been - popped from the stack. */ - while(true) { - $current = end($this->stack)->nodeName; - array_pop($this->stack); - - if($current === 'table') { - break; - } - } - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'tbody', 'td', - 'tfoot', 'th', 'thead', 'tr'))) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* Parse error. Process the token as if the insertion mode was "in - body", with the following exception: */ - - /* If the current node is a table, tbody, tfoot, thead, or tr - element, then, whenever a node would be inserted into the current - node, it must instead be inserted into the foster parent element. */ - if(in_array(end($this->stack)->nodeName, - array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { - /* The foster parent element is the parent element of the last - table element in the stack of open elements, if there is a - table element and it has such a parent element. If there is no - table element in the stack of open elements (innerHTML case), - then the foster parent element is the first element in the - stack of open elements (the html element). Otherwise, if there - is a table element in the stack of open elements, but the last - table element in the stack of open elements has no parent, or - its parent node is not an element, then the foster parent - element is the element before the last table element in the - stack of open elements. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === 'table') { - $table = $this->stack[$n]; - break; - } - } - - if(isset($table) && $table->parentNode !== null) { - $this->foster_parent = $table->parentNode; - - } elseif(!isset($table)) { - $this->foster_parent = $this->stack[0]; - - } elseif(isset($table) && ($table->parentNode === null || - $table->parentNode->nodeType !== XML_ELEMENT_NODE)) { - $this->foster_parent = $this->stack[$n - 1]; - } - } - - $this->inBody($token); - } - } - - private function inCaption($token) { - /* An end tag whose tag name is "caption" */ - if($token['type'] === HTML5::ENDTAG && $token['name'] === 'caption') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore - - /* Otherwise: */ - } else { - /* Generate implied end tags. */ - $this->generateImpliedEndTags(); - - /* Now, if the current node is not a caption element, then this - is a parse error. */ - // w/e - - /* Pop elements from this stack until a caption element has - been popped from the stack. */ - while(true) { - $node = end($this->stack)->nodeName; - array_pop($this->stack); - - if($node === 'caption') { - break; - } - } - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in table". */ - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr", or an end tag whose tag - name is "table" */ - } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) || ($token['type'] === HTML5::ENDTAG && - $token['name'] === 'table')) { - /* Parse error. Act as if an end tag with the tag name "caption" - had been seen, then, if that token wasn't ignored, reprocess the - current token. */ - $this->inCaption(array( - 'name' => 'caption', - 'type' => HTML5::ENDTAG - )); - - return $this->inTable($token); - - /* An end tag whose tag name is one of: "body", "col", "colgroup", - "html", "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'col', 'colgroup', 'html', 'tbody', 'tfoot', 'th', - 'thead', 'tr'))) { - // Parse error. Ignore the token. - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->inBody($token); - } - } - - private function inColumnGroup($token) { - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $text = $this->dom->createTextNode($token['data']); - end($this->stack)->appendChild($text); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - end($this->stack)->appendChild($comment); - - /* A start tag whose tag name is "col" */ - } elseif($token['type'] === HTML5::STARTTAG && $token['name'] === 'col') { - /* Insert a col element for the token. Immediately pop the current - node off the stack of open elements. */ - $this->insertElement($token); - array_pop($this->stack); - - /* An end tag whose tag name is "colgroup" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'colgroup') { - /* If the current node is the root html element, then this is a - parse error, ignore the token. (innerHTML case) */ - if(end($this->stack)->nodeName === 'html') { - // Ignore - - /* Otherwise, pop the current node (which will be a colgroup - element) from the stack of open elements. Switch the insertion - mode to "in table". */ - } else { - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* An end tag whose tag name is "col" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'col') { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Act as if an end tag with the tag name "colgroup" had been seen, - and then, if that token wasn't ignored, reprocess the current token. */ - $this->inColumnGroup(array( - 'name' => 'colgroup', - 'type' => HTML5::ENDTAG - )); - - return $this->inTable($token); - } - } - - private function inTableBody($token) { - $clear = array('tbody', 'tfoot', 'thead', 'html'); - - /* A start tag whose tag name is "tr" */ - if($token['type'] === HTML5::STARTTAG && $token['name'] === 'tr') { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Insert a tr element for the token, then switch the insertion - mode to "in row". */ - $this->insertElement($token); - $this->mode = self::IN_ROW; - - /* A start tag whose tag name is one of: "th", "td" */ - } elseif($token['type'] === HTML5::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td')) { - /* Parse error. Act as if a start tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->inTableBody(array( - 'name' => 'tr', - 'type' => HTML5::STARTTAG, - 'attr' => array() - )); - - return $this->inRow($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node from the stack of open elements. Switch - the insertion mode to "in table". */ - array_pop($this->stack); - $this->mode = self::IN_TABLE; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", or an end tag whose tag name is "table" */ - } elseif(($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoor', 'thead'))) || - ($token['type'] === HTML5::STARTTAG && $token['name'] === 'table')) { - /* If the stack of open elements does not have a tbody, thead, or - tfoot element in table scope, this is a parse error. Ignore the - token. (innerHTML case) */ - if(!$this->elementInScope(array('tbody', 'thead', 'tfoot'), true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Clear the stack back to a table body context. */ - $this->clearStackToTableContext($clear); - - /* Act as if an end tag with the same tag name as the current - node ("tbody", "tfoot", or "thead") had been seen, then - reprocess the current token. */ - $this->inTableBody(array( - 'name' => end($this->stack)->nodeName, - 'type' => HTML5::ENDTAG - )); - - return $this->mainPhase($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->inTable($token); - } - } - - private function inRow($token) { - $clear = array('tr', 'html'); - - /* A start tag whose tag name is one of: "th", "td" */ - if($token['type'] === HTML5::STARTTAG && - ($token['name'] === 'th' || $token['name'] === 'td')) { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Insert an HTML element for the token, then switch the insertion - mode to "in cell". */ - $this->insertElement($token); - $this->mode = self::IN_CELL; - - /* Insert a marker at the end of the list of active formatting - elements. */ - $this->a_formatting[] = self::MARKER; - - /* An end tag whose tag name is "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'tr') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Clear the stack back to a table row context. */ - $this->clearStackToTableContext($clear); - - /* Pop the current node (which will be a tr element) from the - stack of open elements. Switch the insertion mode to "in table - body". */ - array_pop($this->stack); - $this->mode = self::IN_TBODY; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "tfoot", "thead", "tr" or an end tag whose tag name is "table" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'tfoot', 'thead', 'tr'))) { - /* Act as if an end tag with the tag name "tr" had been seen, then, - if that token wasn't ignored, reprocess the current token. */ - $this->inRow(array( - 'name' => 'tr', - 'type' => HTML5::ENDTAG - )); - - return $this->inCell($token); - - /* An end tag whose tag name is one of: "tbody", "tfoot", "thead" */ - } elseif($token['type'] === HTML5::ENDTAG && - in_array($token['name'], array('tbody', 'tfoot', 'thead'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Otherwise, act as if an end tag with the tag name "tr" had - been seen, then reprocess the current token. */ - $this->inRow(array( - 'name' => 'tr', - 'type' => HTML5::ENDTAG - )); - - return $this->inCell($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html", "td", "th" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html', 'td', 'th', 'tr'))) { - /* Parse error. Ignore the token. */ - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in table". */ - $this->inTable($token); - } - } - - private function inCell($token) { - /* An end tag whose tag name is one of: "td", "th" */ - if($token['type'] === HTML5::ENDTAG && - ($token['name'] === 'td' || $token['name'] === 'th')) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as that of the token, then this is a - parse error and the token must be ignored. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise: */ - } else { - /* Generate implied end tags, except for elements with the same - tag name as the token. */ - $this->generateImpliedEndTags(array($token['name'])); - - /* Now, if the current node is not an element with the same tag - name as the token, then this is a parse error. */ - // k - - /* Pop elements from this stack until an element with the same - tag name as the token has been popped from the stack. */ - while(true) { - $node = end($this->stack)->nodeName; - array_pop($this->stack); - - if($node === $token['name']) { - break; - } - } - - /* Clear the list of active formatting elements up to the last - marker. */ - $this->clearTheActiveFormattingElementsUpToTheLastMarker(); - - /* Switch the insertion mode to "in row". (The current node - will be a tr element at this point.) */ - $this->mode = self::IN_ROW; - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (innerHTML case) */ - if(!$this->elementInScope(array('td', 'th'), true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* A start tag whose tag name is one of: "caption", "col", "colgroup", - "tbody", "td", "tfoot", "th", "thead", "tr" */ - } elseif($token['type'] === HTML5::STARTTAG && in_array($token['name'], - array('caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', - 'thead', 'tr'))) { - /* If the stack of open elements does not have a td or th element - in table scope, then this is a parse error; ignore the token. - (innerHTML case) */ - if(!$this->elementInScope(array('td', 'th'), true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* An end tag whose tag name is one of: "body", "caption", "col", - "colgroup", "html" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('body', 'caption', 'col', 'colgroup', 'html'))) { - /* Parse error. Ignore the token. */ - - /* An end tag whose tag name is one of: "table", "tbody", "tfoot", - "thead", "tr" */ - } elseif($token['type'] === HTML5::ENDTAG && in_array($token['name'], - array('table', 'tbody', 'tfoot', 'thead', 'tr'))) { - /* If the stack of open elements does not have an element in table - scope with the same tag name as that of the token (which can only - happen for "tbody", "tfoot" and "thead", or, in the innerHTML case), - then this is a parse error and the token must be ignored. */ - if(!$this->elementInScope($token['name'], true)) { - // Ignore. - - /* Otherwise, close the cell (see below) and reprocess the current - token. */ - } else { - $this->closeCell(); - return $this->inRow($token); - } - - /* Anything else */ - } else { - /* Process the token as if the insertion mode was "in body". */ - $this->inBody($token); - } - } - - private function inSelect($token) { - /* Handle the token as follows: */ - - /* A character token */ - if($token['type'] === HTML5::CHARACTR) { - /* Append the token's character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag token whose tag name is "option" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'option') { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if(end($this->stack)->nodeName === 'option') { - $this->inSelect(array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* A start tag token whose tag name is "optgroup" */ - } elseif($token['type'] === HTML5::STARTTAG && - $token['name'] === 'optgroup') { - /* If the current node is an option element, act as if an end tag - with the tag name "option" had been seen. */ - if(end($this->stack)->nodeName === 'option') { - $this->inSelect(array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - )); - } - - /* If the current node is an optgroup element, act as if an end tag - with the tag name "optgroup" had been seen. */ - if(end($this->stack)->nodeName === 'optgroup') { - $this->inSelect(array( - 'name' => 'optgroup', - 'type' => HTML5::ENDTAG - )); - } - - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* An end tag token whose tag name is "optgroup" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'optgroup') { - /* First, if the current node is an option element, and the node - immediately before it in the stack of open elements is an optgroup - element, then act as if an end tag with the tag name "option" had - been seen. */ - $elements_in_stack = count($this->stack); - - if($this->stack[$elements_in_stack - 1]->nodeName === 'option' && - $this->stack[$elements_in_stack - 2]->nodeName === 'optgroup') { - $this->inSelect(array( - 'name' => 'option', - 'type' => HTML5::ENDTAG - )); - } - - /* If the current node is an optgroup element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if($this->stack[$elements_in_stack - 1] === 'optgroup') { - array_pop($this->stack); - } - - /* An end tag token whose tag name is "option" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'option') { - /* If the current node is an option element, then pop that node - from the stack of open elements. Otherwise, this is a parse error, - ignore the token. */ - if(end($this->stack)->nodeName === 'option') { - array_pop($this->stack); - } - - /* An end tag whose tag name is "select" */ - } elseif($token['type'] === HTML5::ENDTAG && - $token['name'] === 'select') { - /* If the stack of open elements does not have an element in table - scope with the same tag name as the token, this is a parse error. - Ignore the token. (innerHTML case) */ - if(!$this->elementInScope($token['name'], true)) { - // w/e - - /* Otherwise: */ - } else { - /* Pop elements from the stack of open elements until a select - element has been popped from the stack. */ - while(true) { - $current = end($this->stack)->nodeName; - array_pop($this->stack); - - if($current === 'select') { - break; - } - } - - /* Reset the insertion mode appropriately. */ - $this->resetInsertionMode(); - } - - /* A start tag whose tag name is "select" */ - } elseif($token['name'] === 'select' && - $token['type'] === HTML5::STARTTAG) { - /* Parse error. Act as if the token had been an end tag with the - tag name "select" instead. */ - $this->inSelect(array( - 'name' => 'select', - 'type' => HTML5::ENDTAG - )); - - /* An end tag whose tag name is one of: "caption", "table", "tbody", - "tfoot", "thead", "tr", "td", "th" */ - } elseif(in_array($token['name'], array('caption', 'table', 'tbody', - 'tfoot', 'thead', 'tr', 'td', 'th')) && $token['type'] === HTML5::ENDTAG) { - /* Parse error. */ - // w/e - - /* If the stack of open elements has an element in table scope with - the same tag name as that of the token, then act as if an end tag - with the tag name "select" had been seen, and reprocess the token. - Otherwise, ignore the token. */ - if($this->elementInScope($token['name'], true)) { - $this->inSelect(array( - 'name' => 'select', - 'type' => HTML5::ENDTAG - )); - - $this->mainPhase($token); - } - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function afterBody($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Process the token as it would be processed if the insertion mode - was "in body". */ - $this->inBody($token); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the first element in the stack of open - elements (the html element), with the data attribute set to the - data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->stack[0]->appendChild($comment); - - /* An end tag with the tag name "html" */ - } elseif($token['type'] === HTML5::ENDTAG && $token['name'] === 'html') { - /* If the parser was originally created in order to handle the - setting of an element's innerHTML attribute, this is a parse error; - ignore the token. (The element will be an html element in this - case.) (innerHTML case) */ - - /* Otherwise, switch to the trailing end phase. */ - $this->phase = self::END_PHASE; - - /* Anything else */ - } else { - /* Parse error. Set the insertion mode to "in body" and reprocess - the token. */ - $this->mode = self::IN_BODY; - return $this->inBody($token); - } - } - - private function inFrameset($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* A start tag with the tag name "frameset" */ - } elseif($token['name'] === 'frameset' && - $token['type'] === HTML5::STARTTAG) { - $this->insertElement($token); - - /* An end tag with the tag name "frameset" */ - } elseif($token['name'] === 'frameset' && - $token['type'] === HTML5::ENDTAG) { - /* If the current node is the root html element, then this is a - parse error; ignore the token. (innerHTML case) */ - if(end($this->stack)->nodeName === 'html') { - // Ignore - - } else { - /* Otherwise, pop the current node from the stack of open - elements. */ - array_pop($this->stack); - - /* If the parser was not originally created in order to handle - the setting of an element's innerHTML attribute (innerHTML case), - and the current node is no longer a frameset element, then change - the insertion mode to "after frameset". */ - $this->mode = self::AFTR_FRAME; - } - - /* A start tag with the tag name "frame" */ - } elseif($token['name'] === 'frame' && - $token['type'] === HTML5::STARTTAG) { - /* Insert an HTML element for the token. */ - $this->insertElement($token); - - /* Immediately pop the current node off the stack of open elements. */ - array_pop($this->stack); - - /* A start tag with the tag name "noframes" */ - } elseif($token['name'] === 'noframes' && - $token['type'] === HTML5::STARTTAG) { - /* Process the token as if the insertion mode had been "in body". */ - $this->inBody($token); - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function afterFrameset($token) { - /* Handle the token as follows: */ - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - U+000D CARRIAGE RETURN (CR), or U+0020 SPACE */ - if($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Append the character to the current node. */ - $this->insertText($token['data']); - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the current node with the data - attribute set to the data given in the comment token. */ - $this->insertComment($token['data']); - - /* An end tag with the tag name "html" */ - } elseif($token['name'] === 'html' && - $token['type'] === HTML5::ENDTAG) { - /* Switch to the trailing end phase. */ - $this->phase = self::END_PHASE; - - /* A start tag with the tag name "noframes" */ - } elseif($token['name'] === 'noframes' && - $token['type'] === HTML5::STARTTAG) { - /* Process the token as if the insertion mode had been "in body". */ - $this->inBody($token); - - /* Anything else */ - } else { - /* Parse error. Ignore the token. */ - } - } - - private function trailingEndPhase($token) { - /* After the main phase, as each token is emitted from the tokenisation - stage, it must be processed as described in this section. */ - - /* A DOCTYPE token */ - if($token['type'] === HTML5::DOCTYPE) { - // Parse error. Ignore the token. - - /* A comment token */ - } elseif($token['type'] === HTML5::COMMENT) { - /* Append a Comment node to the Document object with the data - attribute set to the data given in the comment token. */ - $comment = $this->dom->createComment($token['data']); - $this->dom->appendChild($comment); - - /* A character token that is one of one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE */ - } elseif($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) { - /* Process the token as it would be processed in the main phase. */ - $this->mainPhase($token); - - /* A character token that is not one of U+0009 CHARACTER TABULATION, - U+000A LINE FEED (LF), U+000B LINE TABULATION, U+000C FORM FEED (FF), - or U+0020 SPACE. Or a start tag token. Or an end tag token. */ - } elseif(($token['type'] === HTML5::CHARACTR && - preg_match('/^[\t\n\x0b\x0c ]+$/', $token['data'])) || - $token['type'] === HTML5::STARTTAG || $token['type'] === HTML5::ENDTAG) { - /* Parse error. Switch back to the main phase and reprocess the - token. */ - $this->phase = self::MAIN_PHASE; - return $this->mainPhase($token); - - /* An end-of-file token */ - } elseif($token['type'] === HTML5::EOF) { - /* OMG DONE!! */ - } - } - - private function insertElement($token, $append = true, $check = false) { - // Proprietary workaround for libxml2's limitations with tag names - if ($check) { - // Slightly modified HTML5 tag-name modification, - // removing anything that's not an ASCII letter, digit, or hyphen - $token['name'] = preg_replace('/[^a-z0-9-]/i', '', $token['name']); - // Remove leading hyphens and numbers - $token['name'] = ltrim($token['name'], '-0..9'); - // In theory, this should ever be needed, but just in case - if ($token['name'] === '') $token['name'] = 'span'; // arbitrary generic choice - } - - $el = $this->dom->createElement($token['name']); - - foreach($token['attr'] as $attr) { - if(!$el->hasAttribute($attr['name'])) { - $el->setAttribute($attr['name'], $attr['value']); - } - } - - $this->appendToRealParent($el); - $this->stack[] = $el; - - return $el; - } - - private function insertText($data) { - $text = $this->dom->createTextNode($data); - $this->appendToRealParent($text); - } - - private function insertComment($data) { - $comment = $this->dom->createComment($data); - $this->appendToRealParent($comment); - } - - private function appendToRealParent($node) { - if($this->foster_parent === null) { - end($this->stack)->appendChild($node); - - } elseif($this->foster_parent !== null) { - /* If the foster parent element is the parent element of the - last table element in the stack of open elements, then the new - node must be inserted immediately before the last table element - in the stack of open elements in the foster parent element; - otherwise, the new node must be appended to the foster parent - element. */ - for($n = count($this->stack) - 1; $n >= 0; $n--) { - if($this->stack[$n]->nodeName === 'table' && - $this->stack[$n]->parentNode !== null) { - $table = $this->stack[$n]; - break; - } - } - - if(isset($table) && $this->foster_parent->isSameNode($table->parentNode)) - $this->foster_parent->insertBefore($node, $table); - else - $this->foster_parent->appendChild($node); - - $this->foster_parent = null; - } - } - - private function elementInScope($el, $table = false) { - if(is_array($el)) { - foreach($el as $element) { - if($this->elementInScope($element, $table)) { - return true; - } - } - - return false; - } - - $leng = count($this->stack); - - for($n = 0; $n < $leng; $n++) { - /* 1. Initialise node to be the current node (the bottommost node of - the stack). */ - $node = $this->stack[$leng - 1 - $n]; - - if($node->tagName === $el) { - /* 2. If node is the target node, terminate in a match state. */ - return true; - - } elseif($node->tagName === 'table') { - /* 3. Otherwise, if node is a table element, terminate in a failure - state. */ - return false; - - } elseif($table === true && in_array($node->tagName, array('caption', 'td', - 'th', 'button', 'marquee', 'object'))) { - /* 4. Otherwise, if the algorithm is the "has an element in scope" - variant (rather than the "has an element in table scope" variant), - and node is one of the following, terminate in a failure state. */ - return false; - - } elseif($node === $node->ownerDocument->documentElement) { - /* 5. Otherwise, if node is an html element (root element), terminate - in a failure state. (This can only happen if the node is the topmost - node of the stack of open elements, and prevents the next step from - being invoked if there are no more elements in the stack.) */ - return false; - } - - /* Otherwise, set node to the previous entry in the stack of open - elements and return to step 2. (This will never fail, since the loop - will always terminate in the previous step if the top of the stack - is reached.) */ - } - } - - private function reconstructActiveFormattingElements() { - /* 1. If there are no entries in the list of active formatting elements, - then there is nothing to reconstruct; stop this algorithm. */ - $formatting_elements = count($this->a_formatting); - - if($formatting_elements === 0) { - return false; - } - - /* 3. Let entry be the last (most recently added) element in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. If the last (most recently added) entry in the list of active - formatting elements is a marker, or if it is an element that is in the - stack of open elements, then there is nothing to reconstruct; stop this - algorithm. */ - if($entry === self::MARKER || in_array($entry, $this->stack, true)) { - return false; - } - - for($a = $formatting_elements - 1; $a >= 0; true) { - /* 4. If there are no entries before entry in the list of active - formatting elements, then jump to step 8. */ - if($a === 0) { - $step_seven = false; - break; - } - - /* 5. Let entry be the entry one earlier than entry in the list of - active formatting elements. */ - $a--; - $entry = $this->a_formatting[$a]; - - /* 6. If entry is neither a marker nor an element that is also in - thetack of open elements, go to step 4. */ - if($entry === self::MARKER || in_array($entry, $this->stack, true)) { - break; - } - } - - while(true) { - /* 7. Let entry be the element one later than entry in the list of - active formatting elements. */ - if(isset($step_seven) && $step_seven === true) { - $a++; - $entry = $this->a_formatting[$a]; - } - - /* 8. Perform a shallow clone of the element entry to obtain clone. */ - $clone = $entry->cloneNode(); - - /* 9. Append clone to the current node and push it onto the stack - of open elements so that it is the new current node. */ - end($this->stack)->appendChild($clone); - $this->stack[] = $clone; - - /* 10. Replace the entry for entry in the list with an entry for - clone. */ - $this->a_formatting[$a] = $clone; - - /* 11. If the entry for clone in the list of active formatting - elements is not the last entry in the list, return to step 7. */ - if(end($this->a_formatting) !== $clone) { - $step_seven = true; - } else { - break; - } - } - } - - private function clearTheActiveFormattingElementsUpToTheLastMarker() { - /* When the steps below require the UA to clear the list of active - formatting elements up to the last marker, the UA must perform the - following steps: */ - - while(true) { - /* 1. Let entry be the last (most recently added) entry in the list - of active formatting elements. */ - $entry = end($this->a_formatting); - - /* 2. Remove entry from the list of active formatting elements. */ - array_pop($this->a_formatting); - - /* 3. If entry was a marker, then stop the algorithm at this point. - The list has been cleared up to the last marker. */ - if($entry === self::MARKER) { - break; - } - } - } - - private function generateImpliedEndTags($exclude = array()) { - /* When the steps below require the UA to generate implied end tags, - then, if the current node is a dd element, a dt element, an li element, - a p element, a td element, a th element, or a tr element, the UA must - act as if an end tag with the respective tag name had been seen and - then generate implied end tags again. */ - $node = end($this->stack); - $elements = array_diff(array('dd', 'dt', 'li', 'p', 'td', 'th', 'tr'), $exclude); - - while(in_array(end($this->stack)->nodeName, $elements)) { - array_pop($this->stack); - } - } - - private function getElementCategory($node) { - $name = $node->tagName; - if(in_array($name, $this->special)) - return self::SPECIAL; - - elseif(in_array($name, $this->scoping)) - return self::SCOPING; - - elseif(in_array($name, $this->formatting)) - return self::FORMATTING; - - else - return self::PHRASING; - } - - private function clearStackToTableContext($elements) { - /* When the steps above require the UA to clear the stack back to a - table context, it means that the UA must, while the current node is not - a table element or an html element, pop elements from the stack of open - elements. If this causes any elements to be popped from the stack, then - this is a parse error. */ - while(true) { - $node = end($this->stack)->nodeName; - - if(in_array($node, $elements)) { - break; - } else { - array_pop($this->stack); - } - } - } - - private function resetInsertionMode() { - /* 1. Let last be false. */ - $last = false; - $leng = count($this->stack); - - for($n = $leng - 1; $n >= 0; $n--) { - /* 2. Let node be the last node in the stack of open elements. */ - $node = $this->stack[$n]; - - /* 3. If node is the first node in the stack of open elements, then - set last to true. If the element whose innerHTML attribute is being - set is neither a td element nor a th element, then set node to the - element whose innerHTML attribute is being set. (innerHTML case) */ - if($this->stack[0]->isSameNode($node)) { - $last = true; - } - - /* 4. If node is a select element, then switch the insertion mode to - "in select" and abort these steps. (innerHTML case) */ - if($node->nodeName === 'select') { - $this->mode = self::IN_SELECT; - break; - - /* 5. If node is a td or th element, then switch the insertion mode - to "in cell" and abort these steps. */ - } elseif($node->nodeName === 'td' || $node->nodeName === 'th') { - $this->mode = self::IN_CELL; - break; - - /* 6. If node is a tr element, then switch the insertion mode to - "in row" and abort these steps. */ - } elseif($node->nodeName === 'tr') { - $this->mode = self::IN_ROW; - break; - - /* 7. If node is a tbody, thead, or tfoot element, then switch the - insertion mode to "in table body" and abort these steps. */ - } elseif(in_array($node->nodeName, array('tbody', 'thead', 'tfoot'))) { - $this->mode = self::IN_TBODY; - break; - - /* 8. If node is a caption element, then switch the insertion mode - to "in caption" and abort these steps. */ - } elseif($node->nodeName === 'caption') { - $this->mode = self::IN_CAPTION; - break; - - /* 9. If node is a colgroup element, then switch the insertion mode - to "in column group" and abort these steps. (innerHTML case) */ - } elseif($node->nodeName === 'colgroup') { - $this->mode = self::IN_CGROUP; - break; - - /* 10. If node is a table element, then switch the insertion mode - to "in table" and abort these steps. */ - } elseif($node->nodeName === 'table') { - $this->mode = self::IN_TABLE; - break; - - /* 11. If node is a head element, then switch the insertion mode - to "in body" ("in body"! not "in head"!) and abort these steps. - (innerHTML case) */ - } elseif($node->nodeName === 'head') { - $this->mode = self::IN_BODY; - break; - - /* 12. If node is a body element, then switch the insertion mode to - "in body" and abort these steps. */ - } elseif($node->nodeName === 'body') { - $this->mode = self::IN_BODY; - break; - - /* 13. If node is a frameset element, then switch the insertion - mode to "in frameset" and abort these steps. (innerHTML case) */ - } elseif($node->nodeName === 'frameset') { - $this->mode = self::IN_FRAME; - break; - - /* 14. If node is an html element, then: if the head element - pointer is null, switch the insertion mode to "before head", - otherwise, switch the insertion mode to "after head". In either - case, abort these steps. (innerHTML case) */ - } elseif($node->nodeName === 'html') { - $this->mode = ($this->head_pointer === null) - ? self::BEFOR_HEAD - : self::AFTER_HEAD; - - break; - - /* 15. If last is true, then set the insertion mode to "in body" - and abort these steps. (innerHTML case) */ - } elseif($last) { - $this->mode = self::IN_BODY; - break; - } - } - } - - private function closeCell() { - /* If the stack of open elements has a td or th element in table scope, - then act as if an end tag token with that tag name had been seen. */ - foreach(array('td', 'th') as $cell) { - if($this->elementInScope($cell, true)) { - $this->inCell(array( - 'name' => $cell, - 'type' => HTML5::ENDTAG - )); - - break; - } - } - } - - public function save() { - return $this->dom; - } -} -?> diff --git a/extlib/HTMLPurifier/HTMLPurifier/PercentEncoder.php b/extlib/HTMLPurifier/HTMLPurifier/PercentEncoder.php deleted file mode 100644 index a43c44f4c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/PercentEncoder.php +++ /dev/null @@ -1,98 +0,0 @@ -preserve[$i] = true; // digits - for ($i = 65; $i <= 90; $i++) $this->preserve[$i] = true; // upper-case - for ($i = 97; $i <= 122; $i++) $this->preserve[$i] = true; // lower-case - $this->preserve[45] = true; // Dash - - $this->preserve[46] = true; // Period . - $this->preserve[95] = true; // Underscore _ - $this->preserve[126]= true; // Tilde ~ - - // extra letters not to escape - if ($preserve !== false) { - for ($i = 0, $c = strlen($preserve); $i < $c; $i++) { - $this->preserve[ord($preserve[$i])] = true; - } - } - } - - /** - * Our replacement for urlencode, it encodes all non-reserved characters, - * as well as any extra characters that were instructed to be preserved. - * @note - * Assumes that the string has already been normalized, making any - * and all percent escape sequences valid. Percents will not be - * re-escaped, regardless of their status in $preserve - * @param $string String to be encoded - * @return Encoded string. - */ - public function encode($string) { - $ret = ''; - for ($i = 0, $c = strlen($string); $i < $c; $i++) { - if ($string[$i] !== '%' && !isset($this->preserve[$int = ord($string[$i])]) ) { - $ret .= '%' . sprintf('%02X', $int); - } else { - $ret .= $string[$i]; - } - } - return $ret; - } - - /** - * Fix up percent-encoding by decoding unreserved characters and normalizing. - * @warning This function is affected by $preserve, even though the - * usual desired behavior is for this not to preserve those - * characters. Be careful when reusing instances of PercentEncoder! - * @param $string String to normalize - */ - public function normalize($string) { - if ($string == '') return ''; - $parts = explode('%', $string); - $ret = array_shift($parts); - foreach ($parts as $part) { - $length = strlen($part); - if ($length < 2) { - $ret .= '%25' . $part; - continue; - } - $encoding = substr($part, 0, 2); - $text = substr($part, 2); - if (!ctype_xdigit($encoding)) { - $ret .= '%25' . $part; - continue; - } - $int = hexdec($encoding); - if (isset($this->preserve[$int])) { - $ret .= chr($int) . $text; - continue; - } - $encoding = strtoupper($encoding); - $ret .= '%' . $encoding . $text; - } - return $ret; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Printer.php b/extlib/HTMLPurifier/HTMLPurifier/Printer.php deleted file mode 100644 index e7eb82e83..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Printer.php +++ /dev/null @@ -1,176 +0,0 @@ -getAll(); - $context = new HTMLPurifier_Context(); - $this->generator = new HTMLPurifier_Generator($config, $context); - } - - /** - * Main function that renders object or aspect of that object - * @note Parameters vary depending on printer - */ - // function render() {} - - /** - * Returns a start tag - * @param $tag Tag name - * @param $attr Attribute array - */ - protected function start($tag, $attr = array()) { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_Start($tag, $attr ? $attr : array()) - ); - } - - /** - * Returns an end teg - * @param $tag Tag name - */ - protected function end($tag) { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_End($tag) - ); - } - - /** - * Prints a complete element with content inside - * @param $tag Tag name - * @param $contents Element contents - * @param $attr Tag attributes - * @param $escape Bool whether or not to escape contents - */ - protected function element($tag, $contents, $attr = array(), $escape = true) { - return $this->start($tag, $attr) . - ($escape ? $this->escape($contents) : $contents) . - $this->end($tag); - } - - protected function elementEmpty($tag, $attr = array()) { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_Empty($tag, $attr) - ); - } - - protected function text($text) { - return $this->generator->generateFromToken( - new HTMLPurifier_Token_Text($text) - ); - } - - /** - * Prints a simple key/value row in a table. - * @param $name Key - * @param $value Value - */ - protected function row($name, $value) { - if (is_bool($value)) $value = $value ? 'On' : 'Off'; - return - $this->start('tr') . "\n" . - $this->element('th', $name) . "\n" . - $this->element('td', $value) . "\n" . - $this->end('tr') - ; - } - - /** - * Escapes a string for HTML output. - * @param $string String to escape - */ - protected function escape($string) { - $string = HTMLPurifier_Encoder::cleanUTF8($string); - $string = htmlspecialchars($string, ENT_COMPAT, 'UTF-8'); - return $string; - } - - /** - * Takes a list of strings and turns them into a single list - * @param $array List of strings - * @param $polite Bool whether or not to add an end before the last - */ - protected function listify($array, $polite = false) { - if (empty($array)) return 'None'; - $ret = ''; - $i = count($array); - foreach ($array as $value) { - $i--; - $ret .= $value; - if ($i > 0 && !($polite && $i == 1)) $ret .= ', '; - if ($polite && $i == 1) $ret .= 'and '; - } - return $ret; - } - - /** - * Retrieves the class of an object without prefixes, as well as metadata - * @param $obj Object to determine class of - * @param $prefix Further prefix to remove - */ - protected function getClass($obj, $sec_prefix = '') { - static $five = null; - if ($five === null) $five = version_compare(PHP_VERSION, '5', '>='); - $prefix = 'HTMLPurifier_' . $sec_prefix; - if (!$five) $prefix = strtolower($prefix); - $class = str_replace($prefix, '', get_class($obj)); - $lclass = strtolower($class); - $class .= '('; - switch ($lclass) { - case 'enum': - $values = array(); - foreach ($obj->valid_values as $value => $bool) { - $values[] = $value; - } - $class .= implode(', ', $values); - break; - case 'css_composite': - $values = array(); - foreach ($obj->defs as $def) { - $values[] = $this->getClass($def, $sec_prefix); - } - $class .= implode(', ', $values); - break; - case 'css_multiple': - $class .= $this->getClass($obj->single, $sec_prefix) . ', '; - $class .= $obj->max; - break; - case 'css_denyelementdecorator': - $class .= $this->getClass($obj->def, $sec_prefix) . ', '; - $class .= $obj->element; - break; - case 'css_importantdecorator': - $class .= $this->getClass($obj->def, $sec_prefix); - if ($obj->allow) $class .= ', !important'; - break; - } - $class .= ')'; - return $class; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php b/extlib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php deleted file mode 100644 index 81f986590..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Printer/CSSDefinition.php +++ /dev/null @@ -1,38 +0,0 @@ -def = $config->getCSSDefinition(); - $ret = ''; - - $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); - $ret .= $this->start('table'); - - $ret .= $this->element('caption', 'Properties ($info)'); - - $ret .= $this->start('thead'); - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Property', array('class' => 'heavy')); - $ret .= $this->element('th', 'Definition', array('class' => 'heavy', 'style' => 'width:auto;')); - $ret .= $this->end('tr'); - $ret .= $this->end('thead'); - - ksort($this->def->info); - foreach ($this->def->info as $property => $obj) { - $name = $this->getClass($obj, 'AttrDef_'); - $ret .= $this->row($property, $name); - } - - $ret .= $this->end('table'); - $ret .= $this->end('div'); - - return $ret; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css b/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css deleted file mode 100644 index 3ff1a88aa..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.css +++ /dev/null @@ -1,10 +0,0 @@ - -.hp-config {} - -.hp-config tbody th {text-align:right; padding-right:0.5em;} -.hp-config thead, .hp-config .namespace {background:#3C578C; color:#FFF;} -.hp-config .namespace th {text-align:center;} -.hp-config .verbose {display:none;} -.hp-config .controls {text-align:center;} - -/* vim: et sw=4 sts=4 */ diff --git a/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js b/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js deleted file mode 100644 index cba00c9b8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.js +++ /dev/null @@ -1,5 +0,0 @@ -function toggleWriteability(id_of_patient, checked) { - document.getElementById(id_of_patient).disabled = checked; -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php b/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php deleted file mode 100644 index 02aa65689..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Printer/ConfigForm.php +++ /dev/null @@ -1,368 +0,0 @@ -docURL = $doc_url; - $this->name = $name; - $this->compress = $compress; - // initialize sub-printers - $this->fields[0] = new HTMLPurifier_Printer_ConfigForm_default(); - $this->fields[HTMLPurifier_VarParser::BOOL] = new HTMLPurifier_Printer_ConfigForm_bool(); - } - - /** - * Sets default column and row size for textareas in sub-printers - * @param $cols Integer columns of textarea, null to use default - * @param $rows Integer rows of textarea, null to use default - */ - public function setTextareaDimensions($cols = null, $rows = null) { - if ($cols) $this->fields['default']->cols = $cols; - if ($rows) $this->fields['default']->rows = $rows; - } - - /** - * Retrieves styling, in case it is not accessible by webserver - */ - public static function getCSS() { - return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.css'); - } - - /** - * Retrieves JavaScript, in case it is not accessible by webserver - */ - public static function getJavaScript() { - return file_get_contents(HTMLPURIFIER_PREFIX . '/HTMLPurifier/Printer/ConfigForm.js'); - } - - /** - * Returns HTML output for a configuration form - * @param $config Configuration object of current form state, or an array - * where [0] has an HTML namespace and [1] is being rendered. - * @param $allowed Optional namespace(s) and directives to restrict form to. - */ - public function render($config, $allowed = true, $render_controls = true) { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - - $this->config = $config; - $this->genConfig = $gen_config; - $this->prepareGenerator($gen_config); - - $allowed = HTMLPurifier_Config::getAllowedDirectivesForForm($allowed, $config->def); - $all = array(); - foreach ($allowed as $key) { - list($ns, $directive) = $key; - $all[$ns][$directive] = $config->get($ns .'.'. $directive); - } - - $ret = ''; - $ret .= $this->start('table', array('class' => 'hp-config')); - $ret .= $this->start('thead'); - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Directive', array('class' => 'hp-directive')); - $ret .= $this->element('th', 'Value', array('class' => 'hp-value')); - $ret .= $this->end('tr'); - $ret .= $this->end('thead'); - foreach ($all as $ns => $directives) { - $ret .= $this->renderNamespace($ns, $directives); - } - if ($render_controls) { - $ret .= $this->start('tbody'); - $ret .= $this->start('tr'); - $ret .= $this->start('td', array('colspan' => 2, 'class' => 'controls')); - $ret .= $this->elementEmpty('input', array('type' => 'submit', 'value' => 'Submit')); - $ret .= '[Reset]'; - $ret .= $this->end('td'); - $ret .= $this->end('tr'); - $ret .= $this->end('tbody'); - } - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders a single namespace - * @param $ns String namespace name - * @param $directive Associative array of directives to values - */ - protected function renderNamespace($ns, $directives) { - $ret = ''; - $ret .= $this->start('tbody', array('class' => 'namespace')); - $ret .= $this->start('tr'); - $ret .= $this->element('th', $ns, array('colspan' => 2)); - $ret .= $this->end('tr'); - $ret .= $this->end('tbody'); - $ret .= $this->start('tbody'); - foreach ($directives as $directive => $value) { - $ret .= $this->start('tr'); - $ret .= $this->start('th'); - if ($this->docURL) { - $url = str_replace('%s', urlencode("$ns.$directive"), $this->docURL); - $ret .= $this->start('a', array('href' => $url)); - } - $attr = array('for' => "{$this->name}:$ns.$directive"); - - // crop directive name if it's too long - if (!$this->compress || (strlen($directive) < $this->compress)) { - $directive_disp = $directive; - } else { - $directive_disp = substr($directive, 0, $this->compress - 2) . '...'; - $attr['title'] = $directive; - } - - $ret .= $this->element( - 'label', - $directive_disp, - // component printers must create an element with this id - $attr - ); - if ($this->docURL) $ret .= $this->end('a'); - $ret .= $this->end('th'); - - $ret .= $this->start('td'); - $def = $this->config->def->info["$ns.$directive"]; - if (is_int($def)) { - $allow_null = $def < 0; - $type = abs($def); - } else { - $type = $def->type; - $allow_null = isset($def->allow_null); - } - if (!isset($this->fields[$type])) $type = 0; // default - $type_obj = $this->fields[$type]; - if ($allow_null) { - $type_obj = new HTMLPurifier_Printer_ConfigForm_NullDecorator($type_obj); - } - $ret .= $type_obj->render($ns, $directive, $value, $this->name, array($this->genConfig, $this->config)); - $ret .= $this->end('td'); - $ret .= $this->end('tr'); - } - $ret .= $this->end('tbody'); - return $ret; - } - -} - -/** - * Printer decorator for directives that accept null - */ -class HTMLPurifier_Printer_ConfigForm_NullDecorator extends HTMLPurifier_Printer { - /** - * Printer being decorated - */ - protected $obj; - /** - * @param $obj Printer to decorate - */ - public function __construct($obj) { - parent::__construct(); - $this->obj = $obj; - } - public function render($ns, $directive, $value, $name, $config) { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - $this->prepareGenerator($gen_config); - - $ret = ''; - $ret .= $this->start('label', array('for' => "$name:Null_$ns.$directive")); - $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); - $ret .= $this->text(' Null/Disabled'); - $ret .= $this->end('label'); - $attr = array( - 'type' => 'checkbox', - 'value' => '1', - 'class' => 'null-toggle', - 'name' => "$name"."[Null_$ns.$directive]", - 'id' => "$name:Null_$ns.$directive", - 'onclick' => "toggleWriteability('$name:$ns.$directive',checked)" // INLINE JAVASCRIPT!!!! - ); - if ($this->obj instanceof HTMLPurifier_Printer_ConfigForm_bool) { - // modify inline javascript slightly - $attr['onclick'] = "toggleWriteability('$name:Yes_$ns.$directive',checked);toggleWriteability('$name:No_$ns.$directive',checked)"; - } - if ($value === null) $attr['checked'] = 'checked'; - $ret .= $this->elementEmpty('input', $attr); - $ret .= $this->text(' or '); - $ret .= $this->elementEmpty('br'); - $ret .= $this->obj->render($ns, $directive, $value, $name, array($gen_config, $config)); - return $ret; - } -} - -/** - * Swiss-army knife configuration form field printer - */ -class HTMLPurifier_Printer_ConfigForm_default extends HTMLPurifier_Printer { - public $cols = 18; - public $rows = 5; - public function render($ns, $directive, $value, $name, $config) { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - $this->prepareGenerator($gen_config); - // this should probably be split up a little - $ret = ''; - $def = $config->def->info["$ns.$directive"]; - if (is_int($def)) { - $type = abs($def); - } else { - $type = $def->type; - } - if (is_array($value)) { - switch ($type) { - case HTMLPurifier_VarParser::LOOKUP: - $array = $value; - $value = array(); - foreach ($array as $val => $b) { - $value[] = $val; - } - case HTMLPurifier_VarParser::ALIST: - $value = implode(PHP_EOL, $value); - break; - case HTMLPurifier_VarParser::HASH: - $nvalue = ''; - foreach ($value as $i => $v) { - $nvalue .= "$i:$v" . PHP_EOL; - } - $value = $nvalue; - break; - default: - $value = ''; - } - } - if ($type === HTMLPurifier_VarParser::MIXED) { - return 'Not supported'; - $value = serialize($value); - } - $attr = array( - 'name' => "$name"."[$ns.$directive]", - 'id' => "$name:$ns.$directive" - ); - if ($value === null) $attr['disabled'] = 'disabled'; - if (isset($def->allowed)) { - $ret .= $this->start('select', $attr); - foreach ($def->allowed as $val => $b) { - $attr = array(); - if ($value == $val) $attr['selected'] = 'selected'; - $ret .= $this->element('option', $val, $attr); - } - $ret .= $this->end('select'); - } elseif ( - $type === HTMLPurifier_VarParser::TEXT || - $type === HTMLPurifier_VarParser::ITEXT || - $type === HTMLPurifier_VarParser::ALIST || - $type === HTMLPurifier_VarParser::HASH || - $type === HTMLPurifier_VarParser::LOOKUP - ) { - $attr['cols'] = $this->cols; - $attr['rows'] = $this->rows; - $ret .= $this->start('textarea', $attr); - $ret .= $this->text($value); - $ret .= $this->end('textarea'); - } else { - $attr['value'] = $value; - $attr['type'] = 'text'; - $ret .= $this->elementEmpty('input', $attr); - } - return $ret; - } -} - -/** - * Bool form field printer - */ -class HTMLPurifier_Printer_ConfigForm_bool extends HTMLPurifier_Printer { - public function render($ns, $directive, $value, $name, $config) { - if (is_array($config) && isset($config[0])) { - $gen_config = $config[0]; - $config = $config[1]; - } else { - $gen_config = $config; - } - $this->prepareGenerator($gen_config); - $ret = ''; - $ret .= $this->start('div', array('id' => "$name:$ns.$directive")); - - $ret .= $this->start('label', array('for' => "$name:Yes_$ns.$directive")); - $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); - $ret .= $this->text(' Yes'); - $ret .= $this->end('label'); - - $attr = array( - 'type' => 'radio', - 'name' => "$name"."[$ns.$directive]", - 'id' => "$name:Yes_$ns.$directive", - 'value' => '1' - ); - if ($value === true) $attr['checked'] = 'checked'; - if ($value === null) $attr['disabled'] = 'disabled'; - $ret .= $this->elementEmpty('input', $attr); - - $ret .= $this->start('label', array('for' => "$name:No_$ns.$directive")); - $ret .= $this->element('span', "$ns.$directive:", array('class' => 'verbose')); - $ret .= $this->text(' No'); - $ret .= $this->end('label'); - - $attr = array( - 'type' => 'radio', - 'name' => "$name"."[$ns.$directive]", - 'id' => "$name:No_$ns.$directive", - 'value' => '0' - ); - if ($value === false) $attr['checked'] = 'checked'; - if ($value === null) $attr['disabled'] = 'disabled'; - $ret .= $this->elementEmpty('input', $attr); - - $ret .= $this->end('div'); - - return $ret; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php b/extlib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php deleted file mode 100644 index 8a8f126b8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Printer/HTMLDefinition.php +++ /dev/null @@ -1,272 +0,0 @@ -config =& $config; - - $this->def = $config->getHTMLDefinition(); - - $ret .= $this->start('div', array('class' => 'HTMLPurifier_Printer')); - - $ret .= $this->renderDoctype(); - $ret .= $this->renderEnvironment(); - $ret .= $this->renderContentSets(); - $ret .= $this->renderInfo(); - - $ret .= $this->end('div'); - - return $ret; - } - - /** - * Renders the Doctype table - */ - protected function renderDoctype() { - $doctype = $this->def->doctype; - $ret = ''; - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Doctype'); - $ret .= $this->row('Name', $doctype->name); - $ret .= $this->row('XML', $doctype->xml ? 'Yes' : 'No'); - $ret .= $this->row('Default Modules', implode($doctype->modules, ', ')); - $ret .= $this->row('Default Tidy Modules', implode($doctype->tidyModules, ', ')); - $ret .= $this->end('table'); - return $ret; - } - - - /** - * Renders environment table, which is miscellaneous info - */ - protected function renderEnvironment() { - $def = $this->def; - - $ret = ''; - - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Environment'); - - $ret .= $this->row('Parent of fragment', $def->info_parent); - $ret .= $this->renderChildren($def->info_parent_def->child); - $ret .= $this->row('Block wrap name', $def->info_block_wrapper); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Global attributes'); - $ret .= $this->element('td', $this->listifyAttr($def->info_global_attr),0,0); - $ret .= $this->end('tr'); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Tag transforms'); - $list = array(); - foreach ($def->info_tag_transform as $old => $new) { - $new = $this->getClass($new, 'TagTransform_'); - $list[] = "<$old> with $new"; - } - $ret .= $this->element('td', $this->listify($list)); - $ret .= $this->end('tr'); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_pre)); - $ret .= $this->end('tr'); - - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->info_attr_transform_post)); - $ret .= $this->end('tr'); - - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders the Content Sets table - */ - protected function renderContentSets() { - $ret = ''; - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Content Sets'); - foreach ($this->def->info_content_sets as $name => $lookup) { - $ret .= $this->heavyHeader($name); - $ret .= $this->start('tr'); - $ret .= $this->element('td', $this->listifyTagLookup($lookup)); - $ret .= $this->end('tr'); - } - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders the Elements ($info) table - */ - protected function renderInfo() { - $ret = ''; - $ret .= $this->start('table'); - $ret .= $this->element('caption', 'Elements ($info)'); - ksort($this->def->info); - $ret .= $this->heavyHeader('Allowed tags', 2); - $ret .= $this->start('tr'); - $ret .= $this->element('td', $this->listifyTagLookup($this->def->info), array('colspan' => 2)); - $ret .= $this->end('tr'); - foreach ($this->def->info as $name => $def) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', "<$name>", array('class'=>'heavy', 'colspan' => 2)); - $ret .= $this->end('tr'); - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Inline content'); - $ret .= $this->element('td', $def->descendants_are_inline ? 'Yes' : 'No'); - $ret .= $this->end('tr'); - if (!empty($def->excludes)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Excludes'); - $ret .= $this->element('td', $this->listifyTagLookup($def->excludes)); - $ret .= $this->end('tr'); - } - if (!empty($def->attr_transform_pre)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Pre-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_pre)); - $ret .= $this->end('tr'); - } - if (!empty($def->attr_transform_post)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Post-AttrTransform'); - $ret .= $this->element('td', $this->listifyObjectList($def->attr_transform_post)); - $ret .= $this->end('tr'); - } - if (!empty($def->auto_close)) { - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Auto closed by'); - $ret .= $this->element('td', $this->listifyTagLookup($def->auto_close)); - $ret .= $this->end('tr'); - } - $ret .= $this->start('tr'); - $ret .= $this->element('th', 'Allowed attributes'); - $ret .= $this->element('td',$this->listifyAttr($def->attr), array(), 0); - $ret .= $this->end('tr'); - - if (!empty($def->required_attr)) { - $ret .= $this->row('Required attributes', $this->listify($def->required_attr)); - } - - $ret .= $this->renderChildren($def->child); - } - $ret .= $this->end('table'); - return $ret; - } - - /** - * Renders a row describing the allowed children of an element - * @param $def HTMLPurifier_ChildDef of pertinent element - */ - protected function renderChildren($def) { - $context = new HTMLPurifier_Context(); - $ret = ''; - $ret .= $this->start('tr'); - $elements = array(); - $attr = array(); - if (isset($def->elements)) { - if ($def->type == 'strictblockquote') { - $def->validateChildren(array(), $this->config, $context); - } - $elements = $def->elements; - } - if ($def->type == 'chameleon') { - $attr['rowspan'] = 2; - } elseif ($def->type == 'empty') { - $elements = array(); - } elseif ($def->type == 'table') { - $elements = array_flip(array('col', 'caption', 'colgroup', 'thead', - 'tfoot', 'tbody', 'tr')); - } - $ret .= $this->element('th', 'Allowed children', $attr); - - if ($def->type == 'chameleon') { - - $ret .= $this->element('td', - 'Block: ' . - $this->escape($this->listifyTagLookup($def->block->elements)),0,0); - $ret .= $this->end('tr'); - $ret .= $this->start('tr'); - $ret .= $this->element('td', - 'Inline: ' . - $this->escape($this->listifyTagLookup($def->inline->elements)),0,0); - - } elseif ($def->type == 'custom') { - - $ret .= $this->element('td', ''.ucfirst($def->type).': ' . - $def->dtd_regex); - - } else { - $ret .= $this->element('td', - ''.ucfirst($def->type).': ' . - $this->escape($this->listifyTagLookup($elements)),0,0); - } - $ret .= $this->end('tr'); - return $ret; - } - - /** - * Listifies a tag lookup table. - * @param $array Tag lookup array in form of array('tagname' => true) - */ - protected function listifyTagLookup($array) { - ksort($array); - $list = array(); - foreach ($array as $name => $discard) { - if ($name !== '#PCDATA' && !isset($this->def->info[$name])) continue; - $list[] = $name; - } - return $this->listify($list); - } - - /** - * Listifies a list of objects by retrieving class names and internal state - * @param $array List of objects - * @todo Also add information about internal state - */ - protected function listifyObjectList($array) { - ksort($array); - $list = array(); - foreach ($array as $discard => $obj) { - $list[] = $this->getClass($obj, 'AttrTransform_'); - } - return $this->listify($list); - } - - /** - * Listifies a hash of attributes to AttrDef classes - * @param $array Array hash in form of array('attrname' => HTMLPurifier_AttrDef) - */ - protected function listifyAttr($array) { - ksort($array); - $list = array(); - foreach ($array as $name => $obj) { - if ($obj === false) continue; - $list[] = "$name = " . $this->getClass($obj, 'AttrDef_') . ''; - } - return $this->listify($list); - } - - /** - * Creates a heavy header row - */ - protected function heavyHeader($text, $num = 1) { - $ret = ''; - $ret .= $this->start('tr'); - $ret .= $this->element('th', $text, array('colspan' => $num, 'class' => 'heavy')); - $ret .= $this->end('tr'); - return $ret; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/PropertyList.php b/extlib/HTMLPurifier/HTMLPurifier/PropertyList.php deleted file mode 100644 index 2b99fb7bc..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/PropertyList.php +++ /dev/null @@ -1,86 +0,0 @@ -parent = $parent; - } - - /** - * Recursively retrieves the value for a key - */ - public function get($name) { - if ($this->has($name)) return $this->data[$name]; - // possible performance bottleneck, convert to iterative if necessary - if ($this->parent) return $this->parent->get($name); - throw new HTMLPurifier_Exception("Key '$name' not found"); - } - - /** - * Sets the value of a key, for this plist - */ - public function set($name, $value) { - $this->data[$name] = $value; - } - - /** - * Returns true if a given key exists - */ - public function has($name) { - return array_key_exists($name, $this->data); - } - - /** - * Resets a value to the value of it's parent, usually the default. If - * no value is specified, the entire plist is reset. - */ - public function reset($name = null) { - if ($name == null) $this->data = array(); - else unset($this->data[$name]); - } - - /** - * Squashes this property list and all of its property lists into a single - * array, and returns the array. This value is cached by default. - * @param $force If true, ignores the cache and regenerates the array. - */ - public function squash($force = false) { - if ($this->cache !== null && !$force) return $this->cache; - if ($this->parent) { - return $this->cache = array_merge($this->parent->squash($force), $this->data); - } else { - return $this->cache = $this->data; - } - } - - /** - * Returns the parent plist. - */ - public function getParent() { - return $this->parent; - } - - /** - * Sets the parent plist. - */ - public function setParent($plist) { - $this->parent = $plist; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php b/extlib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php deleted file mode 100644 index 8f250443e..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/PropertyListIterator.php +++ /dev/null @@ -1,32 +0,0 @@ -l = strlen($filter); - $this->filter = $filter; - } - - public function accept() { - $key = $this->getInnerIterator()->key(); - if( strncmp($key, $this->filter, $this->l) !== 0 ) { - return false; - } - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Strategy.php b/extlib/HTMLPurifier/HTMLPurifier/Strategy.php deleted file mode 100644 index 246286521..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Strategy.php +++ /dev/null @@ -1,26 +0,0 @@ -strategies as $strategy) { - $tokens = $strategy->execute($tokens, $config, $context); - } - return $tokens; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Strategy/Core.php b/extlib/HTMLPurifier/HTMLPurifier/Strategy/Core.php deleted file mode 100644 index d90e15860..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Strategy/Core.php +++ /dev/null @@ -1,18 +0,0 @@ -strategies[] = new HTMLPurifier_Strategy_RemoveForeignElements(); - $this->strategies[] = new HTMLPurifier_Strategy_MakeWellFormed(); - $this->strategies[] = new HTMLPurifier_Strategy_FixNesting(); - $this->strategies[] = new HTMLPurifier_Strategy_ValidateAttributes(); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php b/extlib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php deleted file mode 100644 index f81802391..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Strategy/FixNesting.php +++ /dev/null @@ -1,328 +0,0 @@ -getHTMLDefinition(); - - // insert implicit "parent" node, will be removed at end. - // DEFINITION CALL - $parent_name = $definition->info_parent; - array_unshift($tokens, new HTMLPurifier_Token_Start($parent_name)); - $tokens[] = new HTMLPurifier_Token_End($parent_name); - - // setup the context variable 'IsInline', for chameleon processing - // is 'false' when we are not inline, 'true' when it must always - // be inline, and an integer when it is inline for a certain - // branch of the document tree - $is_inline = $definition->info_parent_def->descendants_are_inline; - $context->register('IsInline', $is_inline); - - // setup error collector - $e =& $context->get('ErrorCollector', true); - - //####################################################################// - // Loop initialization - - // stack that contains the indexes of all parents, - // $stack[count($stack)-1] being the current parent - $stack = array(); - - // stack that contains all elements that are excluded - // it is organized by parent elements, similar to $stack, - // but it is only populated when an element with exclusions is - // processed, i.e. there won't be empty exclusions. - $exclude_stack = array(); - - // variable that contains the start token while we are processing - // nodes. This enables error reporting to do its job - $start_token = false; - $context->register('CurrentToken', $start_token); - - //####################################################################// - // Loop - - // iterate through all start nodes. Determining the start node - // is complicated so it has been omitted from the loop construct - for ($i = 0, $size = count($tokens) ; $i < $size; ) { - - //################################################################// - // Gather information on children - - // child token accumulator - $child_tokens = array(); - - // scroll to the end of this node, report number, and collect - // all children - for ($j = $i, $depth = 0; ; $j++) { - if ($tokens[$j] instanceof HTMLPurifier_Token_Start) { - $depth++; - // skip token assignment on first iteration, this is the - // token we currently are on - if ($depth == 1) continue; - } elseif ($tokens[$j] instanceof HTMLPurifier_Token_End) { - $depth--; - // skip token assignment on last iteration, this is the - // end token of the token we're currently on - if ($depth == 0) break; - } - $child_tokens[] = $tokens[$j]; - } - - // $i is index of start token - // $j is index of end token - - $start_token = $tokens[$i]; // to make token available via CurrentToken - - //################################################################// - // Gather information on parent - - // calculate parent information - if ($count = count($stack)) { - $parent_index = $stack[$count-1]; - $parent_name = $tokens[$parent_index]->name; - if ($parent_index == 0) { - $parent_def = $definition->info_parent_def; - } else { - $parent_def = $definition->info[$parent_name]; - } - } else { - // processing as if the parent were the "root" node - // unknown info, it won't be used anyway, in the future, - // we may want to enforce one element only (this is - // necessary for HTML Purifier to clean entire documents - $parent_index = $parent_name = $parent_def = null; - } - - // calculate context - if ($is_inline === false) { - // check if conditions make it inline - if (!empty($parent_def) && $parent_def->descendants_are_inline) { - $is_inline = $count - 1; - } - } else { - // check if we're out of inline - if ($count === $is_inline) { - $is_inline = false; - } - } - - //################################################################// - // Determine whether element is explicitly excluded SGML-style - - // determine whether or not element is excluded by checking all - // parent exclusions. The array should not be very large, two - // elements at most. - $excluded = false; - if (!empty($exclude_stack)) { - foreach ($exclude_stack as $lookup) { - if (isset($lookup[$tokens[$i]->name])) { - $excluded = true; - // no need to continue processing - break; - } - } - } - - //################################################################// - // Perform child validation - - if ($excluded) { - // there is an exclusion, remove the entire node - $result = false; - $excludes = array(); // not used, but good to initialize anyway - } else { - // DEFINITION CALL - if ($i === 0) { - // special processing for the first node - $def = $definition->info_parent_def; - } else { - $def = $definition->info[$tokens[$i]->name]; - - } - - if (!empty($def->child)) { - // have DTD child def validate children - $result = $def->child->validateChildren( - $child_tokens, $config, $context); - } else { - // weird, no child definition, get rid of everything - $result = false; - } - - // determine whether or not this element has any exclusions - $excludes = $def->excludes; - } - - // $result is now a bool or array - - //################################################################// - // Process result by interpreting $result - - if ($result === true || $child_tokens === $result) { - // leave the node as is - - // register start token as a parental node start - $stack[] = $i; - - // register exclusions if there are any - if (!empty($excludes)) $exclude_stack[] = $excludes; - - // move cursor to next possible start node - $i++; - - } elseif($result === false) { - // remove entire node - - if ($e) { - if ($excluded) { - $e->send(E_ERROR, 'Strategy_FixNesting: Node excluded'); - } else { - $e->send(E_ERROR, 'Strategy_FixNesting: Node removed'); - } - } - - // calculate length of inner tokens and current tokens - $length = $j - $i + 1; - - // perform removal - array_splice($tokens, $i, $length); - - // update size - $size -= $length; - - // there is no start token to register, - // current node is now the next possible start node - // unless it turns out that we need to do a double-check - - // this is a rought heuristic that covers 100% of HTML's - // cases and 99% of all other cases. A child definition - // that would be tricked by this would be something like: - // ( | a b c) where it's all or nothing. Fortunately, - // our current implementation claims that that case would - // not allow empty, even if it did - if (!$parent_def->child->allow_empty) { - // we need to do a double-check - $i = $parent_index; - array_pop($stack); - } - - // PROJECTED OPTIMIZATION: Process all children elements before - // reprocessing parent node. - - } else { - // replace node with $result - - // calculate length of inner tokens - $length = $j - $i - 1; - - if ($e) { - if (empty($result) && $length) { - $e->send(E_ERROR, 'Strategy_FixNesting: Node contents removed'); - } else { - $e->send(E_WARNING, 'Strategy_FixNesting: Node reorganized'); - } - } - - // perform replacement - array_splice($tokens, $i + 1, $length, $result); - - // update size - $size -= $length; - $size += count($result); - - // register start token as a parental node start - $stack[] = $i; - - // register exclusions if there are any - if (!empty($excludes)) $exclude_stack[] = $excludes; - - // move cursor to next possible start node - $i++; - - } - - //################################################################// - // Scroll to next start node - - // We assume, at this point, that $i is the index of the token - // that is the first possible new start point for a node. - - // Test if the token indeed is a start tag, if not, move forward - // and test again. - $size = count($tokens); - while ($i < $size and !$tokens[$i] instanceof HTMLPurifier_Token_Start) { - if ($tokens[$i] instanceof HTMLPurifier_Token_End) { - // pop a token index off the stack if we ended a node - array_pop($stack); - // pop an exclusion lookup off exclusion stack if - // we ended node and that node had exclusions - if ($i == 0 || $i == $size - 1) { - // use specialized var if it's the super-parent - $s_excludes = $definition->info_parent_def->excludes; - } else { - $s_excludes = $definition->info[$tokens[$i]->name]->excludes; - } - if ($s_excludes) { - array_pop($exclude_stack); - } - } - $i++; - } - - } - - //####################################################################// - // Post-processing - - // remove implicit parent tokens at the beginning and end - array_shift($tokens); - array_pop($tokens); - - // remove context variables - $context->destroy('IsInline'); - $context->destroy('CurrentToken'); - - //####################################################################// - // Return - - return $tokens; - - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php b/extlib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php deleted file mode 100644 index feb0c32b4..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Strategy/MakeWellFormed.php +++ /dev/null @@ -1,457 +0,0 @@ -getHTMLDefinition(); - - // local variables - $generator = new HTMLPurifier_Generator($config, $context); - $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); - $e = $context->get('ErrorCollector', true); - $t = false; // token index - $i = false; // injector index - $token = false; // the current token - $reprocess = false; // whether or not to reprocess the same token - $stack = array(); - - // member variables - $this->stack =& $stack; - $this->t =& $t; - $this->tokens =& $tokens; - $this->config = $config; - $this->context = $context; - - // context variables - $context->register('CurrentNesting', $stack); - $context->register('InputIndex', $t); - $context->register('InputTokens', $tokens); - $context->register('CurrentToken', $token); - - // -- begin INJECTOR -- - - $this->injectors = array(); - - $injectors = $config->getBatch('AutoFormat'); - $def_injectors = $definition->info_injector; - $custom_injectors = $injectors['Custom']; - unset($injectors['Custom']); // special case - foreach ($injectors as $injector => $b) { - // XXX: Fix with a legitimate lookup table of enabled filters - if (strpos($injector, '.') !== false) continue; - $injector = "HTMLPurifier_Injector_$injector"; - if (!$b) continue; - $this->injectors[] = new $injector; - } - foreach ($def_injectors as $injector) { - // assumed to be objects - $this->injectors[] = $injector; - } - foreach ($custom_injectors as $injector) { - if (is_string($injector)) { - $injector = "HTMLPurifier_Injector_$injector"; - $injector = new $injector; - } - $this->injectors[] = $injector; - } - - // give the injectors references to the definition and context - // variables for performance reasons - foreach ($this->injectors as $ix => $injector) { - $error = $injector->prepare($config, $context); - if (!$error) continue; - array_splice($this->injectors, $ix, 1); // rm the injector - trigger_error("Cannot enable {$injector->name} injector because $error is not allowed", E_USER_WARNING); - } - - // -- end INJECTOR -- - - // a note on punting: - // In order to reduce code duplication, whenever some code needs - // to make HTML changes in order to make things "correct", the - // new HTML gets sent through the purifier, regardless of its - // status. This means that if we add a start token, because it - // was totally necessary, we don't have to update nesting; we just - // punt ($reprocess = true; continue;) and it does that for us. - - // isset is in loop because $tokens size changes during loop exec - for ( - $t = 0; - $t == 0 || isset($tokens[$t - 1]); - // only increment if we don't need to reprocess - $reprocess ? $reprocess = false : $t++ - ) { - - // check for a rewind - if (is_int($i) && $i >= 0) { - // possibility: disable rewinding if the current token has a - // rewind set on it already. This would offer protection from - // infinite loop, but might hinder some advanced rewinding. - $rewind_to = $this->injectors[$i]->getRewind(); - if (is_int($rewind_to) && $rewind_to < $t) { - if ($rewind_to < 0) $rewind_to = 0; - while ($t > $rewind_to) { - $t--; - $prev = $tokens[$t]; - // indicate that other injectors should not process this token, - // but we need to reprocess it - unset($prev->skip[$i]); - $prev->rewind = $i; - if ($prev instanceof HTMLPurifier_Token_Start) array_pop($this->stack); - elseif ($prev instanceof HTMLPurifier_Token_End) $this->stack[] = $prev->start; - } - } - $i = false; - } - - // handle case of document end - if (!isset($tokens[$t])) { - // kill processing if stack is empty - if (empty($this->stack)) break; - - // peek - $top_nesting = array_pop($this->stack); - $this->stack[] = $top_nesting; - - // send error - if ($e && !isset($top_nesting->armor['MakeWellFormed_TagClosedError'])) { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by document end', $top_nesting); - } - - // append, don't splice, since this is the end - $tokens[] = new HTMLPurifier_Token_End($top_nesting->name); - - // punt! - $reprocess = true; - continue; - } - - $token = $tokens[$t]; - - //echo '
    '; printTokens($tokens, $t); printTokens($this->stack); - - // quick-check: if it's not a tag, no need to process - if (empty($token->is_tag)) { - if ($token instanceof HTMLPurifier_Token_Text) { - foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleText($token); - $this->processToken($token, $i); - $reprocess = true; - break; - } - } - // another possibility is a comment - continue; - } - - if (isset($definition->info[$token->name])) { - $type = $definition->info[$token->name]->child->type; - } else { - $type = false; // Type is unknown, treat accordingly - } - - // quick tag checks: anything that's *not* an end tag - $ok = false; - if ($type === 'empty' && $token instanceof HTMLPurifier_Token_Start) { - // claims to be a start tag but is empty - $token = new HTMLPurifier_Token_Empty($token->name, $token->attr); - $ok = true; - } elseif ($type && $type !== 'empty' && $token instanceof HTMLPurifier_Token_Empty) { - // claims to be empty but really is a start tag - $this->swap(new HTMLPurifier_Token_End($token->name)); - $this->insertBefore(new HTMLPurifier_Token_Start($token->name, $token->attr)); - // punt (since we had to modify the input stream in a non-trivial way) - $reprocess = true; - continue; - } elseif ($token instanceof HTMLPurifier_Token_Empty) { - // real empty token - $ok = true; - } elseif ($token instanceof HTMLPurifier_Token_Start) { - // start tag - - // ...unless they also have to close their parent - if (!empty($this->stack)) { - - $parent = array_pop($this->stack); - $this->stack[] = $parent; - - if (isset($definition->info[$parent->name])) { - $elements = $definition->info[$parent->name]->child->getAllowedElements($config); - $autoclose = !isset($elements[$token->name]); - } else { - $autoclose = false; - } - - $carryover = false; - if ($autoclose && $definition->info[$parent->name]->formatting) { - $carryover = true; - } - - if ($autoclose) { - // errors need to be updated - $new_token = new HTMLPurifier_Token_End($parent->name); - $new_token->start = $parent; - if ($carryover) { - $element = clone $parent; - $element->armor['MakeWellFormed_TagClosedError'] = true; - $element->carryover = true; - $this->processToken(array($new_token, $token, $element)); - } else { - $this->insertBefore($new_token); - } - if ($e && !isset($parent->armor['MakeWellFormed_TagClosedError'])) { - if (!$carryover) { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag auto closed', $parent); - } else { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag carryover', $parent); - } - } - $reprocess = true; - continue; - } - - } - $ok = true; - } - - if ($ok) { - foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleElement($token); - $this->processToken($token, $i); - $reprocess = true; - break; - } - if (!$reprocess) { - // ah, nothing interesting happened; do normal processing - $this->swap($token); - if ($token instanceof HTMLPurifier_Token_Start) { - $this->stack[] = $token; - } elseif ($token instanceof HTMLPurifier_Token_End) { - throw new HTMLPurifier_Exception('Improper handling of end tag in start code; possible error in MakeWellFormed'); - } - } - continue; - } - - // sanity check: we should be dealing with a closing tag - if (!$token instanceof HTMLPurifier_Token_End) { - throw new HTMLPurifier_Exception('Unaccounted for tag token in input stream, bug in HTML Purifier'); - } - - // make sure that we have something open - if (empty($this->stack)) { - if ($escape_invalid_tags) { - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag to text'); - $this->swap(new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - )); - } else { - $this->remove(); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Unnecessary end tag removed'); - } - $reprocess = true; - continue; - } - - // first, check for the simplest case: everything closes neatly. - // Eventually, everything passes through here; if there are problems - // we modify the input stream accordingly and then punt, so that - // the tokens get processed again. - $current_parent = array_pop($this->stack); - if ($current_parent->name == $token->name) { - $token->start = $current_parent; - foreach ($this->injectors as $i => $injector) { - if (isset($token->skip[$i])) continue; - if ($token->rewind !== null && $token->rewind !== $i) continue; - $injector->handleEnd($token); - $this->processToken($token, $i); - $this->stack[] = $current_parent; - $reprocess = true; - break; - } - continue; - } - - // okay, so we're trying to close the wrong tag - - // undo the pop previous pop - $this->stack[] = $current_parent; - - // scroll back the entire nest, trying to find our tag. - // (feature could be to specify how far you'd like to go) - $size = count($this->stack); - // -2 because -1 is the last element, but we already checked that - $skipped_tags = false; - for ($j = $size - 2; $j >= 0; $j--) { - if ($this->stack[$j]->name == $token->name) { - $skipped_tags = array_slice($this->stack, $j); - break; - } - } - - // we didn't find the tag, so remove - if ($skipped_tags === false) { - if ($escape_invalid_tags) { - $this->swap(new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - )); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag to text'); - } else { - $this->remove(); - if ($e) $e->send(E_WARNING, 'Strategy_MakeWellFormed: Stray end tag removed'); - } - $reprocess = true; - continue; - } - - // do errors, in REVERSE $j order: a,b,c with
    - $c = count($skipped_tags); - if ($e) { - for ($j = $c - 1; $j > 0; $j--) { - // notice we exclude $j == 0, i.e. the current ending tag, from - // the errors... - if (!isset($skipped_tags[$j]->armor['MakeWellFormed_TagClosedError'])) { - $e->send(E_NOTICE, 'Strategy_MakeWellFormed: Tag closed by element end', $skipped_tags[$j]); - } - } - } - - // insert tags, in FORWARD $j order: c,b,a with
    - $replace = array($token); - for ($j = 1; $j < $c; $j++) { - // ...as well as from the insertions - $new_token = new HTMLPurifier_Token_End($skipped_tags[$j]->name); - $new_token->start = $skipped_tags[$j]; - array_unshift($replace, $new_token); - if (isset($definition->info[$new_token->name]) && $definition->info[$new_token->name]->formatting) { - $element = clone $skipped_tags[$j]; - $element->carryover = true; - $element->armor['MakeWellFormed_TagClosedError'] = true; - $replace[] = $element; - } - } - $this->processToken($replace); - $reprocess = true; - continue; - } - - $context->destroy('CurrentNesting'); - $context->destroy('InputTokens'); - $context->destroy('InputIndex'); - $context->destroy('CurrentToken'); - - unset($this->injectors, $this->stack, $this->tokens, $this->t); - return $tokens; - } - - /** - * Processes arbitrary token values for complicated substitution patterns. - * In general: - * - * If $token is an array, it is a list of tokens to substitute for the - * current token. These tokens then get individually processed. If there - * is a leading integer in the list, that integer determines how many - * tokens from the stream should be removed. - * - * If $token is a regular token, it is swapped with the current token. - * - * If $token is false, the current token is deleted. - * - * If $token is an integer, that number of tokens (with the first token - * being the current one) will be deleted. - * - * @param $token Token substitution value - * @param $injector Injector that performed the substitution; default is if - * this is not an injector related operation. - */ - protected function processToken($token, $injector = -1) { - - // normalize forms of token - if (is_object($token)) $token = array(1, $token); - if (is_int($token)) $token = array($token); - if ($token === false) $token = array(1); - if (!is_array($token)) throw new HTMLPurifier_Exception('Invalid token type from injector'); - if (!is_int($token[0])) array_unshift($token, 1); - if ($token[0] === 0) throw new HTMLPurifier_Exception('Deleting zero tokens is not valid'); - - // $token is now an array with the following form: - // array(number nodes to delete, new node 1, new node 2, ...) - - $delete = array_shift($token); - $old = array_splice($this->tokens, $this->t, $delete, $token); - - if ($injector > -1) { - // determine appropriate skips - $oldskip = isset($old[0]) ? $old[0]->skip : array(); - foreach ($token as $object) { - $object->skip = $oldskip; - $object->skip[$injector] = true; - } - } - - } - - /** - * Inserts a token before the current token. Cursor now points to this token - */ - private function insertBefore($token) { - array_splice($this->tokens, $this->t, 0, array($token)); - } - - /** - * Removes current token. Cursor now points to new token occupying previously - * occupied space. - */ - private function remove() { - array_splice($this->tokens, $this->t, 1); - } - - /** - * Swap current token with new token. Cursor points to new token (no change). - */ - private function swap($token) { - $this->tokens[$this->t] = $token; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php b/extlib/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php deleted file mode 100644 index cf3a33e40..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Strategy/RemoveForeignElements.php +++ /dev/null @@ -1,171 +0,0 @@ -getHTMLDefinition(); - $generator = new HTMLPurifier_Generator($config, $context); - $result = array(); - - $escape_invalid_tags = $config->get('Core.EscapeInvalidTags'); - $remove_invalid_img = $config->get('Core.RemoveInvalidImg'); - - // currently only used to determine if comments should be kept - $trusted = $config->get('HTML.Trusted'); - - $remove_script_contents = $config->get('Core.RemoveScriptContents'); - $hidden_elements = $config->get('Core.HiddenElements'); - - // remove script contents compatibility - if ($remove_script_contents === true) { - $hidden_elements['script'] = true; - } elseif ($remove_script_contents === false && isset($hidden_elements['script'])) { - unset($hidden_elements['script']); - } - - $attr_validator = new HTMLPurifier_AttrValidator(); - - // removes tokens until it reaches a closing tag with its value - $remove_until = false; - - // converts comments into text tokens when this is equal to a tag name - $textify_comments = false; - - $token = false; - $context->register('CurrentToken', $token); - - $e = false; - if ($config->get('Core.CollectErrors')) { - $e =& $context->get('ErrorCollector'); - } - - foreach($tokens as $token) { - if ($remove_until) { - if (empty($token->is_tag) || $token->name !== $remove_until) { - continue; - } - } - if (!empty( $token->is_tag )) { - // DEFINITION CALL - - // before any processing, try to transform the element - if ( - isset($definition->info_tag_transform[$token->name]) - ) { - $original_name = $token->name; - // there is a transformation for this tag - // DEFINITION CALL - $token = $definition-> - info_tag_transform[$token->name]-> - transform($token, $config, $context); - if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Tag transform', $original_name); - } - - if (isset($definition->info[$token->name])) { - - // mostly everything's good, but - // we need to make sure required attributes are in order - if ( - ($token instanceof HTMLPurifier_Token_Start || $token instanceof HTMLPurifier_Token_Empty) && - $definition->info[$token->name]->required_attr && - ($token->name != 'img' || $remove_invalid_img) // ensure config option still works - ) { - $attr_validator->validateToken($token, $config, $context); - $ok = true; - foreach ($definition->info[$token->name]->required_attr as $name) { - if (!isset($token->attr[$name])) { - $ok = false; - break; - } - } - if (!$ok) { - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Missing required attribute', $name); - continue; - } - $token->armor['ValidateAttributes'] = true; - } - - if (isset($hidden_elements[$token->name]) && $token instanceof HTMLPurifier_Token_Start) { - $textify_comments = $token->name; - } elseif ($token->name === $textify_comments && $token instanceof HTMLPurifier_Token_End) { - $textify_comments = false; - } - - } elseif ($escape_invalid_tags) { - // invalid tag, generate HTML representation and insert in - if ($e) $e->send(E_WARNING, 'Strategy_RemoveForeignElements: Foreign element to text'); - $token = new HTMLPurifier_Token_Text( - $generator->generateFromToken($token) - ); - } else { - // check if we need to destroy all of the tag's children - // CAN BE GENERICIZED - if (isset($hidden_elements[$token->name])) { - if ($token instanceof HTMLPurifier_Token_Start) { - $remove_until = $token->name; - } elseif ($token instanceof HTMLPurifier_Token_Empty) { - // do nothing: we're still looking - } else { - $remove_until = false; - } - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign meta element removed'); - } else { - if ($e) $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Foreign element removed'); - } - continue; - } - } elseif ($token instanceof HTMLPurifier_Token_Comment) { - // textify comments in script tags when they are allowed - if ($textify_comments !== false) { - $data = $token->data; - $token = new HTMLPurifier_Token_Text($data); - } elseif ($trusted) { - // keep, but perform comment cleaning - if ($e) { - // perform check whether or not there's a trailing hyphen - if (substr($token->data, -1) == '-') { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Trailing hyphen in comment removed'); - } - } - $token->data = rtrim($token->data, '-'); - $found_double_hyphen = false; - while (strpos($token->data, '--') !== false) { - if ($e && !$found_double_hyphen) { - $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Hyphens in comment collapsed'); - } - $found_double_hyphen = true; // prevent double-erroring - $token->data = str_replace('--', '-', $token->data); - } - } else { - // strip comments - if ($e) $e->send(E_NOTICE, 'Strategy_RemoveForeignElements: Comment removed'); - continue; - } - } elseif ($token instanceof HTMLPurifier_Token_Text) { - } else { - continue; - } - $result[] = $token; - } - if ($remove_until && $e) { - // we removed tokens until the end, throw error - $e->send(E_ERROR, 'Strategy_RemoveForeignElements: Token removed to end', $remove_until); - } - - $context->destroy('CurrentToken'); - - return $result; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php b/extlib/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php deleted file mode 100644 index c3328a9d4..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Strategy/ValidateAttributes.php +++ /dev/null @@ -1,39 +0,0 @@ -register('CurrentToken', $token); - - foreach ($tokens as $key => $token) { - - // only process tokens that have attributes, - // namely start and empty tags - if (!$token instanceof HTMLPurifier_Token_Start && !$token instanceof HTMLPurifier_Token_Empty) continue; - - // skip tokens that are armored - if (!empty($token->armor['ValidateAttributes'])) continue; - - // note that we have no facilities here for removing tokens - $validator->validateToken($token, $config, $context); - - $tokens[$key] = $token; // for PHP 4 - } - $context->destroy('CurrentToken'); - - return $tokens; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/StringHash.php b/extlib/HTMLPurifier/HTMLPurifier/StringHash.php deleted file mode 100644 index 62085c5c2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/StringHash.php +++ /dev/null @@ -1,39 +0,0 @@ -accessed[$index] = true; - return parent::offsetGet($index); - } - - /** - * Returns a lookup array of all array indexes that have been accessed. - * @return Array in form array($index => true). - */ - public function getAccessed() { - return $this->accessed; - } - - /** - * Resets the access array. - */ - public function resetAccessed() { - $this->accessed = array(); - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/StringHashParser.php b/extlib/HTMLPurifier/HTMLPurifier/StringHashParser.php deleted file mode 100644 index f3e70c712..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/StringHashParser.php +++ /dev/null @@ -1,110 +0,0 @@ - 'DefaultKeyValue', - * 'KEY' => 'Value', - * 'KEY2' => 'Value2', - * 'MULTILINE-KEY' => "Multiline\nvalue.\n", - * ) - * - * We use this as an easy to use file-format for configuration schema - * files, but the class itself is usage agnostic. - * - * You can use ---- to forcibly terminate parsing of a single string-hash; - * this marker is used in multi string-hashes to delimit boundaries. - */ -class HTMLPurifier_StringHashParser -{ - - public $default = 'ID'; - - /** - * Parses a file that contains a single string-hash. - */ - public function parseFile($file) { - if (!file_exists($file)) return false; - $fh = fopen($file, 'r'); - if (!$fh) return false; - $ret = $this->parseHandle($fh); - fclose($fh); - return $ret; - } - - /** - * Parses a file that contains multiple string-hashes delimited by '----' - */ - public function parseMultiFile($file) { - if (!file_exists($file)) return false; - $ret = array(); - $fh = fopen($file, 'r'); - if (!$fh) return false; - while (!feof($fh)) { - $ret[] = $this->parseHandle($fh); - } - fclose($fh); - return $ret; - } - - /** - * Internal parser that acepts a file handle. - * @note While it's possible to simulate in-memory parsing by using - * custom stream wrappers, if such a use-case arises we should - * factor out the file handle into its own class. - * @param $fh File handle with pointer at start of valid string-hash - * block. - */ - protected function parseHandle($fh) { - $state = false; - $single = false; - $ret = array(); - do { - $line = fgets($fh); - if ($line === false) break; - $line = rtrim($line, "\n\r"); - if (!$state && $line === '') continue; - if ($line === '----') break; - if (strncmp('--#', $line, 3) === 0) { - // Comment - continue; - } elseif (strncmp('--', $line, 2) === 0) { - // Multiline declaration - $state = trim($line, '- '); - if (!isset($ret[$state])) $ret[$state] = ''; - continue; - } elseif (!$state) { - $single = true; - if (strpos($line, ':') !== false) { - // Single-line declaration - list($state, $line) = explode(':', $line, 2); - $line = trim($line); - } else { - // Use default declaration - $state = $this->default; - } - } - if ($single) { - $ret[$state] = $line; - $single = false; - $state = false; - } else { - $ret[$state] .= "$line\n"; - } - } while (!feof($fh)); - return $ret; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/TagTransform.php b/extlib/HTMLPurifier/HTMLPurifier/TagTransform.php deleted file mode 100644 index 210a44721..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/TagTransform.php +++ /dev/null @@ -1,36 +0,0 @@ - 'xx-small', - '1' => 'xx-small', - '2' => 'small', - '3' => 'medium', - '4' => 'large', - '5' => 'x-large', - '6' => 'xx-large', - '7' => '300%', - '-1' => 'smaller', - '-2' => '60%', - '+1' => 'larger', - '+2' => '150%', - '+3' => '200%', - '+4' => '300%' - ); - - public function transform($tag, $config, $context) { - - if ($tag instanceof HTMLPurifier_Token_End) { - $new_tag = clone $tag; - $new_tag->name = $this->transform_to; - return $new_tag; - } - - $attr = $tag->attr; - $prepend_style = ''; - - // handle color transform - if (isset($attr['color'])) { - $prepend_style .= 'color:' . $attr['color'] . ';'; - unset($attr['color']); - } - - // handle face transform - if (isset($attr['face'])) { - $prepend_style .= 'font-family:' . $attr['face'] . ';'; - unset($attr['face']); - } - - // handle size transform - if (isset($attr['size'])) { - // normalize large numbers - if ($attr['size']{0} == '+' || $attr['size']{0} == '-') { - $size = (int) $attr['size']; - if ($size < -2) $attr['size'] = '-2'; - if ($size > 4) $attr['size'] = '+4'; - } else { - $size = (int) $attr['size']; - if ($size > 7) $attr['size'] = '7'; - } - if (isset($this->_size_lookup[$attr['size']])) { - $prepend_style .= 'font-size:' . - $this->_size_lookup[$attr['size']] . ';'; - } - unset($attr['size']); - } - - if ($prepend_style) { - $attr['style'] = isset($attr['style']) ? - $prepend_style . $attr['style'] : - $prepend_style; - } - - $new_tag = clone $tag; - $new_tag->name = $this->transform_to; - $new_tag->attr = $attr; - - return $new_tag; - - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php b/extlib/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php deleted file mode 100644 index 0e36130f2..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/TagTransform/Simple.php +++ /dev/null @@ -1,35 +0,0 @@ -transform_to = $transform_to; - $this->style = $style; - } - - public function transform($tag, $config, $context) { - $new_tag = clone $tag; - $new_tag->name = $this->transform_to; - if (!is_null($this->style) && - ($new_tag instanceof HTMLPurifier_Token_Start || $new_tag instanceof HTMLPurifier_Token_Empty) - ) { - $this->prependCSS($new_tag->attr, $this->style); - } - return $new_tag; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Token.php b/extlib/HTMLPurifier/HTMLPurifier/Token.php deleted file mode 100644 index 7900e6cb1..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Token.php +++ /dev/null @@ -1,57 +0,0 @@ -line = $l; - $this->col = $c; - } - - /** - * Convenience function for DirectLex settings line/col position. - */ - public function rawPosition($l, $c) { - if ($c === -1) $l++; - $this->line = $l; - $this->col = $c; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Token/Comment.php b/extlib/HTMLPurifier/HTMLPurifier/Token/Comment.php deleted file mode 100644 index dc6bdcabb..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Token/Comment.php +++ /dev/null @@ -1,22 +0,0 @@ -data = $data; - $this->line = $line; - $this->col = $col; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Token/Empty.php b/extlib/HTMLPurifier/HTMLPurifier/Token/Empty.php deleted file mode 100644 index 2a82b47ad..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Token/Empty.php +++ /dev/null @@ -1,11 +0,0 @@ -!empty($obj->is_tag) - * without having to use a function call is_a(). - */ - public $is_tag = true; - - /** - * The lower-case name of the tag, like 'a', 'b' or 'blockquote'. - * - * @note Strictly speaking, XML tags are case sensitive, so we shouldn't - * be lower-casing them, but these tokens cater to HTML tags, which are - * insensitive. - */ - public $name; - - /** - * Associative array of the tag's attributes. - */ - public $attr = array(); - - /** - * Non-overloaded constructor, which lower-cases passed tag name. - * - * @param $name String name. - * @param $attr Associative array of attributes. - */ - public function __construct($name, $attr = array(), $line = null, $col = null) { - $this->name = ctype_lower($name) ? $name : strtolower($name); - foreach ($attr as $key => $value) { - // normalization only necessary when key is not lowercase - if (!ctype_lower($key)) { - $new_key = strtolower($key); - if (!isset($attr[$new_key])) { - $attr[$new_key] = $attr[$key]; - } - if ($new_key !== $key) { - unset($attr[$key]); - } - } - } - $this->attr = $attr; - $this->line = $line; - $this->col = $col; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/Token/Text.php b/extlib/HTMLPurifier/HTMLPurifier/Token/Text.php deleted file mode 100644 index 82efd823d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/Token/Text.php +++ /dev/null @@ -1,33 +0,0 @@ -data = $data; - $this->is_whitespace = ctype_space($data); - $this->line = $line; - $this->col = $col; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/TokenFactory.php b/extlib/HTMLPurifier/HTMLPurifier/TokenFactory.php deleted file mode 100644 index 7cf48fb41..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/TokenFactory.php +++ /dev/null @@ -1,94 +0,0 @@ -p_start = new HTMLPurifier_Token_Start('', array()); - $this->p_end = new HTMLPurifier_Token_End(''); - $this->p_empty = new HTMLPurifier_Token_Empty('', array()); - $this->p_text = new HTMLPurifier_Token_Text(''); - $this->p_comment= new HTMLPurifier_Token_Comment(''); - } - - /** - * Creates a HTMLPurifier_Token_Start. - * @param $name Tag name - * @param $attr Associative array of attributes - * @return Generated HTMLPurifier_Token_Start - */ - public function createStart($name, $attr = array()) { - $p = clone $this->p_start; - $p->__construct($name, $attr); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_End. - * @param $name Tag name - * @return Generated HTMLPurifier_Token_End - */ - public function createEnd($name) { - $p = clone $this->p_end; - $p->__construct($name); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_Empty. - * @param $name Tag name - * @param $attr Associative array of attributes - * @return Generated HTMLPurifier_Token_Empty - */ - public function createEmpty($name, $attr = array()) { - $p = clone $this->p_empty; - $p->__construct($name, $attr); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_Text. - * @param $data Data of text token - * @return Generated HTMLPurifier_Token_Text - */ - public function createText($data) { - $p = clone $this->p_text; - $p->__construct($data); - return $p; - } - - /** - * Creates a HTMLPurifier_Token_Comment. - * @param $data Data of comment token - * @return Generated HTMLPurifier_Token_Comment - */ - public function createComment($data) { - $p = clone $this->p_comment; - $p->__construct($data); - return $p; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URI.php b/extlib/HTMLPurifier/HTMLPurifier/URI.php deleted file mode 100644 index 8b50d0d18..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URI.php +++ /dev/null @@ -1,173 +0,0 @@ -scheme = is_null($scheme) || ctype_lower($scheme) ? $scheme : strtolower($scheme); - $this->userinfo = $userinfo; - $this->host = $host; - $this->port = is_null($port) ? $port : (int) $port; - $this->path = $path; - $this->query = $query; - $this->fragment = $fragment; - } - - /** - * Retrieves a scheme object corresponding to the URI's scheme/default - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context - * @return Scheme object appropriate for validating this URI - */ - public function getSchemeObj($config, $context) { - $registry = HTMLPurifier_URISchemeRegistry::instance(); - if ($this->scheme !== null) { - $scheme_obj = $registry->getScheme($this->scheme, $config, $context); - if (!$scheme_obj) return false; // invalid scheme, clean it out - } else { - // no scheme: retrieve the default one - $def = $config->getDefinition('URI'); - $scheme_obj = $registry->getScheme($def->defaultScheme, $config, $context); - if (!$scheme_obj) { - // something funky happened to the default scheme object - trigger_error( - 'Default scheme object "' . $def->defaultScheme . '" was not readable', - E_USER_WARNING - ); - return false; - } - } - return $scheme_obj; - } - - /** - * Generic validation method applicable for all schemes. May modify - * this URI in order to get it into a compliant form. - * @param $config Instance of HTMLPurifier_Config - * @param $context Instance of HTMLPurifier_Context - * @return True if validation/filtering succeeds, false if failure - */ - public function validate($config, $context) { - - // ABNF definitions from RFC 3986 - $chars_sub_delims = '!$&\'()*+,;='; - $chars_gen_delims = ':/?#[]@'; - $chars_pchar = $chars_sub_delims . ':@'; - - // validate scheme (MUST BE FIRST!) - if (!is_null($this->scheme) && is_null($this->host)) { - $def = $config->getDefinition('URI'); - if ($def->defaultScheme === $this->scheme) { - $this->scheme = null; - } - } - - // validate host - if (!is_null($this->host)) { - $host_def = new HTMLPurifier_AttrDef_URI_Host(); - $this->host = $host_def->validate($this->host, $config, $context); - if ($this->host === false) $this->host = null; - } - - // validate username - if (!is_null($this->userinfo)) { - $encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . ':'); - $this->userinfo = $encoder->encode($this->userinfo); - } - - // validate port - if (!is_null($this->port)) { - if ($this->port < 1 || $this->port > 65535) $this->port = null; - } - - // validate path - $path_parts = array(); - $segments_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/'); - if (!is_null($this->host)) { - // path-abempty (hier and relative) - $this->path = $segments_encoder->encode($this->path); - } elseif ($this->path !== '' && $this->path[0] === '/') { - // path-absolute (hier and relative) - if (strlen($this->path) >= 2 && $this->path[1] === '/') { - // This shouldn't ever happen! - $this->path = ''; - } else { - $this->path = $segments_encoder->encode($this->path); - } - } elseif (!is_null($this->scheme) && $this->path !== '') { - // path-rootless (hier) - // Short circuit evaluation means we don't need to check nz - $this->path = $segments_encoder->encode($this->path); - } elseif (is_null($this->scheme) && $this->path !== '') { - // path-noscheme (relative) - // (once again, not checking nz) - $segment_nc_encoder = new HTMLPurifier_PercentEncoder($chars_sub_delims . '@'); - $c = strpos($this->path, '/'); - if ($c !== false) { - $this->path = - $segment_nc_encoder->encode(substr($this->path, 0, $c)) . - $segments_encoder->encode(substr($this->path, $c)); - } else { - $this->path = $segment_nc_encoder->encode($this->path); - } - } else { - // path-empty (hier and relative) - $this->path = ''; // just to be safe - } - - // qf = query and fragment - $qf_encoder = new HTMLPurifier_PercentEncoder($chars_pchar . '/?'); - - if (!is_null($this->query)) { - $this->query = $qf_encoder->encode($this->query); - } - - if (!is_null($this->fragment)) { - $this->fragment = $qf_encoder->encode($this->fragment); - } - - return true; - - } - - /** - * Convert URI back to string - * @return String URI appropriate for output - */ - public function toString() { - // reconstruct authority - $authority = null; - if (!is_null($this->host)) { - $authority = ''; - if(!is_null($this->userinfo)) $authority .= $this->userinfo . '@'; - $authority .= $this->host; - if(!is_null($this->port)) $authority .= ':' . $this->port; - } - - // reconstruct the result - $result = ''; - if (!is_null($this->scheme)) $result .= $this->scheme . ':'; - if (!is_null($authority)) $result .= '//' . $authority; - $result .= $this->path; - if (!is_null($this->query)) $result .= '?' . $this->query; - if (!is_null($this->fragment)) $result .= '#' . $this->fragment; - - return $result; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIDefinition.php b/extlib/HTMLPurifier/HTMLPurifier/URIDefinition.php deleted file mode 100644 index ea2b8fe24..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIDefinition.php +++ /dev/null @@ -1,93 +0,0 @@ -registerFilter(new HTMLPurifier_URIFilter_DisableExternal()); - $this->registerFilter(new HTMLPurifier_URIFilter_DisableExternalResources()); - $this->registerFilter(new HTMLPurifier_URIFilter_HostBlacklist()); - $this->registerFilter(new HTMLPurifier_URIFilter_MakeAbsolute()); - $this->registerFilter(new HTMLPurifier_URIFilter_Munge()); - } - - public function registerFilter($filter) { - $this->registeredFilters[$filter->name] = $filter; - } - - public function addFilter($filter, $config) { - $r = $filter->prepare($config); - if ($r === false) return; // null is ok, for backwards compat - if ($filter->post) { - $this->postFilters[$filter->name] = $filter; - } else { - $this->filters[$filter->name] = $filter; - } - } - - protected function doSetup($config) { - $this->setupMemberVariables($config); - $this->setupFilters($config); - } - - protected function setupFilters($config) { - foreach ($this->registeredFilters as $name => $filter) { - $conf = $config->get('URI.' . $name); - if ($conf !== false && $conf !== null) { - $this->addFilter($filter, $config); - } - } - unset($this->registeredFilters); - } - - protected function setupMemberVariables($config) { - $this->host = $config->get('URI.Host'); - $base_uri = $config->get('URI.Base'); - if (!is_null($base_uri)) { - $parser = new HTMLPurifier_URIParser(); - $this->base = $parser->parse($base_uri); - $this->defaultScheme = $this->base->scheme; - if (is_null($this->host)) $this->host = $this->base->host; - } - if (is_null($this->defaultScheme)) $this->defaultScheme = $config->get('URI.DefaultScheme'); - } - - public function filter(&$uri, $config, $context) { - foreach ($this->filters as $name => $f) { - $result = $f->filter($uri, $config, $context); - if (!$result) return false; - } - return true; - } - - public function postFilter(&$uri, $config, $context) { - foreach ($this->postFilters as $name => $f) { - $result = $f->filter($uri, $config, $context); - if (!$result) return false; - } - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIFilter.php b/extlib/HTMLPurifier/HTMLPurifier/URIFilter.php deleted file mode 100644 index c116f93df..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIFilter.php +++ /dev/null @@ -1,45 +0,0 @@ -getDefinition('URI')->host; - if ($our_host !== null) $this->ourHostParts = array_reverse(explode('.', $our_host)); - } - public function filter(&$uri, $config, $context) { - if (is_null($uri->host)) return true; - if ($this->ourHostParts === false) return false; - $host_parts = array_reverse(explode('.', $uri->host)); - foreach ($this->ourHostParts as $i => $x) { - if (!isset($host_parts[$i])) return false; - if ($host_parts[$i] != $this->ourHostParts[$i]) return false; - } - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php b/extlib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php deleted file mode 100644 index 881abc43c..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/DisableExternalResources.php +++ /dev/null @@ -1,12 +0,0 @@ -get('EmbeddedURI', true)) return true; - return parent::filter($uri, $config, $context); - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php b/extlib/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php deleted file mode 100644 index 045aa0992..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/HostBlacklist.php +++ /dev/null @@ -1,21 +0,0 @@ -blacklist = $config->get('URI.HostBlacklist'); - return true; - } - public function filter(&$uri, $config, $context) { - foreach($this->blacklist as $blacklisted_host_fragment) { - if (strpos($uri->host, $blacklisted_host_fragment) !== false) { - return false; - } - } - return true; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php b/extlib/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php deleted file mode 100644 index f46ab2630..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/MakeAbsolute.php +++ /dev/null @@ -1,114 +0,0 @@ -getDefinition('URI'); - $this->base = $def->base; - if (is_null($this->base)) { - trigger_error('URI.MakeAbsolute is being ignored due to lack of value for URI.Base configuration', E_USER_WARNING); - return false; - } - $this->base->fragment = null; // fragment is invalid for base URI - $stack = explode('/', $this->base->path); - array_pop($stack); // discard last segment - $stack = $this->_collapseStack($stack); // do pre-parsing - $this->basePathStack = $stack; - return true; - } - public function filter(&$uri, $config, $context) { - if (is_null($this->base)) return true; // abort early - if ( - $uri->path === '' && is_null($uri->scheme) && - is_null($uri->host) && is_null($uri->query) && is_null($uri->fragment) - ) { - // reference to current document - $uri = clone $this->base; - return true; - } - if (!is_null($uri->scheme)) { - // absolute URI already: don't change - if (!is_null($uri->host)) return true; - $scheme_obj = $uri->getSchemeObj($config, $context); - if (!$scheme_obj) { - // scheme not recognized - return false; - } - if (!$scheme_obj->hierarchical) { - // non-hierarchal URI with explicit scheme, don't change - return true; - } - // special case: had a scheme but always is hierarchical and had no authority - } - if (!is_null($uri->host)) { - // network path, don't bother - return true; - } - if ($uri->path === '') { - $uri->path = $this->base->path; - } elseif ($uri->path[0] !== '/') { - // relative path, needs more complicated processing - $stack = explode('/', $uri->path); - $new_stack = array_merge($this->basePathStack, $stack); - if ($new_stack[0] !== '' && !is_null($this->base->host)) { - array_unshift($new_stack, ''); - } - $new_stack = $this->_collapseStack($new_stack); - $uri->path = implode('/', $new_stack); - } else { - // absolute path, but still we should collapse - $uri->path = implode('/', $this->_collapseStack(explode('/', $uri->path))); - } - // re-combine - $uri->scheme = $this->base->scheme; - if (is_null($uri->userinfo)) $uri->userinfo = $this->base->userinfo; - if (is_null($uri->host)) $uri->host = $this->base->host; - if (is_null($uri->port)) $uri->port = $this->base->port; - return true; - } - - /** - * Resolve dots and double-dots in a path stack - */ - private function _collapseStack($stack) { - $result = array(); - $is_folder = false; - for ($i = 0; isset($stack[$i]); $i++) { - $is_folder = false; - // absorb an internally duplicated slash - if ($stack[$i] == '' && $i && isset($stack[$i+1])) continue; - if ($stack[$i] == '..') { - if (!empty($result)) { - $segment = array_pop($result); - if ($segment === '' && empty($result)) { - // error case: attempted to back out too far: - // restore the leading slash - $result[] = ''; - } elseif ($segment === '..') { - $result[] = '..'; // cannot remove .. with .. - } - } else { - // relative path, preserve the double-dots - $result[] = '..'; - } - $is_folder = true; - continue; - } - if ($stack[$i] == '.') { - // silently absorb - $is_folder = true; - continue; - } - $result[] = $stack[$i]; - } - if ($is_folder) $result[] = ''; - return $result; - } -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php b/extlib/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php deleted file mode 100644 index efa10a645..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIFilter/Munge.php +++ /dev/null @@ -1,58 +0,0 @@ -target = $config->get('URI.' . $this->name); - $this->parser = new HTMLPurifier_URIParser(); - $this->doEmbed = $config->get('URI.MungeResources'); - $this->secretKey = $config->get('URI.MungeSecretKey'); - return true; - } - public function filter(&$uri, $config, $context) { - if ($context->get('EmbeddedURI', true) && !$this->doEmbed) return true; - - $scheme_obj = $uri->getSchemeObj($config, $context); - if (!$scheme_obj) return true; // ignore unknown schemes, maybe another postfilter did it - if (is_null($uri->host) || empty($scheme_obj->browsable)) { - return true; - } - // don't redirect if target host is our host - if ($uri->host === $config->getDefinition('URI')->host) { - return true; - } - - $this->makeReplace($uri, $config, $context); - $this->replace = array_map('rawurlencode', $this->replace); - - $new_uri = strtr($this->target, $this->replace); - $new_uri = $this->parser->parse($new_uri); - // don't redirect if the target host is the same as the - // starting host - if ($uri->host === $new_uri->host) return true; - $uri = $new_uri; // overwrite - return true; - } - - protected function makeReplace($uri, $config, $context) { - $string = $uri->toString(); - // always available - $this->replace['%s'] = $string; - $this->replace['%r'] = $context->get('EmbeddedURI', true); - $token = $context->get('CurrentToken', true); - $this->replace['%n'] = $token ? $token->name : null; - $this->replace['%m'] = $context->get('CurrentAttr', true); - $this->replace['%p'] = $context->get('CurrentCSSProperty', true); - // not always available - if ($this->secretKey) $this->replace['%t'] = sha1($this->secretKey . ':' . $string); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIParser.php b/extlib/HTMLPurifier/HTMLPurifier/URIParser.php deleted file mode 100644 index 7179e4ab8..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIParser.php +++ /dev/null @@ -1,70 +0,0 @@ -percentEncoder = new HTMLPurifier_PercentEncoder(); - } - - /** - * Parses a URI. - * @param $uri string URI to parse - * @return HTMLPurifier_URI representation of URI. This representation has - * not been validated yet and may not conform to RFC. - */ - public function parse($uri) { - - $uri = $this->percentEncoder->normalize($uri); - - // Regexp is as per Appendix B. - // Note that ["<>] are an addition to the RFC's recommended - // characters, because they represent external delimeters. - $r_URI = '!'. - '(([^:/?#"<>]+):)?'. // 2. Scheme - '(//([^/?#"<>]*))?'. // 4. Authority - '([^?#"<>]*)'. // 5. Path - '(\?([^#"<>]*))?'. // 7. Query - '(#([^"<>]*))?'. // 8. Fragment - '!'; - - $matches = array(); - $result = preg_match($r_URI, $uri, $matches); - - if (!$result) return false; // *really* invalid URI - - // seperate out parts - $scheme = !empty($matches[1]) ? $matches[2] : null; - $authority = !empty($matches[3]) ? $matches[4] : null; - $path = $matches[5]; // always present, can be empty - $query = !empty($matches[6]) ? $matches[7] : null; - $fragment = !empty($matches[8]) ? $matches[9] : null; - - // further parse authority - if ($authority !== null) { - $r_authority = "/^((.+?)@)?(\[[^\]]+\]|[^:]*)(:(\d*))?/"; - $matches = array(); - preg_match($r_authority, $authority, $matches); - $userinfo = !empty($matches[1]) ? $matches[2] : null; - $host = !empty($matches[3]) ? $matches[3] : ''; - $port = !empty($matches[4]) ? (int) $matches[5] : null; - } else { - $port = $host = $userinfo = null; - } - - return new HTMLPurifier_URI( - $scheme, $userinfo, $host, $port, $path, $query, $fragment); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIScheme.php b/extlib/HTMLPurifier/HTMLPurifier/URIScheme.php deleted file mode 100644 index 039710fd1..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIScheme.php +++ /dev/null @@ -1,42 +0,0 @@ -, resolves edge cases - * with making relative URIs absolute - */ - public $hierarchical = false; - - /** - * Validates the components of a URI - * @note This implementation should be called by children if they define - * a default port, as it does port processing. - * @param $uri Instance of HTMLPurifier_URI - * @param $config HTMLPurifier_Config object - * @param $context HTMLPurifier_Context object - * @return Bool success or failure - */ - public function validate(&$uri, $config, $context) { - if ($this->default_port == $uri->port) $uri->port = null; - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php b/extlib/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php deleted file mode 100644 index 5849bf7ff..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/ftp.php +++ /dev/null @@ -1,43 +0,0 @@ -query = null; - - // typecode check - $semicolon_pos = strrpos($uri->path, ';'); // reverse - if ($semicolon_pos !== false) { - $type = substr($uri->path, $semicolon_pos + 1); // no semicolon - $uri->path = substr($uri->path, 0, $semicolon_pos); - $type_ret = ''; - if (strpos($type, '=') !== false) { - // figure out whether or not the declaration is correct - list($key, $typecode) = explode('=', $type, 2); - if ($key !== 'type') { - // invalid key, tack it back on encoded - $uri->path .= '%3B' . $type; - } elseif ($typecode === 'a' || $typecode === 'i' || $typecode === 'd') { - $type_ret = ";type=$typecode"; - } - } else { - $uri->path .= '%3B' . $type; - } - $uri->path = str_replace(';', '%3B', $uri->path); - $uri->path .= $type_ret; - } - - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/http.php b/extlib/HTMLPurifier/HTMLPurifier/URIScheme/http.php deleted file mode 100644 index b097a31d6..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/http.php +++ /dev/null @@ -1,20 +0,0 @@ -userinfo = null; - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/https.php b/extlib/HTMLPurifier/HTMLPurifier/URIScheme/https.php deleted file mode 100644 index 29e380919..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/https.php +++ /dev/null @@ -1,12 +0,0 @@ -userinfo = null; - $uri->host = null; - $uri->port = null; - // we need to validate path against RFC 2368's addr-spec - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/news.php b/extlib/HTMLPurifier/HTMLPurifier/URIScheme/news.php deleted file mode 100644 index f5f54f4f5..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/news.php +++ /dev/null @@ -1,22 +0,0 @@ -userinfo = null; - $uri->host = null; - $uri->port = null; - $uri->query = null; - // typecode check needed on path - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php b/extlib/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php deleted file mode 100644 index 5bf93ea78..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URIScheme/nntp.php +++ /dev/null @@ -1,20 +0,0 @@ -userinfo = null; - $uri->query = null; - return true; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php b/extlib/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php deleted file mode 100644 index 576bf7b6d..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/URISchemeRegistry.php +++ /dev/null @@ -1,68 +0,0 @@ -get('URI.AllowedSchemes'); - if (!$config->get('URI.OverrideAllowedSchemes') && - !isset($allowed_schemes[$scheme]) - ) { - return; - } - - if (isset($this->schemes[$scheme])) return $this->schemes[$scheme]; - if (!isset($allowed_schemes[$scheme])) return; - - $class = 'HTMLPurifier_URIScheme_' . $scheme; - if (!class_exists($class)) return; - $this->schemes[$scheme] = new $class(); - return $this->schemes[$scheme]; - } - - /** - * Registers a custom scheme to the cache, bypassing reflection. - * @param $scheme Scheme name - * @param $scheme_obj HTMLPurifier_URIScheme object - */ - public function register($scheme, $scheme_obj) { - $this->schemes[$scheme] = $scheme_obj; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/UnitConverter.php b/extlib/HTMLPurifier/HTMLPurifier/UnitConverter.php deleted file mode 100644 index 545d42622..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/UnitConverter.php +++ /dev/null @@ -1,254 +0,0 @@ - array( - 'px' => 3, // This is as per CSS 2.1 and Firefox. Your mileage may vary - 'pt' => 4, - 'pc' => 48, - 'in' => 288, - self::METRIC => array('pt', '0.352777778', 'mm'), - ), - self::METRIC => array( - 'mm' => 1, - 'cm' => 10, - self::ENGLISH => array('mm', '2.83464567', 'pt'), - ), - ); - - /** - * Minimum bcmath precision for output. - */ - protected $outputPrecision; - - /** - * Bcmath precision for internal calculations. - */ - protected $internalPrecision; - - /** - * Whether or not BCMath is available - */ - private $bcmath; - - public function __construct($output_precision = 4, $internal_precision = 10, $force_no_bcmath = false) { - $this->outputPrecision = $output_precision; - $this->internalPrecision = $internal_precision; - $this->bcmath = !$force_no_bcmath && function_exists('bcmul'); - } - - /** - * Converts a length object of one unit into another unit. - * @param HTMLPurifier_Length $length - * Instance of HTMLPurifier_Length to convert. You must validate() - * it before passing it here! - * @param string $to_unit - * Unit to convert to. - * @note - * About precision: This conversion function pays very special - * attention to the incoming precision of values and attempts - * to maintain a number of significant figure. Results are - * fairly accurate up to nine digits. Some caveats: - * - If a number is zero-padded as a result of this significant - * figure tracking, the zeroes will be eliminated. - * - If a number contains less than four sigfigs ($outputPrecision) - * and this causes some decimals to be excluded, those - * decimals will be added on. - */ - public function convert($length, $to_unit) { - - if (!$length->isValid()) return false; - - $n = $length->getN(); - $unit = $length->getUnit(); - - if ($n === '0' || $unit === false) { - return new HTMLPurifier_Length('0', false); - } - - $state = $dest_state = false; - foreach (self::$units as $k => $x) { - if (isset($x[$unit])) $state = $k; - if (isset($x[$to_unit])) $dest_state = $k; - } - if (!$state || !$dest_state) return false; - - // Some calculations about the initial precision of the number; - // this will be useful when we need to do final rounding. - $sigfigs = $this->getSigFigs($n); - if ($sigfigs < $this->outputPrecision) $sigfigs = $this->outputPrecision; - - // BCMath's internal precision deals only with decimals. Use - // our default if the initial number has no decimals, or increase - // it by how ever many decimals, thus, the number of guard digits - // will always be greater than or equal to internalPrecision. - $log = (int) floor(log(abs($n), 10)); - $cp = ($log < 0) ? $this->internalPrecision - $log : $this->internalPrecision; // internal precision - - for ($i = 0; $i < 2; $i++) { - - // Determine what unit IN THIS SYSTEM we need to convert to - if ($dest_state === $state) { - // Simple conversion - $dest_unit = $to_unit; - } else { - // Convert to the smallest unit, pending a system shift - $dest_unit = self::$units[$state][$dest_state][0]; - } - - // Do the conversion if necessary - if ($dest_unit !== $unit) { - $factor = $this->div(self::$units[$state][$unit], self::$units[$state][$dest_unit], $cp); - $n = $this->mul($n, $factor, $cp); - $unit = $dest_unit; - } - - // Output was zero, so bail out early. Shouldn't ever happen. - if ($n === '') { - $n = '0'; - $unit = $to_unit; - break; - } - - // It was a simple conversion, so bail out - if ($dest_state === $state) { - break; - } - - if ($i !== 0) { - // Conversion failed! Apparently, the system we forwarded - // to didn't have this unit. This should never happen! - return false; - } - - // Pre-condition: $i == 0 - - // Perform conversion to next system of units - $n = $this->mul($n, self::$units[$state][$dest_state][1], $cp); - $unit = self::$units[$state][$dest_state][2]; - $state = $dest_state; - - // One more loop around to convert the unit in the new system. - - } - - // Post-condition: $unit == $to_unit - if ($unit !== $to_unit) return false; - - // Useful for debugging: - //echo "
    n";
    -        //echo "$n\nsigfigs = $sigfigs\nnew_log = $new_log\nlog = $log\nrp = $rp\n
    \n"; - - $n = $this->round($n, $sigfigs); - if (strpos($n, '.') !== false) $n = rtrim($n, '0'); - $n = rtrim($n, '.'); - - return new HTMLPurifier_Length($n, $unit); - } - - /** - * Returns the number of significant figures in a string number. - * @param string $n Decimal number - * @return int number of sigfigs - */ - public function getSigFigs($n) { - $n = ltrim($n, '0+-'); - $dp = strpos($n, '.'); // decimal position - if ($dp === false) { - $sigfigs = strlen(rtrim($n, '0')); - } else { - $sigfigs = strlen(ltrim($n, '0.')); // eliminate extra decimal character - if ($dp !== 0) $sigfigs--; - } - return $sigfigs; - } - - /** - * Adds two numbers, using arbitrary precision when available. - */ - private function add($s1, $s2, $scale) { - if ($this->bcmath) return bcadd($s1, $s2, $scale); - else return $this->scale($s1 + $s2, $scale); - } - - /** - * Multiples two numbers, using arbitrary precision when available. - */ - private function mul($s1, $s2, $scale) { - if ($this->bcmath) return bcmul($s1, $s2, $scale); - else return $this->scale($s1 * $s2, $scale); - } - - /** - * Divides two numbers, using arbitrary precision when available. - */ - private function div($s1, $s2, $scale) { - if ($this->bcmath) return bcdiv($s1, $s2, $scale); - else return $this->scale($s1 / $s2, $scale); - } - - /** - * Rounds a number according to the number of sigfigs it should have, - * using arbitrary precision when available. - */ - private function round($n, $sigfigs) { - $new_log = (int) floor(log(abs($n), 10)); // Number of digits left of decimal - 1 - $rp = $sigfigs - $new_log - 1; // Number of decimal places needed - $neg = $n < 0 ? '-' : ''; // Negative sign - if ($this->bcmath) { - if ($rp >= 0) { - $n = bcadd($n, $neg . '0.' . str_repeat('0', $rp) . '5', $rp + 1); - $n = bcdiv($n, '1', $rp); - } else { - // This algorithm partially depends on the standardized - // form of numbers that comes out of bcmath. - $n = bcadd($n, $neg . '5' . str_repeat('0', $new_log - $sigfigs), 0); - $n = substr($n, 0, $sigfigs + strlen($neg)) . str_repeat('0', $new_log - $sigfigs + 1); - } - return $n; - } else { - return $this->scale(round($n, $sigfigs - $new_log - 1), $rp + 1); - } - } - - /** - * Scales a float to $scale digits right of decimal point, like BCMath. - */ - private function scale($r, $scale) { - if ($scale < 0) { - // The f sprintf type doesn't support negative numbers, so we - // need to cludge things manually. First get the string. - $r = sprintf('%.0f', (float) $r); - // Due to floating point precision loss, $r will more than likely - // look something like 4652999999999.9234. We grab one more digit - // than we need to precise from $r and then use that to round - // appropriately. - $precise = (string) round(substr($r, 0, strlen($r) + $scale), -1); - // Now we return it, truncating the zero that was rounded off. - return substr($precise, 0, -1) . str_repeat('0', -$scale + 1); - } - return sprintf('%.' . $scale . 'f', (float) $r); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/VarParser.php b/extlib/HTMLPurifier/HTMLPurifier/VarParser.php deleted file mode 100644 index 68e72ae86..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/VarParser.php +++ /dev/null @@ -1,154 +0,0 @@ - self::STRING, - 'istring' => self::ISTRING, - 'text' => self::TEXT, - 'itext' => self::ITEXT, - 'int' => self::INT, - 'float' => self::FLOAT, - 'bool' => self::BOOL, - 'lookup' => self::LOOKUP, - 'list' => self::ALIST, - 'hash' => self::HASH, - 'mixed' => self::MIXED - ); - - /** - * Lookup table of types that are string, and can have aliases or - * allowed value lists. - */ - static public $stringTypes = array( - self::STRING => true, - self::ISTRING => true, - self::TEXT => true, - self::ITEXT => true, - ); - - /** - * Validate a variable according to type. Throws - * HTMLPurifier_VarParserException if invalid. - * It may return NULL as a valid type if $allow_null is true. - * - * @param $var Variable to validate - * @param $type Type of variable, see HTMLPurifier_VarParser->types - * @param $allow_null Whether or not to permit null as a value - * @return Validated and type-coerced variable - */ - final public function parse($var, $type, $allow_null = false) { - if (is_string($type)) { - if (!isset(HTMLPurifier_VarParser::$types[$type])) { - throw new HTMLPurifier_VarParserException("Invalid type '$type'"); - } else { - $type = HTMLPurifier_VarParser::$types[$type]; - } - } - $var = $this->parseImplementation($var, $type, $allow_null); - if ($allow_null && $var === null) return null; - // These are basic checks, to make sure nothing horribly wrong - // happened in our implementations. - switch ($type) { - case (self::STRING): - case (self::ISTRING): - case (self::TEXT): - case (self::ITEXT): - if (!is_string($var)) break; - if ($type == self::ISTRING || $type == self::ITEXT) $var = strtolower($var); - return $var; - case (self::INT): - if (!is_int($var)) break; - return $var; - case (self::FLOAT): - if (!is_float($var)) break; - return $var; - case (self::BOOL): - if (!is_bool($var)) break; - return $var; - case (self::LOOKUP): - case (self::ALIST): - case (self::HASH): - if (!is_array($var)) break; - if ($type === self::LOOKUP) { - foreach ($var as $k) if ($k !== true) $this->error('Lookup table contains value other than true'); - } elseif ($type === self::ALIST) { - $keys = array_keys($var); - if (array_keys($keys) !== $keys) $this->error('Indices for list are not uniform'); - } - return $var; - case (self::MIXED): - return $var; - default: - $this->errorInconsistent(get_class($this), $type); - } - $this->errorGeneric($var, $type); - } - - /** - * Actually implements the parsing. Base implementation is to not - * do anything to $var. Subclasses should overload this! - */ - protected function parseImplementation($var, $type, $allow_null) { - return $var; - } - - /** - * Throws an exception. - */ - protected function error($msg) { - throw new HTMLPurifier_VarParserException($msg); - } - - /** - * Throws an inconsistency exception. - * @note This should not ever be called. It would be called if we - * extend the allowed values of HTMLPurifier_VarParser without - * updating subclasses. - */ - protected function errorInconsistent($class, $type) { - throw new HTMLPurifier_Exception("Inconsistency in $class: ".HTMLPurifier_VarParser::getTypeName($type)." not implemented"); - } - - /** - * Generic error for if a type didn't work. - */ - protected function errorGeneric($var, $type) { - $vtype = gettype($var); - $this->error("Expected type ".HTMLPurifier_VarParser::getTypeName($type).", got $vtype"); - } - - static public function getTypeName($type) { - static $lookup; - if (!$lookup) { - // Lazy load the alternative lookup table - $lookup = array_flip(HTMLPurifier_VarParser::$types); - } - if (!isset($lookup[$type])) return 'unknown'; - return $lookup[$type]; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php b/extlib/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php deleted file mode 100644 index c954250e9..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/VarParser/Flexible.php +++ /dev/null @@ -1,96 +0,0 @@ - $j) $var[$i] = trim($j); - if ($type === self::HASH) { - // key:value,key2:value2 - $nvar = array(); - foreach ($var as $keypair) { - $c = explode(':', $keypair, 2); - if (!isset($c[1])) continue; - $nvar[$c[0]] = $c[1]; - } - $var = $nvar; - } - } - if (!is_array($var)) break; - $keys = array_keys($var); - if ($keys === array_keys($keys)) { - if ($type == self::ALIST) return $var; - elseif ($type == self::LOOKUP) { - $new = array(); - foreach ($var as $key) { - $new[$key] = true; - } - return $new; - } else break; - } - if ($type === self::LOOKUP) { - foreach ($var as $key => $value) { - $var[$key] = true; - } - } - return $var; - default: - $this->errorInconsistent(__CLASS__, $type); - } - $this->errorGeneric($var, $type); - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/VarParser/Native.php b/extlib/HTMLPurifier/HTMLPurifier/VarParser/Native.php deleted file mode 100644 index b02a6de54..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/VarParser/Native.php +++ /dev/null @@ -1,26 +0,0 @@ -evalExpression($var); - } - - protected function evalExpression($expr) { - $var = null; - $result = eval("\$var = $expr;"); - if ($result === false) { - throw new HTMLPurifier_VarParserException("Fatal error in evaluated code"); - } - return $var; - } - -} - -// vim: et sw=4 sts=4 diff --git a/extlib/HTMLPurifier/HTMLPurifier/VarParserException.php b/extlib/HTMLPurifier/HTMLPurifier/VarParserException.php deleted file mode 100644 index 5df341495..000000000 --- a/extlib/HTMLPurifier/HTMLPurifier/VarParserException.php +++ /dev/null @@ -1,11 +0,0 @@ - - Copyright (C) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - vim: et sw=4 sts=4 diff --git a/plugins/OStatus/README b/plugins/OStatus/README index 09a59e349..3a98b7b25 100644 --- a/plugins/OStatus/README +++ b/plugins/OStatus/README @@ -12,10 +12,6 @@ $config['ostatus']['hub_retries'] (default 0) Number of times to retry a PuSH send to consumers if using internal hub -$config['ostatus']['purify_cache'] - (default cache disabled) - Set to a writable cache directory for HTMLPurifier's configuration settings, can speed up processing of remote messages (have not tested by how much) - For testing, shouldn't be used in production: diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 33b0ceb9e..a366c1c2c 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -668,28 +668,9 @@ class Ostatus_profile extends Memcached_DataObject */ protected function purify($html) { - require_once(INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php'); - - // By default Purifier wants to cache data to its own code directories, - // and spews error messages if they're not writable. - $config = HTMLPurifier_Config::createDefault(); - if (common_config('ostatus', 'purify_cache')) { - $config->set('Cache.SerializerPath', common_config('ostatus', 'purify_cache')); - } else { - // Although recommended in the documentation, this produces a notice: - // "Core.DefinitionCache is an alias, preferred directive name is Cache.DefinitionImpl" - // If I then follow *those* directions, I get a warning and it doesn't work: - // "Cannot set undefined directive Core.DefinitionImpl" - // So... lesser of two evils. Suppressing the notice from output, - // though it'll still be seen and logged by StatusNet's error handler. - $old = error_reporting(); - error_reporting($old & ~E_NOTICE); - $config->set('Core.DefinitionCache', null); - error_reporting($old); - } - - $purifier = new HTMLPurifier($config); - return $purifier->purify($html); + require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php'; + $config = array('safe' => 1); + return htmLawed($html, $config); } /** -- cgit v1.2.3-54-g00ecf From 543ff40ef62f2587cde084b03bcf2e956f52ce2f Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 24 Feb 2010 16:51:24 -0800 Subject: Populate more profile information when doing a remote subscribe --- lib/activity.php | 56 +++++++++++++++++------ plugins/OStatus/classes/Ostatus_profile.php | 71 +++++++++++++++++++++++++++-- tests/ActivityParseTests.php | 2 + 3 files changed, 113 insertions(+), 16 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/lib/activity.php b/lib/activity.php index 33932ad0e..eace672a5 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -154,7 +154,15 @@ class PoCo PoCo::NS ); - array_push($urls, new PoCoURL($type, $value, $primary)); + $isPrimary = false; + + if (isset($primary) && $primary == 'true') { + $isPrimary = true; + } + + // @todo check to make sure a primary hasn't already been added + + array_push($urls, new PoCoURL($type, $value, $isPrimary)); } return $urls; } @@ -213,6 +221,15 @@ class PoCo return $poco; } + function getPrimaryURL() + { + foreach ($this->urls as $url) { + if ($url->primary) { + return $url; + } + } + } + function asString() { $xs = new XMLStringer(true); @@ -494,6 +511,12 @@ class ActivityObject $this->element = $element; + $this->geopoint = $this->_childContent( + $element, + ActivityContext::POINT, + ActivityContext::GEORSS + ); + if ($element->tagName == 'author') { $this->type = self::PERSON; // XXX: is this fair? @@ -759,22 +782,29 @@ class ActivityContext for ($i = 0; $i < $points->length; $i++) { $point = $points->item($i)->textContent; - $point = str_replace(',', ' ', $point); // per spec "treat commas as whitespace" - $point = preg_replace('/\s+/', ' ', $point); - $point = trim($point); - $coords = explode(' ', $point); - if (count($coords) == 2) { - list($lat, $lon) = $coords; - if (is_numeric($lat) && is_numeric($lon)) { - common_log(LOG_INFO, "Looking up location for $lat $lon from georss"); - return Location::fromLatLon($lat, $lon); - } - } - common_log(LOG_ERR, "Ignoring bogus georss:point value $point"); + return self::locationFromPoint($point); } return null; } + + // XXX: Move to ActivityUtils or Location? + static function locationFromPoint($point) + { + $point = str_replace(',', ' ', $point); // per spec "treat commas as whitespace" + $point = preg_replace('/\s+/', ' ', $point); + $point = trim($point); + $coords = explode(' ', $point); + if (count($coords) == 2) { + list($lat, $lon) = $coords; + if (is_numeric($lat) && is_numeric($lon)) { + common_log(LOG_INFO, "Looking up location for $lat $lon from georss point"); + return Location::fromLatLon($lat, $lon); + } + } + common_log(LOG_ERR, "Ignoring bogus georss:point value $point"); + return null; + } } /** diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 5e38a523e..144fdfa8b 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1118,11 +1118,20 @@ class Ostatus_profile extends Memcached_DataObject $profile->profileurl = $hints['profileurl']; } - // @fixme bio + $profile->bio = self::getActivityObjectBio($object, $hints); + $profile->location = self::getActivityObjectLocation($object, $hints); + $profile->homepage = self::getActivityObjectHomepage($object, $hints); + + if (!empty($object->geopoint)) { + $location = ActivityContext::locationFromPoint($object->geopoint); + if (!empty($location)) { + $profile->lat = $location->lat; + $profile->lon = $location->lon; + } + } + // @fixme tags/categories - // @fixme location? // @todo tags from categories - // @todo lat/lon/location? if ($profile->id) { common_log(LOG_DEBUG, "Updating OStatus profile $profile->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true)); @@ -1154,6 +1163,62 @@ class Ostatus_profile extends Memcached_DataObject } } + protected static function getActivityObjectHomepage($object, $hints=array()) + { + $homepage = null; + $poco = $object->poco; + + if (!empty($poco)) { + $url = $poco->getPrimaryURL(); + if ($url->type == 'homepage') { + $homepage = $url->value; + } + } + + // @todo Try for a another PoCo URL? + + return $homepage; + } + + protected static function getActivityObjectLocation($object, $hints=array()) + { + $location = null; + + if (!empty($object->poco)) { + if (isset($object->poco->address->formatted)) { + $location = $object->poco->address->formatted; + if (mb_strlen($location) > 255) { + $location = mb_substr($note, 0, 255 - 3) . ' … '; + } + } + } + + // @todo Try to find location some othe way? Via goerss point? + + return $location; + } + + protected static function getActivityObjectBio($object, $hints=array()) + { + $bio = null; + + if (!empty($object->poco)) { + $note = $object->poco->note; + if (!empty($note)) { + if (mb_strlen($note) > Profile::maxBio()) { + // XXX: truncate ok? + $bio = mb_substr($note, 0, Profile::maxBio() - 3) . ' … '; + } else { + $bio = $note; + } + } + } + + // @todo Try to get bio info some other way? + + return $bio; + } + protected static function getActivityObjectNickname($object, $hints=array()) { if ($object->poco) { diff --git a/tests/ActivityParseTests.php b/tests/ActivityParseTests.php index 5de97d2e2..d1d871734 100644 --- a/tests/ActivityParseTests.php +++ b/tests/ActivityParseTests.php @@ -133,6 +133,8 @@ class ActivityParseTests extends PHPUnit_Framework_TestCase $this->assertEquals($poco->urls[0]->type, 'homepage'); $this->assertEquals($poco->urls[0]->value, 'http://example.com/blog.html'); $this->assertEquals($poco->urls[0]->primary, 'true'); + $this->assertEquals($act->actor->geopoint, '37.7749295 -122.4194155'); + } } -- cgit v1.2.3-54-g00ecf From 942521ef3058ee4416d737a144f7e48d9f0f342c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 24 Feb 2010 22:02:43 -0500 Subject: if OStatus post is too long, show the summary and save it as an HTML attachment --- plugins/OStatus/classes/Ostatus_profile.php | 60 +++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 4f73fd65b..10c2ff87c 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -607,6 +607,32 @@ class Ostatus_profile extends Memcached_DataObject $rendered = $this->purify($activity->object->content); $content = html_entity_decode(strip_tags($rendered)); + $shortened = content_shorten_links($content); + + // If it's too long, try using the summary, and make the + // HTML an attachment. + + $attachment = null; + + if (Notice::contentTooLong($shortened)) { + $attachment = $this->saveHTMLFile($activity->object->title, $rendered); + $summary = $activity->object->summary; + if (empty($summary)) { + $summary = $content; + } + $shortSummary = content_shorten_links($summary); + if (Notice::contentTooLong($shortSummary)) { + $url = common_shorten_url(common_local_url('attachment', + array('attachment' => $attachment->id))); + $shortSummary = substr($shortSummary, + 0, + Notice::maxContent() - (mb_strlen($url) + 2)); + $shortSummary .= '… ' . $url; + $content = $shortSummary; + $rendered = common_render_text($content); + } + } + $options = array('is_local' => Notice::REMOTE_OMB, 'url' => $sourceUrl, 'uri' => $sourceUri, @@ -654,6 +680,9 @@ class Ostatus_profile extends Memcached_DataObject $options); if ($saved) { Ostatus_source::saveNew($saved, $this, $method); + if (!empty($attachment)) { + File_to_post::processNew($attachment->id, $saved->id); + } } } catch (Exception $e) { common_log(LOG_ERR, "OStatus save of remote message $sourceUri failed: " . $e->getMessage()); @@ -1386,4 +1415,35 @@ class Ostatus_profile extends Memcached_DataObject return null; } + + function saveHTMLFile($title, $rendered) + { + $final = sprintf("\n%s". + '
    %s
    ', + htmlspecialchars($title), + $rendered); + + $filename = File::filename($this->localProfile(), + 'ostatus', // ignored? + 'text/html'); + + $filepath = File::path($filename); + + file_put_contents($filepath, $final); + + $file = new File; + + $file->filename = $filename; + $file->url = File::url($filename); + $file->size = filesize($filepath); + $file->date = time(); + $file->mimetype = 'text/html'; + + $file_id = $file->insert(); + + if ($file_id === false) { + common_log_db_error($file, "INSERT", __FILE__); + throw new ServerException(_('Could not store HTML content of long post as file.')); + } + } } -- cgit v1.2.3-54-g00ecf From b08a5271395b92e01a17f7298cad7bb203149a7b Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 24 Feb 2010 22:22:42 -0500 Subject: content_* to common_* --- plugins/OStatus/classes/Ostatus_profile.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 10c2ff87c..e9c77654d 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -607,7 +607,7 @@ class Ostatus_profile extends Memcached_DataObject $rendered = $this->purify($activity->object->content); $content = html_entity_decode(strip_tags($rendered)); - $shortened = content_shorten_links($content); + $shortened = common_shorten_links($content); // If it's too long, try using the summary, and make the // HTML an attachment. @@ -620,7 +620,7 @@ class Ostatus_profile extends Memcached_DataObject if (empty($summary)) { $summary = $content; } - $shortSummary = content_shorten_links($summary); + $shortSummary = common_shorten_links($summary); if (Notice::contentTooLong($shortSummary)) { $url = common_shorten_url(common_local_url('attachment', array('attachment' => $attachment->id))); -- cgit v1.2.3-54-g00ecf From 374c488cf136b378d4fb47c4a6add309694e9146 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Wed, 24 Feb 2010 22:26:40 -0500 Subject: return attachement from saveHTMLFile() --- plugins/OStatus/classes/Ostatus_profile.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index e9c77654d..300e38c05 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1445,5 +1445,7 @@ class Ostatus_profile extends Memcached_DataObject common_log_db_error($file, "INSERT", __FILE__); throw new ServerException(_('Could not store HTML content of long post as file.')); } + + return $file; } } -- cgit v1.2.3-54-g00ecf From 79c0d52daa92b60e8eed80fa9459367c26f97122 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 25 Feb 2010 11:26:33 -0800 Subject: OStatus: save categories from the Atom entry as hashtags. --- classes/Notice.php | 37 +++++++++++++++-- lib/activity.php | 61 ++++++++++++++++++++++++++++- lib/distribqueuehandler.php | 12 ------ plugins/OStatus/classes/Ostatus_profile.php | 14 ++++++- 4 files changed, 106 insertions(+), 18 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/classes/Notice.php b/classes/Notice.php index e8d5c45cb..46c5ebb37 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -121,6 +121,9 @@ class Notice extends Memcached_DataObject $result = parent::delete(); } + /** + * Extract #hashtags from this notice's content and save them to the database. + */ function saveTags() { /* extract all #hastags */ @@ -129,14 +132,22 @@ class Notice extends Memcached_DataObject return true; } + /* Add them to the database */ + return $this->saveKnownTags($match[1]); + } + + /** + * Record the given set of hash tags in the db for this notice. + * Given tag strings will be normalized and checked for dupes. + */ + function saveKnownTags($hashtags) + { //turn each into their canonical tag //this is needed to remove dupes before saving e.g. #hash.tag = #hashtag - $hashtags = array(); - for($i=0; $isaveTag($hashtag); @@ -145,6 +156,10 @@ class Notice extends Memcached_DataObject return true; } + /** + * Record a single hash tag as associated with this notice. + * Tag format and uniqueness must be validated by caller. + */ function saveTag($hashtag) { $tag = new Notice_tag(); @@ -194,6 +209,8 @@ class Notice extends Memcached_DataObject * place of extracting @-replies from content. * array 'groups' list of group IDs to deliver to, in place of * extracting ! tags from content + * array 'tags' list of hashtag strings to save with the notice + * in place of extracting # tags from content * @fixme tag override * * @return Notice @@ -343,6 +360,8 @@ class Notice extends Memcached_DataObject $notice->blowOnInsert(); + // Save per-notice metadata... + if (isset($replies)) { $notice->saveKnownReplies($replies); } else { @@ -355,6 +374,16 @@ class Notice extends Memcached_DataObject $notice->saveGroups(); } + if (isset($tags)) { + $notice->saveKnownTags($tags); + } else { + $notice->saveTags(); + } + + // @fixme pass in data for URLs too? + $notice->saveUrls(); + + // Prepare inbox delivery, may be queued to background. $notice->distribute(); return $notice; diff --git a/lib/activity.php b/lib/activity.php index 3de5f62c7..e592aad6f 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -859,6 +859,7 @@ class Activity public $content; // HTML content of activity public $id; // ID of the activity public $title; // title of the activity + public $categories = array(); // list of AtomCategory objects /** * Turns a regular old Atom into a magical activity @@ -947,6 +948,14 @@ class Activity $this->summary = ActivityUtils::childContent($entry, 'summary'); $this->id = ActivityUtils::childContent($entry, 'id'); $this->content = ActivityUtils::getContent($entry); + + $catEls = $entry->getElementsByTagNameNS(self::ATOM, 'category'); + if ($catEls) { + for ($i = 0; $i < $catEls->length; $i++) { + $catEl = $catEls->item($i); + $this->categories[] = new AtomCategory($catEl); + } + } } /** @@ -1011,6 +1020,10 @@ class Activity $xs->raw($this->target->asString('activity:target')); } + foreach ($this->categories as $cat) { + $xs->raw($cat->asString()); + } + $xs->elementEnd('entry'); return $xs->getString(); @@ -1020,4 +1033,50 @@ class Activity { return ActivityUtils::child($element, $tag, $namespace); } -} \ No newline at end of file +} + +class AtomCategory +{ + public $term; + public $scheme; + public $label; + + function __construct($element=null) + { + if ($element && $element->attributes) { + $this->term = $this->extract($element, 'term'); + $this->scheme = $this->extract($element, 'scheme'); + $this->label = $this->extract($element, 'label'); + } + } + + protected function extract($element, $attrib) + { + $node = $element->attributes->getNamedItemNS(Activity::ATOM, $attrib); + if ($node) { + return trim($node->textContent); + } + $node = $element->attributes->getNamedItem($attrib); + if ($node) { + return trim($node->textContent); + } + return null; + } + + function asString() + { + $attribs = array(); + if ($this->term !== null) { + $attribs['term'] = $this->term; + } + if ($this->scheme !== null) { + $attribs['scheme'] = $this->scheme; + } + if ($this->label !== null) { + $attribs['label'] = $this->label; + } + $xs = new XMLStringer(); + $xs->element('category', $attribs); + return $xs->asString(); + } +} diff --git a/lib/distribqueuehandler.php b/lib/distribqueuehandler.php index dc183fb36..d2be7a92c 100644 --- a/lib/distribqueuehandler.php +++ b/lib/distribqueuehandler.php @@ -62,24 +62,12 @@ class DistribQueueHandler { // XXX: do we need to change this for remote users? - try { - $notice->saveTags(); - } catch (Exception $e) { - $this->logit($notice, $e); - } - try { $notice->addToInboxes(); } catch (Exception $e) { $this->logit($notice, $e); } - try { - $notice->saveUrls(); - } catch (Exception $e) { - $this->logit($notice, $e); - } - try { Event::handle('EndNoticeSave', array($notice)); // Enqueue for other handlers diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 300e38c05..d66939399 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -638,7 +638,9 @@ class Ostatus_profile extends Memcached_DataObject 'uri' => $sourceUri, 'rendered' => $rendered, 'replies' => array(), - 'groups' => array()); + 'groups' => array(), + 'tags' => array()); + // Check for optional attributes... @@ -673,6 +675,16 @@ class Ostatus_profile extends Memcached_DataObject } } + // Atom categories <-> hashtags + foreach ($activity->categories as $cat) { + if ($cat->term) { + $term = common_canonical_tag($cat->term); + if ($term) { + $options['tags'][] = $term; + } + } + } + try { $saved = Notice::saveNew($oprofile->profile_id, $content, -- cgit v1.2.3-54-g00ecf From b5b5184c885e74d9532409a5962f851c4baf41c4 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 25 Feb 2010 13:02:08 -0800 Subject: OStatus: fix remote groups to work with new user_groups/local_groups split. - fix generation so we get the profile info (what's available so far) - use id instead of nickname for group join/leave forms so we can join/leave remote groups while the rest of the groups UI remains limited to local groups (plugins are responsible for making sure remote notifications and permission checks are done) - fix remote notification when joining group through OStatus's remote subscribe form --- actions/joingroup.php | 39 ++++++++++++++++------------- actions/leavegroup.php | 39 ++++++++++++++++------------- classes/User_group.php | 2 +- lib/activity.php | 7 ++++-- lib/joinform.php | 2 +- lib/leaveform.php | 2 +- lib/router.php | 3 +++ plugins/OStatus/actions/ostatussub.php | 14 ++++++++--- plugins/OStatus/classes/Ostatus_profile.php | 26 +++++++++++-------- 9 files changed, 79 insertions(+), 55 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/actions/joingroup.php b/actions/joingroup.php index ba642f712..f87e5dae2 100644 --- a/actions/joingroup.php +++ b/actions/joingroup.php @@ -62,30 +62,33 @@ class JoingroupAction extends Action } $nickname_arg = $this->trimmed('nickname'); - $nickname = common_canonical_nickname($nickname_arg); - - // Permanent redirect on non-canonical nickname + $id = intval($this->arg('id')); + if ($id) { + $this->group = User_group::staticGet('id', $id); + } else if ($nickname_arg) { + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('leavegroup', $args), 301); + return false; + } - if ($nickname_arg != $nickname) { - $args = array('nickname' => $nickname); - common_redirect(common_local_url('joingroup', $args), 301); - return false; - } + $local = Local_group::staticGet('nickname', $nickname); - if (!$nickname) { - $this->clientError(_('No nickname.'), 404); - return false; - } - - $local = Local_group::staticGet('nickname', $nickname); + if (!$local) { + $this->clientError(_('No such group.'), 404); + return false; + } - if (!$local) { - $this->clientError(_('No such group.'), 404); + $this->group = User_group::staticGet('id', $local->group_id); + } else { + $this->clientError(_('No nickname or ID.'), 404); return false; } - $this->group = User_group::staticGet('id', $local->group_id); - if (!$this->group) { $this->clientError(_('No such group.'), 404); return false; diff --git a/actions/leavegroup.php b/actions/leavegroup.php index 222d4c1b4..329b5aafe 100644 --- a/actions/leavegroup.php +++ b/actions/leavegroup.php @@ -62,30 +62,33 @@ class LeavegroupAction extends Action } $nickname_arg = $this->trimmed('nickname'); - $nickname = common_canonical_nickname($nickname_arg); - - // Permanent redirect on non-canonical nickname + $id = intval($this->arg('id')); + if ($id) { + $this->group = User_group::staticGet('id', $id); + } else if ($nickname_arg) { + $nickname = common_canonical_nickname($nickname_arg); + + // Permanent redirect on non-canonical nickname + + if ($nickname_arg != $nickname) { + $args = array('nickname' => $nickname); + common_redirect(common_local_url('leavegroup', $args), 301); + return false; + } - if ($nickname_arg != $nickname) { - $args = array('nickname' => $nickname); - common_redirect(common_local_url('leavegroup', $args), 301); - return false; - } + $local = Local_group::staticGet('nickname', $nickname); - if (!$nickname) { - $this->clientError(_('No nickname.'), 404); - return false; - } - - $local = Local_group::staticGet('nickname', $nickname); + if (!$local) { + $this->clientError(_('No such group.'), 404); + return false; + } - if (!$local) { - $this->clientError(_('No such group.'), 404); + $this->group = User_group::staticGet('id', $local->group_id); + } else { + $this->clientError(_('No nickname or ID.'), 404); return false; } - $this->group = User_group::staticGet('id', $local->group_id); - if (!$this->group) { $this->clientError(_('No such group.'), 404); return false; diff --git a/classes/User_group.php b/classes/User_group.php index f24bef764..2a58d8895 100644 --- a/classes/User_group.php +++ b/classes/User_group.php @@ -404,7 +404,7 @@ class User_group extends Memcached_DataObject $xs = new XMLStringer(true); $xs->elementStart('activity:subject'); - $xs->element('activity:object', null, 'http://activitystrea.ms/schema/1.0/group'); + $xs->element('activity:object-type', null, 'http://activitystrea.ms/schema/1.0/group'); $xs->element('id', null, $this->permalink()); $xs->element('title', null, $this->getBestName()); $xs->element( diff --git a/lib/activity.php b/lib/activity.php index e592aad6f..4e7ff5336 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -610,7 +610,10 @@ class ActivityObject $object->id = $profile->getUri(); $object->title = $profile->getBestName(); $object->link = $profile->profileurl; - $object->avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); + if ($avatar) { + $object->avatar = $avatar->displayUrl(); + } if (isset($profile->lat) && isset($profile->lon)) { $object->geopoint = (float)$profile->lat . ' ' . (float)$profile->lon; @@ -664,7 +667,7 @@ class ActivityObject 'rel' => 'avatar', 'href' => empty($this->avatar) ? Avatar::defaultImage(AVATAR_PROFILE_SIZE) - : $this->avatar->displayUrl() + : $this->avatar ), null ); diff --git a/lib/joinform.php b/lib/joinform.php index aefb553aa..aa8bc20e2 100644 --- a/lib/joinform.php +++ b/lib/joinform.php @@ -100,7 +100,7 @@ class JoinForm extends Form function action() { return common_local_url('joingroup', - array('nickname' => $this->group->nickname)); + array('id' => $this->group->id)); } /** diff --git a/lib/leaveform.php b/lib/leaveform.php index e63d96ee8..5469b5704 100644 --- a/lib/leaveform.php +++ b/lib/leaveform.php @@ -100,7 +100,7 @@ class LeaveForm extends Form function action() { return common_local_url('leavegroup', - array('nickname' => $this->group->nickname)); + array('id' => $this->group->id)); } /** diff --git a/lib/router.php b/lib/router.php index 987d0152e..0e15d83b9 100644 --- a/lib/router.php +++ b/lib/router.php @@ -247,6 +247,9 @@ class Router $m->connect('group/:nickname/'.$v, array('action' => $v.'group'), array('nickname' => '[a-zA-Z0-9]+')); + $m->connect('group/:id/id/'.$v, + array('action' => $v.'group'), + array('id' => '[0-9]+')); } foreach (array('members', 'logo', 'rss', 'designsettings') as $n) { diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index 12832cdcf..aae22f868 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -333,10 +333,18 @@ class OStatusSubAction extends Action $group = $this->oprofile->localGroup(); if ($user->isMember($group)) { $this->showForm(_m('Already a member!')); - } elseif (Group_member::join($this->oprofile->group_id, $user->id)) { - $this->successGroup(); + return; + } + if (Event::handle('StartJoinGroup', array($group, $user))) { + $ok = Group_member::join($this->oprofile->group_id, $user->id); + if ($ok) { + Event::handle('EndJoinGroup', array($group, $user)); + $this->successGroup(); + } else { + $this->showForm(_m('Remote group join failed!')); + } } else { - $this->showForm(_m('Remote group join failed!')); + $this->showForm(_m('Remote group join aborted!')); } } else { $local = $this->oprofile->localProfile(); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index d66939399..f23017077 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -162,7 +162,7 @@ class Ostatus_profile extends Memcached_DataObject 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'gif' => 'image/gif'); - $extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION); + $extension = pathinfo(parse_url($object->avatar, PHP_URL_PATH), PATHINFO_EXTENSION); if (isset($map[$extension])) { // @fixme this ain't used/saved yet $object->avatarType = $map[$extension]; @@ -332,6 +332,9 @@ class Ostatus_profile extends Memcached_DataObject */ public function unsubscribe() { $feedsub = FeedSub::staticGet('uri', $this->feeduri); + if (!$feedsub) { + return true; + } if ($feedsub->sub_state == 'active') { return $feedsub->unsubscribe(); } else if ($feedsub->sub_state == '' || $feedsub->sub_state == 'inactive' || $feedsub->sub_state == 'unsubscribe') { @@ -356,7 +359,7 @@ class Ostatus_profile extends Memcached_DataObject $count = $this->localProfile()->subscriberCount(); } if ($count == 0) { - common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $oprofile->feeduri"); + common_log(LOG_INFO, "Unsubscribing from now-unused remote feed $this->feeduri"); $this->unsubscribe(); return true; } else { @@ -1095,8 +1098,8 @@ class Ostatus_profile extends Memcached_DataObject if ($object->type == ActivityObject::PERSON) { $profile = new Profile(); + $profile->created = common_sql_now(); self::updateProfile($profile, $object, $hints); - $profile->created = common_sql_now(); $oprofile->profile_id = $profile->insert(); if (!$oprofile->profile_id) { @@ -1104,6 +1107,7 @@ class Ostatus_profile extends Memcached_DataObject } } else { $group = new User_group(); + $group->uri = $homeuri; $group->created = common_sql_now(); self::updateGroup($group, $object, $hints); @@ -1183,19 +1187,19 @@ class Ostatus_profile extends Memcached_DataObject { $orig = clone($group); - // @fixme need to make nick unique etc *hack hack* $group->nickname = self::getActivityObjectNickname($object, $hints); $group->fullname = $object->title; - // @fixme no canonical profileurl; using homepage instead for now - $group->homepage = $object->id; + if (!empty($object->link)) { + $group->mainpage = $object->link; + } else if (array_key_exists('profileurl', $hints)) { + $group->mainpage = $hints['profileurl']; + } - // @fixme homepage - // @fixme bio - // @fixme tags/categories - // @fixme location? // @todo tags from categories - // @todo lat/lon/location? + $group->description = self::getActivityObjectBio($object, $hints); + $group->location = self::getActivityObjectLocation($object, $hints); + $group->homepage = self::getActivityObjectHomepage($object, $hints); if ($group->id) { common_log(LOG_DEBUG, "Updating OStatus group $group->id from remote info $object->id: " . var_export($object, true) . var_export($hints, true)); -- cgit v1.2.3-54-g00ecf From 5ae64a7adbe7631be918f25eeda383f77b42b758 Mon Sep 17 00:00:00 2001 From: James Walker Date: Thu, 25 Feb 2010 17:34:56 -0500 Subject: moving references to Webfinger to Discovery --- plugins/OStatus/actions/ostatusinit.php | 4 ++-- plugins/OStatus/actions/webfinger.php | 6 +++--- plugins/OStatus/classes/Ostatus_profile.php | 8 ++++---- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/actions/ostatusinit.php b/plugins/OStatus/actions/ostatusinit.php index 3f2f6368f..5c8575595 100644 --- a/plugins/OStatus/actions/ostatusinit.php +++ b/plugins/OStatus/actions/ostatusinit.php @@ -131,9 +131,9 @@ class OStatusInitAction extends Action function connectWebfinger($acct) { - $w = new Webfinger; + $disco = new Discovery; - $result = $w->lookup($acct); + $result = $disco->lookup($acct); if (!$result) { $this->clientError(_m("Couldn't look up OStatus account profile.")); } diff --git a/plugins/OStatus/actions/webfinger.php b/plugins/OStatus/actions/webfinger.php index 34336a903..fa41b2444 100644 --- a/plugins/OStatus/actions/webfinger.php +++ b/plugins/OStatus/actions/webfinger.php @@ -40,7 +40,7 @@ class WebfingerAction extends Action function handle() { - $acct = Webfinger::normalize($this->uri); + $acct = Discovery::normalize($this->uri); $xrd = new XRD(); @@ -55,11 +55,11 @@ class WebfingerAction extends Action $xrd->subject = $this->uri; $xrd->alias[] = common_profile_url($nick); - $xrd->links[] = array('rel' => Webfinger::PROFILEPAGE, + $xrd->links[] = array('rel' => Discovery::PROFILEPAGE, 'type' => 'text/html', 'href' => common_profile_url($nick)); - $xrd->links[] = array('rel' => Webfinger::UPDATESFROM, + $xrd->links[] = array('rel' => Discovery::UPDATESFROM, 'href' => common_local_url('ApiTimelineUser', array('id' => $this->user->id, 'format' => 'atom')), diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index f23017077..0fec8fc43 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1328,9 +1328,9 @@ class Ostatus_profile extends Memcached_DataObject // Now, try some discovery - $wf = new Webfinger(); + $disco = new Discovery(); - $result = $wf->lookup($addr); + $result = $disco->lookup($addr); if (!$result) { return null; @@ -1338,13 +1338,13 @@ class Ostatus_profile extends Memcached_DataObject foreach ($result->links as $link) { switch ($link['rel']) { - case Webfinger::PROFILEPAGE: + case Discovery::PROFILEPAGE: $profileUrl = $link['href']; break; case 'salmon': $salmonEndpoint = $link['href']; break; - case Webfinger::UPDATESFROM: + case Discovery::UPDATESFROM: $feedUrl = $link['href']; break; default: -- cgit v1.2.3-54-g00ecf From 45435a7c033d9c5acf0352bc44408667c5690198 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Fri, 26 Feb 2010 00:01:22 +0000 Subject: OStatus: if no available and no profileurl hint passed in, use object id if it's an HTTP(S) URL. Fixes profile link for Google accounts. --- plugins/OStatus/classes/Ostatus_profile.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index f23017077..86a3c9354 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1160,6 +1160,8 @@ class Ostatus_profile extends Memcached_DataObject $profile->profileurl = $object->link; } else if (array_key_exists('profileurl', $hints)) { $profile->profileurl = $hints['profileurl']; + } else if (Validate::uri($object->id, array('allowed_schemes' => array('http', 'https')))) { + $profile->profileurl = $object->id; } $profile->bio = self::getActivityObjectBio($object, $hints); -- cgit v1.2.3-54-g00ecf From 7922edb5b6c4ad55a1b81fa16c93e5656b676b26 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 25 Feb 2010 16:06:49 -0800 Subject: Add lots of fun avatars to our Atom output --- classes/Notice.php | 1 + lib/activity.php | 146 ++++++++++++++++++++++------ lib/atomnoticefeed.php | 5 + plugins/OStatus/classes/Ostatus_profile.php | 3 +- 4 files changed, 124 insertions(+), 31 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/classes/Notice.php b/classes/Notice.php index 46c5ebb37..ac4640534 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -1096,6 +1096,7 @@ class Notice extends Memcached_DataObject 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:georss' => 'http://www.georss.org/georss', 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', + 'xmlns:media' => 'http://purl.org/syndication/atommedia', 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0'); } else { diff --git a/lib/activity.php b/lib/activity.php index 30cdc5a56..0f30e8bf5 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -465,6 +465,57 @@ class ActivityUtils } } +// XXX: Arg! This wouldn't be necessary if we used Avatars conistently +class AvatarLink +{ + public $url; + public $type; + public $size; + public $width; + + static function fromAvatar($avatar) + { + if (empty($avatar)) { + return null; + } + $alink = new AvatarLink(); + $alink->type = $avatar->mediatype; + $alink->height = $avatar->mediatype; + $alink->width = $avatar->width; + $alink->url = $avatar->displayUrl(); + return $alink; + } + + static function fromFilename($filename, $size) + { + $alink = new AvatarLink(); + $alink->url = $filename; + $alink->height = $size; + if (!empty($filename)) { + $alink->width = $size; + $alink->type = self::mediatype($filename); + } else { + $alink->url = User_group::defaultLogo($size); + $alink->type = 'image/png'; + } + return $alink; + } + + // yuck! + static function mediatype($filename) { + $ext = strtolower(end(explode('.', $filename))); + if ($ext == 'jpeg') { + $ext = 'jpg'; + } + // hope we don't support any others + $types = array('png', 'gif', 'jpg', 'jpeg'); + if (in_array($ext, $types)) { + return 'image/' . $ext; + } + return null; + } +} + /** * A noun-ish thing in the activity universe * @@ -521,7 +572,7 @@ class ActivityObject public $content; public $link; public $source; - public $avatar; + public $avatarLinks = array(); public $geopoint; public $poco; public $displayName; @@ -641,13 +692,40 @@ class ActivityObject $object->id = $profile->getUri(); $object->title = $profile->getBestName(); $object->link = $profile->profileurl; - $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE); - if ($avatar) { - $object->avatar = $avatar->displayUrl(); + + $orig = $profile->getOriginalAvatar(); + + if (!empty($orig)) { + $object->avatarLinks[] = AvatarLink::fromAvatar($orig); + } + + $sizes = array( + AVATAR_PROFILE_SIZE, + AVATAR_STREAM_SIZE, + AVATAR_MINI_SIZE + ); + + foreach ($sizes as $size) { + + $alink = null; + $avatar = $profile->getAvatar($size); + + if (!empty($avatar)) { + $alink = AvatarLink::fromAvatar($avatar); + } else { + $alink = new AvatarLink(); + $alink->type = 'image/png'; + $alink->height = $size; + $alink->width = $size; + $alink->url = Avatar::defaultImage($size); + } + + $object->avatarLinks[] = $alink; } if (isset($profile->lat) && isset($profile->lon)) { - $object->geopoint = (float)$profile->lat . ' ' . (float)$profile->lon; + $object->geopoint = (float)$profile->lat + . ' ' . (float)$profile->lon; } $object->poco = PoCo::fromProfile($profile); @@ -663,13 +741,28 @@ class ActivityObject $object->id = $group->getUri(); $object->title = $group->getBestName(); $object->link = $group->getUri(); - $object->avatar = $group->getAvatar(); + + $object->avatarLinks[] = AvatarLink::fromFilename( + $group->homepage_logo, + AVATAR_PROFILE_SIZE + ); + + $object->avatarLinks[] = AvatarLink::fromFilename( + $group->stream_logo, + AVATAR_STREAM_SIZE + ); + + $object->avatarLinks[] = AvatarLink::fromFilename( + $group->mini_logo, + AVATAR_MINI_SIZE + ); $object->poco = PoCo::fromGroup($group); return $object; } + function asString($tag='activity:object') { $xs = new XMLStringer(true); @@ -705,29 +798,21 @@ class ActivityObject ); } - if ($this->type == ActivityObject::PERSON) { - $xs->element( - 'link', array( - 'type' => empty($this->avatar) ? 'image/png' : $this->avatar->mediatype, - 'rel' => 'avatar', - 'href' => empty($this->avatar) - ? Avatar::defaultImage(AVATAR_PROFILE_SIZE) - : $this->avatar - ), - null - ); - } - - // XXX: Gotta figure out mime-type! Gar. - - if ($this->type == ActivityObject::GROUP) { - $xs->element( - 'link', array( - 'rel' => 'avatar', - 'href' => $this->avatar - ), - null - ); + if ($this->type == ActivityObject::PERSON + || $this->type == ActivityObject::GROUP) { + + foreach ($this->avatarLinks as $avatar) { + $xs->element( + 'link', array( + 'rel' => 'avatar', + 'type' => $avatar->type, + 'media:width' => $avatar->width, + 'media:height' => $avatar->height, + 'href' => $avatar->url + ), + null + ); + } } if (!empty($this->geopoint)) { @@ -1038,7 +1123,8 @@ class Activity 'xmlns:activity' => 'http://activitystrea.ms/spec/1.0/', 'xmlns:georss' => 'http://www.georss.org/georss', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', - 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0'); + 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', + 'xmlns:media' => 'http://purl.org/syndication/atommedia'); } else { $attrs = array(); } diff --git a/lib/atomnoticefeed.php b/lib/atomnoticefeed.php index d2bf2a416..3c3556cb9 100644 --- a/lib/atomnoticefeed.php +++ b/lib/atomnoticefeed.php @@ -64,6 +64,11 @@ class AtomNoticeFeed extends Atom10Feed 'http://activitystrea.ms/spec/1.0/' ); + $this->addNamespace( + 'media', + 'http://purl.org/syndication/atommedia' + ); + $this->addNamespace( 'poco', 'http://portablecontacts.net/spec/1.0' diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index f23017077..7026d82f2 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -401,7 +401,8 @@ class Ostatus_profile extends Memcached_DataObject 'xmlns:thr' => 'http://purl.org/syndication/thread/1.0', 'xmlns:georss' => 'http://www.georss.org/georss', 'xmlns:ostatus' => 'http://ostatus.org/schema/1.0', - 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0'); + 'xmlns:poco' => 'http://portablecontacts.net/spec/1.0', + 'xmlns:media' => 'http://purl.org/syndication/atommedia'); $entry = new XMLStringer(); $entry->elementStart('entry', $attributes); -- cgit v1.2.3-54-g00ecf From 894b221e8aec57768d6804ee3bf53c85196e09bf Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 25 Feb 2010 19:58:51 -0500 Subject: Parse an hcard for hints, if available --- plugins/OStatus/classes/Ostatus_profile.php | 119 ++++++++++++++++++++++++---- 1 file changed, 104 insertions(+), 15 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index f23017077..2d517ed22 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -644,7 +644,6 @@ class Ostatus_profile extends Memcached_DataObject 'groups' => array(), 'tags' => array()); - // Check for optional attributes... if (!empty($activity->time)) { @@ -1155,7 +1154,13 @@ class Ostatus_profile extends Memcached_DataObject $orig = clone($profile); $profile->nickname = self::getActivityObjectNickname($object, $hints); - $profile->fullname = $object->title; + + if (!empty($object->title)) { + $profile->fullname = $object->title; + } else if (array_key_exists('fullname', $hints)) { + $profile->fullname = $hints['fullname']; + } + if (!empty($object->link)) { $profile->profileurl = $object->link; } else if (array_key_exists('profileurl', $hints)) { @@ -1228,12 +1233,16 @@ class Ostatus_profile extends Memcached_DataObject { $location = null; - if (!empty($object->poco)) { - if (isset($object->poco->address->formatted)) { - $location = $object->poco->address->formatted; - if (mb_strlen($location) > 255) { - $location = mb_substr($note, 0, 255 - 3) . ' … '; - } + if (!empty($object->poco) && + isset($object->poco->address->formatted)) { + $location = $object->poco->address->formatted; + } else if (array_key_exists('location', $hints)) { + $location = $hints['location']; + } + + if (!empty($location)) { + if (mb_strlen($location) > 255) { + $location = mb_substr($note, 0, 255 - 3) . ' … '; } } @@ -1248,13 +1257,16 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($object->poco)) { $note = $object->poco->note; - if (!empty($note)) { - if (mb_strlen($note) > Profile::maxBio()) { - // XXX: truncate ok? - $bio = mb_substr($note, 0, Profile::maxBio() - 3) . ' … '; - } else { - $bio = $note; - } + } else if (array_key_exists('bio', $hints)) { + $note = $hints['bio']; + } + + if (!empty($note)) { + if (Profile::bioTooLong($note)) { + // XXX: truncate ok? + $bio = mb_substr($note, 0, Profile::maxBio() - 3) . ' … '; + } else { + $bio = $note; } } @@ -1270,10 +1282,15 @@ class Ostatus_profile extends Memcached_DataObject return common_nicknamize($object->poco->preferredUsername); } } + if (!empty($object->nickname)) { return common_nicknamize($object->nickname); } + if (array_key_exists('nickname', $hints)) { + return $hints['nickname']; + } + // Try the definitive ID $nickname = self::nicknameFromURI($object->id); @@ -1347,6 +1364,9 @@ class Ostatus_profile extends Memcached_DataObject case Webfinger::UPDATESFROM: $feedUrl = $link['href']; break; + case Webfinger::HCARD: + $hcardUrl = $link['href']; + break; default: common_log(LOG_NOTICE, "Don't know what to do with rel = '{$link['rel']}'"); break; @@ -1358,6 +1378,12 @@ class Ostatus_profile extends Memcached_DataObject 'feedurl' => $feedUrl, 'salmon' => $salmonEndpoint); + if (isset($hcardUrl)) { + $hcardHints = self::slurpHcard($hcardUrl); + // Note: Webfinger > hcard + $hints = array_merge($hcardHints, $hints); + } + // If we got a feed URL, try that if (isset($feedUrl)) { @@ -1464,4 +1490,67 @@ class Ostatus_profile extends Memcached_DataObject return $file; } + + protected static function slurpHcard($url) + { + set_include_path(get_include_path() . PATH_SEPARATOR . INSTALLDIR . '/plugins/OStatus/extlib/hkit/'); + require_once('hkit.class.php'); + + $h = new hKit; + + // Google Buzz hcards need to be tidied. Probably others too. + + $h->tidy_mode = 'proxy'; // 'proxy', 'exec', 'php' or 'none' + + // Get by URL + $hcards = $h->getByURL('hcard', $url); + + if (empty($hcards)) { + return array(); + } + + // @fixme more intelligent guess on multi-hcard pages + $hcard = $hcards[0]; + + $hints = array(); + + $hints['profileurl'] = $url; + + if (array_key_exists('nickname', $hcard)) { + $hints['nickname'] = $hcard['nickname']; + } + + if (array_key_exists('fn', $hcard)) { + $hints['fullname'] = $hcard['fn']; + } else if (array_key_exists('n', $hcard)) { + $hints['fullname'] = implode(' ', $hcard['n']); + } + + if (array_key_exists('photo', $hcard)) { + $hints['avatar'] = $hcard['photo']; + } + + if (array_key_exists('note', $hcard)) { + $hints['bio'] = $hcard['note']; + } + + if (array_key_exists('adr', $hcard)) { + if (is_string($hcard['adr'])) { + $hints['location'] = $hcard['adr']; + } else if (is_array($hcard['adr'])) { + $hints['location'] = implode(' ', $hcard['adr']); + } + } + + if (array_key_exists('url', $hcard)) { + if (is_string($hcard['url'])) { + $hints['homepage'] = $hcard['url']; + } else if (is_array($hcard['adr'])) { + // HACK get the last one; that's how our hcards look + $hints['homepage'] = $hcard['url'][count($hcard['url'])-1]; + } + } + + return $hints; + } } -- cgit v1.2.3-54-g00ecf From c693365ae7a27f82afef1765610c41a4bff33726 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 25 Feb 2010 20:29:52 -0500 Subject: cache results of webfinger lookups --- plugins/OStatus/classes/Ostatus_profile.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 8ca28432b..ad9170f5b 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1338,11 +1338,26 @@ class Ostatus_profile extends Memcached_DataObject public static function ensureWebfinger($addr) { + // First, try the cache + + $uri = self::cacheGet(sprintf('ostatus_profile:webfinger:%s', $addr)); + + if ($uri !== false) { + if (is_null($uri)) { + return null; + } + $oprofile = Ostatus_profile::staticGet('uri', $uri); + if (!empty($oprofile)) { + return $oprofile; + } + } + // First, look it up $oprofile = Ostatus_profile::staticGet('uri', 'acct:'.$addr); if (!empty($oprofile)) { + self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri); return $oprofile; } @@ -1353,6 +1368,7 @@ class Ostatus_profile extends Memcached_DataObject $result = $wf->lookup($addr); if (!$result) { + self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null); return null; } @@ -1392,6 +1408,7 @@ class Ostatus_profile extends Memcached_DataObject if (isset($feedUrl)) { try { $oprofile = self::ensureProfile($feedUrl, $hints); + self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri); return $oprofile; } catch (Exception $e) { common_log(LOG_WARNING, "Failed creating profile from feed URL '$feedUrl': " . $e->getMessage()); @@ -1404,6 +1421,7 @@ class Ostatus_profile extends Memcached_DataObject if (isset($profileUrl)) { try { $oprofile = self::ensureProfile($profileUrl, $hints); + self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri); return $oprofile; } catch (Exception $e) { common_log(LOG_WARNING, "Failed creating profile from profile URL '$profileUrl': " . $e->getMessage()); @@ -1455,6 +1473,7 @@ class Ostatus_profile extends Memcached_DataObject throw new Exception("Couldn't save ostatus_profile for '$addr'"); } + self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri); return $oprofile; } -- cgit v1.2.3-54-g00ecf From e197208a4a2ab742df481968e30cd99678be96ad Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 25 Feb 2010 21:07:52 -0500 Subject: info about discovery in Ostatus_profile::ensureWebfinger() --- plugins/OStatus/classes/Ostatus_profile.php | 2 ++ 1 file changed, 2 insertions(+) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index ad9170f5b..26d776d52 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1407,6 +1407,7 @@ class Ostatus_profile extends Memcached_DataObject if (isset($feedUrl)) { try { + common_log(LOG_INFO, "Discovery on acct:$addr with feed URL $feedUrl"); $oprofile = self::ensureProfile($feedUrl, $hints); self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri); return $oprofile; @@ -1420,6 +1421,7 @@ class Ostatus_profile extends Memcached_DataObject if (isset($profileUrl)) { try { + common_log(LOG_INFO, "Discovery on acct:$addr with profile URL $profileUrl"); $oprofile = self::ensureProfile($profileUrl, $hints); self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->uri); return $oprofile; -- cgit v1.2.3-54-g00ecf From 2feb09f4346e54805f68a9f677f5a94340875d8e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 25 Feb 2010 18:51:44 -0800 Subject: OStatus: pull best-sized avatar image (96x96 if found, otherwise largest, otherwise if none labeled takes the first) --- lib/activity.php | 43 +++++++++++++++++++++++++++-- plugins/OStatus/classes/Ostatus_profile.php | 16 +++++++++-- 2 files changed, 55 insertions(+), 4 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/lib/activity.php b/lib/activity.php index 0f30e8bf5..b64a82f0a 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -360,6 +360,25 @@ class ActivityUtils return null; } + static function getLinks(DOMNode $element, $rel, $type=null) + { + $links = $element->getElementsByTagnameNS(self::ATOM, self::LINK); + $out = array(); + + foreach ($links as $link) { + + $linkRel = $link->getAttribute(self::REL); + $linkType = $link->getAttribute(self::TYPE); + + if ($linkRel == $rel && + (is_null($type) || $linkType == $type)) { + $out[] = $link; + } + } + + return $out; + } + /** * Gets the first child element with the given tag * @@ -472,6 +491,24 @@ class AvatarLink public $type; public $size; public $width; + public $height; + + function __construct($element=null) + { + if ($element) { + // @fixme use correct namespaces + $this->url = $element->getAttribute('href'); + $this->type = $element->getAttribute('type'); + $width = $element->getAttribute('media:width'); + if ($width != null) { + $this->width = intval($width); + } + $height = $element->getAttribute('media:height'); + if ($height != null) { + $this->height = intval($height); + } + } + } static function fromAvatar($avatar) { @@ -640,8 +677,10 @@ class ActivityObject if ($this->type == self::PERSON || $this->type == self::GROUP) { $this->displayName = $this->title; - // @fixme we may have multiple avatars with different resolutions specified - $this->avatar = ActivityUtils::getLink($element, 'avatar'); + $avatars = ActivityUtils::getLinks($element, 'avatar'); + foreach ($avatars as $link) { + $this->avatarLinks[] = new AvatarLink($link); + } $this->poco = new PoCo($element); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index ad9170f5b..9b7be4e9a 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -912,8 +912,20 @@ class Ostatus_profile extends Memcached_DataObject protected static function getActivityObjectAvatar($object, $hints=array()) { - if ($object->avatar) { - return $object->avatar; + if ($object->avatarLinks) { + $best = false; + // Take the exact-size avatar, or the largest avatar, or the first avatar if all sizeless + foreach ($object->avatarLinks as $avatar) { + if ($avatar->width == AVATAR_PROFILE_SIZE && $avatar->height = AVATAR_PROFILE_SIZE) { + // Exact match! + $best = $avatar; + break; + } + if (!$best || $avatar->width > $best->width) { + $best = $avatar; + } + } + return $best->url; } else if (array_key_exists('avatar', $hints)) { return $hints['avatar']; } -- cgit v1.2.3-54-g00ecf From d8d8d59a03ef3fa12a0ea668b4a2f383f80486dc Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Thu, 25 Feb 2010 18:55:11 -0800 Subject: - Updates to use new activity object factories - Prune obsolete feed creation method --- plugins/OStatus/classes/Ostatus_profile.php | 104 ++-------------------------- 1 file changed, 5 insertions(+), 99 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index ad9170f5b..c3ea042ff 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -150,27 +150,7 @@ class Ostatus_profile extends Memcached_DataObject function asActivityObject() { if ($this->isGroup()) { - $object = new ActivityObject(); - $object->type = 'http://activitystrea.ms/schema/1.0/group'; - $object->id = $this->uri; - $self = $this->localGroup(); - - // @fixme put a standard getAvatar() interface on groups too - if ($self->homepage_logo) { - $object->avatar = $self->homepage_logo; - $map = array('png' => 'image/png', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'gif' => 'image/gif'); - $extension = pathinfo(parse_url($object->avatar, PHP_URL_PATH), PATHINFO_EXTENSION); - if (isset($map[$extension])) { - // @fixme this ain't used/saved yet - $object->avatarType = $map[$extension]; - } - } - - $object->link = $this->uri; // @fixme accurate? - return $object; + return ActivityObject::fromGroup($this->localGroup()); } else { return ActivityObject::fromProfile($this->localProfile()); } @@ -189,57 +169,13 @@ class Ostatus_profile extends Memcached_DataObject */ function asActivityNoun($element) { - $xs = new XMLStringer(true); - $avatarHref = Avatar::defaultImage(AVATAR_PROFILE_SIZE); - $avatarType = 'image/png'; if ($this->isGroup()) { - $type = 'http://activitystrea.ms/schema/1.0/group'; - $self = $this->localGroup(); - - // @fixme put a standard getAvatar() interface on groups too - if ($self->homepage_logo) { - $avatarHref = $self->homepage_logo; - $map = array('png' => 'image/png', - 'jpg' => 'image/jpeg', - 'jpeg' => 'image/jpeg', - 'gif' => 'image/gif'); - $extension = pathinfo(parse_url($avatarHref, PHP_URL_PATH), PATHINFO_EXTENSION); - if (isset($map[$extension])) { - $avatarType = $map[$extension]; - } - } + $noun = ActivityObject::fromGroup($this->localGroup()); + return $noun->asString('activity:' . $element); } else { - $type = 'http://activitystrea.ms/schema/1.0/person'; - $self = $this->localProfile(); - $avatar = $self->getAvatar(AVATAR_PROFILE_SIZE); - if ($avatar) { - $avatarHref = $avatar->url; - $avatarType = $avatar->mediatype; - } + $noun = ActivityObject::fromProfile($this->localProfile()); + return $noun->asString('activity:' . $element); } - $xs->elementStart('activity:' . $element); - $xs->element( - 'activity:object-type', - null, - $type - ); - $xs->element( - 'id', - null, - $this->uri); // ? - $xs->element('title', null, $self->getBestName()); - - $xs->element( - 'link', array( - 'type' => $avatarType, - 'href' => $avatarHref - ), - '' - ); - - $xs->elementEnd('activity:' . $element); - - return $xs->getString(); } /** @@ -486,36 +422,6 @@ class Ostatus_profile extends Memcached_DataObject } } - function atomFeed($actor) - { - $feed = new Atom10Feed(); - // @fixme should these be set up somewhere else? - $feed->addNamespace('activity', 'http://activitystrea.ms/spec/1.0/'); - $feed->addNamespace('thr', 'http://purl.org/syndication/thread/1.0'); - $feed->addNamespace('georss', 'http://www.georss.org/georss'); - $feed->addNamespace('ostatus', 'http://ostatus.org/schema/1.0'); - - $taguribase = common_config('integration', 'taguri'); - $feed->setId("tag:{$taguribase}:UserTimeline:{$actor->id}"); // ??? - - $feed->setTitle($actor->getBestName() . ' timeline'); // @fixme - $feed->setUpdated(time()); - $feed->setPublished(time()); - - $feed->addLink(common_local_url('ApiTimelineUser', - array('id' => $actor->id, - 'type' => 'atom')), - array('rel' => 'self', - 'type' => 'application/atom+xml')); - - $feed->addLink(common_local_url('userbyid', - array('id' => $actor->id)), - array('rel' => 'alternate', - 'type' => 'text/html')); - - return $feed; - } - /** * Read and post notices for updates from the feed. * Currently assumes that all items in the feed are new, -- cgit v1.2.3-54-g00ecf From 2528bb452b4ed27cc8685f5c8d900fe0742ef229 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Thu, 25 Feb 2010 19:48:28 -0800 Subject: OStatus: clean up known-URL hinting during profile setup, saves some extra HTTP hits we were getting when mentioning Buzz feeds. --- plugins/OStatus/classes/Ostatus_profile.php | 82 +++++++++++++++-------------- 1 file changed, 43 insertions(+), 39 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 75b4bef41..c462fead6 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -791,11 +791,18 @@ class Ostatus_profile extends Memcached_DataObject { // Get the canonical feed URI and check it $discover = new FeedDiscovery(); - $feeduri = $discover->discoverFromURL($profile_uri); + if ($hints['feedurl']) { + $feeduri = $hints['feedurl']; + $feeduri = $discover->discoverFromFeedURL($feeduri); + } else { + $feeduri = $discover->discoverFromURL($profile_uri); + $hints['feedurl'] = $feeduri; + } - //$feedsub = FeedSub::ensureFeed($feeduri, $discover->feed); $huburi = $discover->getAtomLink('hub'); + $hints['hub'] = $huburi; $salmonuri = $discover->getAtomLink('salmon'); + $hints['salmon'] = $salmonuri; if (!$huburi) { // We can only deal with folks with a PuSH hub @@ -810,7 +817,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($subject)) { $subjObject = new ActivityObject($subject); - return self::ensureActivityObjectProfile($subjObject, $feeduri, $salmonuri, $hints); + return self::ensureActivityObjectProfile($subjObject, $hints); } // Otherwise, try the feed author @@ -819,7 +826,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($author)) { $authorObject = new ActivityObject($author); - return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri, $hints); + return self::ensureActivityObjectProfile($authorObject, $hints); } // Sheesh. Not a very nice feed! Let's try fingerpoken in the @@ -835,7 +842,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($actor)) { $actorObject = new ActivityObject($actor); - return self::ensureActivityObjectProfile($actorObject, $feeduri, $salmonuri, $hints); + return self::ensureActivityObjectProfile($actorObject, $hints); } @@ -843,7 +850,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($author)) { $authorObject = new ActivityObject($author); - return self::ensureActivityObjectProfile($authorObject, $feeduri, $salmonuri, $hints); + return self::ensureActivityObjectProfile($authorObject, $hints); } } @@ -988,18 +995,18 @@ class Ostatus_profile extends Memcached_DataObject * @return Ostatus_profile */ - public static function ensureActorProfile($activity, $feeduri=null, $salmonuri=null) + public static function ensureActorProfile($activity, $hints=array()) { - return self::ensureActivityObjectProfile($activity->actor, $feeduri, $salmonuri); + return self::ensureActivityObjectProfile($activity->actor, $hints); } - public static function ensureActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) + public static function ensureActivityObjectProfile($object, $hints=array()) { $profile = self::getActivityObjectProfile($object); if ($profile) { $profile->updateFromActivityObject($object, $hints); } else { - $profile = self::createActivityObjectProfile($object, $feeduri, $salmonuri, $hints); + $profile = self::createActivityObjectProfile($object, $hints); } return $profile; } @@ -1045,58 +1052,55 @@ class Ostatus_profile extends Memcached_DataObject * @fixme validate stuff somewhere */ - protected static function createActorProfile($activity, $feeduri=null, $salmonuri=null) - { - $actor = $activity->actor; - - self::createActivityObjectProfile($actor, $feeduri, $salmonuri); - } - /** * Create local ostatus_profile and profile/user_group entries for * the provided remote user or group. * * @param ActivityObject $object - * @param string $feeduri - * @param string $salmonuri * @param array $hints * - * @fixme fold $feeduri/$salmonuri into $hints * @return Ostatus_profile */ - protected static function createActivityObjectProfile($object, $feeduri=null, $salmonuri=null, $hints=array()) + protected static function createActivityObjectProfile($object, $hints=array()) { - $homeuri = $object->id; + $homeuri = $object->id; + $discover = false; if (!$homeuri) { common_log(LOG_DEBUG, __METHOD__ . " empty actor profile URI: " . var_export($activity, true)); throw new ServerException("No profile URI"); } - if (empty($feeduri)) { - if (array_key_exists('feedurl', $hints)) { - $feeduri = $hints['feedurl']; - } + if (array_key_exists('feedurl', $hints)) { + $feeduri = $hints['feedurl']; + } else { + $discover = new FeedDiscovery(); + $feeduri = $discover->discoverFromURL($homeuri); } - if (empty($salmonuri)) { - if (array_key_exists('salmon', $hints)) { - $salmonuri = $hints['salmon']; + if (array_key_exists('salmon', $hints)) { + $salmonuri = $hints['salmon']; + } else { + if (!$discover) { + $discover = new FeedDiscovery(); + $discover->discoverFromFeedURL($hints['feedurl']); } + $salmonuri = $discover->getAtomLink('salmon'); } - if (!$feeduri || !$salmonuri) { - // Get the canonical feed URI and check it - $discover = new FeedDiscovery(); - $feeduri = $discover->discoverFromURL($homeuri); - + if (array_key_exists('hub', $hints)) { + $huburi = $hints['hub']; + } else { + if (!$discover) { + $discover = new FeedDiscovery(); + $discover->discoverFromFeedURL($hints['feedurl']); + } $huburi = $discover->getAtomLink('hub'); - $salmonuri = $discover->getAtomLink('salmon'); + } - if (!$huburi) { - // We can only deal with folks with a PuSH hub - throw new FeedSubNoHubException(); - } + if (!$huburi) { + // We can only deal with folks with a PuSH hub + throw new FeedSubNoHubException(); } $oprofile = new Ostatus_profile(); -- cgit v1.2.3-54-g00ecf From ab8bb4d79e5f267323440b8cee01458b393ce2d1 Mon Sep 17 00:00:00 2001 From: James Walker Date: Fri, 26 Feb 2010 04:07:58 -0500 Subject: more cleanup --- plugins/OStatus/classes/Ostatus_profile.php | 2 +- plugins/OStatus/lib/discovery.php | 4 ++++ plugins/OStatus/lib/magicenvelope.php | 21 +++++++++++++++------ 3 files changed, 20 insertions(+), 7 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 4a9aafce1..091056c54 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1305,7 +1305,7 @@ class Ostatus_profile extends Memcached_DataObject case Discovery::UPDATESFROM: $feedUrl = $link['href']; break; - case Webfinger::HCARD: + case Discovery::HCARD: $hcardUrl = $link['href']; break; default: diff --git a/plugins/OStatus/lib/discovery.php b/plugins/OStatus/lib/discovery.php index 1ab6e51bd..388df0a28 100644 --- a/plugins/OStatus/lib/discovery.php +++ b/plugins/OStatus/lib/discovery.php @@ -111,6 +111,10 @@ class Discovery } public static function getService($links, $service) { + if (!is_array($links)) { + return false; + } + foreach ($links as $link) { if ($link['rel'] == $service) { return $link; diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php index c642af548..457c0fba2 100644 --- a/plugins/OStatus/lib/magicenvelope.php +++ b/plugins/OStatus/lib/magicenvelope.php @@ -52,12 +52,17 @@ class MagicEnvelope { $disco = new Discovery(); - $xrd = $disco->lookup($signer_uri); - if ($link = Discovery::getService($xrd->links, Magicsig::PUBLICKEYREL)) { - list($type, $keypair) = explode(';', $link['href']); - return $keypair; + try { + $xrd = $disco->lookup($signer_uri); + } catch (Exception $e) { + return false; + } + if ($xrd->links) { + if ($link = Discovery::getService($xrd->links, Magicsig::PUBLICKEYREL)) { + list($type, $keypair) = explode(';', $link['href']); + return $keypair; + } } - throw new Exception('Unable to locate signer public key'); } @@ -70,7 +75,11 @@ class MagicEnvelope throw new Exception("Unable to determine entry author."); } - $signature_alg = Magicsig::fromString($this->getKeyPair($signer_uri)); + $keypair = $this->getKeyPair($signer_uri); + if (!$keypair) { + throw new Exception("Unable to retrive keypair for ". $signer_uri); + } + $signature_alg = Magicsig::fromString($keypair); $armored_text = base64_encode($text); return array( -- cgit v1.2.3-54-g00ecf From bbb0a7d5bc2c8815a2057eb0a6588d8d60ff4416 Mon Sep 17 00:00:00 2001 From: James Walker Date: Fri, 26 Feb 2010 13:17:24 -0500 Subject: updating to use latest salmon NS definitions --- plugins/OStatus/OStatusPlugin.php | 3 ++- plugins/OStatus/actions/xrd.php | 8 ++++++-- plugins/OStatus/classes/Ostatus_profile.php | 6 +++--- plugins/OStatus/lib/salmon.php | 5 +++++ 4 files changed, 16 insertions(+), 6 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 46f986682..89fa45f91 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -149,7 +149,8 @@ class OStatusPlugin extends Plugin // Also, we'll add in the salmon link $salmon = common_local_url($salmonAction, array('id' => $id)); - $feed->addLink($salmon, array('rel' => 'salmon')); + $feed->addLink($salmon, array('rel' => Salmon::NS_REPLIES)); + $feed->addLink($salmon, array('rel' => Salmon::NS_MENTIONS)); } return true; diff --git a/plugins/OStatus/actions/xrd.php b/plugins/OStatus/actions/xrd.php index e6b694d61..4350ad025 100644 --- a/plugins/OStatus/actions/xrd.php +++ b/plugins/OStatus/actions/xrd.php @@ -80,10 +80,14 @@ class XrdAction extends Action 'href' => common_local_url('foaf', array('nickname' => $nick))); - $salmon_url = common_local_url('salmon', + // Salmon + $salmon_url = common_local_url('usersalmon', array('id' => $this->user->id)); - $xrd->links[] = array('rel' => 'salmon', + $xrd->links[] = array('rel' => Salmon::NS_REPLIES, + 'href' => $salmon_url); + + $xrd->links[] = array('rel' => Salmon::NS_MENTIONS, 'href' => $salmon_url); // Get this user's keypair diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 091056c54..c6f7378c4 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -707,7 +707,7 @@ class Ostatus_profile extends Memcached_DataObject $huburi = $discover->getAtomLink('hub'); $hints['hub'] = $huburi; - $salmonuri = $discover->getAtomLink('salmon'); + $salmonuri = $discover->getAtomLink(Salmon::NS_REPLIES); $hints['salmon'] = $salmonuri; if (!$huburi) { @@ -991,7 +991,7 @@ class Ostatus_profile extends Memcached_DataObject $discover = new FeedDiscovery(); $discover->discoverFromFeedURL($hints['feedurl']); } - $salmonuri = $discover->getAtomLink('salmon'); + $salmonuri = $discover->getAtomLink(Salmon::NS_REPLIES); } if (array_key_exists('hub', $hints)) { @@ -1299,7 +1299,7 @@ class Ostatus_profile extends Memcached_DataObject case Discovery::PROFILEPAGE: $profileUrl = $link['href']; break; - case 'salmon': + case Salmon::NS_REPLIES: $salmonEndpoint = $link['href']; break; case Discovery::UPDATESFROM: diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index 9d4359f74..c59de2a14 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -28,6 +28,11 @@ */ class Salmon { + + const NS_REPLIES = "http://salmon-protocol.org/ns/salmon-replies"; + + const NS_MENTIONS = "http://salmon-protocol.org/ns/salmon-mention"; + /** * Sign and post the given Atom entry as a Salmon message. * -- cgit v1.2.3-54-g00ecf From 223ebc765c454e030a49df7e2e1b9cdc2b005fe6 Mon Sep 17 00:00:00 2001 From: James Walker Date: Fri, 26 Feb 2010 14:21:21 -0500 Subject: move signing to take a local actor profile and use local keys --- plugins/OStatus/OStatusPlugin.php | 14 +++++++------- plugins/OStatus/classes/Magicsig.php | 3 ++- plugins/OStatus/classes/Ostatus_profile.php | 11 ++++++----- plugins/OStatus/lib/magicenvelope.php | 12 +----------- plugins/OStatus/lib/ostatusqueuehandler.php | 2 +- plugins/OStatus/lib/salmon.php | 24 ++++++++++++++++++------ plugins/OStatus/lib/salmonqueuehandler.php | 4 +++- 7 files changed, 38 insertions(+), 32 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 89fa45f91..720dedd0a 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -415,7 +415,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($subscriber); $act->object = ActivityObject::fromProfile($other); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $subscriber); return true; } @@ -463,7 +463,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($profile); $act->object = ActivityObject::fromProfile($other); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $profile); return true; } @@ -505,7 +505,7 @@ class OStatusPlugin extends Plugin $member->getBestName(), $oprofile->getBestName()); - if ($oprofile->notifyActivity($act)) { + if ($oprofile->notifyActivity($act, $member)) { return true; } else { $oprofile->garbageCollect(); @@ -555,7 +555,7 @@ class OStatusPlugin extends Plugin $member->getBestName(), $oprofile->getBestName()); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $member); } } @@ -598,7 +598,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($profile); $act->object = ActivityObject::fromNotice($notice); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $profile); return true; } @@ -642,7 +642,7 @@ class OStatusPlugin extends Plugin $act->actor = ActivityObject::fromProfile($profile); $act->object = ActivityObject::fromNotice($notice); - $oprofile->notifyActivity($act); + $oprofile->notifyActivity($act, $profile); return true; } @@ -731,7 +731,7 @@ class OStatusPlugin extends Plugin $act->object = $act->actor; while ($oprofile->fetch()) { - $oprofile->notifyDeferred($act); + $oprofile->notifyDeferred($act, $profile); } return true; diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index 02882d19b..751527c81 100644 --- a/plugins/OStatus/classes/Magicsig.php +++ b/plugins/OStatus/classes/Magicsig.php @@ -49,7 +49,8 @@ class Magicsig extends Memcached_DataObject public /*static*/ function staticGet($k, $v=null) { - return parent::staticGet(__CLASS__, $k, $v); + $obj = parent::staticGet(__CLASS__, $k, $v); + return Magicsig::fromString($obj->keypair); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index c6f7378c4..35539bff7 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -357,7 +357,7 @@ class Ostatus_profile extends Memcached_DataObject common_log(LOG_INFO, "Posting to Salmon endpoint $this->salmonuri: $xml"); $salmon = new Salmon(); // ? - return $salmon->post($this->salmonuri, $xml); + return $salmon->post($this->salmonuri, $xml, $actor); } return false; } @@ -369,11 +369,11 @@ class Ostatus_profile extends Memcached_DataObject * @param mixed $entry XML string, Notice, or Activity * @return boolean success */ - public function notifyActivity($entry) + public function notifyActivity($entry, $actor) { if ($this->salmonuri) { $salmon = new Salmon(); - return $salmon->post($this->salmonuri, $this->notifyPrepXml($entry)); + return $salmon->post($this->salmonuri, $this->notifyPrepXml($entry), $actor); } return false; @@ -386,11 +386,12 @@ class Ostatus_profile extends Memcached_DataObject * @param mixed $entry XML string, Notice, or Activity * @return boolean success */ - public function notifyDeferred($entry) + public function notifyDeferred($entry, $actor) { if ($this->salmonuri) { $data = array('salmonuri' => $this->salmonuri, - 'entry' => $this->notifyPrepXml($entry)); + 'entry' => $this->notifyPrepXml($entry), + 'actor' => $actor->id); $qm = QueueManager::get(); return $qm->enqueue($data, 'salmon'); diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php index 457c0fba2..f33119b8f 100644 --- a/plugins/OStatus/lib/magicenvelope.php +++ b/plugins/OStatus/lib/magicenvelope.php @@ -67,18 +67,8 @@ class MagicEnvelope } - public function signMessage($text, $mimetype, $signer_uri) + public function signMessage($text, $mimetype, $keypair) { - $signer_uri = $this->normalizeUser($signer_uri); - - if (!$this->checkAuthor($text, $signer_uri)) { - throw new Exception("Unable to determine entry author."); - } - - $keypair = $this->getKeyPair($signer_uri); - if (!$keypair) { - throw new Exception("Unable to retrive keypair for ". $signer_uri); - } $signature_alg = Magicsig::fromString($keypair); $armored_text = base64_encode($text); diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php index 0da85600f..6ca31c485 100644 --- a/plugins/OStatus/lib/ostatusqueuehandler.php +++ b/plugins/OStatus/lib/ostatusqueuehandler.php @@ -87,7 +87,7 @@ class OStatusQueueHandler extends QueueHandler // remote user or group. // @fixme as an optimization we can skip this if the // remote profile is subscribed to the author. - $oprofile->notifyDeferred($this->notice); + $oprofile->notifyDeferred($this->notice, $this->user); } } diff --git a/plugins/OStatus/lib/salmon.php b/plugins/OStatus/lib/salmon.php index c59de2a14..6e2459544 100644 --- a/plugins/OStatus/lib/salmon.php +++ b/plugins/OStatus/lib/salmon.php @@ -42,14 +42,14 @@ class Salmon * @param string $xml * @return boolean success */ - public function post($endpoint_uri, $xml) + public function post($endpoint_uri, $xml, $actor) { if (empty($endpoint_uri)) { return false; } if (!common_config('ostatus', 'skip_signatures')) { - $xml = $this->createMagicEnv($xml); + $xml = $this->createMagicEnv($xml, $actor); } $headers = array('Content-Type: application/atom+xml'); @@ -70,15 +70,27 @@ class Salmon return true; } - public function createMagicEnv($text) + public function createMagicEnv($text, $actor) { + common_log(LOG_DEBUG, "Got actor as : ". print_r($actor, true)); $magic_env = new MagicEnvelope(); - // TODO: Should probably be getting the signer uri as an argument? - $signer_uri = $magic_env->getAuthor($text); + $user = User::staticGet('id', $actor->id); + if ($user->id) { + // Use local key + $magickey = Magicsig::staticGet('user_id', $user->id); + if (!$magickey) { + // No keypair yet, let's generate one. + $magickey = new Magicsig(); + $magickey->generate($user->id); + } + common_log(LOG_DEBUG, "Salmon: Loaded key for ". $user->id); + } else { + throw new Exception("Salmon invalid actor for signing"); + } try { - $env = $magic_env->signMessage($text, 'application/atom+xml', $signer_uri); + $env = $magic_env->signMessage($text, 'application/atom+xml', $magickey->toString()); } catch (Exception $e) { common_log(LOG_ERR, "Salmon signing failed: ". $e->getMessage()); return $text; diff --git a/plugins/OStatus/lib/salmonqueuehandler.php b/plugins/OStatus/lib/salmonqueuehandler.php index aa97018dc..7eeb5f8e9 100644 --- a/plugins/OStatus/lib/salmonqueuehandler.php +++ b/plugins/OStatus/lib/salmonqueuehandler.php @@ -35,8 +35,10 @@ class SalmonQueueHandler extends QueueHandler assert(is_string($data['salmonuri'])); assert(is_string($data['entry'])); + $actor = Profile::staticGet($data['actor']); + $salmon = new Salmon(); - $salmon->post($data['salmonuri'], $data['entry']); + $salmon->post($data['salmonuri'], $data['entry'], $actor); // @fixme detect failure and attempt to resend return true; -- cgit v1.2.3-54-g00ecf From f23a877cd8ad8d583b74c312c7e9baa842b5a86a Mon Sep 17 00:00:00 2001 From: James Walker Date: Fri, 26 Feb 2010 15:38:48 -0500 Subject: Discovery::lookup now throws an exception --- plugins/OStatus/classes/Ostatus_profile.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 35539bff7..7b1aec76b 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1288,9 +1288,9 @@ class Ostatus_profile extends Memcached_DataObject $disco = new Discovery(); - $result = $disco->lookup($addr); - - if (!$result) { + try { + $result = $disco->lookup($addr); + } catch (Exception $e) { self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null); return null; } -- cgit v1.2.3-54-g00ecf From 45e8819c1b9cc618e9b2d6678b0ff14c653a09d3 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 16:35:36 -0800 Subject: Fix a bunch of notice & warning-level messages that were breaking my inter-instance communications --- plugins/OStatus/classes/Magicsig.php | 4 +++- plugins/OStatus/classes/Ostatus_profile.php | 2 +- plugins/OStatus/lib/discovery.php | 2 +- plugins/OStatus/lib/xrd.php | 11 ++++++++--- 4 files changed, 13 insertions(+), 6 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index 96900d876..5a46aeeb6 100644 --- a/plugins/OStatus/classes/Magicsig.php +++ b/plugins/OStatus/classes/Magicsig.php @@ -146,8 +146,10 @@ class Magicsig extends Memcached_DataObject $mod = base64_url_decode($matches[1]); $exp = base64_url_decode($matches[2]); - if ($matches[4]) { + if (!empty($matches[4])) { $private_exp = base64_url_decode($matches[4]); + } else { + $private_exp = false; } $params['public_key'] = new Crypt_RSA_KEY($mod, $exp, 'public'); diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 7b1aec76b..93e8934c9 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1145,7 +1145,7 @@ class Ostatus_profile extends Memcached_DataObject if (!empty($poco)) { $url = $poco->getPrimaryURL(); - if ($url->type == 'homepage') { + if ($url && $url->type == 'homepage') { $homepage = $url->value; } } diff --git a/plugins/OStatus/lib/discovery.php b/plugins/OStatus/lib/discovery.php index 388df0a28..f8449b309 100644 --- a/plugins/OStatus/lib/discovery.php +++ b/plugins/OStatus/lib/discovery.php @@ -94,7 +94,7 @@ class Discovery $links = call_user_func(array($class, 'discover'), $uri); if ($link = Discovery::getService($links, Discovery::LRDD_REL)) { // Load the LRDD XRD - if ($link['template']) { + if (!empty($link['template'])) { $xrd_uri = Discovery::applyTemplate($link['template'], $uri); } else { $xrd_uri = $link['href']; diff --git a/plugins/OStatus/lib/xrd.php b/plugins/OStatus/lib/xrd.php index 16d27f8eb..1de065db9 100644 --- a/plugins/OStatus/lib/xrd.php +++ b/plugins/OStatus/lib/xrd.php @@ -53,17 +53,22 @@ class XRD $xrd = new XRD(); $dom = new DOMDocument(); - $dom->loadXML($xml); + if (!$dom->loadXML($xml)) { + throw new Exception("Invalid XML"); + } $xrd_element = $dom->getElementsByTagName('XRD')->item(0); // Check for host-meta host - $host = $xrd_element->getElementsByTagName('Host')->item(0)->nodeValue; + $host = $xrd_element->getElementsByTagName('Host')->item(0); if ($host) { - $xrd->host = $host; + $xrd->host = $host->nodeValue; } // Loop through other elements foreach ($xrd_element->childNodes as $node) { + if (!($node instanceof DOMElement)) { + continue; + } switch ($node->tagName) { case 'Expires': $xrd->expires = $node->nodeValue; -- cgit v1.2.3-54-g00ecf From 493b5479c15f0e135054fd6b961e50e690077eda Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 1 Mar 2010 16:36:33 -0800 Subject: OStatus: support @example.com/path/to/profile mentions as well as @profile@example.com (latter requires webfinger, former doesn't) Plus misc warnings/notices cleanup in the submission path. --- actions/newnotice.php | 3 ++ plugins/OStatus/OStatusPlugin.php | 57 ++++++++++++++++++++++------- plugins/OStatus/classes/Ostatus_profile.php | 2 +- plugins/OStatus/lib/discovery.php | 2 +- plugins/OStatus/lib/xrd.php | 10 ++--- 5 files changed, 54 insertions(+), 20 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/actions/newnotice.php b/actions/newnotice.php index 78480abab..ed0fa1b2b 100644 --- a/actions/newnotice.php +++ b/actions/newnotice.php @@ -294,6 +294,9 @@ class NewnoticeAction extends Action if ($profile) { $content = '@' . $profile->nickname . ' '; } + } else { + // @fixme most of these bits above aren't being passed on above + $inreplyto = null; } $notice_form = new NoticeForm($this, '', $content, null, $inreplyto); diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index 720dedd0a..4ffbba45b 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -222,31 +222,62 @@ class OStatusPlugin extends Plugin } /** - * + * Find any explicit remote mentions. Accepted forms: + * Webfinger: @user@example.com + * Profile link: @example.com/mublog/user + * @param Profile $sender (os user?) + * @param string $text input markup text + * @param array &$mention in/out param: set of found mentions + * @return boolean hook return value */ function onEndFindMentions($sender, $text, &$mentions) { - preg_match_all('/(?:^|\s+)@((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)/', + preg_match_all('!(?:^|\s+) + @( # Webfinger: + (?:\w+\.)*\w+ # user + @ # @ + (?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+ # domain + | # Profile: + (?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+ # domain + (?:/\w+)+ # /path1(/path2...) + )!x', $text, $wmatches, PREG_OFFSET_CAPTURE); foreach ($wmatches[1] as $wmatch) { - - $webfinger = $wmatch[0]; - - $this->log(LOG_INFO, "Checking Webfinger for address '$webfinger'"); - - $oprofile = Ostatus_profile::ensureWebfinger($webfinger); + $target = $wmatch[0]; + $oprofile = null; + + if (strpos($target, '/') === false) { + $this->log(LOG_INFO, "Checking Webfinger for address '$target'"); + try { + $oprofile = Ostatus_profile::ensureWebfinger($target); + } catch (Exception $e) { + $this->log(LOG_ERR, "Webfinger check failed: " . $e->getMessage()); + } + } else { + $schemes = array('https', 'http'); + foreach ($schemes as $scheme) { + $url = "$scheme://$target"; + $this->log(LOG_INFO, "Checking profile address '$url'"); + try { + $oprofile = Ostatus_profile::ensureProfile($url); + if ($oprofile) { + continue; + } + } catch (Exception $e) { + $this->log(LOG_ERR, "Profile check failed: " . $e->getMessage()); + } + } + } if (empty($oprofile)) { - - $this->log(LOG_INFO, "No Ostatus_profile found for address '$webfinger'"); - + $this->log(LOG_INFO, "No Ostatus_profile found for address '$target'"); } else { - $this->log(LOG_INFO, "Ostatus_profile found for address '$webfinger'"); + $this->log(LOG_INFO, "Ostatus_profile found for address '$target'"); if ($oprofile->isGroup()) { continue; @@ -261,7 +292,7 @@ class OStatusPlugin extends Plugin } } $mentions[] = array('mentioned' => array($profile), - 'text' => $wmatch[0], + 'text' => $target, 'position' => $pos, 'url' => $profile->profileurl); } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 7b1aec76b..668a31df4 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -698,7 +698,7 @@ class Ostatus_profile extends Memcached_DataObject { // Get the canonical feed URI and check it $discover = new FeedDiscovery(); - if ($hints['feedurl']) { + if (isset($hints['feedurl'])) { $feeduri = $hints['feedurl']; $feeduri = $discover->discoverFromFeedURL($feeduri); } else { diff --git a/plugins/OStatus/lib/discovery.php b/plugins/OStatus/lib/discovery.php index 388df0a28..f8449b309 100644 --- a/plugins/OStatus/lib/discovery.php +++ b/plugins/OStatus/lib/discovery.php @@ -94,7 +94,7 @@ class Discovery $links = call_user_func(array($class, 'discover'), $uri); if ($link = Discovery::getService($links, Discovery::LRDD_REL)) { // Load the LRDD XRD - if ($link['template']) { + if (!empty($link['template'])) { $xrd_uri = Discovery::applyTemplate($link['template'], $uri); } else { $xrd_uri = $link['href']; diff --git a/plugins/OStatus/lib/xrd.php b/plugins/OStatus/lib/xrd.php index 16d27f8eb..48c1c88e1 100644 --- a/plugins/OStatus/lib/xrd.php +++ b/plugins/OStatus/lib/xrd.php @@ -156,20 +156,20 @@ class XRD function saveLink($doc, $link) { $link_element = $doc->createElement('Link'); - if ($link['rel']) { + if (!empty($link['rel'])) { $link_element->setAttribute('rel', $link['rel']); } - if ($link['type']) { + if (!empty($link['type'])) { $link_element->setAttribute('type', $link['type']); } - if ($link['href']) { + if (!empty($link['href'])) { $link_element->setAttribute('href', $link['href']); } - if ($link['template']) { + if (!empty($link['template'])) { $link_element->setAttribute('template', $link['template']); } - if (is_array($link['title'])) { + if (!empty($link['title']) && is_array($link['title'])) { foreach($link['title'] as $title) { $title = $doc->createElement('Title', $title); $link_element->appendChild($title); -- cgit v1.2.3-54-g00ecf From 79ffebb51b1141791d5ee7478e3a7beaa9fe8faa Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 2 Mar 2010 16:30:09 -0800 Subject: OStatus: save file records for enclosures Also stripping id from foreign HTML messages (could interfere with UI) and disabled failing attachment popup for a.attachment links that don't have a proper id, so you can click through instead of getting an error. Issues: * any other links aren't marked and saved * inconsistent behavior between local and remote attachments (local displays in lightbox, remote doesn't) * if the enclosure'd object isn't referenced in the content, you won't be offered a link to it in our UI --- classes/File.php | 7 +++++++ classes/Notice.php | 28 ++++++++++++++++++++++++++-- js/util.js | 7 +++++-- lib/activity.php | 5 +++++ plugins/OStatus/classes/Ostatus_profile.php | 12 ++++++++++-- 5 files changed, 53 insertions(+), 6 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/classes/File.php b/classes/File.php index 189e04ce0..1b8ef1b3e 100644 --- a/classes/File.php +++ b/classes/File.php @@ -286,5 +286,12 @@ class File extends Memcached_DataObject } return $enclosure; } + + // quick back-compat hack, since there's still code using this + function isEnclosure() + { + $enclosure = $this->getEnclosure(); + return !empty($enclosure); + } } diff --git a/classes/Notice.php b/classes/Notice.php index 63dc96897..c1263c782 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -211,6 +211,8 @@ class Notice extends Memcached_DataObject * extracting ! tags from content * array 'tags' list of hashtag strings to save with the notice * in place of extracting # tags from content + * array 'urls' list of attached/referred URLs to save with the + * notice in place of extracting links from content * @fixme tag override * * @return Notice @@ -380,8 +382,11 @@ class Notice extends Memcached_DataObject $notice->saveTags(); } - // @fixme pass in data for URLs too? - $notice->saveUrls(); + if (isset($urls)) { + $notice->saveKnownUrls($urls); + } else { + $notice->saveUrls(); + } // Prepare inbox delivery, may be queued to background. $notice->distribute(); @@ -427,6 +432,25 @@ class Notice extends Memcached_DataObject common_replace_urls_callback($this->content, array($this, 'saveUrl'), $this->id); } + /** + * Save the given URLs as related links/attachments to the db + * + * follow redirects and save all available file information + * (mimetype, date, size, oembed, etc.) + * + * @return void + */ + function saveKnownUrls($urls) + { + // @fixme validation? + foreach ($urls as $url) { + File::processNew($url, $this->id); + } + } + + /** + * @private callback + */ function saveUrl($data) { list($url, $notice_id) = $data; File::processNew($url, $notice_id); diff --git a/js/util.js b/js/util.js index d08c46fe6..3efda0d7b 100644 --- a/js/util.js +++ b/js/util.js @@ -423,8 +423,11 @@ var SN = { // StatusNet }; notice.find('a.attachment').click(function() { - $().jOverlay({url: $('address .url')[0].href+'attachment/' + ($(this).attr('id').substring('attachment'.length + 1)) + '/ajax'}); - return false; + var attachId = ($(this).attr('id').substring('attachment'.length + 1)); + if (attachId) { + $().jOverlay({url: $('address .url')[0].href+'attachment/' + attachId + '/ajax'}); + return false; + } }); if ($('#shownotice').length == 0) { diff --git a/lib/activity.php b/lib/activity.php index b20153213..ce14fa254 100644 --- a/lib/activity.php +++ b/lib/activity.php @@ -1044,6 +1044,7 @@ class Activity public $id; // ID of the activity public $title; // title of the activity public $categories = array(); // list of AtomCategory objects + public $enclosures = array(); // list of enclosure URL references /** * Turns a regular old Atom into a magical activity @@ -1140,6 +1141,10 @@ class Activity $this->categories[] = new AtomCategory($catEl); } } + + foreach (ActivityUtils::getLinks($entry, 'enclosure') as $link) { + $this->enclosures[] = $link->getAttribute('href'); + } } /** diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index a33e95d93..059c19e7c 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -550,7 +550,8 @@ class Ostatus_profile extends Memcached_DataObject 'rendered' => $rendered, 'replies' => array(), 'groups' => array(), - 'tags' => array()); + 'tags' => array(), + 'urls' => array()); // Check for optional attributes... @@ -595,6 +596,12 @@ class Ostatus_profile extends Memcached_DataObject } } + // Atom enclosures -> attachment URLs + foreach ($activity->enclosures as $href) { + // @fixme save these locally or....? + $options['urls'][] = $href; + } + try { $saved = Notice::saveNew($oprofile->profile_id, $content, @@ -620,7 +627,8 @@ class Ostatus_profile extends Memcached_DataObject protected function purify($html) { require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php'; - $config = array('safe' => 1); + $config = array('safe' => 1, + 'deny_attribute' => 'id,style,on*'); return htmLawed($html, $config); } -- cgit v1.2.3-54-g00ecf From f3cea2430497e751bc7776fe3abf0603e2b55f8b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 3 Mar 2010 09:36:26 -0800 Subject: Fix for hcard parsing: typo caused notice spew accessing unset array index --- plugins/OStatus/classes/Ostatus_profile.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 059c19e7c..7ab031aa5 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1500,7 +1500,7 @@ class Ostatus_profile extends Memcached_DataObject if (array_key_exists('url', $hcard)) { if (is_string($hcard['url'])) { $hints['homepage'] = $hcard['url']; - } else if (is_array($hcard['adr'])) { + } else if (is_array($hcard['url'])) { // HACK get the last one; that's how our hcards look $hints['homepage'] = $hcard['url'][count($hcard['url'])-1]; } -- cgit v1.2.3-54-g00ecf From a4d9171306a06983094fdc90dbf040335c4f803f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 3 Mar 2010 18:23:28 -0800 Subject: Fix up catching of webfinger setup fails --- plugins/OStatus/actions/ostatussub.php | 3 +-- plugins/OStatus/classes/Ostatus_profile.php | 13 ++++++++++--- 2 files changed, 11 insertions(+), 5 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/actions/ostatussub.php b/plugins/OStatus/actions/ostatussub.php index 7ca8a7869..e8a2c78ae 100644 --- a/plugins/OStatus/actions/ostatussub.php +++ b/plugins/OStatus/actions/ostatussub.php @@ -260,7 +260,7 @@ class OStatusSubAction extends Action $this->error = _m('Could not find a feed linked from this URL.'); } catch (FeedSubUnrecognizedTypeException $e) { $this->error = _m('Not a recognized feed type.'); - } catch (FeedSubException $e) { + } catch (Exception $e) { // Any new ones we forgot about $this->error = sprintf(_m('Bad feed URL: %s %s'), get_class($e), $e->getMessage()); } @@ -315,7 +315,6 @@ class OStatusSubAction extends Action if ($this->pullRemoteProfile()) { $this->validateRemoteProfile(); } - return true; } diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index 7ab031aa5..b3b4336b5 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1267,6 +1267,11 @@ class Ostatus_profile extends Memcached_DataObject } } + /** + * @param string $addr webfinger address + * @return Ostatus_profile + * @throws Exception on error conditions + */ public static function ensureWebfinger($addr) { // First, try the cache @@ -1275,7 +1280,8 @@ class Ostatus_profile extends Memcached_DataObject if ($uri !== false) { if (is_null($uri)) { - return null; + // Negative cache entry + throw new Exception('Not a valid webfinger address.'); } $oprofile = Ostatus_profile::staticGet('uri', $uri); if (!empty($oprofile)) { @@ -1299,8 +1305,9 @@ class Ostatus_profile extends Memcached_DataObject try { $result = $disco->lookup($addr); } catch (Exception $e) { + // Save negative cache entry so we don't waste time looking it up again. self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null); - return null; + throw new Exception('Not a valid webfinger address.'); } foreach ($result->links as $link) { @@ -1410,7 +1417,7 @@ class Ostatus_profile extends Memcached_DataObject return $oprofile; } - return null; + throw new Exception("Couldn't find a valid profile for '$addr'"); } function saveHTMLFile($title, $rendered) -- cgit v1.2.3-54-g00ecf From 14065ca350cae576461f1a2db33694e38cebf751 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 3 Mar 2010 18:28:39 -0800 Subject: OStatus: code cleanup on webfinger fallback path --- plugins/OStatus/classes/Ostatus_profile.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) (limited to 'plugins/OStatus/classes/Ostatus_profile.php') diff --git a/plugins/OStatus/classes/Ostatus_profile.php b/plugins/OStatus/classes/Ostatus_profile.php index b3b4336b5..fcca1a252 100644 --- a/plugins/OStatus/classes/Ostatus_profile.php +++ b/plugins/OStatus/classes/Ostatus_profile.php @@ -1306,20 +1306,23 @@ class Ostatus_profile extends Memcached_DataObject $result = $disco->lookup($addr); } catch (Exception $e) { // Save negative cache entry so we don't waste time looking it up again. + // @fixme distinguish temporary failures? self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), null); throw new Exception('Not a valid webfinger address.'); } + $hints = array('webfinger' => $addr); + foreach ($result->links as $link) { switch ($link['rel']) { case Discovery::PROFILEPAGE: - $profileUrl = $link['href']; + $hints['profileurl'] = $profileUrl = $link['href']; break; case Salmon::NS_REPLIES: - $salmonEndpoint = $link['href']; + $hints['salmon'] = $salmonEndpoint = $link['href']; break; case Discovery::UPDATESFROM: - $feedUrl = $link['href']; + $hints['feedurl'] = $feedUrl = $link['href']; break; case Discovery::HCARD: $hcardUrl = $link['href']; @@ -1330,11 +1333,6 @@ class Ostatus_profile extends Memcached_DataObject } } - $hints = array('webfinger' => $addr, - 'profileurl' => $profileUrl, - 'feedurl' => $feedUrl, - 'salmon' => $salmonEndpoint); - if (isset($hcardUrl)) { $hcardHints = self::slurpHcard($hcardUrl); // Note: Webfinger > hcard -- cgit v1.2.3-54-g00ecf