diff --git a/CONFIGURE b/CONFIGURE
index 4a7926f8c9..e6b09a4ce3 100644
--- a/CONFIGURE
+++ b/CONFIGURE
@@ -120,8 +120,9 @@ db
--
This section is a reference to the configuration options for
-DB_DataObject (see ). The ones that you may want to
-set are listed below for clarity.
+DB_DataObject (see
+).
+The ones that you may want to set are listed below for clarity.
database: a DSN (Data Source Name) for your StatusNet database. This is
in the format 'protocol://username:password@hostname/databasename',
@@ -322,8 +323,8 @@ server: If set, defines another server where avatars are stored in the
the client to speed up page loading, either with another
virtual server or with an NFS or SAMBA share. Clients
typically only make 2 connections to a single server at a
- time , so this can parallelize the job.
- Defaults to null.
+ time ,
+ so this can parallelize the job. Defaults to null.
ssl: Whether to access avatars using HTTPS. Defaults to null, meaning
to guess based on site-wide SSL settings.
@@ -496,9 +497,9 @@ Profile management.
biolimit: max character length of bio; 0 means no limit; null means to use
the site text limit default.
-backup: whether users can backup their own profiles. Defaults to true.
+backup: whether users can backup their own profiles. Defaults to false.
restore: whether users can restore their profiles from backup files. Defaults
- to true.
+ to false.
delete: whether users can delete their own accounts. Defaults to false.
move: whether users can move their accounts to another server. Defaults
to true.
@@ -672,7 +673,7 @@ Web crawlers. See http://www.robotstxt.org/ for more information
on the format of this file.
crawldelay: if non-empty, this value is provided as the Crawl-Delay:
- for the robots.txt file. see http://ur1.ca/l5a0
+ for the robots.txt file. see
for more information. Default is zero, no explicit delay.
disallow: Array of (virtual) directories to disallow. Default is 'main',
'search', 'message', 'settings', 'admin'. Ignored when site
diff --git a/INSTALL b/INSTALL
index 06f3a23ee1..85a9ebccfb 100644
--- a/INSTALL
+++ b/INSTALL
@@ -26,16 +26,12 @@ PHP modules
The following software packages are *required* for this software to
run correctly.
-- PHP 5.5+ For newer versions, some functions that are used may be
- disabled by default, such as the pcntl_* family. See the
- section on 'Queues and daemons' for more information.
-- MariaDB 5+ GNU Social uses, by default, a MariaDB server for data
- storage. Versions 5.x and 10.x have both reportedly
- worked well. It is also possible to run MySQL 5.5+.
-- Web server Apache, lighttpd and nginx will all work. CGI mode is
- recommended and also some variant of 'suexec' (or a
- proper setup php-fpm pool)
- NOTE: mod_rewrite or its equivalent is extremely useful.
+- PHP 5.6+ PHP7.x is also supported.
+- MariaDB 5+ MariaDB 10.x is also supported.
+- Web server Apache, lighttpd and nginx will all work, see sample
+ configuration files in the web root. Please use PHP-FPM
+ and configure mod_rewrite (or equivalent) for an optimal
+ experience.
Your PHP installation must include the following PHP extensions for a
functional setup of GNU Social:
@@ -49,9 +45,22 @@ functional setup of GNU Social:
- php5-mysqlnd The native driver for PHP5 MariaDB connections. If you
use MySQL, 'php5-mysql' or 'php5-mysqli' may be enough.
-The above package names are for Debian based systems. In the case of
-Arch Linux, PHP is compiled with support for most extensions but they
-require manual enabling in the relevant php.ini file (mostly php5-gmp).
+Or, for PHP7, some or all of these will be necessary. PHP7 works and on
+the development servers we are successful running PHP7.2. This is a good
+list of PHP modules you will want installed with PHP7:
+ php7.0-bcmath
+ php7.0-curl
+ php7.0-exif
+ php7.0-gd
+ php7.0-intl
+ php7.0-mbstring
+ php7.0-mysql
+ php7.0-opcache
+ php7.0-readline
+ php7.0-xmlwriter
+
+NOTE: In Arch Linux, at least PHP5 requires manual enabling in the
+relevant php.ini for some modules, most notably 'gmp'.
Better performance
------------------
@@ -61,19 +70,10 @@ For some functionality, you will also need the following extensions:
- opcache Improves performance a _lot_. Included in PHP, must be
enabled manually in php.ini for most distributions. Find
and set at least: opcache.enable=1
-- mailparse Efficient parsing of email requires this extension.
- Submission by email or SMS-over-email uses this.
-- sphinx A client for the sphinx server, an alternative to MySQL
- or Postgresql fulltext search. You will also need a
- Sphinx server to serve the search queries.
- gettext For multiple languages. Default on many PHP installs;
will be emulated if not present.
- exif For thumbnails to be properly oriented.
-You may also experience better performance from your site if you configure
-a PHP cache/accelerator. Most distributions come with "opcache" support.
-Enable it in your php.ini where it is documented together with its settings.
-
Installation
============
diff --git a/README.md b/README.md
index 7dfe8ae05c..5822b366ff 100644
--- a/README.md
+++ b/README.md
@@ -107,6 +107,7 @@ So far it includes the following changes:
- Backing up a user's account is more and more complete.
- Emojis 😸 (utf8mb4 support)
+- Fully qualified group mentions (!group@example.com)
The last release, 1.1.3, gave us these improvements:
diff --git a/UPGRADE b/UPGRADE
index aaf9109e39..c175ee4f87 100644
--- a/UPGRADE
+++ b/UPGRADE
@@ -11,6 +11,9 @@ and follow this procedure:
0. Backup your data. The StatusNet upgrade discussions below have some
guidelines to back up the database and files (mysqldump and rsync).
+ MAKE SURE YOU ARE THE SAME USER THAT RUNS THE PHP FILES WHILE PERFORMING
+ THE COMMANDS BELOW (I usually prepend the commands with 'sudo -u social')
+
1. Stop your queue daemons (you can run this command even if you do not
use the queue daemons):
$ bash scripts/stopdaemons.sh
diff --git a/actions/addpeopletag.php b/actions/addpeopletag.php
index b501ce0fd9..94a2cddfb0 100644
--- a/actions/addpeopletag.php
+++ b/actions/addpeopletag.php
@@ -65,7 +65,7 @@ class AddpeopletagAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -119,7 +119,7 @@ class AddpeopletagAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
// Throws exception on error
$ptag = Profile_tag::setTag($this->user->id, $this->tagged->id,
diff --git a/actions/all.php b/actions/all.php
index 19413076b5..84e4dd5305 100644
--- a/actions/all.php
+++ b/actions/all.php
@@ -170,12 +170,6 @@ class AllAction extends ShowstreamAction
}
$ibs->show();
}
- // XXX: make this a little more convenient
-
- if (!common_config('performance', 'high')) {
- $pop = new InboxTagCloudSection($this, $this->target);
- $pop->show();
- }
}
}
diff --git a/actions/allrss.php b/actions/allrss.php
index 4b6df25048..d7f11bb1eb 100644
--- a/actions/allrss.php
+++ b/actions/allrss.php
@@ -46,7 +46,7 @@ class AllrssAction extends TargetedRss10Action
{
protected function getNotices()
{
- $stream = new InboxNoticeStream($this->target);
+ $stream = new InboxNoticeStream($this->target, $this->scoped);
return $stream->getNotices(0, $this->limit)->fetchAll();
}
diff --git a/actions/apiaccountupdatedeliverydevice.php b/actions/apiaccountupdatedeliverydevice.php
index a3cbb418b6..549ab5243a 100644
--- a/actions/apiaccountupdatedeliverydevice.php
+++ b/actions/apiaccountupdatedeliverydevice.php
@@ -54,7 +54,7 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -73,9 +73,9 @@ class ApiAccountUpdateDeliveryDeviceAction extends ApiAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (!in_array($this->format, array('xml', 'json'))) {
$this->clientError(
diff --git a/actions/apiatomservice.php b/actions/apiatomservice.php
index 9e45a30fcc..7673cee043 100644
--- a/actions/apiatomservice.php
+++ b/actions/apiatomservice.php
@@ -51,7 +51,7 @@ class ApiAtomServiceAction extends ApiBareAuthAction
* @return boolean success flag
*
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$this->user = $this->getTargetUser($this->arg('id'));
@@ -71,9 +71,9 @@ class ApiAtomServiceAction extends ApiBareAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
header('Content-Type: application/atomsvc+xml');
diff --git a/actions/apigrouplistall.php b/actions/apigrouplistall.php
index 2fb3714257..90f8eed8fa 100644
--- a/actions/apigrouplistall.php
+++ b/actions/apigrouplistall.php
@@ -58,7 +58,7 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -77,9 +77,9 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$sitename = common_config('site', 'name');
// TRANS: Message is used as a title when listing the lastest 20 groups. %s is a site name.
diff --git a/actions/apihelptest.php b/actions/apihelptest.php
index a9cd7394c9..e57ebb710f 100644
--- a/actions/apihelptest.php
+++ b/actions/apihelptest.php
@@ -28,9 +28,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Returns the string "ok" in the requested format with a 200 OK HTTP status code.
@@ -44,29 +42,9 @@ if (!defined('STATUSNET')) {
*/
class ApiHelpTestAction extends ApiPrivateAuthAction
{
- /**
- * Take arguments for running
- *
- * @param array $args $_REQUEST args
- *
- * @return boolean success flag
- */
- function prepare($args)
+ protected function handle()
{
- parent::prepare($args);
- return true;
- }
-
- /**
- * Handle the request
- *
- * @param array $args $_REQUEST data (unused)
- *
- * @return void
- */
- function handle($args)
- {
- parent::handle($args);
+ parent::handle();
if ($this->format == 'xml') {
$this->initDocument('xml');
@@ -77,12 +55,8 @@ class ApiHelpTestAction extends ApiPrivateAuthAction
print '"ok"';
$this->endDocument('json');
} else {
- $this->clientError(
- // TRANS: Client error displayed when coming across a non-supported API method.
- _('API method not found.'),
- 404,
- $this->format
- );
+ // TRANS: Client error displayed when coming across a non-supported API method.
+ throw new ClientException(_('API method not found.'), 404);
}
}
diff --git a/actions/apilistsubscriber.php b/actions/apilistsubscriber.php
index f5cda15ae9..f0391cc445 100644
--- a/actions/apilistsubscriber.php
+++ b/actions/apilistsubscriber.php
@@ -33,7 +33,7 @@ class ApiListSubscriberAction extends ApiBareAuthAction
{
var $list = null;
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -52,9 +52,9 @@ class ApiListSubscriberAction extends ApiBareAuthAction
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$arr = array('profile_tag_id' => $this->list->id,
'profile_id' => $this->target->id);
diff --git a/actions/apioauthaccesstoken.php b/actions/apioauthaccesstoken.php
index a1a70a9b9e..8dd0a40b22 100644
--- a/actions/apioauthaccesstoken.php
+++ b/actions/apioauthaccesstoken.php
@@ -52,9 +52,9 @@ class ApiOAuthAccessTokenAction extends ApiOAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$datastore = new ApiGNUsocialOAuthDataStore();
$server = new OAuthServer($datastore);
diff --git a/actions/apioauthauthorize.php b/actions/apioauthauthorize.php
index d0dcf9c9c7..8f0329cb80 100644
--- a/actions/apioauthauthorize.php
+++ b/actions/apioauthauthorize.php
@@ -60,7 +60,7 @@ class ApiOAuthAuthorizeAction extends ApiOAuthAction
return false;
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -88,9 +88,9 @@ class ApiOAuthAuthorizeAction extends ApiOAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
diff --git a/actions/apioauthrequesttoken.php b/actions/apioauthrequesttoken.php
index e961f4f464..1279d5e092 100644
--- a/actions/apioauthrequesttoken.php
+++ b/actions/apioauthrequesttoken.php
@@ -49,7 +49,7 @@ class ApiOAuthRequestTokenAction extends ApiOAuthAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -69,9 +69,9 @@ class ApiOAuthRequestTokenAction extends ApiOAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$datastore = new ApiGNUsocialOAuthDataStore();
$server = new OAuthServer($datastore);
diff --git a/actions/apisearchatom.php b/actions/apisearchatom.php
index def999e632..e82ea39f9f 100644
--- a/actions/apisearchatom.php
+++ b/actions/apisearchatom.php
@@ -88,7 +88,7 @@ class ApiSearchAtomAction extends ApiPrivateAuthAction
*
* @return boolean success
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -128,9 +128,9 @@ class ApiSearchAtomAction extends ApiPrivateAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
common_debug("In apisearchatom handle()");
$this->showAtom();
}
diff --git a/actions/apisearchjson.php b/actions/apisearchjson.php
index 794f23077b..d49444369d 100644
--- a/actions/apisearchjson.php
+++ b/actions/apisearchjson.php
@@ -57,7 +57,7 @@ class ApiSearchJSONAction extends ApiPrivateAuthAction
*
* @return boolean true if nothing goes wrong
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -95,9 +95,9 @@ class ApiSearchJSONAction extends ApiPrivateAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showResults();
}
diff --git a/actions/apistatusesdestroy.php b/actions/apistatusesdestroy.php
index 2d32124c42..0bad3da5df 100644
--- a/actions/apistatusesdestroy.php
+++ b/actions/apistatusesdestroy.php
@@ -34,9 +34,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Deletes one of the authenticating user's statuses (notices).
@@ -55,87 +53,46 @@ if (!defined('STATUSNET')) {
*/
class ApiStatusesDestroyAction extends ApiAuthAction
{
- var $status = null;
-
- /**
- * Take arguments for running
- *
- * @param array $args $_REQUEST args
- *
- * @return boolean success flag
- */
- function prepare($args)
+ protected function prepare(array $args=array())
{
parent::prepare($args);
- $this->user = $this->auth_user;
- $this->notice_id = (int)$this->trimmed('id');
-
- if (empty($notice_id)) {
- $this->notice_id = (int)$this->arg('id');
+ if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
+ // TRANS: Client error displayed trying to delete a status not using POST or DELETE.
+ // TRANS: POST and DELETE should not be translated.
+ throw new ClientException(_('This method requires a POST or DELETE.'));
}
- $this->notice = Notice::getKV((int)$this->notice_id);
+ // FIXME: Return with a Not Acceptable status code?
+ if (!in_array($this->format, array('xml', 'json'))) {
+ // TRANS: Client error displayed when coming across a non-supported API method.
+ throw new ClientException(_('API method not found.'), 404);
+ }
+
+ try {
+ $this->notice = Notice::getByID($this->trimmed('id'));
+ } catch (NoResultException $e) {
+ // TRANS: Client error displayed trying to delete a status with an invalid ID.
+ throw new ClientException(_('No status found with that ID.'), 404);
+ }
return true;
}
- /**
- * Handle the request
- *
- * Delete the notice and all related replies
- *
- * @param array $args $_REQUEST data (unused)
- *
- * @return void
- */
- function handle($args)
+ protected function handle()
{
- parent::handle($args);
+ parent::handle();
- if (!in_array($this->format, array('xml', 'json'))) {
- $this->clientError(
- // TRANS: Client error displayed when coming across a non-supported API method.
- _('API method not found.'),
- 404
- );
- return;
+ if (!$this->scoped->sameAs($this->notice->getProfile()) && !$this->scoped->hasRight(Right::DELETEOTHERSNOTICE)) {
+ // TRANS: Client error displayed trying to delete a status of another user.
+ throw new AuthorizationException(_('You may not delete another user\'s status.'));
}
- if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) {
- $this->clientError(
- // TRANS: Client error displayed trying to delete a status not using POST or DELETE.
- // TRANS: POST and DELETE should not be translated.
- _('This method requires a POST or DELETE.'),
- 400,
- $this->format
- );
- return;
- }
-
- if (empty($this->notice)) {
- $this->clientError(
- // TRANS: Client error displayed trying to delete a status with an invalid ID.
- _('No status found with that ID.'),
- 404, $this->format
- );
- return;
- }
-
- if ($this->user->id == $this->notice->profile_id) {
- if (Event::handle('StartDeleteOwnNotice', array($this->user, $this->notice))) {
- $this->notice->deleteAs($this->scoped);
- Event::handle('EndDeleteOwnNotice', array($this->user, $this->notice));
- }
- $this->showNotice();
- } else {
- $this->clientError(
- // TRANS: Client error displayed trying to delete a status of another user.
- _('You may not delete another user\'s status.'),
- 403,
- $this->format
- );
+ if (Event::handle('StartDeleteOwnNotice', array($this->scoped->getUser(), $this->notice))) {
+ $this->notice->deleteAs($this->scoped);
+ Event::handle('EndDeleteOwnNotice', array($this->scoped->getUser(), $this->notice));
}
+ $this->showNotice();
}
/**
diff --git a/actions/apistatusesupdate.php b/actions/apistatusesupdate.php
index 09663ac7c2..de00325494 100644
--- a/actions/apistatusesupdate.php
+++ b/actions/apistatusesupdate.php
@@ -46,7 +46,7 @@
/api/statuses/update.:format
@par Formats (:format)
- xml, json
+ xml, json, atom
@par HTTP Method(s)
POST
@@ -174,7 +174,7 @@ class ApiStatusesUpdateAction extends ApiAuthAction
foreach (array_unique($matches[0]) as $match) {
try {
$this->media_ids[$match] = File::getByID($match);
- } catch (EmptyIdException $e) {
+ } catch (EmptyPkeyValueException $e) {
// got a zero from the client, at least Twidere does this on occasion
} catch (NoResultException $e) {
// File ID was not found. Do we abort and report to the client?
@@ -339,6 +339,8 @@ class ApiStatusesUpdateAction extends ApiAuthAction
$this->showSingleXmlStatus($this->notice);
} elseif ($this->format == 'json') {
$this->show_single_json_status($this->notice);
+ } elseif ($this->format == 'atom') {
+ $this->showSingleAtomStatus($this->notice);
}
}
}
diff --git a/actions/apitrends.php b/actions/apitrends.php
index a39769a34e..996ad66301 100644
--- a/actions/apitrends.php
+++ b/actions/apitrends.php
@@ -53,7 +53,7 @@ class ApiTrendsAction extends ApiPrivateAuthAction
*
* @return boolean false if user doesn't exist
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
return true;
@@ -66,9 +66,9 @@ class ApiTrendsAction extends ApiPrivateAuthAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showTrends();
}
diff --git a/actions/approvegroup.php b/actions/approvegroup.php
index 2c8b354d34..e080be6474 100644
--- a/actions/approvegroup.php
+++ b/actions/approvegroup.php
@@ -50,7 +50,7 @@ class ApprovegroupAction extends Action
/**
* Prepare to run
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -139,9 +139,9 @@ class ApprovegroupAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
try {
if ($this->approve) {
diff --git a/actions/approvesub.php b/actions/approvesub.php
index c9fa804412..147d37f297 100644
--- a/actions/approvesub.php
+++ b/actions/approvesub.php
@@ -50,7 +50,7 @@ class ApprovesubAction extends Action
/**
* Prepare to run
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -97,9 +97,9 @@ class ApprovesubAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$cur = common_current_user();
try {
diff --git a/actions/attachment.php b/actions/attachment.php
index 1126759832..3f2ae5c1ce 100644
--- a/actions/attachment.php
+++ b/actions/attachment.php
@@ -96,7 +96,7 @@ class AttachmentAction extends ManagedAction
{
if (empty($this->attachment->filename)) {
// if it's not a local file, gtfo
- common_redirect($this->attachment->url, 303);
+ common_redirect($this->attachment->getUrl(), 303);
}
parent::showPage();
@@ -132,9 +132,5 @@ class AttachmentAction extends ManagedAction
function showSections() {
$ns = new AttachmentNoticeSection($this);
$ns->show();
- if (!common_config('performance', 'high')) {
- $atcs = new AttachmentTagCloudSection($this);
- $atcs->show();
- }
}
}
diff --git a/actions/attachment_download.php b/actions/attachment_download.php
new file mode 100644
index 0000000000..6792c45993
--- /dev/null
+++ b/actions/attachment_download.php
@@ -0,0 +1,20 @@
+
+ * @license https://www.gnu.org/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
+ * @link https:/gnu.io/social
+ */
+class Attachment_downloadAction extends AttachmentAction
+{
+ public function showPage()
+ {
+ common_redirect($this->attachment->getUrl(), 302);
+ }
+}
diff --git a/actions/attachment_thumbnail.php b/actions/attachment_thumbnail.php
index 3b8eec3ca6..cc1b0f09c6 100644
--- a/actions/attachment_thumbnail.php
+++ b/actions/attachment_thumbnail.php
@@ -62,6 +62,6 @@ class Attachment_thumbnailAction extends AttachmentAction
common_redirect($e->file->getUrl(), 302);
}
- common_redirect(File_thumbnail::url($thumbnail->filename), 302);
+ common_redirect(File_thumbnail::url($thumbnail->getFilename()), 302);
}
}
diff --git a/actions/avatarsettings.php b/actions/avatarsettings.php
index 4b618eb9be..6c9f3fc3d0 100644
--- a/actions/avatarsettings.php
+++ b/actions/avatarsettings.php
@@ -49,6 +49,20 @@ class AvatarsettingsAction extends SettingsAction
var $imagefile = null;
var $filename = null;
+ function prepare(array $args=array())
+ {
+ $avatarpath = Avatar::path('');
+
+ if (!is_writable($avatarpath)) {
+ throw new Exception(_("The administrator of your site needs to
+ add write permissions on the avatar upload folder before
+ you're able to set one."));
+ }
+
+ parent::prepare($args);
+ return true;
+ }
+
/**
* Title of the page
*
@@ -92,16 +106,6 @@ class AvatarsettingsAction extends SettingsAction
function showUploadForm()
{
- $user = common_current_user();
-
- $profile = $user->getProfile();
-
- if (!$profile) {
- common_log_db_error($user, 'SELECT', __FILE__);
- // TRANS: Error message displayed when referring to a user without a profile.
- $this->serverError(_('User has no profile.'));
- }
-
$this->elementStart('form', array('enctype' => 'multipart/form-data',
'method' => 'post',
'id' => 'form_settings_avatar',
@@ -116,7 +120,7 @@ class AvatarsettingsAction extends SettingsAction
if (Event::handle('StartAvatarFormData', array($this))) {
$this->elementStart('ul', 'form_data');
try {
- $original = Avatar::getUploaded($profile);
+ $original = Avatar::getUploaded($this->scoped);
$this->elementStart('li', array('id' => 'avatar_original',
'class' => 'avatar_view'));
@@ -126,7 +130,7 @@ class AvatarsettingsAction extends SettingsAction
$this->element('img', array('src' => $original->displayUrl(),
'width' => $original->width,
'height' => $original->height,
- 'alt' => $user->nickname));
+ 'alt' => $this->scoped->getNickname()));
$this->elementEnd('div');
$this->elementEnd('li');
} catch (NoAvatarException $e) {
@@ -134,7 +138,7 @@ class AvatarsettingsAction extends SettingsAction
}
try {
- $avatar = $profile->getAvatar(AVATAR_PROFILE_SIZE);
+ $avatar = $this->scoped->getAvatar(AVATAR_PROFILE_SIZE);
$this->elementStart('li', array('id' => 'avatar_preview',
'class' => 'avatar_view'));
// TRANS: Header on avatar upload page for thumbnail of to be used rendition of uploaded avatar (h2).
@@ -143,7 +147,7 @@ class AvatarsettingsAction extends SettingsAction
$this->element('img', array('src' => $avatar->displayUrl(),
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
- 'alt' => $user->nickname));
+ 'alt' => $this->scoped->getNickname()));
$this->elementEnd('div');
if (!empty($avatar->filename)) {
// TRANS: Button on avatar upload page to delete current avatar.
@@ -180,16 +184,6 @@ class AvatarsettingsAction extends SettingsAction
function showCropForm()
{
- $user = common_current_user();
-
- $profile = $user->getProfile();
-
- if (!$profile) {
- common_log_db_error($user, 'SELECT', __FILE__);
- // TRANS: Error message displayed when referring to a user without a profile.
- $this->serverError(_('User has no profile.'));
- }
-
$this->elementStart('form', array('method' => 'post',
'id' => 'form_settings_avatar',
'class' => 'form_settings',
@@ -211,7 +205,7 @@ class AvatarsettingsAction extends SettingsAction
$this->element('img', array('src' => Avatar::url($this->filedata['filename']),
'width' => $this->filedata['width'],
'height' => $this->filedata['height'],
- 'alt' => $user->nickname));
+ 'alt' => $this->scoped->getNickname()));
$this->elementEnd('div');
$this->elementEnd('li');
@@ -224,7 +218,7 @@ class AvatarsettingsAction extends SettingsAction
$this->element('img', array('src' => Avatar::url($this->filedata['filename']),
'width' => AVATAR_PROFILE_SIZE,
'height' => AVATAR_PROFILE_SIZE,
- 'alt' => $user->nickname));
+ 'alt' => $this->scoped->getNickname()));
$this->elementEnd('div');
foreach (array('avatar_crop_x', 'avatar_crop_y',
diff --git a/actions/backupaccount.php b/actions/backupaccount.php
index 7f37f3b851..637da0ac3c 100644
--- a/actions/backupaccount.php
+++ b/actions/backupaccount.php
@@ -74,6 +74,9 @@ class BackupaccountAction extends FormAction
// @fixme atom feed logic is in getString...
// but we just want it to output to the outputter.
$this->raw($stream->getString());
+
+ // Don't print the page HTML
+ exit(0);
}
public function isReadOnly($args) {
diff --git a/actions/block.php b/actions/block.php
index 53d8ae7ae0..1d2910dc19 100644
--- a/actions/block.php
+++ b/actions/block.php
@@ -53,7 +53,7 @@ class BlockAction extends ProfileFormAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
if (!parent::prepare($args)) {
return false;
@@ -78,7 +78,7 @@ class BlockAction extends ProfileFormAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) {
diff --git a/actions/blockedfromgroup.php b/actions/blockedfromgroup.php
index a2e7c5767f..d2873fe467 100644
--- a/actions/blockedfromgroup.php
+++ b/actions/blockedfromgroup.php
@@ -151,7 +151,7 @@ class GroupBlockList extends ProfileList
$this->group = $group;
}
- function newListItem($profile)
+ function newListItem(Profile $profile)
{
return new GroupBlockListItem($profile, $this->group, $this->action);
}
diff --git a/actions/cancelgroup.php b/actions/cancelgroup.php
index 93f630e060..5688b5c451 100644
--- a/actions/cancelgroup.php
+++ b/actions/cancelgroup.php
@@ -50,7 +50,7 @@ class CancelgroupAction extends Action
/**
* Prepare to run
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -127,9 +127,9 @@ class CancelgroupAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
try {
$this->request->abort();
diff --git a/actions/confirmaddress.php b/actions/confirmaddress.php
index 806851001e..9ac6848d7a 100644
--- a/actions/confirmaddress.php
+++ b/actions/confirmaddress.php
@@ -27,9 +27,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Confirm an address
@@ -44,25 +42,14 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-class ConfirmaddressAction extends Action
+class ConfirmaddressAction extends ManagedAction
{
/** type of confirmation. */
- var $address;
+ protected $address;
- /**
- * Accept a confirmation code
- *
- * Checks the code and confirms the address in the
- * user record
- *
- * @param args $args $_REQUEST array
- *
- * @return void
- */
- function handle($args)
+ protected function doPreparation()
{
- parent::handle($args);
if (!common_logged_in()) {
common_set_returnto($this->selfUrl());
common_redirect(common_local_url('login'));
@@ -70,32 +57,45 @@ class ConfirmaddressAction extends Action
$code = $this->trimmed('code');
if (!$code) {
// TRANS: Client error displayed when not providing a confirmation code in the contact address confirmation action.
- $this->clientError(_('No confirmation code.'));
+ throw new ClientException(_('No confirmation code.'));
}
$confirm = Confirm_address::getKV('code', $code);
- if (!$confirm) {
+ if (!$confirm instanceof Confirm_address) {
// TRANS: Client error displayed when providing a non-existing confirmation code in the contact address confirmation action.
- $this->clientError(_('Confirmation code not found.'));
+ throw new ClientException(_('Confirmation code not found.'), 404);
}
- $cur = common_current_user();
- if ($cur->id != $confirm->user_id) {
+
+ try {
+ $profile = Profile::getByID($confirm->user_id);
+ } catch (NoResultException $e) {
+ common_log(LOG_INFO, 'Tried to confirm the email for a deleted profile: '._ve(['id'=>$confirm->user_id, 'email'=>$confirm->address]));
+ $confirm->delete();
+ throw $e;
+ }
+ if (!$profile->sameAs($this->scoped)) {
// TRANS: Client error displayed when not providing a confirmation code for another user in the contact address confirmation action.
- $this->clientError(_('That confirmation code is not for you!'));
+ throw new AuthorizationException(_('That confirmation code is not for you!'));
}
+
$type = $confirm->address_type;
$transports = array();
Event::handle('GetImTransports', array(&$transports));
if (!in_array($type, array('email', 'sms')) && !in_array($type, array_keys($transports))) {
// TRANS: Server error for an unknown address type, which can be 'email', 'sms', or the name of an IM network (such as 'xmpp' or 'aim')
- $this->serverError(sprintf(_('Unrecognized address type %s'), $type));
+ throw new ServerException(sprintf(_('Unrecognized address type %s'), $type));
}
$this->address = $confirm->address;
+
+ $cur = $this->scoped->getUser();
+
$cur->query('BEGIN');
- if (in_array($type, array('email', 'sms')))
- {
+ if (in_array($type, array('email', 'sms'))) {
+ common_debug("Confirming {$type} address for user {$this->scoped->getID()}");
if ($cur->$type == $confirm->address) {
+ // Already verified, so delete the confirm_address entry
+ $confirm->delete();
// TRANS: Client error for an already confirmed email/jabber/sms address.
- $this->clientError(_('That address has already been confirmed.'));
+ throw new AlreadyFulfilledException(_('That address has already been confirmed.'));
}
$orig_user = clone($cur);
@@ -122,16 +122,18 @@ class ConfirmaddressAction extends Action
$user_im_prefs->user_id = $cur->id;
if ($user_im_prefs->find() && $user_im_prefs->fetch()) {
if($user_im_prefs->screenname == $confirm->address){
+ // Already verified, so delete the confirm_address entry
+ $confirm->delete();
// TRANS: Client error for an already confirmed IM address.
- $this->clientError(_('That address has already been confirmed.'));
+ throw new AlreadyFulfilledException(_('That address has already been confirmed.'));
}
$user_im_prefs->screenname = $confirm->address;
$result = $user_im_prefs->update();
- if (!$result) {
+ if ($result === false) {
common_log_db_error($user_im_prefs, 'UPDATE', __FILE__);
// TRANS: Server error displayed when updating IM preferences fails.
- $this->serverError(_('Could not update user IM preferences.'));
+ throw new ServerException(_('Could not update user IM preferences.'));
}
}else{
$user_im_prefs = new User_im_prefs();
@@ -140,26 +142,18 @@ class ConfirmaddressAction extends Action
$user_im_prefs->user_id = $cur->id;
$result = $user_im_prefs->insert();
- if (!$result) {
+ if ($result === false) {
common_log_db_error($user_im_prefs, 'INSERT', __FILE__);
// TRANS: Server error displayed when adding IM preferences fails.
- $this->serverError(_('Could not insert user IM preferences.'));
+ throw new ServerException(_('Could not insert user IM preferences.'));
}
}
}
- $result = $confirm->delete();
-
- if (!$result) {
- common_log_db_error($confirm, 'DELETE', __FILE__);
- // TRANS: Server error displayed when an address confirmation code deletion from the
- // TRANS: database fails in the contact address confirmation action.
- $this->serverError(_('Could not delete address confirmation.'));
- }
+ $confirm->delete();
$cur->query('COMMIT');
- $this->showPage();
}
/**
@@ -180,8 +174,6 @@ class ConfirmaddressAction extends Action
*/
function showContent()
{
- $cur = common_current_user();
-
$this->element('p', null,
// TRANS: Success message for the contact address confirmation action.
// TRANS: %s can be 'email', 'jabber', or 'sms'.
diff --git a/actions/deleteapplication.php b/actions/deleteapplication.php
index 5d7441098c..0a9af4cfb9 100644
--- a/actions/deleteapplication.php
+++ b/actions/deleteapplication.php
@@ -51,7 +51,7 @@ class DeleteapplicationAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
if (!parent::prepare($args)) {
return false;
@@ -89,7 +89,7 @@ class DeleteapplicationAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
diff --git a/actions/deletegroup.php b/actions/deletegroup.php
index c64bc1d8e9..2a4343b06e 100644
--- a/actions/deletegroup.php
+++ b/actions/deletegroup.php
@@ -55,7 +55,7 @@ class DeletegroupAction extends RedirectingAction
* @fixme merge common setup code with other group actions
* @fixme allow group admins to delete their own groups
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -114,9 +114,9 @@ class DeletegroupAction extends RedirectingAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) {
$this->returnToPrevious();
diff --git a/actions/deleteuser.php b/actions/deleteuser.php
index 6e0c6ebf7f..d1b73f4746 100644
--- a/actions/deleteuser.php
+++ b/actions/deleteuser.php
@@ -80,7 +80,7 @@ class DeleteuserAction extends ProfileFormAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) {
diff --git a/actions/editapplication.php b/actions/editapplication.php
index c7e5f9052c..d492afb08f 100644
--- a/actions/editapplication.php
+++ b/actions/editapplication.php
@@ -57,7 +57,7 @@ class EditApplicationAction extends Action
/**
* Prepare to run
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -94,9 +94,9 @@ class EditApplicationAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->handlePost($args);
diff --git a/actions/editpeopletag.php b/actions/editpeopletag.php
index 45c8adaa7d..0fbba56224 100644
--- a/actions/editpeopletag.php
+++ b/actions/editpeopletag.php
@@ -60,7 +60,7 @@ class EditpeopletagAction extends Action
* Prepare to run
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -135,9 +135,9 @@ class EditpeopletagAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->trySave();
} else {
diff --git a/actions/emailsettings.php b/actions/emailsettings.php
index c02f1cdfad..b5933fdb65 100644
--- a/actions/emailsettings.php
+++ b/actions/emailsettings.php
@@ -369,8 +369,7 @@ class EmailsettingsAction extends SettingsAction
throw new ServerException(_('Could not insert confirmation code.'));
}
- common_debug('Sending confirmation address for user '.$user->getID().' to email '.$email);
- mail_confirm_address($user, $confirm->code, $user->getNickname(), $email);
+ $confirm->sendConfirmation();
Event::handle('EndAddEmailAddress', array($user, $email));
}
@@ -401,13 +400,7 @@ class EmailsettingsAction extends SettingsAction
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
}
- $result = $confirm->delete();
-
- if ($result === false) {
- common_log_db_error($confirm, 'DELETE', __FILE__);
- // TRANS: Server error thrown on database error canceling e-mail address confirmation.
- throw new ServerException(_('Could not delete email confirmation.'));
- }
+ $confirm->delete();
// TRANS: Message given after successfully canceling e-mail address confirmation.
return _('Email confirmation cancelled.');
diff --git a/actions/featured.php b/actions/featured.php
index 394cfe6a8b..5609d6bace 100644
--- a/actions/featured.php
+++ b/actions/featured.php
@@ -54,7 +54,7 @@ class FeaturedAction extends Action
return true;
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
@@ -74,9 +74,9 @@ class FeaturedAction extends Action
}
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
diff --git a/actions/foafgroup.php b/actions/foafgroup.php
index f9c61ac5dc..e0cec8866c 100644
--- a/actions/foafgroup.php
+++ b/actions/foafgroup.php
@@ -35,7 +35,7 @@ class FoafGroupAction extends Action
return true;
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -76,9 +76,9 @@ class FoafGroupAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
header('Content-Type: application/rdf+xml');
diff --git a/actions/geocode.php b/actions/geocode.php
index 9e208914c1..dc547f2c36 100644
--- a/actions/geocode.php
+++ b/actions/geocode.php
@@ -47,7 +47,7 @@ class GeocodeAction extends Action
var $lon = null;
var $location = null;
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$token = $this->trimmed('token');
@@ -70,7 +70,7 @@ class GeocodeAction extends Action
* @return nothing
*
*/
- function handle($args)
+ function handle()
{
header('Content-Type: application/json; charset=utf-8');
$location_object = array();
diff --git a/actions/grantrole.php b/actions/grantrole.php
index 35f0dcf961..2fc4907cfc 100644
--- a/actions/grantrole.php
+++ b/actions/grantrole.php
@@ -49,7 +49,7 @@ class GrantRoleAction extends ProfileFormAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
if (!parent::prepare($args)) {
return false;
diff --git a/actions/groupblock.php b/actions/groupblock.php
index d65b62bdff..43fa1f52d5 100644
--- a/actions/groupblock.php
+++ b/actions/groupblock.php
@@ -52,7 +52,7 @@ class GroupblockAction extends RedirectingAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
if (!common_logged_in()) {
@@ -110,9 +110,9 @@ class GroupblockAction extends RedirectingAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($this->arg('no')) {
$this->returnToPrevious();
diff --git a/actions/groupbyid.php b/actions/groupbyid.php
index de87ec5c67..ed4ec979a9 100644
--- a/actions/groupbyid.php
+++ b/actions/groupbyid.php
@@ -42,7 +42,7 @@ if (!defined('GNUSOCIAL')) { exit(1); }
* @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/
*/
-class GroupbyidAction extends ManagedAction
+class GroupbyidAction extends ShowgroupAction
{
/** group we're viewing. */
protected $group = null;
@@ -55,10 +55,10 @@ class GroupbyidAction extends ManagedAction
protected function doPreparation()
{
$this->group = User_group::getByID($this->arg('id'));
- }
+ $this->target = $this->group->getProfile();
- public function showPage()
- {
- common_redirect($this->group->homeUrl(), 303);
+ if ($this->target->isLocal()) {
+ common_redirect($this->target->getUrl());
+ }
}
}
diff --git a/actions/grouplogo.php b/actions/grouplogo.php
index 0d9c135785..e3906847d3 100644
--- a/actions/grouplogo.php
+++ b/actions/grouplogo.php
@@ -211,6 +211,10 @@ class GrouplogoAction extends GroupAction
'height' => AVATAR_PROFILE_SIZE,
'alt' => $this->group->nickname));
$this->elementEnd('div');
+ if (!empty($this->group->homepage_logo)) {
+ // TRANS: Button on group logo upload page to delete current group logo.
+ $this->submit('delete', _('Delete'));
+ }
$this->elementEnd('li');
}
@@ -315,6 +319,8 @@ class GrouplogoAction extends GroupAction
$this->uploadLogo();
} else if ($this->arg('crop')) {
$this->cropLogo();
+ } else if ($this->arg('delete')) {
+ $this->deleteLogo();
} else {
// TRANS: Form validation error message when an unsupported argument is used.
$this->showForm(_('Unexpected form submission.'));
@@ -409,6 +415,29 @@ class GrouplogoAction extends GroupAction
}
}
+ /**
+ * Get rid of the current group logo.
+ *
+ * @return void
+ */
+ function deleteLogo()
+ {
+ $orig = clone($this->group);
+ Avatar::deleteFromProfile($this->group->getProfile());
+ @unlink(Avatar::path(basename($this->group->original_logo)));
+ @unlink(Avatar::path(basename($this->group->homepage_logo)));
+ @unlink(Avatar::path(basename($this->group->stream_logo)));
+ @unlink(Avatar::path(basename($this->group->mini_logo)));
+ $this->group->original_logo=User_group::defaultLogo(AVATAR_PROFILE_SIZE);
+ $this->group->homepage_logo=User_group::defaultLogo(AVATAR_PROFILE_SIZE);
+ $this->group->stream_logo=User_group::defaultLogo(AVATAR_STREAM_SIZE);
+ $this->group->mini_logo=User_group::defaultLogo(AVATAR_MINI_SIZE);
+ $this->group->update($orig);
+
+ // TRANS: Success message for deleting the group logo.
+ $this->showForm(_('Logo deleted.'));
+ }
+
function showPageNotice()
{
if ($this->msg) {
diff --git a/actions/groupmembers.php b/actions/groupmembers.php
index 44c4dd6f99..d8b2a1e7f3 100644
--- a/actions/groupmembers.php
+++ b/actions/groupmembers.php
@@ -27,12 +27,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once(INSTALLDIR.'/lib/profilelist.php');
-require_once INSTALLDIR.'/lib/publicgroupnav.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* List of group members
@@ -52,15 +47,6 @@ class GroupmembersAction extends GroupAction
return true;
}
- protected function prepare(array $args=array())
- {
- parent::prepare($args);
-
- $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
- return true;
- }
-
function title()
{
if ($this->page == 1) {
diff --git a/actions/groupqueue.php b/actions/groupqueue.php
index c50eff36f8..98da77e01a 100644
--- a/actions/groupqueue.php
+++ b/actions/groupqueue.php
@@ -153,7 +153,7 @@ class GroupqueueAction extends GroupAction
// @todo FIXME: documentation missing.
class GroupQueueList extends GroupMemberList
{
- function newListItem($profile)
+ function newListItem(Profile $profile)
{
return new GroupQueueListItem($profile, $this->group, $this->action);
}
diff --git a/actions/groups.php b/actions/groups.php
index d1bc8d9458..2e88d0a960 100644
--- a/actions/groups.php
+++ b/actions/groups.php
@@ -67,16 +67,16 @@ class GroupsAction extends Action
}
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
diff --git a/actions/groupunblock.php b/actions/groupunblock.php
index a24177f64c..42c68b7abd 100644
--- a/actions/groupunblock.php
+++ b/actions/groupunblock.php
@@ -52,7 +52,7 @@ class GroupunblockAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
if (!common_logged_in()) {
@@ -103,9 +103,9 @@ class GroupunblockAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->unblockProfile();
}
diff --git a/actions/imsettings.php b/actions/imsettings.php
index 40bea10e68..80f7f78770 100644
--- a/actions/imsettings.php
+++ b/actions/imsettings.php
@@ -359,13 +359,7 @@ class ImsettingsAction extends SettingsAction
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
}
- $result = $confirm->delete();
-
- if ($result === false) {
- common_log_db_error($confirm, 'DELETE', __FILE__);
- // TRANS: Server error thrown on database error canceling IM address confirmation.
- throw new ServerException(_('Could not delete confirmation.'));
- }
+ $confirm->delete();
// TRANS: Message given after successfully canceling IM address confirmation.
return _('IM confirmation cancelled.');
diff --git a/actions/invite.php b/actions/invite.php
index 89b7e83bf6..827e026696 100644
--- a/actions/invite.php
+++ b/actions/invite.php
@@ -38,9 +38,9 @@ class InviteAction extends Action
return false;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (!common_config('invite', 'enabled')) {
// TRANS: Client error displayed when trying to sent invites while they have been disabled.
$this->clientError(_('Invites have been disabled.'));
diff --git a/actions/makeadmin.php b/actions/makeadmin.php
index 05912e150c..1bb830f81e 100644
--- a/actions/makeadmin.php
+++ b/actions/makeadmin.php
@@ -54,7 +54,7 @@ class MakeadminAction extends RedirectingAction
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
if (!common_logged_in()) {
@@ -111,9 +111,9 @@ class MakeadminAction extends RedirectingAction
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$this->makeAdmin();
}
diff --git a/actions/microsummary.php b/actions/microsummary.php
deleted file mode 100644
index 2742eb9a04..0000000000
--- a/actions/microsummary.php
+++ /dev/null
@@ -1,82 +0,0 @@
-
- * @author Robin Millette
- * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link http://status.net/
- *
- * StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, 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
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-/**
- * Microsummary action class.
- *
- * @category Action
- * @package StatusNet
- * @author Evan Prodromou
- * @author Robin Millette
- * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
- * @link http://status.net/
- */
-class MicrosummaryAction extends Action
-{
- /**
- * Class handler.
- *
- * @param array $args array of arguments
- *
- * @return nothing
- */
- function handle($args)
- {
- parent::handle($args);
-
- $nickname = common_canonical_nickname($this->arg('nickname'));
- $user = User::getKV('nickname', $nickname);
-
- if (!$user) {
- // TRANS: Client error displayed trying to make a micro summary without providing a valid user.
- $this->clientError(_('No such user.'), 404);
- }
-
- $notice = $user->getCurrentNotice();
-
- if (!$notice) {
- // TRANS: Client error displayed trying to make a micro summary without providing a status.
- $this->clientError(_('No current status.'), 404);
- }
-
- header('Content-Type: text/plain');
-
- print $user->nickname . ': ' . $notice->content;
- }
-
- function isReadOnly($args)
- {
- return true;
- }
-}
diff --git a/actions/newnotice.php b/actions/newnotice.php
index 6ee2092061..170e5bcdf8 100644
--- a/actions/newnotice.php
+++ b/actions/newnotice.php
@@ -47,6 +47,8 @@ class NewnoticeAction extends FormAction
{
protected $form = 'Notice';
+ protected $inreplyto = null;
+
/**
* Title of the page
*
@@ -75,6 +77,11 @@ class NewnoticeAction extends FormAction
}
}
+ if ($this->int('inreplyto')) {
+ // Throws exception if the inreplyto Notice is given but not found.
+ $this->inreplyto = Notice::getByID($this->int('inreplyto'));
+ }
+
// Backwards compatibility for "share this" widget things.
// If no 'content', use 'status_textarea'
$this->formOpts['content'] = $this->trimmed('content') ?: $this->trimmed('status_textarea');
@@ -115,7 +122,7 @@ class NewnoticeAction extends FormAction
// simply no attached media to the new notice
if (empty($content)) {
// TRANS: Client error displayed trying to send a notice without content.
- $this->clientError(_('No content!'));
+ throw new ClientException(_('No content!'));
}
}
@@ -132,13 +139,6 @@ class NewnoticeAction extends FormAction
return;
}
- if ($this->int('inreplyto')) {
- // Throws exception if the inreplyto Notice is given but not found.
- $parent = Notice::getByID($this->int('inreplyto'));
- } else {
- $parent = null;
- }
-
$act = new Activity();
$act->verb = ActivityVerb::POST;
$act->time = time();
@@ -157,9 +157,9 @@ class NewnoticeAction extends FormAction
$act->context = new ActivityContext();
- if ($parent instanceof Notice) {
- $act->context->replyToID = $parent->getUri();
- $act->context->replyToUrl = $parent->getUrl(true); // maybe we don't have to send true here to force a URL?
+ if ($this->inreplyto instanceof Notice) {
+ $act->context->replyToID = $this->inreplyto->getUri();
+ $act->context->replyToUrl = $this->inreplyto->getUrl(true); // maybe we don't have to send true here to force a URL?
}
if ($this->scoped->shareLocation()) {
@@ -188,14 +188,14 @@ class NewnoticeAction extends FormAction
// FIXME: We should be able to get the attentions from common_render_content!
// and maybe even directly save whether they're local or not!
- $act->context->attention = common_get_attentions($content, $this->scoped, $parent);
+ $act->context->attention = common_get_attentions($content, $this->scoped, $this->inreplyto);
// $options gets filled with possible scoping settings
ToSelector::fillActivity($this, $act, $options);
$actobj = new ActivityObject();
$actobj->type = ActivityObject::NOTE;
- $actobj->content = common_render_content($content, $this->scoped, $parent);
+ $actobj->content = common_render_content($content, $this->scoped, $this->inreplyto);
// Finally add the activity object to our activity
$act->objects[] = $actobj;
@@ -224,6 +224,9 @@ class NewnoticeAction extends FormAction
if ($this->getInfo() && $this->stored instanceof Notice) {
$this->showNotice($this->stored);
} elseif (!$this->getError()) {
+ if (!GNUsocial::isAjax() && $this->inreplyto instanceof Notice) {
+ $this->showNotice($this->inreplyto);
+ }
parent::showContent();
}
}
diff --git a/actions/noticesearch.php b/actions/noticesearch.php
index fd8fdf68e5..0d6fb51fb4 100644
--- a/actions/noticesearch.php
+++ b/actions/noticesearch.php
@@ -50,7 +50,7 @@ class NoticesearchAction extends SearchAction
{
protected $q = null;
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -65,8 +65,7 @@ class NoticesearchAction extends SearchAction
if (!empty($this->q)) {
- $profile = Profile::current();
- $stream = new SearchNoticeStream($this->q, $profile);
+ $stream = new SearchNoticeStream($this->q, $this->scoped);
$page = $this->trimmed('page');
if (empty($page)) {
@@ -186,7 +185,7 @@ class SearchNoticeList extends NoticeList {
$this->terms = $terms;
}
- function newListItem($notice)
+ function newListItem(Notice $notice)
{
return new SearchNoticeListItem($notice, $this->out, $this->terms);
}
diff --git a/actions/nudge.php b/actions/nudge.php
index 801d1f0681..8ade7fa272 100644
--- a/actions/nudge.php
+++ b/actions/nudge.php
@@ -55,9 +55,9 @@ class NudgeAction extends Action
*
* @return nothing
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (!common_logged_in()) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
diff --git a/actions/opensearch.php b/actions/opensearch.php
index b1529860a8..b7ed3212c2 100644
--- a/actions/opensearch.php
+++ b/actions/opensearch.php
@@ -53,9 +53,9 @@ class OpensearchAction extends Action
*
* @return boolean false if user doesn't exist
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$type = $this->trimmed('type');
$short_name = '';
if ($type == 'people') {
diff --git a/actions/otp.php b/actions/otp.php
index c44f3673ae..bb888acb6a 100644
--- a/actions/otp.php
+++ b/actions/otp.php
@@ -53,7 +53,7 @@ class OtpAction extends Action
var $returnto;
var $lt;
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -110,9 +110,9 @@ class OtpAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
// success!
if (!common_set_user($this->user)) {
diff --git a/actions/peopletag.php b/actions/peopletag.php
index b0a6e2635d..ed811ce262 100644
--- a/actions/peopletag.php
+++ b/actions/peopletag.php
@@ -62,7 +62,7 @@ class PeopletagAction extends Action
}
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
@@ -84,9 +84,9 @@ class PeopletagAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
diff --git a/actions/peopletagautocomplete.php b/actions/peopletagautocomplete.php
index c239c03bfb..3daac8e868 100644
--- a/actions/peopletagautocomplete.php
+++ b/actions/peopletagautocomplete.php
@@ -44,7 +44,7 @@ class PeopletagautocompleteAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -112,7 +112,7 @@ class PeopletagautocompleteAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
//common_log(LOG_DEBUG, 'Autocomplete data: ' . json_encode($this->tags));
if ($this->tags) {
diff --git a/actions/peopletagged.php b/actions/peopletagged.php
index cf9ec053ed..db2420a8a3 100644
--- a/actions/peopletagged.php
+++ b/actions/peopletagged.php
@@ -53,7 +53,7 @@ class PeopletaggedAction extends Action
return true;
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
@@ -117,9 +117,9 @@ class PeopletaggedAction extends Action
}
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
@@ -167,7 +167,7 @@ class PeopletagMemberList extends ProfileList
$this->peopletag = $peopletag;
}
- function newListItem($profile)
+ function newListItem(Profile $profile)
{
return new PeopletagMemberListItem($profile, $this->peopletag, $this->action);
}
diff --git a/actions/peopletagsbyuser.php b/actions/peopletagsbyuser.php
index 4a04ea2fbb..7463108774 100644
--- a/actions/peopletagsbyuser.php
+++ b/actions/peopletagsbyuser.php
@@ -68,7 +68,7 @@ class PeopletagsbyuserAction extends Action
}
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -135,9 +135,9 @@ class PeopletagsbyuserAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
# Post from the tag dropdown; redirect to a GET
diff --git a/actions/peopletagsforuser.php b/actions/peopletagsforuser.php
index 7679be0b86..a930328156 100644
--- a/actions/peopletagsforuser.php
+++ b/actions/peopletagsforuser.php
@@ -54,7 +54,7 @@ class PeopletagsforuserAction extends Action
}
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -95,9 +95,9 @@ class PeopletagsforuserAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
diff --git a/actions/peopletagsubscribers.php b/actions/peopletagsubscribers.php
index 589d49caa3..927cf66e64 100644
--- a/actions/peopletagsubscribers.php
+++ b/actions/peopletagsubscribers.php
@@ -53,7 +53,7 @@ class PeopletagsubscribersAction extends Action
return true;
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
@@ -117,9 +117,9 @@ class PeopletagsubscribersAction extends Action
}
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
@@ -167,7 +167,7 @@ class PeopletagSubscriberList extends ProfileList
$this->peopletag = $peopletag;
}
- function newListItem($profile)
+ function newListItem(Profile $profile)
{
return new PeopletagSubscriberListItem($profile, $this->peopletag, $this->action);
}
diff --git a/actions/peopletagsubscriptions.php b/actions/peopletagsubscriptions.php
index 64edd09290..ae491bbe59 100644
--- a/actions/peopletagsubscriptions.php
+++ b/actions/peopletagsubscriptions.php
@@ -56,7 +56,7 @@ class PeopletagsubscriptionsAction extends Action
}
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -97,9 +97,9 @@ class PeopletagsubscriptionsAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
diff --git a/actions/pluginenable.php b/actions/pluginenable.php
index 707cbdecaa..d0467ec2d8 100644
--- a/actions/pluginenable.php
+++ b/actions/pluginenable.php
@@ -64,7 +64,7 @@ class PluginEnableAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -121,7 +121,7 @@ class PluginEnableAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
$key = 'disable-' . $this->plugin;
Config::save('plugins', $key, $this->overrideValue());
diff --git a/actions/profilecompletion.php b/actions/profilecompletion.php
index aaf6026eb5..4b0c4607e7 100644
--- a/actions/profilecompletion.php
+++ b/actions/profilecompletion.php
@@ -68,7 +68,7 @@ class ProfilecompletionAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -120,7 +120,7 @@ class ProfilecompletionAction extends Action
* @return void
*/
- function handle($args)
+ function handle()
{
$this->msg = null;
diff --git a/actions/profiletagbyid.php b/actions/profiletagbyid.php
index ce7298a2f6..4c333ae9b6 100644
--- a/actions/profiletagbyid.php
+++ b/actions/profiletagbyid.php
@@ -45,7 +45,7 @@ class ProfiletagbyidAction extends Action
return true;
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -83,7 +83,7 @@ class ProfiletagbyidAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
common_redirect($this->peopletag->homeUrl(), 303);
}
diff --git a/actions/public.php b/actions/public.php
index 000f82cb93..a2958e8806 100644
--- a/actions/public.php
+++ b/actions/public.php
@@ -86,12 +86,6 @@ class PublicAction extends SitestreamAction
$ibs->show();
}
- $p = Profile::current();
-
- if (!common_config('performance', 'high')) {
- $cloud = new PublicTagCloudSection($this);
- $cloud->show();
- }
$feat = new FeaturedUsersSection($this);
$feat->show();
}
diff --git a/actions/recoverpassword.php b/actions/recoverpassword.php
index a3a5b8e5bc..f3192b5dd3 100644
--- a/actions/recoverpassword.php
+++ b/actions/recoverpassword.php
@@ -29,9 +29,9 @@ class RecoverpasswordAction extends Action
var $msg = null;
var $success = null;
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (common_logged_in()) {
// TRANS: Client error displayed trying to recover password while already logged in.
$this->clientError(_('You are already logged in!'));
@@ -79,13 +79,7 @@ class RecoverpasswordAction extends Action
// Burn this code
- $result = $confirm->delete();
-
- if (!$result) {
- common_log_db_error($confirm, 'DELETE', __FILE__);
- // TRANS: Server error displayed removing a password recovery code from the database.
- $this->serverError(_('Error with confirmation code.'));
- }
+ $confirm->delete();
// These should be reaped, but for now we just check mod time
// Note: it's still deleted; let's avoid a second attempt!
diff --git a/actions/redirect.php b/actions/redirect.php
index 9cb03708db..1fb952da41 100644
--- a/actions/redirect.php
+++ b/actions/redirect.php
@@ -63,7 +63,7 @@ class RedirectAction extends Action
*
* @return nothing
*/
- function handle($args)
+ function handle()
{
common_redirect(common_local_url($this->arg('nextAction'), $this->arg('args')));
}
diff --git a/actions/register.php b/actions/register.php
index 97f0e19cfd..6f23244d13 100644
--- a/actions/register.php
+++ b/actions/register.php
@@ -120,9 +120,9 @@ class RegisterAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (common_config('site', 'closed')) {
// TRANS: Client error displayed when trying to register to a closed site.
diff --git a/actions/removepeopletag.php b/actions/removepeopletag.php
index 8192e86b44..20fd80bc7a 100644
--- a/actions/removepeopletag.php
+++ b/actions/removepeopletag.php
@@ -66,7 +66,7 @@ class RemovepeopletagAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -120,7 +120,7 @@ class RemovepeopletagAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
// Throws exception on error
diff --git a/actions/revokerole.php b/actions/revokerole.php
index b55399598e..42aa2a7fa5 100644
--- a/actions/revokerole.php
+++ b/actions/revokerole.php
@@ -49,7 +49,7 @@ class RevokeRoleAction extends ProfileFormAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
if (!parent::prepare($args)) {
return false;
diff --git a/actions/rsd.php b/actions/rsd.php
index bd8042f0cd..0feab73c28 100644
--- a/actions/rsd.php
+++ b/actions/rsd.php
@@ -85,7 +85,7 @@ class RsdAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -126,7 +126,7 @@ class RsdAction extends Action
*
* @return nothing
*/
- function handle($args)
+ function handle()
{
header('Content-Type: application/rsd+xml');
diff --git a/actions/sandbox.php b/actions/sandbox.php
index af2eab3842..37a1af8c19 100644
--- a/actions/sandbox.php
+++ b/actions/sandbox.php
@@ -49,7 +49,7 @@ class SandboxAction extends ProfileFormAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
if (!parent::prepare($args)) {
return false;
diff --git a/actions/selftag.php b/actions/selftag.php
index b886c3d9f7..2df1a29253 100644
--- a/actions/selftag.php
+++ b/actions/selftag.php
@@ -158,9 +158,9 @@ class SelftagAction extends Action
class SelfTagProfileList extends ProfileList
{
- function newListItem($profile)
+ function newListItem(Profile $target)
{
- return new SelfTagProfileListItem($profile, $this->action);
+ return new SelfTagProfileListItem($target, $this->action);
}
}
diff --git a/actions/showapplication.php b/actions/showapplication.php
index d8ac293d40..8696cb3caf 100644
--- a/actions/showapplication.php
+++ b/actions/showapplication.php
@@ -65,7 +65,7 @@ class ShowApplicationAction extends Action
*
* @return success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -101,9 +101,9 @@ class ShowApplicationAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
diff --git a/actions/showgroup.php b/actions/showgroup.php
index 46d0a227f5..8cc65aa906 100644
--- a/actions/showgroup.php
+++ b/actions/showgroup.php
@@ -28,12 +28,7 @@
* @link http://status.net/
*/
-if (!defined('STATUSNET') && !defined('LACONICA')) {
- exit(1);
-}
-
-require_once INSTALLDIR.'/lib/noticelist.php';
-require_once INSTALLDIR.'/lib/feedlist.php';
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
* Group main page
@@ -48,7 +43,6 @@ class ShowgroupAction extends GroupAction
{
/** page we're viewing. */
var $page = null;
- var $userProfile = null;
var $notice = null;
/**
@@ -82,85 +76,15 @@ class ShowgroupAction extends GroupAction
}
}
- /**
- * Prepare the action
- *
- * Reads and validates arguments and instantiates the attributes.
- *
- * @param array $args $_REQUEST args
- *
- * @return boolean success flag
- */
- protected function prepare(array $args=array())
+ public function getStream()
{
- parent::prepare($args);
-
- $this->page = ($this->arg('page')) ? ($this->arg('page')+0) : 1;
-
- $this->userProfile = Profile::current();
-
- $user = common_current_user();
-
- if (!empty($user) && $user->streamModeOnly()) {
- $stream = new GroupNoticeStream($this->group, $this->userProfile);
+ if ($this->scoped instanceof Profile && $this->scoped->isLocal() && $this->scoped->getUser()->streamModeOnly()) {
+ $stream = new GroupNoticeStream($this->group, $this->scoped);
} else {
- $stream = new ThreadingGroupNoticeStream($this->group, $this->userProfile);
+ $stream = new ThreadingGroupNoticeStream($this->group, $this->scoped);
}
- $this->notice = $stream->getNotices(($this->page-1)*NOTICES_PER_PAGE,
- NOTICES_PER_PAGE + 1);
-
- common_set_returnto($this->selfUrl());
-
- return true;
- }
-
- /**
- * Handle the request
- *
- * Shows a profile for the group, some controls, and a list of
- * group notices.
- *
- * @return void
- */
- protected function handle()
- {
- parent::handle();
- $this->showPage();
- }
-
- /**
- * Show the page content
- *
- * Shows a group profile and a list of group notices
- */
- function showContent()
- {
- $this->showGroupNotices();
- }
-
- /**
- * Show the group notices
- *
- * @return void
- */
- function showGroupNotices()
- {
- $user = common_current_user();
-
- if (!empty($user) && $user->streamModeOnly()) {
- $nl = new PrimaryNoticeList($this->notice, $this, array('show_n'=>NOTICES_PER_PAGE));
- } else {
- $nl = new ThreadedNoticeList($this->notice, $this, $this->userProfile);
- }
-
- $cnt = $nl->show();
-
- $this->pagination($this->page > 1,
- $cnt > NOTICES_PER_PAGE,
- $this->page,
- 'showgroup',
- array('nickname' => $this->group->nickname));
+ return $stream;
}
/**
diff --git a/actions/showstream.php b/actions/showstream.php
index 3ac837a67e..79a3706d77 100644
--- a/actions/showstream.php
+++ b/actions/showstream.php
@@ -113,6 +113,18 @@ class ShowstreamAction extends NoticestreamAction
$this->target->getNickname(), $this->tag)));
}
+ if (!$this->target->isLocal()) {
+ // remote profiles at least have Atom, but we can't guarantee anything else
+ return array(
+ new Feed(Feed::ATOM,
+ $this->target->getAtomFeed(),
+ // TRANS: Title for link to notice feed.
+ // TRANS: %s is a user nickname.
+ sprintf(_('Notice feed for %s (Atom)'),
+ $this->target->getNickname()))
+ );
+ }
+
return array(new Feed(Feed::JSON,
common_local_url('ApiTimelineUser',
array(
@@ -139,10 +151,7 @@ class ShowstreamAction extends NoticestreamAction
sprintf(_('Notice feed for %s (RSS 2.0)'),
$this->target->getNickname())),
new Feed(Feed::ATOM,
- common_local_url('ApiTimelineUser',
- array(
- 'id' => $this->target->getID(),
- 'format' => 'atom')),
+ $this->target->getAtomFeed(),
// TRANS: Title for link to notice feed.
// TRANS: %s is a user nickname.
sprintf(_('Notice feed for %s (Atom)'),
@@ -155,6 +164,17 @@ class ShowstreamAction extends NoticestreamAction
sprintf(_('FOAF for %s'), $this->target->getNickname())));
}
+ public function extraHeaders()
+ {
+ parent::extraHeaders();
+ // Publish all the rel="me" in the HTTP headers on our main profile page
+ if (get_class($this) == 'ShowstreamAction') {
+ foreach ($this->target->getRelMes() as $relMe) {
+ header('Link: <'.htmlspecialchars($relMe['href']).'>; rel="me"', false);
+ }
+ }
+ }
+
function extraHead()
{
if ($this->target->bio) {
@@ -162,12 +182,6 @@ class ShowstreamAction extends NoticestreamAction
'content' => $this->target->getDescription()));
}
- // See https://wiki.mozilla.org/Microsummaries
-
- $this->element('link', array('rel' => 'microsummary',
- 'href' => common_local_url('microsummary',
- array('nickname' => $this->target->getNickname()))));
-
$rsd = common_local_url('rsd',
array('nickname' => $this->target->getNickname()));
@@ -210,19 +224,20 @@ class ShowstreamAction extends NoticestreamAction
function showNotices()
{
- $pnl = new NoticeList($this->notice, $this);
+ $pnl = new PrimaryNoticeList($this->notice, $this);
$cnt = $pnl->show();
if (0 == $cnt) {
$this->showEmptyListMessage();
}
- $args = array('nickname' => $this->target->getNickname());
+ // either nickname or id will be used, depending on which action (showstream, userbyid...)
+ $args = array('nickname' => $this->target->getNickname(), 'id' => $this->target->getID());
if (!empty($this->tag))
{
$args['tag'] = $this->tag;
}
$this->pagination($this->page>1, $cnt>NOTICES_PER_PAGE, $this->page,
- 'showstream', $args);
+ $this->getActionName(), $args);
}
function showAnonymousMessage()
@@ -246,15 +261,6 @@ class ShowstreamAction extends NoticestreamAction
$this->elementEnd('div');
}
- function showSections()
- {
- parent::showSections();
- if (!common_config('performance', 'high')) {
- $cloud = new PersonalTagCloudSection($this->target, $this);
- $cloud->show();
- }
- }
-
function noticeFormOptions()
{
$options = parent::noticeFormOptions();
diff --git a/actions/sitenoticeadminpanel.php b/actions/sitenoticeadminpanel.php
index 72c9f9a128..a0a8b03b64 100644
--- a/actions/sitenoticeadminpanel.php
+++ b/actions/sitenoticeadminpanel.php
@@ -110,9 +110,7 @@ class SitenoticeadminpanelAction extends AdminPanelAction
}
// scrub HTML input
- require_once INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php';
- $purifier = new HTMLPurifier();
- $siteNotice = $purifier->purify($siteNotice);
+ $siteNotice = common_purify($siteNotice);
}
}
diff --git a/actions/smssettings.php b/actions/smssettings.php
index ca6a7d04ef..c002474ce2 100644
--- a/actions/smssettings.php
+++ b/actions/smssettings.php
@@ -368,13 +368,7 @@ class SmssettingsAction extends SettingsAction
throw new AlreadyFulfilledException(_('No pending confirmation to cancel.'));
}
- $result = $confirm->delete();
-
- if ($result === false) {
- common_log_db_error($confirm, 'DELETE', __FILE__);
- // TRANS: Server error thrown on database error canceling SMS phone number confirmation.
- throw new ServerException(_('Could not delete SMS confirmation.'));
- }
+ $confirm->delete();
// TRANS: Message given after successfully canceling SMS phone number confirmation.
return _('SMS confirmation cancelled.');
diff --git a/actions/subedit.php b/actions/subedit.php
index 47fe19ea24..d6a0e95468 100644
--- a/actions/subedit.php
+++ b/actions/subedit.php
@@ -24,7 +24,7 @@ class SubeditAction extends Action
{
var $profile = null;
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -58,9 +58,9 @@ class SubeditAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$cur = common_current_user();
diff --git a/actions/subscribe.php b/actions/subscribe.php
index 320409afa0..7e8c8178eb 100644
--- a/actions/subscribe.php
+++ b/actions/subscribe.php
@@ -64,7 +64,7 @@ class SubscribeAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -118,7 +118,7 @@ class SubscribeAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
// Throws exception on error
diff --git a/actions/subscribepeopletag.php b/actions/subscribepeopletag.php
index b83ff70010..3d2654dd04 100644
--- a/actions/subscribepeopletag.php
+++ b/actions/subscribepeopletag.php
@@ -50,7 +50,7 @@ class SubscribepeopletagAction extends Action
/**
* Prepare to run
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -106,9 +106,9 @@ class SubscribepeopletagAction extends Action
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$cur = common_current_user();
diff --git a/actions/sup.php b/actions/sup.php
index 911f0d9e55..5e26e8b106 100644
--- a/actions/sup.php
+++ b/actions/sup.php
@@ -22,9 +22,9 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); }
// @todo FIXME: documentation needed.
class SupAction extends Action
{
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$seconds = $this->trimmed('seconds');
diff --git a/actions/tag.php b/actions/tag.php
index 751e8dcec5..ccd2c7fbe0 100644
--- a/actions/tag.php
+++ b/actions/tag.php
@@ -34,7 +34,7 @@ class TagAction extends ManagedAction
$this->tag = common_canonical_tag($taginput);
if (empty($this->tag)) {
- common_redirect(common_local_url('publictagcloud'), 301);
+ throw new ClientException(_('No valid tag data.'));
}
// after common_canonical_tag we have a lowercase, no-specials tag string
diff --git a/actions/unblock.php b/actions/unblock.php
index 82d0d32c10..1da6ece4ba 100644
--- a/actions/unblock.php
+++ b/actions/unblock.php
@@ -44,7 +44,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
*/
class UnblockAction extends ProfileFormAction
{
- function prepare($args)
+ function prepare(array $args = array())
{
if (!parent::prepare($args)) {
return false;
diff --git a/actions/unsandbox.php b/actions/unsandbox.php
index b87736fcff..1311b13f3e 100644
--- a/actions/unsandbox.php
+++ b/actions/unsandbox.php
@@ -49,7 +49,7 @@ class UnsandboxAction extends ProfileFormAction
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
if (!parent::prepare($args)) {
return false;
diff --git a/actions/unsubscribe.php b/actions/unsubscribe.php
index 4b85ba427c..524fbea284 100644
--- a/actions/unsubscribe.php
+++ b/actions/unsubscribe.php
@@ -44,9 +44,9 @@ if (!defined('STATUSNET') && !defined('LACONICA')) {
*/
class UnsubscribeAction extends Action
{
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (!common_logged_in()) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
$this->clientError(_('Not logged in.'));
diff --git a/actions/unsubscribepeopletag.php b/actions/unsubscribepeopletag.php
index b5757ad6fd..5448661354 100644
--- a/actions/unsubscribepeopletag.php
+++ b/actions/unsubscribepeopletag.php
@@ -51,7 +51,7 @@ class UnsubscribepeopletagAction extends Action
* Prepare to run
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -106,9 +106,9 @@ class UnsubscribepeopletagAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$cur = common_current_user();
diff --git a/classes/Attention.php b/classes/Attention.php
index 5299a095ae..d048187db2 100644
--- a/classes/Attention.php
+++ b/classes/Attention.php
@@ -53,7 +53,7 @@ class Attention extends Managed_DataObject
{
try {
$att = Attention::getByKeys(['notice_id'=>$notice->getID(), 'profile_id'=>$target->getID()]);
- throw new AlreadyFulfilledException('Attention already exists with reason: '.var_export($att->reason,true));
+ throw new AlreadyFulfilledException('Attention already exists with reason: '._ve($att->reason));
} catch (NoResultException $e) {
$att = new Attention();
@@ -67,6 +67,7 @@ class Attention extends Managed_DataObject
throw new Exception('Failed Attention::saveNew for notice id=='.$notice->getID().' target id=='.$target->getID().', reason=="'.$reason.'"');
}
}
+ self::blow('attention:stream:%d', $target->getID());
return $att;
}
}
diff --git a/classes/Avatar.php b/classes/Avatar.php
index d8cc134b80..516abafe25 100644
--- a/classes/Avatar.php
+++ b/classes/Avatar.php
@@ -207,8 +207,11 @@ class Avatar extends Managed_DataObject
}
}
- static function defaultImage($size)
+ static function defaultImage($size=null)
{
+ if (is_null($size)) {
+ $size = AVATAR_PROFILE_SIZE;
+ }
static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile',
AVATAR_STREAM_SIZE => 'stream',
AVATAR_MINI_SIZE => 'mini');
diff --git a/classes/Confirm_address.php b/classes/Confirm_address.php
index 97e1a75dab..f8b5b21124 100644
--- a/classes/Confirm_address.php
+++ b/classes/Confirm_address.php
@@ -35,18 +35,18 @@ class Confirm_address extends Managed_DataObject
);
}
- static function getAddress($address, $addressType)
+ static function getByAddress($address, $addressType)
{
$ca = new Confirm_address();
$ca->address = $address;
$ca->address_type = $addressType;
- if ($ca->find(true)) {
- return $ca;
+ if (!$ca->find(true)) {
+ throw new NoResultException($ca);
}
- return null;
+ return $ca;
}
static function saveNew($user, $address, $addressType, $extra=null)
@@ -66,4 +66,96 @@ class Confirm_address extends Managed_DataObject
return $ca;
}
+
+ public function getAddress()
+ {
+ return $this->address;
+ }
+
+ public function getAddressType()
+ {
+ return $this->address_type;
+ }
+
+ public function getCode()
+ {
+ return $this->code;
+ }
+
+ public function getProfile()
+ {
+ return Profile::getByID($this->user_id);
+ }
+
+ public function getUrl()
+ {
+ return common_local_url('confirmaddress', array('code' => $this->code));
+ }
+
+ /**
+ * Supply arguments in $args. Currently known args:
+ * headers Array with headers (only used for email)
+ * nickname How we great the user (defaults to nickname, but can be any string)
+ * sitename Name we sign the email with (defaults to sitename, but can be any string)
+ * url The confirmation address URL.
+ */
+ public function sendConfirmation(array $args=array())
+ {
+ common_debug('Sending confirmation URL for user '._ve($this->user_id).' using '._ve($this->address_type));
+
+ $defaults = [
+ 'headers' => array(),
+ 'nickname' => $this->getProfile()->getNickname(),
+ 'sitename' => common_config('site', 'name'),
+ 'url' => $this->getUrl(),
+ ];
+ foreach (array_keys($defaults) as $key) {
+ if (!isset($args[$key])) {
+ $args[$key] = $defaults[$key];
+ }
+ }
+
+ switch ($this->getAddressType()) {
+ case 'email':
+ $this->sendEmailConfirmation($args);
+ break;
+ default:
+ throw ServerException('Unable to handle confirm_address address type: '._ve($this->address_type));
+ }
+ }
+
+ public function sendEmailConfirmation(array $args=array())
+ {
+ // TRANS: Subject for address confirmation email.
+ $subject = _('Email address confirmation');
+
+ // TRANS: Body for address confirmation email.
+ // TRANS: %1$s is the addressed user's nickname, %2$s is the StatusNet sitename,
+ // TRANS: %3$s is the URL to confirm at.
+ $body = sprintf(_("Hey, %1\$s.\n\n".
+ "Someone just entered this email address on %2\$s.\n\n" .
+ "If it was you, and you want to confirm your entry, ".
+ "use the URL below:\n\n\t%3\$s\n\n" .
+ "If not, just ignore this message.\n\n".
+ "Thanks for your time, \n%2\$s\n"),
+ $args['nickname'],
+ $args['sitename'],
+ $args['url']);
+
+ require_once(INSTALLDIR . '/lib/mail.php');
+ return mail_to_user($this->getProfile()->getUser(), $subject, $body, $args['headers'], $this->getAddress());
+ }
+
+ public function delete($useWhere=false)
+ {
+ $result = parent::delete($useWhere);
+
+ if ($result === false) {
+ common_log_db_error($confirm, 'DELETE', __FILE__);
+ // TRANS: Server error displayed when an address confirmation code deletion from the
+ // TRANS: database fails in the contact address confirmation action.
+ throw new ServerException(_('Could not delete address confirmation.'));
+ }
+ return $result;
+ }
}
diff --git a/classes/Conversation.php b/classes/Conversation.php
index 1dba2c1f4a..d18321deba 100644
--- a/classes/Conversation.php
+++ b/classes/Conversation.php
@@ -36,6 +36,7 @@ class Conversation extends Managed_DataObject
public $__table = 'conversation'; // table name
public $id; // int(4) primary_key not_null auto_increment
public $uri; // varchar(191) unique_key not 255 because utf8mb4 takes more space
+ public $url; // varchar(191) unique_key not 255 because utf8mb4 takes more space
public $created; // datetime not_null
public $modified; // timestamp not_null default_CURRENT_TIMESTAMP
@@ -45,6 +46,7 @@ class Conversation extends Managed_DataObject
'fields' => array(
'id' => array('type' => 'serial', 'not null' => true, 'description' => 'Unique identifier, (again) unrelated to notice id since 2016-01-06'),
'uri' => array('type' => 'varchar', 'not null'=>true, 'length' => 191, 'description' => 'URI of the conversation'),
+ 'url' => array('type' => 'varchar', 'length' => 191, 'description' => 'Resolvable URL, preferrably remote (local can be generated on the fly)'),
'created' => array('type' => 'datetime', 'not null' => true, 'description' => 'date this record was created'),
'modified' => array('type' => 'timestamp', 'not null' => true, 'description' => 'date this record was modified'),
),
@@ -89,15 +91,21 @@ class Conversation extends Managed_DataObject
*
* @return Conversation the new conversation DO
*/
- static function create($uri=null, $created=null)
+ static function create(ActivityContext $ctx=null, $created=null)
{
// Be aware that the Notice does not have an id yet since it's not inserted!
$conv = new Conversation();
$conv->created = $created ?: common_sql_now();
- $conv->uri = $uri ?: sprintf('%s%s=%s:%s=%s',
+ if ($ctx instanceof ActivityContext) {
+ $conv->uri = $ctx->conversation;
+ $conv->url = $ctx->conversation_url;
+ } else {
+ $conv->uri = sprintf('%s%s=%s:%s=%s',
TagURI::mint(),
'objectType', 'thread',
'nonce', common_random_hexstr(8));
+ $conv->url = null; // locally generated Conversation objects don't get static URLs stored
+ }
// This insert throws exceptions on failure
$conv->insert();
diff --git a/classes/File.php b/classes/File.php
index 6ba80eb5f6..67b87efd0d 100644
--- a/classes/File.php
+++ b/classes/File.php
@@ -106,6 +106,40 @@ class File extends Managed_DataObject
// We don't have the file's URL since before, so let's continue.
}
+ // if the given url is an local attachment url and the id already exists, don't
+ // save a new file record. This should never happen, but let's make it foolproof
+ // FIXME: how about attachments servers?
+ $u = parse_url($given_url);
+ if (isset($u['host']) && $u['host'] === common_config('site', 'server')) {
+ $r = Router::get();
+ // Skip the / in the beginning or $r->map won't match
+ try {
+ $args = $r->map(mb_substr($u['path'], 1));
+ if ($args['action'] === 'attachment') {
+ try {
+ // $args['attachment'] should always be set if action===attachment, given our routing rules
+ $file = File::getByID($args['attachment']);
+ return $file;
+ } catch (EmptyPkeyValueException $e) {
+ // ...but $args['attachment'] can also be 0...
+ } catch (NoResultException $e) {
+ // apparently this link goes to us, but is _not_ an existing attachment (File) ID?
+ }
+ }
+ } catch (Exception $e) {
+ // Some other exception was thrown from $r->map, likely a
+ // ClientException (404) because of some malformed link to
+ // our own instance. It's still a valid URL however, so we
+ // won't abort anything... I noticed this when linking:
+ // https://social.umeahackerspace.se/mmn/foaf' (notice the
+ // apostrophe in the end, making it unrecognizable for our
+ // URL routing.
+ // That specific issue (the apostrophe being part of a link
+ // is something that may or may not have been fixed since,
+ // in lib/util.php in common_replace_urls_callback().
+ }
+ }
+
$file = new File;
$file->url = $given_url;
if (!empty($redir_data['protected'])) $file->protected = $redir_data['protected'];
@@ -160,10 +194,14 @@ class File extends Managed_DataObject
}
$redir = File_redirection::where($given_url);
- $file = $redir->getFile();
-
- if (!$file instanceof File || empty($file->id)) {
+ try {
+ $file = $redir->getFile();
+ } catch (EmptyPkeyValueException $e) {
+ common_log(LOG_ERR, 'File_redirection::where gave object with empty file_id for given_url '._ve($given_url));
+ throw new ServerException('URL processing failed without new File object');
+ } catch (NoResultException $e) {
// This should not happen
+ common_log(LOG_ERR, 'File_redirection after discovery could still not return a File object.');
throw new ServerException('URL processing failed without new File object');
}
@@ -226,18 +264,19 @@ class File extends Managed_DataObject
public function getFilename()
{
- if (!self::validFilename($this->filename)) {
- // TRANS: Client exception thrown if a file upload does not have a valid name.
- throw new ClientException(_("Invalid filename."));
- }
- return $this->filename;
+ return self::tryFilename($this->filename);
+ }
+
+ public function getSize()
+ {
+ return intval($this->size);
}
// where should the file go?
static function filename(Profile $profile, $origname, $mimetype)
{
- $ext = self::guessMimeExtension($mimetype);
+ $ext = self::guessMimeExtension($mimetype, $origname);
// Normalize and make the original filename more URL friendly.
$origname = basename($origname, ".$ext");
@@ -258,19 +297,54 @@ class File extends Managed_DataObject
return $filename;
}
- static function guessMimeExtension($mimetype)
+ /**
+ * @param $mimetype The mimetype we've discovered for this file.
+ * @param $filename An optional filename which we can use on failure.
+ */
+ static function guessMimeExtension($mimetype, $filename=null)
{
try {
+ // first see if we know the extension for our mimetype
$ext = common_supported_mime_to_ext($mimetype);
- } catch (Exception $e) {
- // We don't support this mimetype, but let's guess the extension
- $matches = array();
- if (!preg_match('/\/([a-z0-9]+)/', mb_strtolower($mimetype), $matches)) {
- throw new Exception('Malformed mimetype: '.$mimetype);
+ // we do, so use it!
+ return $ext;
+ } catch (UnknownMimeExtensionException $e) {
+ // We don't know the extension for this mimetype, but let's guess.
+
+ // If we can't recognize the extension from the MIME, we try
+ // to guess based on filename, if one was supplied.
+ if (!is_null($filename) && preg_match('/^.+\.([A-Za-z0-9]+)$/', $filename, $matches)) {
+ // we matched on a file extension, so let's see if it means something.
+ $ext = mb_strtolower($matches[1]);
+
+ $blacklist = common_config('attachments', 'extblacklist');
+ // If we got an extension from $filename we want to check if it's in a blacklist
+ // so we avoid people uploading .php files etc.
+ if (array_key_exists($ext, $blacklist)) {
+ if (!is_string($blacklist[$ext])) {
+ // we don't have a safe replacement extension
+ throw new ClientException(_('Blacklisted file extension.'));
+ }
+ common_debug('Found replaced extension for filename '._ve($filename).': '._ve($ext));
+
+ // return a safe replacement extension ('php' => 'phps' for example)
+ return $blacklist[$ext];
+ }
+ // the attachment extension based on its filename was not blacklisted so it's ok to use it
+ return $ext;
}
- $ext = $matches[1];
+ } catch (Exception $e) {
+ common_log(LOG_INFO, 'Problem when figuring out extension for mimetype: '._ve($e));
}
- return mb_strtolower($ext);
+
+ // If nothing else has given us a result, try to extract it from
+ // the mimetype value (this turns .jpg to .jpeg for example...)
+ $matches = array();
+ // FIXME: try to build a regexp that will get jpeg from image/jpeg as well as json from application/jrd+json
+ if (!preg_match('/\/([a-z0-9]+)/', mb_strtolower($mimetype), $matches)) {
+ throw new Exception('Malformed mimetype: '.$mimetype);
+ }
+ return mb_strtolower($matches[1]);
}
/**
@@ -281,19 +355,27 @@ class File extends Managed_DataObject
return preg_match('/^[A-Za-z0-9._-]+$/', $filename);
}
+ static function tryFilename($filename)
+ {
+ if (!self::validFilename($filename))
+ {
+ throw new InvalidFilenameException($filename);
+ }
+ // if successful, return the filename for easy if-statementing
+ return $filename;
+ }
+
/**
* @throws ClientException on invalid filename
*/
static function path($filename)
{
- if (!self::validFilename($filename)) {
- // TRANS: Client exception thrown if a file upload does not have a valid name.
- throw new ClientException(_("Invalid filename."));
- }
+ self::tryFilename($filename);
+
$dir = common_config('attachments', 'dir');
- if ($dir[strlen($dir)-1] != '/') {
- $dir .= '/';
+ if (!in_array($dir[mb_strlen($dir)-1], ['/', '\\'])) {
+ $dir .= DIRECTORY_SEPARATOR;
}
return $dir . $filename;
@@ -301,10 +383,7 @@ class File extends Managed_DataObject
static function url($filename)
{
- if (!self::validFilename($filename)) {
- // TRANS: Client exception thrown if a file upload does not have a valid name.
- throw new ClientException(_("Invalid filename."));
- }
+ self::tryFilename($filename);
if (common_config('site','private')) {
@@ -409,6 +488,8 @@ class File extends Managed_DataObject
* @param $width int Max width of thumbnail in pixels. (if null, use common_config values)
* @param $height int Max height of thumbnail in pixels. (if null, square-crop to $width)
* @param $crop bool Crop to the max-values' aspect ratio
+ * @param $force_still bool Don't allow fallback to showing original (such as animated GIF)
+ * @param $upscale mixed Whether or not to scale smaller images up to larger thumbnail sizes. (null = site default)
*
* @return File_thumbnail
*
@@ -424,7 +505,13 @@ class File extends Managed_DataObject
// null means "always use file as thumbnail"
// false means you get choice between frozen frame or original when calling getThumbnail
if (is_null(common_config('thumbnail', 'animated')) || !$force_still) {
- throw new UseFileAsThumbnailException($this->id);
+ try {
+ // remote files with animated GIFs as thumbnails will match this
+ return File_thumbnail::byFile($this);
+ } catch (NoResultException $e) {
+ // and if it's not a remote file, it'll be safe to use the locally stored File
+ throw new UseFileAsThumbnailException($this);
+ }
}
}
@@ -441,13 +528,28 @@ class File extends Managed_DataObject
return $filepath;
}
- public function getUrl($prefer_local=true)
+ public function getAttachmentUrl()
{
- if ($prefer_local && !empty($this->filename)) {
- // A locally stored file, so let's generate a URL for our instance.
- return self::url($this->filename);
+ return common_local_url('attachment', array('attachment'=>$this->getID()));
+ }
+
+ /**
+ * @param mixed $use_local true means require local, null means prefer local, false means use whatever is stored
+ */
+ public function getUrl($use_local=null)
+ {
+ if ($use_local !== false) {
+ if (is_string($this->filename) || !empty($this->filename)) {
+ // A locally stored file, so let's generate a URL for our instance.
+ return self::url($this->getFilename());
+ }
+ if ($use_local) {
+ // if the file wasn't stored locally (has filename) and we require a local URL
+ throw new FileNotStoredLocallyException($this);
+ }
}
+
// No local filename available, return the URL we have stored
return $this->url;
}
@@ -524,7 +626,9 @@ class File extends Managed_DataObject
function stream($offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
- $stream = new FileNoticeStream($this);
+ // FIXME: Try to get the Profile::current() here in some other way to avoid mixing
+ // the current session user with possibly background/queue processing.
+ $stream = new FileNoticeStream($this, Profile::current());
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
@@ -593,6 +697,13 @@ class File extends Managed_DataObject
return $title ?: null;
}
+ public function setTitle($title)
+ {
+ $orig = clone($this);
+ $this->title = mb_strlen($title) > 0 ? $title : null;
+ return $this->update($orig);
+ }
+
static public function hashurl($url)
{
if (empty($url)) {
@@ -624,16 +735,18 @@ class File extends Managed_DataObject
$dupfile = new File();
// First we find file entries that would be duplicates of this when shortened
// ... and we'll just throw the dupes out the window for now! It's already so borken.
- $dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = "%1$s"', $file->shortenedurl));
+ $dupfile->query(sprintf('SELECT * FROM file WHERE LEFT(url, 191) = %1$s', $dupfile->_quote($file->shortenedurl)));
// Leave one of the URLs in the database by using ->find(true) (fetches first entry)
if ($dupfile->find(true)) {
print "\nShortening url entry for $table id: {$file->id} [";
$orig = clone($dupfile);
+ $origurl = $dupfile->url; // save for logging purposes
$dupfile->url = $file->shortenedurl; // make sure it's only 191 chars from now on
$dupfile->update($orig);
print "\nDeleting duplicate entries of too long URL on $table id: {$file->id} [";
// only start deleting with this fetch.
while($dupfile->fetch()) {
+ common_log(LOG_INFO, sprintf('Deleting duplicate File entry of %1$d: %2$d (original URL: %3$s collides with these first 191 characters: %4$s', $dupfile->id, $file->id, $origurl, $file->shortenedurl));
print ".";
$dupfile->delete();
}
diff --git a/classes/File_redirection.php b/classes/File_redirection.php
index 057647dfc5..742a6143cc 100644
--- a/classes/File_redirection.php
+++ b/classes/File_redirection.php
@@ -172,56 +172,82 @@ class File_redirection extends Managed_DataObject
try {
$r = File_redirection::getByUrl($in_url);
- if($r instanceof File_redirection) {
- try {
- $f = File::getKV('id',$r->file_id);
- $r->file = $f;
- $r->redir_url = $f->url;
- } catch (NoResultException $e) {
- // Invalid entry, delete and run again
- common_log(LOG_ERR, "Could not find File with id=".$r->file_id." referenced in File_redirection, deleting File redirection entry and creating new File and File_redirection entries.");
- $r->delete();
- return self::where($in_url);
- }
- return $r;
+
+ try {
+ $f = File::getByID($r->file_id);
+ $r->file = $f;
+ $r->redir_url = $f->url;
+ } catch (NoResultException $e) {
+ // Invalid entry, delete and run again
+ common_log(LOG_ERR, "Could not find File with id=".$r->file_id." referenced in File_redirection, deleting File redirection entry and and trying again...");
+ $r->delete();
+ return self::where($in_url);
}
+
+ // File_redirecion and File record found, return both
+ return $r;
+
} catch (NoResultException $e) {
+ // File_redirecion record not found, but this might be a direct link to a file
try {
$f = File::getByUrl($in_url);
$redir->file_id = $f->id;
$redir->file = $f;
return $redir;
} catch (NoResultException $e) {
- // Oh well, let's keep going
+ // nope, this was not a direct link to a file either, let's keep going
}
}
if ($discover) {
+ // try to follow redirects and get the final url
$redir_info = File_redirection::lookupWhere($in_url);
if(is_string($redir_info)) {
$redir_info = array('url' => $redir_info);
}
-
- // Save the file if we don't have it already
- $redir->file = File::saveNew($redir_info,$redir_info['url']);
-
- // If this is a redirection, save it
- // (if it hasn't been saved yet by some other process while we we
- // were running lookupWhere())
- if($redir_info['url'] != $in_url) {
- try {
- $file_redir = File_redirection::getByUrl($in_url);
- } catch (NoResultException $e) {
- $file_redir = new File_redirection();
- $file_redir->urlhash = File::hashurl($in_url);
- $file_redir->url = $in_url;
- $file_redir->file_id = $redir->file->getID();
- $file_redir->insert();
- $file_redir->redir_url = $redir->file->url;
- }
+
+ // the last url in the redirection chain can actually be a redirect!
+ // this is the case with local /attachment/{file_id} links
+ // in that case we have the file id already
+ try {
+ $r = File_redirection::getByUrl($redir_info['url']);
+
+ $f = File::getKV('id',$r->file_id);
+
+ if($f instanceof File) {
+ $redir->file = $f;
+ $redir->redir_url = $f->url;
+ } else {
+ // Invalid entry in File_redirection, delete and run again
+ common_log(LOG_ERR, "Could not find File with id=".$r->file_id." referenced in File_redirection, deleting File_redirection entry and trying again...");
+ $r->delete();
+ return self::where($in_url);
+ }
+ } catch (NoResultException $e) {
+ // save the file now when we know that we don't have it in File_redirection
+ try {
+ $redir->file = File::saveNew($redir_info,$redir_info['url']);
+ } catch (ServerException $e) {
+ common_log(LOG_ERR, $e);
+ }
+ }
+
+ // If this is a redirection and we have a file to redirect to, save it
+ // (if it doesn't exist in File_redirection already)
+ if($redir->file instanceof File && $redir_info['url'] != $in_url) {
+ try {
+ $file_redir = File_redirection::getByUrl($in_url);
+ } catch (NoResultException $e) {
+ $file_redir = new File_redirection();
+ $file_redir->urlhash = File::hashurl($in_url);
+ $file_redir->url = $in_url;
+ $file_redir->file_id = $redir->file->getID();
+ $file_redir->insert();
+ $file_redir->redir_url = $redir->file->url;
+ }
- $file_redir->file = $redir->file;
- return $file_redir;
+ $file_redir->file = $redir->file;
+ return $file_redir;
}
}
@@ -419,8 +445,8 @@ class File_redirection extends Managed_DataObject
}
public function getFile() {
- if(empty($this->file) && $this->file_id) {
- $this->file = File::getKV('id', $this->file_id);
+ if (!$this->file instanceof File) {
+ $this->file = File::getByID($this->file_id);
}
return $this->file;
diff --git a/classes/File_thumbnail.php b/classes/File_thumbnail.php
index e028409f0f..68cb2a737f 100644
--- a/classes/File_thumbnail.php
+++ b/classes/File_thumbnail.php
@@ -98,6 +98,7 @@ class File_thumbnail extends Managed_DataObject
if ($notNullUrl) {
$thumb->whereAdd('url IS NOT NULL');
}
+ $thumb->orderBy('modified ASC'); // the first created, a somewhat ugly hack
$thumb->limit(1);
if (!$thumb->find(true)) {
throw new NoResultException($thumb);
@@ -129,23 +130,88 @@ class File_thumbnail extends Managed_DataObject
static function path($filename)
{
- // TODO: Store thumbnails in their own directory and don't use File::path here
- return File::path($filename);
+ File::tryFilename($filename);
+
+ // NOTE: If this is left empty in default config, it will be set to File::path('thumb')
+ $dir = common_config('thumbnail', 'dir');
+
+ if (!in_array($dir[mb_strlen($dir)-1], ['/', '\\'])) {
+ $dir .= DIRECTORY_SEPARATOR;
+ }
+
+ return $dir . $filename;
}
static function url($filename)
{
- // TODO: Store thumbnails in their own directory and don't use File::url here
- return File::url($filename);
+ File::tryFilename($filename);
+
+ // FIXME: private site thumbnails?
+
+ $path = common_config('thumbnail', 'path');
+ if (empty($path)) {
+ return File::url('thumb')."/{$filename}";
+ }
+
+ $protocol = (GNUsocial::useHTTPS() ? 'https' : 'http');
+ $server = common_config('thumbnail', 'server') ?: common_config('site', 'server');
+
+ if ($path[mb_strlen($path)-1] != '/') {
+ $path .= '/';
+ }
+ if ($path[0] != '/') {
+ $path = '/'.$path;
+ }
+
+ return $protocol.'://'.$server.$path.$filename;
}
+ public function getFilename()
+ {
+ return File::tryFilename($this->filename);
+ }
+
+ /**
+ *
+ * @return string full filesystem path to the locally stored thumbnail file
+ * @throws
+ */
public function getPath()
{
- $filepath = self::path($this->filename);
- if (!file_exists($filepath)) {
- throw new FileNotFoundException($filepath);
+ $oldpath = File::path($this->getFilename());
+ $thumbpath = self::path($this->getFilename());
+
+ // If we have a file in our old thumbnail storage path, move (or copy) it to the new one
+ // (if the if/elseif don't match, we have a $thumbpath just as we should and can return it)
+ if (file_exists($oldpath) && !file_exists($thumbpath)) {
+ try {
+ // let's get the filename of the File, to check below if it happens to be identical
+ $file_filename = $this->getFile()->getFilename();
+ } catch (NoResultException $e) {
+ // reasonably the function calling us will handle the following as "File_thumbnail entry should be deleted"
+ throw new FileNotFoundException($thumbpath);
+ } catch (InvalidFilenameException $e) {
+ // invalid filename in getFile()->getFilename(), just
+ // means the File object isn't stored locally and that
+ // means it's safe to move it below.
+ $file_filename = null;
+ }
+
+ if ($this->getFilename() === $file_filename) {
+ // special case where thumbnail file exactly matches stored File filename
+ common_debug('File filename and File_thumbnail filename match on '.$this->file_id.', copying instead');
+ copy($oldpath, $thumbpath);
+ } elseif (!rename($oldpath, $thumbpath)) {
+ common_log(LOG_ERR, 'Could not move thumbnail from '._ve($oldpath).' to '._ve($thumbpath));
+ throw new ServerException('Could not move thumbnail from old path to new path.');
+ } else {
+ common_log(LOG_DEBUG, 'Moved thumbnail '.$this->file_id.' from '._ve($oldpath).' to '._ve($thumbpath));
+ }
+ } elseif (!file_exists($thumbpath)) {
+ throw new FileNotFoundException($thumbpath);
}
- return $filepath;
+
+ return $thumbpath;
}
public function getUrl()
@@ -188,11 +254,15 @@ class File_thumbnail extends Managed_DataObject
public function delete($useWhere=false)
{
- if (!empty($this->filename) && file_exists(File_thumbnail::path($this->filename))) {
- $deleted = @unlink(self::path($this->filename));
+ try {
+ $thumbpath = self::path($this->getFilename());
+ // if file does not exist, try to delete it
+ $deleted = !file_exists($thumbpath) || @unlink($thumbpath);
if (!$deleted) {
- common_log(LOG_ERR, sprintf('Could not unlink existing file: "%s"', self::path($this->filename)));
+ common_log(LOG_ERR, 'Could not unlink existing thumbnail file: '._ve($thumbpath));
}
+ } catch (InvalidFilenameException $e) {
+ common_log(LOG_ERR, 'Deleting object but not attempting deleting file: '._ve($e->getMessage()));
}
return parent::delete($useWhere);
@@ -203,6 +273,10 @@ class File_thumbnail extends Managed_DataObject
return File::getByID($this->file_id);
}
+ public function getFileId()
+ {
+ return $this->file_id;
+ }
static public function hashurl($url)
{
diff --git a/classes/Foreign_link.php b/classes/Foreign_link.php
index b3757448ad..8388f12e72 100644
--- a/classes/Foreign_link.php
+++ b/classes/Foreign_link.php
@@ -89,7 +89,7 @@ class Foreign_link extends Managed_DataObject
return $flink;
}
- function set_flags($noticesend, $noticerecv, $replysync, $friendsync)
+ function set_flags($noticesend, $noticerecv, $replysync, $repeatsync, $friendsync)
{
if ($noticesend) {
$this->noticesync |= FOREIGN_NOTICE_SEND;
@@ -109,6 +109,12 @@ class Foreign_link extends Managed_DataObject
$this->noticesync &= ~FOREIGN_NOTICE_SEND_REPLY;
}
+ if ($repeatsync) {
+ $this->noticesync |= FOREIGN_NOTICE_SEND_REPEAT;
+ } else {
+ $this->noticesync &= ~FOREIGN_NOTICE_SEND_REPEAT;
+ }
+
if ($friendsync) {
$this->friendsync |= FOREIGN_FRIEND_RECV;
} else {
diff --git a/classes/Managed_DataObject.php b/classes/Managed_DataObject.php
index 0857bb11f6..8c9a55f2b6 100644
--- a/classes/Managed_DataObject.php
+++ b/classes/Managed_DataObject.php
@@ -383,14 +383,35 @@ abstract class Managed_DataObject extends Memcached_DataObject
static function getByID($id)
{
+ if (!property_exists(get_called_class(), 'id')) {
+ throw new ServerException('Trying to get undefined property of dataobject class.');
+ }
if (empty($id)) {
- throw new EmptyIdException(get_called_class());
+ throw new EmptyPkeyValueException(get_called_class(), 'id');
}
// getByPK throws exception if id is null
// or if the class does not have a single 'id' column as primary key
return static::getByPK(array('id' => $id));
}
+ static function getByUri($uri)
+ {
+ if (!property_exists(get_called_class(), 'uri')) {
+ throw new ServerException('Trying to get undefined property of dataobject class.');
+ }
+ if (empty($uri)) {
+ throw new EmptyPkeyValueException(get_called_class(), 'uri');
+ }
+
+ $class = get_called_class();
+ $obj = new $class();
+ $obj->uri = $uri;
+ if (!$obj->find(true)) {
+ throw new NoResultException($obj);
+ }
+ return $obj;
+ }
+
/**
* Returns an ID, checked that it is set and reasonably valid
*
@@ -483,6 +504,8 @@ abstract class Managed_DataObject extends Memcached_DataObject
throw new ServerException('DataObject must be the result of a query (N>=1) before updateWithKeys()');
}
+ $this->onUpdateKeys($orig);
+
// do it in a transaction
$this->query('BEGIN');
@@ -580,6 +603,11 @@ abstract class Managed_DataObject extends Memcached_DataObject
// NOOP by default
}
+ protected function onUpdateKeys(Managed_DataObject $orig)
+ {
+ // NOOP by default
+ }
+
public function insert()
{
$this->onInsert();
diff --git a/classes/Memcached_DataObject.php b/classes/Memcached_DataObject.php
index 41ce715210..3de7c16d9b 100644
--- a/classes/Memcached_DataObject.php
+++ b/classes/Memcached_DataObject.php
@@ -67,10 +67,11 @@ class Memcached_DataObject extends Safe_DataObject
* @param string $cls Class to fetch
* @param string $keyCol name of column for key
* @param array $keyVals key values to fetch
+ * @param boolean $skipNulls skip provided null values
*
* @return array Array of objects, in order
*/
- static function multiGetClass($cls, $keyCol, array $keyVals)
+ static function multiGetClass($cls, $keyCol, array $keyVals, $skipNulls=true)
{
$obj = new $cls;
@@ -83,6 +84,14 @@ class Memcached_DataObject extends Safe_DataObject
throw new ServerException('Cannot do multiGet on anything but integer columns');
}
+ if ($skipNulls) {
+ foreach ($keyVals as $key=>$val) {
+ if (is_null($val)) {
+ unset($keyVals[$key]);
+ }
+ }
+ }
+
$obj->whereAddIn($keyCol, $keyVals, $colType);
// Since we're inputting straight to a query: format and escape
diff --git a/classes/Notice.php b/classes/Notice.php
index 1a7964fc06..15b96ae670 100644
--- a/classes/Notice.php
+++ b/classes/Notice.php
@@ -88,7 +88,7 @@ class Notice extends Managed_DataObject
'reply_to' => array('type' => 'int', 'description' => 'notice replied to (usually a guess)'),
'is_local' => array('type' => 'int', 'size' => 'tiny', 'default' => 0, 'description' => 'notice was generated by a user'),
'source' => array('type' => 'varchar', 'length' => 32, 'description' => 'source of comment, like "web", "im", or "clientname"'),
- 'conversation' => array('type' => 'int', 'description' => 'id of root notice in this conversation'),
+ 'conversation' => array('type' => 'int', 'description' => 'the local numerical conversation id'),
'repeat_of' => array('type' => 'int', 'description' => 'notice this is a repeat of'),
'object_type' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams object type', 'default' => null),
'verb' => array('type' => 'varchar', 'length' => 191, 'description' => 'URI representing activity streams verb', 'default' => 'http://activitystrea.ms/schema/1.0/post'),
@@ -110,7 +110,10 @@ class Notice extends Managed_DataObject
'notice_profile_id_idx' => array('profile_id', 'created', 'id'),
'notice_repeat_of_created_id_idx' => array('repeat_of', 'created', 'id'),
'notice_conversation_created_id_idx' => array('conversation', 'created', 'id'),
+ 'notice_object_type_idx' => array('object_type'),
'notice_verb_idx' => array('verb'),
+ 'notice_profile_id_verb_idx' => array('profile_id', 'verb'),
+ 'notice_url_idx' => array('url'), // Qvitter wants this
'notice_replyto_idx' => array('reply_to')
)
);
@@ -165,6 +168,18 @@ class Notice extends Managed_DataObject
throw new AuthorizationException(_('You are not allowed to delete another user\'s notice.'));
}
+ $result = null;
+ if (!$delete_event || Event::handle('DeleteNoticeAsProfile', array($this, $actor, &$result))) {
+ // If $delete_event is true, we run the event. If the Event then
+ // returns false it is assumed everything was handled properly
+ // and the notice was deleted.
+ $result = $this->delete();
+ }
+ return $result;
+ }
+
+ protected function deleteRelated()
+ {
if (Event::handle('NoticeDeleteRelated', array($this))) {
// Clear related records
$this->clearReplies();
@@ -176,19 +191,12 @@ class Notice extends Managed_DataObject
$this->clearAttentions();
// NOTE: we don't clear queue items
}
-
- $result = null;
- if (!$delete_event || Event::handle('DeleteNoticeAsProfile', array($this, $actor, &$result))) {
- // If $delete_event is true, we run the event. If the Event then
- // returns false it is assumed everything was handled properly
- // and the notice was deleted.
- $result = $this->delete();
- }
- return $result;
}
public function delete($useWhere=false)
{
+ $this->deleteRelated();
+
$result = parent::delete($useWhere);
$this->blowOnDelete();
@@ -240,10 +248,10 @@ class Notice extends Managed_DataObject
return common_local_url('shownotice', array('notice' => $this->id), null, null, false);
}
- public function getTitle()
+ public function getTitle($imply=true)
{
$title = null;
- if (Event::handle('GetNoticeTitle', array($this, &$title))) {
+ if (Event::handle('GetNoticeTitle', array($this, &$title)) && $imply) {
// TRANS: Title of a notice posted without a title value.
// TRANS: %1$s is a user name, %2$s is the notice creation date/time.
$title = sprintf(_('%1$s\'s status on %2$s'),
@@ -308,7 +316,6 @@ class Notice extends Managed_DataObject
// let's generate a valid link to our locally available notice on demand
return common_local_url('shownotice', array('notice' => $this->getID()), null, null, false);
default:
- common_debug('No URL available for notice: id='.$this->getID());
throw new InvalidUrlException($this->url);
}
}
@@ -344,16 +351,6 @@ class Notice extends Managed_DataObject
}
}
- public static function getByUri($uri)
- {
- $notice = new Notice();
- $notice->uri = $uri;
- if (!$notice->find(true)) {
- throw new NoResultException($notice);
- }
- return $notice;
- }
-
/**
* Extract #hashtags from this notice's content and save them to the database.
*/
@@ -523,12 +520,7 @@ class Notice extends Managed_DataObject
$notice = new Notice();
$notice->profile_id = $profile_id;
- $autosource = common_config('public', 'autosource');
-
- // Sandboxed are non-false, but not 1, either
-
- if (!$profile->hasRight(Right::PUBLICNOTICE) ||
- ($source && $autosource && in_array($source, $autosource))) {
+ if ($source && in_array($source, common_config('public', 'autosource'))) {
$notice->is_local = Notice::LOCAL_NONPUBLIC;
} else {
$notice->is_local = $is_local;
@@ -542,9 +534,6 @@ class Notice extends Managed_DataObject
if (!$notice->isLocal()) {
// Only do these checks for non-local notices. Local notices will generate these values later.
- if (!common_valid_http_url($url)) {
- common_debug('Bad notice URL: ['.$url.'], URI: ['.$uri.']. Cannot link back to original! This is normal for shared notices etc.');
- }
if (empty($uri)) {
throw new ServerException('No URI for remote notice. Cannot accept that.');
}
@@ -651,8 +640,13 @@ class Notice extends Managed_DataObject
} else {
// Conversation entry with specified URI was not found, so we must create it.
common_debug('Conversation URI not found, so we will create it with the URI given in the options to Notice::saveNew: '.$options['conversation']);
+ $convctx = new ActivityContext();
+ $convctx->conversation = $options['conversation'];
+ if (array_key_exists('conversation_url', $options)) {
+ $convctx->conversation_url = $options['conversation_url'];
+ }
// The insert in Conversation::create throws exception on failure
- $conv = Conversation::create($options['conversation'], $notice->created);
+ $conv = Conversation::create($convctx, $notice->created);
}
$notice->conversation = $conv->getID();
unset($conv);
@@ -849,12 +843,12 @@ class Notice extends Managed_DataObject
}
}
- $autosource = common_config('public', 'autosource');
+ // NOTE: Sandboxed users previously got all the notices _during_
+ // sandbox period set to to is_local=Notice::LOCAL_NONPUBLIC here.
+ // Since then we have started just filtering _when_ it gets shown
+ // instead of creating a mixed jumble of differently scoped notices.
- // Sandboxed are non-false, but not 1, either
- if (!$actor->hasRight(Right::PUBLICNOTICE) ||
- ($source && $autosource && in_array($source, $autosource))) {
- // FIXME: ...what about remote nonpublic? Hmmm. That is, if we sandbox remote profiles...
+ if ($source && in_array($source, common_config('public', 'autosource'))) {
$stored->is_local = Notice::LOCAL_NONPUBLIC;
} else {
$stored->is_local = intval($is_local);
@@ -870,23 +864,25 @@ class Notice extends Managed_DataObject
}
}
- $stored->profile_id = $actor->id;
+ $stored->profile_id = $actor->getID();
$stored->source = $source;
$stored->uri = $uri;
$stored->url = $url;
$stored->verb = $act->verb;
- $content = $act->content ?: $act->summary;
- if (is_null($content) && !is_null($actobj)) {
- $content = $actobj->content ?: $actobj->summary;
+ // we use mb_strlen because it _might_ be that the content is just the string "0"...
+ $content = mb_strlen($act->content) ? $act->content : $act->summary;
+ if (mb_strlen($content)===0 && !is_null($actobj)) {
+ $content = mb_strlen($actobj->content) ? $actobj->content : $actobj->summary;
}
- // Strip out any bad HTML
- $stored->rendered = common_purify($content);
+ // Strip out any bad HTML from $content. URI.Base is used to sort out relative URLs.
+ $stored->rendered = common_purify($content, ['URI.Base' => $stored->url ?: null]);
$stored->content = common_strip_html($stored->getRendered(), true, true);
if (trim($stored->content) === '') {
// TRANS: Error message when the plain text content of a notice has zero length.
throw new ClientException(_('Empty notice content, will not save this.'));
}
+ unset($content); // garbage collect
// Maybe a missing act-time should be fatal if the actor is not local?
if (!empty($act->time)) {
@@ -895,13 +891,31 @@ class Notice extends Managed_DataObject
$stored->created = common_sql_now();
}
- $reply = null;
+ $reply = null; // this will store the in-reply-to Notice if found
+ $replyUris = []; // this keeps a list of possible URIs to look up
if ($act->context instanceof ActivityContext && !empty($act->context->replyToID)) {
- $reply = self::getKV('uri', $act->context->replyToID);
+ $replyUris[] = $act->context->replyToID;
}
- if (!$reply instanceof Notice && $act->target instanceof ActivityObject) {
- $reply = self::getKV('uri', $act->target->id);
+ if ($act->target instanceof ActivityObject && !empty($act->target->id)) {
+ $replyUris[] = $act->target->id;
}
+ foreach (array_unique($replyUris) as $replyUri) {
+ $reply = self::getKV('uri', $replyUri);
+ // Only do remote fetching if we're not a private site
+ if (!common_config('site', 'private') && !$reply instanceof Notice) {
+ // the URI is the object we're looking for, $actor is a
+ // Profile that surely knows of it and &$reply where it
+ // will be stored when fetched
+ Event::handle('FetchRemoteNotice', array($replyUri, $actor, &$reply));
+ }
+ // we got what we're in-reply-to now, so let's move on
+ if ($reply instanceof Notice) {
+ break;
+ }
+ // otherwise reset whatever we might've gotten from the event
+ $reply = null;
+ }
+ unset($replyUris); // garbage collect
if ($reply instanceof Notice) {
if (!$reply->inScope($actor)) {
@@ -939,12 +953,13 @@ class Notice extends Managed_DataObject
// Conversation entry with specified URI was not found, so we must create it.
common_debug('Conversation URI not found, so we will create it with the URI given in the context of the activity: '.$act->context->conversation);
// The insert in Conversation::create throws exception on failure
- $conv = Conversation::create($act->context->conversation, $stored->created);
+ $conv = Conversation::create($act->context, $stored->created);
}
$stored->conversation = $conv->getID();
unset($conv);
}
}
+ unset($reply); // garbage collect
// If it's not part of a conversation, it's the beginning of a new conversation.
if (empty($stored->conversation)) {
@@ -962,7 +977,13 @@ class Notice extends Managed_DataObject
$act->context = new ActivityContext();
}
- $stored->scope = self::figureOutScope($actor, $groups, $scope);
+ if (array_key_exists(ActivityContext::ATTN_PUBLIC, $act->context->attention)) {
+ $stored->scope = Notice::PUBLIC_SCOPE;
+ // TODO: maybe we should actually keep this? if the saveAttentions thing wants to use it...
+ unset($act->context->attention[ActivityContext::ATTN_PUBLIC]);
+ } else {
+ $stored->scope = self::figureOutScope($actor, $groups, $scope);
+ }
foreach ($act->categories as $cat) {
if ($cat->term) {
@@ -1009,6 +1030,7 @@ class Notice extends Managed_DataObject
if (empty($object)) {
throw new NoticeSaveException('Unsuccessful call to StoreActivityObject '._ve($stored->getUri()) . ': '._ve($act->asString()));
}
+ unset($object);
// If something changed in the Notice during StoreActivityObject
$stored->update($orig);
@@ -1022,8 +1044,16 @@ class Notice extends Managed_DataObject
throw $e;
}
}
+ unset($notloc); // garbage collect
+
if (!$stored instanceof Notice) {
- throw new ServerException('StartNoticeSave did not give back a Notice');
+ throw new ServerException('StartNoticeSave did not give back a Notice.');
+ } elseif (empty($stored->id)) {
+ throw new ServerException('Supposedly saved Notice has no ID.');
+ }
+
+ if ($self && common_valid_http_url($self)) {
+ $stored->setPref('ostatus', 'self', $self);
}
if ($self && common_valid_http_url($self)) {
@@ -1292,14 +1322,12 @@ class Notice extends Managed_DataObject
$ids[] = $f2p->file_id;
}
- $files = File::multiGet('id', $ids);
- $this->_attachments[$this->id] = $files->fetchAll();
- return $this->_attachments[$this->id];
+ return $this->_setAttachments(File::multiGet('id', $ids)->fetchAll());
}
- function _setAttachments($attachments)
+ public function _setAttachments(array $attachments)
{
- $this->_attachments[$this->id] = $attachments;
+ return $this->_attachments[$this->id] = $attachments;
}
static function publicStream($offset=0, $limit=20, $since_id=null, $max_id=null)
@@ -1308,9 +1336,9 @@ class Notice extends Managed_DataObject
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
- static function conversationStream($id, $offset=0, $limit=20, $since_id=null, $max_id=null)
+ static function conversationStream($id, $offset=0, $limit=20, $since_id=null, $max_id=null, Profile $scoped=null)
{
- $stream = new ConversationNoticeStream($id);
+ $stream = new ConversationNoticeStream($id, $scoped);
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
@@ -1328,8 +1356,9 @@ class Notice extends Managed_DataObject
return false;
}
- $stream = new ConversationNoticeStream($this->conversation);
- $notice = $stream->getNotices(/*offset*/ 1, /*limit*/ 1);
+ //FIXME: Get the Profile::current() stuff some other way
+ // to avoid confusion between queue processing and session.
+ $notice = self::conversationStream($this->conversation, 1, 1, null, null, Profile::current());
// if our "offset 1, limit 1" query got a result, return true else false
return $notice->N > 0;
@@ -1538,12 +1567,16 @@ class Notice extends Managed_DataObject
function getProfileTags()
{
- $profile = $this->getProfile();
- $list = $profile->getOtherTags($profile);
$ptags = array();
+ try {
+ $profile = $this->getProfile();
+ $list = $profile->getOtherTags($profile);
- while($list->fetch()) {
- $ptags[] = clone($list);
+ while($list->fetch()) {
+ $ptags[] = clone($list);
+ }
+ } catch (Exception $e) {
+ common_log(LOG_ERR, "Error during Notice->getProfileTags() for id=={$this->getID()}: {$e->getMessage()}");
}
return $ptags;
@@ -1585,12 +1618,12 @@ class Notice extends Managed_DataObject
if (common_config('group', 'addtag')) {
// we automatically add a tag for every group name, too
-
- $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->nickname),
- 'notice_id' => $this->id));
+ common_debug('Adding hashtag matching group nickname: '._ve($group->getNickname()));
+ $tag = Notice_tag::pkeyGet(array('tag' => common_canonical_tag($group->getNickname()),
+ 'notice_id' => $this->getID()));
if (is_null($tag)) {
- $this->saveTag($group->nickname);
+ $this->saveTag($group->getNickname());
}
}
@@ -1674,8 +1707,6 @@ class Notice extends Managed_DataObject
}
$att = Attention::saveNew($this, $target, $reason);
-
- self::blow('reply:stream:%d', $target->getID());
return true;
}
@@ -1863,7 +1894,7 @@ class Notice extends Managed_DataObject
foreach ($recipientIds as $recipientId) {
try {
$user = User::getByID($recipientId);
- mail_notify_attn($user, $this);
+ mail_notify_attn($user->getProfile(), $this);
} catch (NoResultException $e) {
// No such user
}
@@ -2017,6 +2048,7 @@ class Notice extends Managed_DataObject
$conv = Conversation::getKV('id', $this->conversation);
if ($conv instanceof Conversation) {
$ctx->conversation = $conv->uri;
+ $ctx->conversation_url = $conv->url;
}
}
@@ -2188,7 +2220,7 @@ class Notice extends Managed_DataObject
$object->selfLink = null;
}
- $object->extra[] = array('status_net', array('notice_id' => $this->id));
+ $object->extra[] = array('statusnet:notice_id', null, $this->id);
Event::handle('EndActivityObjectFromNotice', array($this, &$object));
}
@@ -2640,6 +2672,13 @@ class Notice extends Managed_DataObject
return !empty($this->repeat_of);
}
+ public function isRepeated()
+ {
+ $n = new Notice();
+ $n->repeat_of = $this->getID();
+ return $n->find() && $n->N > 0;
+ }
+
/**
* Get the list of hash tags saved with this notice.
*
@@ -2686,7 +2725,7 @@ class Notice extends Managed_DataObject
public static function getAsTimestamp($id)
{
if (empty($id)) {
- throw new EmptyIdException('Notice');
+ throw new EmptyPkeyValueException('Notice', 'id');
}
$timestamp = null;
@@ -2782,10 +2821,10 @@ class Notice extends Managed_DataObject
}
}
- function isPublic()
+ public function isPublic()
{
- return (($this->is_local != Notice::LOCAL_NONPUBLIC) &&
- ($this->is_local != Notice::GATEWAY));
+ $is_local = intval($this->is_local);
+ return !($is_local === Notice::LOCAL_NONPUBLIC || $is_local === Notice::GATEWAY);
}
/**
@@ -3055,6 +3094,19 @@ class Notice extends Managed_DataObject
$files = array();
$f2ps = $f2pMap[$notice->id];
foreach ($f2ps as $f2p) {
+ if (!isset($fileMap[$f2p->file_id])) {
+ // We have probably deleted value from fileMap since
+ // it as a NULL entry (see the following elseif).
+ continue;
+ } elseif (is_null($fileMap[$f2p->file_id])) {
+ // If the file id lookup returned a NULL value, it doesn't
+ // exist in our file table! So this is a remnant file_to_post
+ // entry that is no longer valid and should be removed.
+ common_debug('ATTACHMENT deleting f2p for post_id='.$f2p->post_id.' file_id='.$f2p->file_id);
+ $f2p->delete();
+ unset($fileMap[$f2p->file_id]);
+ continue;
+ }
$files[] = $fileMap[$f2p->file_id];
}
$notice->_setAttachments($files);
@@ -3083,43 +3135,114 @@ class Notice extends Managed_DataObject
// 2015-09-04 We move Notice location data to Notice_location
// First we see if we have to do this at all
- if (!isset($schemadef['fields']['lat'])
- && !isset($schemadef['fields']['lon'])
- && !isset($schemadef['fields']['location_id'])
- && !isset($schemadef['fields']['location_ns'])) {
- // We have already removed the location fields, so no need to migrate.
- return;
- }
- // Then we make sure the Notice_location table is created!
- $schema->ensureTable('notice_location', Notice_location::schemaDef());
+ if (isset($schemadef['fields']['lat'])
+ && isset($schemadef['fields']['lon'])
+ && isset($schemadef['fields']['location_id'])
+ && isset($schemadef['fields']['location_ns'])) {
+ // Then we make sure the Notice_location table is created!
+ $schema->ensureTable('notice_location', Notice_location::schemaDef());
- // Then we continue on our road to migration!
- echo "\nFound old $table table, moving location data to 'notice_location' table... (this will probably take a LONG time, but can be aborted and continued)";
+ // Then we continue on our road to migration!
+ echo "\nFound old $table table, moving location data to 'notice_location' table... (this will probably take a LONG time, but can be aborted and continued)";
- $notice = new Notice();
- $notice->query(sprintf('SELECT id, lat, lon, location_id, location_ns FROM %1$s ' .
- 'WHERE lat IS NOT NULL ' .
- 'OR lon IS NOT NULL ' .
- 'OR location_id IS NOT NULL ' .
- 'OR location_ns IS NOT NULL',
- $schema->quoteIdentifier($table)));
- print "\nFound {$notice->N} notices with location data, inserting";
- while ($notice->fetch()) {
- $notloc = Notice_location::getKV('notice_id', $notice->id);
- if ($notloc instanceof Notice_location) {
- print "-";
- continue;
+ $notice = new Notice();
+ $notice->query(sprintf('SELECT id, lat, lon, location_id, location_ns FROM %1$s ' .
+ 'WHERE lat IS NOT NULL ' .
+ 'OR lon IS NOT NULL ' .
+ 'OR location_id IS NOT NULL ' .
+ 'OR location_ns IS NOT NULL',
+ $schema->quoteIdentifier($table)));
+ print "\nFound {$notice->N} notices with location data, inserting";
+ while ($notice->fetch()) {
+ $notloc = Notice_location::getKV('notice_id', $notice->id);
+ if ($notloc instanceof Notice_location) {
+ print "-";
+ continue;
+ }
+ $notloc = new Notice_location();
+ $notloc->notice_id = $notice->id;
+ $notloc->lat= $notice->lat;
+ $notloc->lon= $notice->lon;
+ $notloc->location_id= $notice->location_id;
+ $notloc->location_ns= $notice->location_ns;
+ $notloc->insert();
+ print ".";
+ }
+ print "\n";
+ }
+
+ /**
+ * Make sure constraints are met before upgrading, if foreign keys
+ * are not already in use.
+ * 2016-03-31
+ */
+ if (!isset($schemadef['foreign keys'])) {
+ $newschemadef = self::schemaDef();
+ printfnq("\nConstraint checking Notice table...\n");
+ /**
+ * Improve typing and make sure no NULL values in any id-related columns are 0
+ * 2016-03-31
+ */
+ foreach (['reply_to', 'repeat_of'] as $field) {
+ $notice = new Notice(); // reset the object
+ $notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE %2$s=0', $notice->escapedTableName(), $field));
+ // Now we're sure that no Notice entries have repeat_of=0, only an id > 0 or NULL
+ unset($notice);
+ }
+
+ /**
+ * This Will find foreign keys which do not fulfill the constraints and fix
+ * where appropriate, such as delete when "repeat_of" ID not found in notice.id
+ * or set to NULL for "reply_to" in the same case.
+ * 2016-03-31
+ *
+ * XXX: How does this work if we would use multicolumn foreign keys?
+ */
+ foreach (['reply_to' => 'reset', 'repeat_of' => 'delete', 'profile_id' => 'delete'] as $field=>$action) {
+ $notice = new Notice();
+
+ $fkeyname = $notice->tableName().'_'.$field.'_fkey';
+ assert(isset($newschemadef['foreign keys'][$fkeyname]));
+ assert($newschemadef['foreign keys'][$fkeyname]);
+
+ $foreign_key = $newschemadef['foreign keys'][$fkeyname];
+ $fkeytable = $foreign_key[0];
+ assert(isset($foreign_key[1][$field]));
+ $fkeycol = $foreign_key[1][$field];
+
+ printfnq("* {$fkeyname} ({$field} => {$fkeytable}.{$fkeycol})\n");
+
+ // NOTE: Above we set all repeat_of to NULL if they were 0, so this really gets them all.
+ $notice->whereAdd(sprintf('%1$s NOT IN (SELECT %2$s FROM %3$s)', $field, $fkeycol, $fkeytable));
+ if ($notice->find()) {
+ printfnq("\tFound {$notice->N} notices with {$field} NOT IN notice.id, {$action}ing...");
+ switch ($action) {
+ case 'delete': // since it's a directly dependant notice for an unknown ID we don't want it in our DB
+ while ($notice->fetch()) {
+ $notice->delete();
+ }
+ break;
+ case 'reset': // just set it to NULL to be compatible with our constraints, if it was related to an unknown ID
+ $ids = [];
+ foreach ($notice->fetchAll('id') as $id) {
+ settype($id, 'int');
+ $ids[] = $id;
+ }
+ unset($notice);
+ $notice = new Notice();
+ $notice->query(sprintf('UPDATE %1$s SET %2$s=NULL WHERE id IN (%3$s)',
+ $notice->escapedTableName(),
+ $field,
+ implode(',', $ids)));
+ break;
+ default:
+ throw new ServerException('The programmer sucks, invalid action name when fixing table.');
+ }
+ printfnq("DONE.\n");
+ }
+ unset($notice);
}
- $notloc = new Notice_location();
- $notloc->notice_id = $notice->id;
- $notloc->lat= $notice->lat;
- $notloc->lon= $notice->lon;
- $notloc->location_id= $notice->location_id;
- $notloc->location_ns= $notice->location_ns;
- $notloc->insert();
- print ".";
}
- print "\n";
}
public function delPref($namespace, $topic) {
diff --git a/classes/Notice_tag.php b/classes/Notice_tag.php
index b864de8009..9d6bec6d2f 100644
--- a/classes/Notice_tag.php
+++ b/classes/Notice_tag.php
@@ -55,7 +55,9 @@ class Notice_tag extends Managed_DataObject
static function getStream($tag, $offset=0, $limit=20, $sinceId=0, $maxId=0)
{
- $stream = new TagNoticeStream($tag);
+ // FIXME: Get the Profile::current value some other way
+ // to avoid confusino between queue processing and session.
+ $stream = new TagNoticeStream($tag, Profile::current());
return $stream;
}
diff --git a/classes/Profile.php b/classes/Profile.php
index 7aae98fb5f..c2cc9b26e2 100644
--- a/classes/Profile.php
+++ b/classes/Profile.php
@@ -89,9 +89,14 @@ class Profile extends Managed_DataObject
public function getUser()
{
if (!isset($this->_user[$this->id])) {
- $user = User::getKV('id', $this->id);
- if (!$user instanceof User) {
- throw new NoSuchUserException(array('id'=>$this->id));
+ $cur_user = common_current_user();
+ if (($cur_user instanceof User) && $cur_user->sameAs($this)) {
+ $user = $cur_user;
+ } else {
+ $user = User::getKV('id', $this->id);
+ if (!$user instanceof User) {
+ throw new NoSuchUserException(array('id'=>$this->id));
+ }
}
$this->_user[$this->id] = $user;
}
@@ -218,18 +223,30 @@ class Profile extends Managed_DataObject
}
/**
- * Gets the full name (if filled) with nickname as a parenthetical, or the nickname alone
- * if no fullname is provided.
+ * Gets the full name (if filled) with acct URI, URL, or URI as a
+ * parenthetical (in that order, for each not found). If no full
+ * name is found only the second part is returned, without ()s.
*
* @return string
*/
function getFancyName()
{
- if ($this->fullname) {
- // TRANS: Full name of a profile or group (%1$s) followed by nickname (%2$s) in parentheses.
- return sprintf(_m('FANCYNAME','%1$s (%2$s)'), $this->fullname, $this->nickname);
+ $uri = null;
+ try {
+ $uri = $this->getAcctUri(false);
+ } catch (ProfileNoAcctUriException $e) {
+ try {
+ $uri = $this->getUrl();
+ } catch (InvalidUrlException $e) {
+ $uri = $this->getUri();
+ }
+ }
+
+ if (mb_strlen($this->getFullname()) > 0) {
+ // TRANS: The "fancy name": Full name of a profile or group (%1$s) followed by some URI (%2$s) in parentheses.
+ return sprintf(_m('FANCYNAME','%1$s (%2$s)'), $this->getFullname(), $uri);
} else {
- return $this->nickname;
+ return $uri;
}
}
@@ -266,7 +283,9 @@ class Profile extends Managed_DataObject
function getTaggedNotices($tag, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
- $stream = new TaggedProfileNoticeStream($this, $tag);
+ //FIXME: Get Profile::current() some other way to avoid possible
+ // confusion between current session profile and background processing.
+ $stream = new TaggedProfileNoticeStream($this, $tag, Profile::current());
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
@@ -819,19 +838,19 @@ class Profile extends Managed_DataObject
$c = Cache::instance();
if (!empty($c)) {
- $cnt = $c->get(Cache::key('profile:notice_count:'.$this->id));
+ $cnt = $c->get(Cache::key('profile:notice_count:'.$this->getID()));
if (is_integer($cnt)) {
return (int) $cnt;
}
}
$notices = new Notice();
- $notices->profile_id = $this->id;
- $notices->verb = ActivityVerb::POST;
- $cnt = (int) $notices->count('distinct id');
+ $notices->profile_id = $this->getID();
+ $notices->verb = ActivityVerb::POST;
+ $cnt = (int) $notices->count('id'); // Not sure if I imagine this, but 'id' was faster than the defaulting 'uri'?
if (!empty($c)) {
- $c->set(Cache::key('profile:notice_count:'.$this->id), $cnt);
+ $c->set(Cache::key('profile:notice_count:'.$this->getID()), $cnt);
}
return $cnt;
@@ -900,13 +919,33 @@ class Profile extends Managed_DataObject
return parent::update($dataObject);
}
+ public function getRelSelf()
+ {
+ return ['href' => $this->getUrl(),
+ 'text' => common_config('site', 'name'),
+ 'image' => Avatar::urlByProfile($this)];
+ }
+
+ // All the known rel="me", used for the IndieWeb audience
+ public function getRelMes()
+ {
+ $relMes = array();
+ try {
+ $relMes[] = $this->getRelSelf();
+ } catch (InvalidUrlException $e) {
+ // no valid profile URL available
+ }
+ if (common_valid_http_url($this->getHomepage())) {
+ $relMes[] = ['href' => $this->getHomepage(),
+ 'text' => _('Homepage'),
+ 'image' => null];
+ }
+ Event::handle('OtherAccountProfiles', array($this, &$relMes));
+ return $relMes;
+ }
+
function delete($useWhere=false)
{
- // just in case it hadn't been done before... (usually set before adding deluser to queue handling!)
- if (!$this->hasRole(Profile_role::DELETED)) {
- $this->grantRole(Profile_role::DELETED);
- }
-
$this->_deleteNotices();
$this->_deleteSubscriptions();
$this->_deleteTags();
@@ -918,6 +957,7 @@ class Profile extends Managed_DataObject
// not on individual objects.
$related = array('Reply',
'Group_member',
+ 'Profile_role'
);
Event::handle('ProfileDeleteRelated', array($this, &$related));
@@ -926,6 +966,8 @@ class Profile extends Managed_DataObject
$inst->profile_id = $this->id;
$inst->delete();
}
+
+ $this->grantRole(Profile_role::DELETED);
$localuser = User::getKV('id', $this->id);
if ($localuser instanceof User) {
@@ -1286,7 +1328,7 @@ class Profile extends Managed_DataObject
case Right::EMAILONREPLY:
case Right::EMAILONSUBSCRIBE:
case Right::EMAILONFAVE:
- $result = !$this->isSandboxed();
+ $result = !$this->isSandboxed() && !$this->isSilenced();
break;
case Right::WEBLOGIN:
$result = !$this->isSilenced();
@@ -1493,6 +1535,14 @@ class Profile extends Managed_DataObject
}
return $url;
}
+ public function getHtmlTitle()
+ {
+ try {
+ return $this->getAcctUri(false);
+ } catch (ProfileNoAcctUriException $e) {
+ return $this->getNickname();
+ }
+ }
public function getNickname()
{
@@ -1548,7 +1598,7 @@ class Profile extends Managed_DataObject
*
* @return string $uri
*/
- public function getAcctUri()
+ public function getAcctUri($scheme=true)
{
$acct = null;
@@ -1559,24 +1609,27 @@ class Profile extends Managed_DataObject
if ($acct === null) {
throw new ProfileNoAcctUriException($this);
}
+ if (parse_url($acct, PHP_URL_SCHEME) !== 'acct') {
+ throw new ServerException('Acct URI does not have acct: scheme');
+ }
- return $acct;
+ // if we don't return the scheme, just remove the 'acct:' in the beginning
+ return $scheme ? $acct : mb_substr($acct, 5);
}
- function hasBlocked($other)
+ function hasBlocked(Profile $other)
{
$block = Profile_block::exists($this, $other);
return !empty($block);
}
- function getAtomFeed()
+ public function getAtomFeed()
{
$feed = null;
if (Event::handle('StartProfileGetAtomFeed', array($this, &$feed))) {
- $user = User::getKV('id', $this->id);
- if (!empty($user)) {
- $feed = common_local_url('ApiTimelineUser', array('id' => $user->id,
+ if ($this->isLocal()) {
+ $feed = common_local_url('ApiTimelineUser', array('id' => $this->getID(),
'format' => 'atom'));
}
Event::handle('EndProfileGetAtomFeed', array($this, $feed));
diff --git a/classes/Profile_list.php b/classes/Profile_list.php
index 000e10b41f..b60f4afffd 100644
--- a/classes/Profile_list.php
+++ b/classes/Profile_list.php
@@ -171,7 +171,9 @@ class Profile_list extends Managed_DataObject
function getNotices($offset, $limit, $since_id=null, $max_id=null)
{
- $stream = new PeopletagNoticeStream($this);
+ // FIXME: Use something else than Profile::current() to avoid
+ // possible confusion between session user and queue processing.
+ $stream = new PeopletagNoticeStream($this, Profile::current());
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
diff --git a/classes/Reply.php b/classes/Reply.php
index d3405e6581..018937ba6c 100644
--- a/classes/Reply.php
+++ b/classes/Reply.php
@@ -57,7 +57,9 @@ class Reply extends Managed_DataObject
static function stream($user_id, $offset=0, $limit=NOTICES_PER_PAGE, $since_id=0, $max_id=0)
{
- $stream = new ReplyNoticeStream($user_id);
+ // FIXME: Use some other method to get Profile::current() in order
+ // to avoid confusion between background processing and session user.
+ $stream = new ReplyNoticeStream($user_id, Profile::current());
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
}
diff --git a/classes/User.php b/classes/User.php
index b52232eb64..b4f263235b 100644
--- a/classes/User.php
+++ b/classes/User.php
@@ -140,16 +140,6 @@ class User extends Managed_DataObject
return $this->uri;
}
- static function getByUri($uri)
- {
- $user = new User();
- $user->uri = $uri;
- if (!$user->find(true)) {
- throw new NoResultException($user);
- }
- return $user;
- }
-
public function getNickname()
{
return $this->getProfile()->getNickname();
@@ -190,7 +180,7 @@ class User extends Managed_DataObject
return Sms_carrier::getKV('id', $this->carrier);
}
- function hasBlocked($other)
+ function hasBlocked(Profile $other)
{
return $this->getProfile()->hasBlocked($other);
}
@@ -299,6 +289,11 @@ class User extends Managed_DataObject
// TRANS: Profile data could not be inserted for some reason.
throw new ServerException(_m('Could not insert profile data for new user.'));
}
+
+ // Necessary because id has been known to be reissued.
+ if ($profile->hasRole(Profile_role::DELETED)) {
+ $profile->revokeRole(Profile_role::DELETED);
+ }
$user->id = $id;
@@ -384,8 +379,7 @@ class User extends Managed_DataObject
if (!empty($email) && empty($user->email)) {
try {
- require_once(INSTALLDIR . '/lib/mail.php');
- mail_confirm_address($user, $confirm->code, $profile->getNickname(), $email);
+ $confirm->sendConfirmation();
} catch (EmailException $e) {
common_log(LOG_ERR, "Could not send user registration email for user id=={$profile->getID()}: {$e->getMessage()}");
if (!$accept_email_fail) {
@@ -704,15 +698,18 @@ class User extends Managed_DataObject
function repeatedByMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
- $stream = new RepeatedByMeNoticeStream($this);
+ // FIXME: Use another way to get Profile::current() since we
+ // want to avoid confusion between session user and queue processing.
+ $stream = new RepeatedByMeNoticeStream($this->getProfile(), Profile::current());
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
function repeatsOfMe($offset=0, $limit=20, $since_id=null, $max_id=null)
{
- $stream = new RepeatsOfMeNoticeStream($this);
-
+ // FIXME: Use another way to get Profile::current() since we
+ // want to avoid confusion between session user and queue processing.
+ $stream = new RepeatsOfMeNoticeStream($this->getProfile(), Profile::current());
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
diff --git a/classes/User_group.php b/classes/User_group.php
index 2484f3f265..8f736de6d0 100644
--- a/classes/User_group.php
+++ b/classes/User_group.php
@@ -105,6 +105,11 @@ class User_group extends Managed_DataObject
return $this->getProfile()->getNickname();
}
+ public function getFullname()
+ {
+ return $this->getProfile()->getFullname();
+ }
+
public static function defaultLogo($size)
{
static $sizenames = array(AVATAR_PROFILE_SIZE => 'profile',
@@ -148,7 +153,9 @@ class User_group extends Managed_DataObject
function getNotices($offset, $limit, $since_id=null, $max_id=null)
{
- $stream = new GroupNoticeStream($this);
+ // FIXME: Get the Profile::current() some other way, to avoid
+ // possible confusion between current session and queue process.
+ $stream = new GroupNoticeStream($this, Profile::current());
return $stream->getNotices($offset, $limit, $since_id, $max_id);
}
diff --git a/extlib/DB.php b/extlib/DB.php
index b9b5c4a79f..1aa9477065 100644
--- a/extlib/DB.php
+++ b/extlib/DB.php
@@ -426,7 +426,7 @@ define('DB_PORTABILITY_ALL', 63);
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB
@@ -577,7 +577,7 @@ class DB
*/
function apiVersion()
{
- return '1.8.2';
+ return '1.9.2';
}
// }}}
@@ -772,7 +772,7 @@ class DB
$parsed['dbsyntax'] = $str;
}
- if (!count($dsn)) {
+ if (!strlen($dsn)) {
return $parsed;
}
@@ -941,7 +941,7 @@ class DB
* @author Stig Bakken
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_Error extends PEAR_Error
@@ -959,18 +959,32 @@ class DB_Error extends PEAR_Error
*
* @see PEAR_Error
*/
- function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
+ function __construct($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null)
{
if (is_int($code)) {
- $this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code,
+ parent::__construct('DB Error: ' . DB::errorMessage($code), $code,
$mode, $level, $debuginfo);
} else {
- $this->PEAR_Error("DB Error: $code", DB_ERROR,
+ parent::__construct("DB Error: $code", DB_ERROR,
$mode, $level, $debuginfo);
}
}
+ /**
+ * Workaround to both avoid the "Redefining already defined constructor"
+ * PHP error and provide backward compatibility in case someone is calling
+ * DB_Error() dynamically
+ */
+ public function __call($method, $arguments)
+ {
+ if ($method == 'DB_Error') {
+ return call_user_func_array(array($this, '__construct'), $arguments);
+ }
+ trigger_error(
+ 'Call to undefined method DB_Error::' . $method . '()', E_USER_ERROR
+ );
+ }
// }}}
}
@@ -988,7 +1002,7 @@ class DB_Error extends PEAR_Error
* @author Stig Bakken
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_result
@@ -1095,7 +1109,7 @@ class DB_result
*
* @return void
*/
- function DB_result(&$dbh, $result, $options = array())
+ function __construct(&$dbh, $result, $options = array())
{
$this->autofree = $dbh->options['autofree'];
$this->dbh = &$dbh;
@@ -1453,7 +1467,7 @@ class DB_result
* @author Stig Bakken
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
* @see DB_common::setFetchMode()
*/
@@ -1468,7 +1482,7 @@ class DB_row
*
* @return void
*/
- function DB_row(&$arr)
+ function __construct(&$arr)
{
foreach ($arr as $key => $value) {
$this->$key = &$arr[$key];
diff --git a/extlib/DB/DataObject.php b/extlib/DB/DataObject.php
index 1a7b34665d..e26cf8efa0 100644
--- a/extlib/DB/DataObject.php
+++ b/extlib/DB/DataObject.php
@@ -15,7 +15,7 @@
* @author Alan Knowles
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
- * @version CVS: $Id: DataObject.php 320069 2011-11-28 04:34:08Z alan_k $
+ * @version CVS: $Id: DataObject.php 336751 2015-05-12 04:39:50Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
@@ -410,7 +410,7 @@ class DB_DataObject extends DB_DataObject_Overload
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
$this->debug($n, "find",1);
}
- if (!$this->__table) {
+ if (!strlen($this->tableName())) {
// xdebug can backtrace this!
trigger_error("NO \$__table SPECIFIED in class definition",E_USER_ERROR);
}
@@ -2073,6 +2073,9 @@ class DB_DataObject extends DB_DataObject_Overload
if (count($args)) {
$this->__table = $args[0];
}
+ if (empty($this->__table)) {
+ return '';
+ }
if (!empty($_DB_DATAOBJECT['CONFIG']['portability']) && $_DB_DATAOBJECT['CONFIG']['portability'] & 1) {
return strtolower($this->__table);
}
@@ -2421,7 +2424,7 @@ class DB_DataObject extends DB_DataObject_Overload
$dsn = isset($this->_database_dsn) ? $this->_database_dsn : null;
if (!$dsn) {
- if (!$this->_database && !empty($this->__table)) {
+ if (!$this->_database && !strlen($this->tableName())) {
$this->_database = isset($options["table_{$this->tableName()}"]) ? $options["table_{$this->tableName()}"] : null;
}
if (!empty($_DB_DATAOBJECT['CONFIG']['debug'])) {
@@ -3522,7 +3525,7 @@ class DB_DataObject extends DB_DataObject_Overload
if ($joinCol !== false) {
$this->raiseError(
"joinAdd: You cannot target a join column in the " .
- "'link from' table ({$obj->__table}). " .
+ "'link from' table ({$obj->tableName()}). " .
"Either remove the fourth argument to joinAdd() ".
"({$joinCol}), or alter your links.ini file.",
DB_DATAOBJECT_ERROR_NODATA);
@@ -3605,7 +3608,7 @@ class DB_DataObject extends DB_DataObject_Overload
if (!$items) {
$this->raiseError(
- "joinAdd: No table definition for {$obj->__table}",
+ "joinAdd: No table definition for {$obj->tableName()}",
DB_DATAOBJECT_ERROR_INVALIDCONFIG);
return false;
}
@@ -3800,6 +3803,7 @@ class DB_DataObject extends DB_DataObject_Overload
*/
function autoJoin($cfg = array())
{
+ global $_DB_DATAOBJECT;
//var_Dump($cfg);exit;
$pre_links = $this->links();
if (!empty($cfg['links'])) {
@@ -3807,7 +3811,8 @@ class DB_DataObject extends DB_DataObject_Overload
}
$map = $this->links( );
-
+ $this->databaseStructure();
+ $dbstructure = $_DB_DATAOBJECT['INI'][$this->_database];
//print_r($map);
$tabdef = $this->table();
@@ -3874,6 +3879,12 @@ class DB_DataObject extends DB_DataObject_Overload
list($tab,$col) = explode(':', $info);
// what about multiple joins on the same table!!!
+
+ // if links point to a table that does not exist - ignore.
+ if (!isset($dbstructure[$tab])) {
+ continue;
+ }
+
$xx = DB_DataObject::factory($tab);
if (!is_object($xx) || !is_a($xx, 'DB_DataObject')) {
continue;
diff --git a/extlib/DB/DataObject/Generator.php b/extlib/DB/DataObject/Generator.php
index a712e6d9eb..c7f87161c3 100644
--- a/extlib/DB/DataObject/Generator.php
+++ b/extlib/DB/DataObject/Generator.php
@@ -15,7 +15,7 @@
* @author Alan Knowles
* @copyright 1997-2006 The PHP Group
* @license http://www.php.net/license/3_01.txt PHP License 3.01
- * @version CVS: $Id: Generator.php 315531 2011-08-26 02:21:29Z alan_k $
+ * @version CVS: $Id: Generator.php 336719 2015-05-05 10:37:33Z alan_k $
* @link http://pear.php.net/package/DB_DataObject
*/
@@ -406,7 +406,7 @@ class DB_DataObject_Generator extends DB_DataObject
* Currenly only works with mysql / mysqli / posgtreas
* to use, you must set option: generate_links=true
*
- * @author Pascal Schöni
+ * @author Pascal Sch�ni
*/
function _createForiegnKeys()
@@ -507,7 +507,7 @@ class DB_DataObject_Generator extends DB_DataObject
* Currenly only works with mysql / mysqli
* to use, you must set option: generate_links=true
*
- * @author Pascal Schöni
+ * @author Pascal Sch�ni
*/
function generateForeignKeys()
{
@@ -895,7 +895,7 @@ class DB_DataObject_Generator extends DB_DataObject
$options = &PEAR::getStaticProperty('DB_DataObject','options');
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
- $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
+ $this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
foreach($this->tables as $this->table) {
@@ -976,8 +976,12 @@ class DB_DataObject_Generator extends DB_DataObject
$head .= $this->derivedHookExtendsDocBlock();
- // requires
- $head .= "require_once '{$this->_extendsFile}';\n\n";
+ // requires - if you set extends_location = (blank) then no require line will be set
+ // this can be used if you have an autoloader
+
+ if (!empty($this->_extendsFile)) {
+ $head .= "require_once '{$this->_extendsFile}';\n\n";
+ }
// add dummy class header in...
// class
$head .= $this->derivedHookClassDocBlock();
@@ -1039,10 +1043,11 @@ class DB_DataObject_Generator extends DB_DataObject
continue;
}
- $p = str_repeat(' ',max(2, (30 - strlen($t->name))));
+ $pad = str_repeat(' ',max(2, (30 - strlen($t->name))));
$length = empty($t->len) ? '' : '('.$t->len.')';
- $body .=" {$var} \${$t->name}; {$p}// {$t->type}$length {$t->flags}\n";
+ $flags = strlen($t->flags) ? (' '. trim($t->flags)) : '';
+ $body .=" {$var} \${$t->name}; {$pad}// {$t->type}{$length}{$flags}\n";
// can not do set as PEAR::DB table info doesnt support it.
//if (substr($t->Type,0,3) == "set")
@@ -1283,7 +1288,7 @@ class DB_DataObject_Generator extends DB_DataObject
$class_prefix = empty($options['class_prefix']) ? '' : $options['class_prefix'];
$this->_extends = empty($options['extends']) ? $this->_extends : $options['extends'];
- $this->_extendsFile = empty($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
+ $this->_extendsFile = !isset($options['extends_location']) ? $this->_extendsFile : $options['extends_location'];
$classname = $this->classname = $this->getClassNameFromTableName($this->table);
diff --git a/extlib/DB/common.php b/extlib/DB/common.php
index 27829a072a..73e3eb69f7 100644
--- a/extlib/DB/common.php
+++ b/extlib/DB/common.php
@@ -42,7 +42,7 @@ require_once 'PEAR.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_common extends PEAR
@@ -145,7 +145,7 @@ class DB_common extends PEAR
*
* @return void
*/
- function DB_common()
+ function __construct()
{
$this->PEAR('DB_Error');
}
diff --git a/extlib/DB/dbase.php b/extlib/DB/dbase.php
index df36f972e3..17750cd5d2 100644
--- a/extlib/DB/dbase.php
+++ b/extlib/DB/dbase.php
@@ -41,7 +41,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_dbase extends DB_common
@@ -140,13 +140,13 @@ class DB_dbase extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_dbase()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/fbsql.php b/extlib/DB/fbsql.php
index b719da38e2..c32a08d120 100644
--- a/extlib/DB/fbsql.php
+++ b/extlib/DB/fbsql.php
@@ -41,7 +41,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
* @since Class functional since Release 1.7.0
*/
@@ -124,13 +124,13 @@ class DB_fbsql extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_fbsql()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/ibase.php b/extlib/DB/ibase.php
index 209ac6c3a1..60e07b5fc3 100644
--- a/extlib/DB/ibase.php
+++ b/extlib/DB/ibase.php
@@ -49,7 +49,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
* @since Class became stable in Release 1.7.0
*/
@@ -180,13 +180,13 @@ class DB_ibase extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_ibase()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/ifx.php b/extlib/DB/ifx.php
index e3150f92fb..5c5709f79e 100644
--- a/extlib/DB/ifx.php
+++ b/extlib/DB/ifx.php
@@ -48,7 +48,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_ifx extends DB_common
@@ -167,13 +167,13 @@ class DB_ifx extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_ifx()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/msql.php b/extlib/DB/msql.php
index c303bb9067..adcedf7a07 100644
--- a/extlib/DB/msql.php
+++ b/extlib/DB/msql.php
@@ -47,7 +47,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
* @since Class not functional until Release 1.7.0
*/
@@ -126,13 +126,13 @@ class DB_msql extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_msql()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/mssql.php b/extlib/DB/mssql.php
index e25caf144e..d68ebfa61e 100644
--- a/extlib/DB/mssql.php
+++ b/extlib/DB/mssql.php
@@ -49,7 +49,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_mssql extends DB_common
@@ -179,13 +179,13 @@ class DB_mssql extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_mssql()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/mysql.php b/extlib/DB/mysql.php
index 21132d62df..ffefbc49fd 100644
--- a/extlib/DB/mysql.php
+++ b/extlib/DB/mysql.php
@@ -41,7 +41,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_mysql extends DB_common
@@ -162,13 +162,13 @@ class DB_mysql extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_mysql()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/mysqli.php b/extlib/DB/mysqli.php
index 5f081f7c4f..4f56f0fdac 100644
--- a/extlib/DB/mysqli.php
+++ b/extlib/DB/mysqli.php
@@ -43,7 +43,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
* @since Class functional since Release 1.6.3
*/
@@ -224,13 +224,13 @@ class DB_mysqli extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_mysqli()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
@@ -497,7 +497,11 @@ class DB_mysqli extends DB_common
*/
function freeResult($result)
{
- return is_resource($result) ? mysqli_free_result($result) : false;
+ if (! $result instanceof mysqli_result) {
+ return false;
+ }
+ mysqli_free_result($result);
+ return true;
}
// }}}
@@ -1031,6 +1035,10 @@ class DB_mysqli extends DB_common
? $this->mysqli_types[$tmp->type]
: 'unknown',
// http://bugs.php.net/?id=36579
+ // Doc Bug #36579: mysqli_fetch_field length handling
+ // https://bugs.php.net/bug.php?id=62426
+ // Bug #62426: mysqli_fetch_field_direct returns incorrect
+ // length on UTF8 fields
'len' => $tmp->length,
'flags' => $flags,
);
diff --git a/extlib/DB/oci8.php b/extlib/DB/oci8.php
index 9685c60e0c..1ca7a04e22 100644
--- a/extlib/DB/oci8.php
+++ b/extlib/DB/oci8.php
@@ -47,7 +47,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_oci8 extends DB_common
@@ -173,13 +173,13 @@ class DB_oci8 extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_oci8()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/odbc.php b/extlib/DB/odbc.php
index 75d4fe74ff..a33406a654 100644
--- a/extlib/DB/odbc.php
+++ b/extlib/DB/odbc.php
@@ -44,7 +44,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_odbc extends DB_common
@@ -153,13 +153,13 @@ class DB_odbc extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_odbc()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/pgsql.php b/extlib/DB/pgsql.php
index adfd6bf0a4..098d9f040a 100644
--- a/extlib/DB/pgsql.php
+++ b/extlib/DB/pgsql.php
@@ -43,7 +43,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_pgsql extends DB_common
@@ -148,13 +148,13 @@ class DB_pgsql extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_pgsql()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/sqlite.php b/extlib/DB/sqlite.php
index 7adfb4c475..9c5c8b3523 100644
--- a/extlib/DB/sqlite.php
+++ b/extlib/DB/sqlite.php
@@ -47,7 +47,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_sqlite extends DB_common
@@ -152,13 +152,13 @@ class DB_sqlite extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_sqlite()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/DB/storage.php b/extlib/DB/storage.php
index 9ac23c825e..640d86f2b9 100644
--- a/extlib/DB/storage.php
+++ b/extlib/DB/storage.php
@@ -38,7 +38,7 @@ require_once 'DB.php';
* @author Stig Bakken
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_storage extends PEAR
@@ -94,7 +94,7 @@ class DB_storage extends PEAR
* a reference to this object
*
*/
- function DB_storage($table, $keycolumn, &$dbh, $validator = null)
+ function __construct($table, $keycolumn, &$dbh, $validator = null)
{
$this->PEAR('DB_Error');
$this->_table = $table;
diff --git a/extlib/DB/sybase.php b/extlib/DB/sybase.php
index d87b18caab..14d054b246 100644
--- a/extlib/DB/sybase.php
+++ b/extlib/DB/sybase.php
@@ -46,7 +46,7 @@ require_once 'DB/common.php';
* @author Daniel Convissor
* @copyright 1997-2007 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
- * @version Release: 1.8.2
+ * @version Release: 1.9.2
* @link http://pear.php.net/package/DB
*/
class DB_sybase extends DB_common
@@ -141,13 +141,13 @@ class DB_sybase extends DB_common
// {{{ constructor
/**
- * This constructor calls $this->DB_common()
+ * This constructor calls parent::__construct()
*
* @return void
*/
- function DB_sybase()
+ function __construct()
{
- $this->DB_common();
+ parent::__construct();
}
// }}}
diff --git a/extlib/HTMLPurifier/HTMLPurifier.includes.php b/extlib/HTMLPurifier/HTMLPurifier.includes.php
index fdb58c2d37..e8bce5c850 100644
--- a/extlib/HTMLPurifier/HTMLPurifier.includes.php
+++ b/extlib/HTMLPurifier/HTMLPurifier.includes.php
@@ -7,7 +7,7 @@
* primary concern and you are using an opcode cache. PLEASE DO NOT EDIT THIS
* FILE, changes will be overwritten the next time the script is run.
*
- * @version 4.7.0
+ * @version 4.9.3
*
* @warning
* You must *not* include any other HTML Purifier files before this file,
@@ -137,6 +137,8 @@ require 'HTMLPurifier/AttrTransform/SafeObject.php';
require 'HTMLPurifier/AttrTransform/SafeParam.php';
require 'HTMLPurifier/AttrTransform/ScriptRequired.php';
require 'HTMLPurifier/AttrTransform/TargetBlank.php';
+require 'HTMLPurifier/AttrTransform/TargetNoopener.php';
+require 'HTMLPurifier/AttrTransform/TargetNoreferrer.php';
require 'HTMLPurifier/AttrTransform/Textarea.php';
require 'HTMLPurifier/ChildDef/Chameleon.php';
require 'HTMLPurifier/ChildDef/Custom.php';
@@ -175,6 +177,8 @@ require 'HTMLPurifier/HTMLModule/StyleAttribute.php';
require 'HTMLPurifier/HTMLModule/Tables.php';
require 'HTMLPurifier/HTMLModule/Target.php';
require 'HTMLPurifier/HTMLModule/TargetBlank.php';
+require 'HTMLPurifier/HTMLModule/TargetNoopener.php';
+require 'HTMLPurifier/HTMLModule/TargetNoreferrer.php';
require 'HTMLPurifier/HTMLModule/Text.php';
require 'HTMLPurifier/HTMLModule/Tidy.php';
require 'HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
@@ -225,5 +229,6 @@ require 'HTMLPurifier/URIScheme/https.php';
require 'HTMLPurifier/URIScheme/mailto.php';
require 'HTMLPurifier/URIScheme/news.php';
require 'HTMLPurifier/URIScheme/nntp.php';
+require 'HTMLPurifier/URIScheme/tel.php';
require 'HTMLPurifier/VarParser/Flexible.php';
require 'HTMLPurifier/VarParser/Native.php';
diff --git a/extlib/HTMLPurifier/HTMLPurifier.php b/extlib/HTMLPurifier/HTMLPurifier.php
index c6041bc113..b4605ebc6e 100644
--- a/extlib/HTMLPurifier/HTMLPurifier.php
+++ b/extlib/HTMLPurifier/HTMLPurifier.php
@@ -19,7 +19,7 @@
*/
/*
- HTML Purifier 4.7.0 - Standards Compliant HTML Filtering
+ HTML Purifier 4.9.3 - Standards Compliant HTML Filtering
Copyright (C) 2006-2008 Edward Z. Yang
This library is free software; you can redistribute it and/or
@@ -58,12 +58,12 @@ class HTMLPurifier
* Version of HTML Purifier.
* @type string
*/
- public $version = '4.7.0';
+ public $version = '4.9.3';
/**
* Constant with version of HTML Purifier.
*/
- const VERSION = '4.7.0';
+ const VERSION = '4.9.3';
/**
* Global configuration object.
@@ -104,7 +104,7 @@ class HTMLPurifier
/**
* Initializes the purifier.
*
- * @param HTMLPurifier_Config $config Optional HTMLPurifier_Config object
+ * @param HTMLPurifier_Config|mixed $config Optional HTMLPurifier_Config object
* for all instances of the purifier, if omitted, a default
* configuration is supplied (which can be overridden on a
* per-use basis).
diff --git a/extlib/HTMLPurifier/HTMLPurifier.safe-includes.php b/extlib/HTMLPurifier/HTMLPurifier.safe-includes.php
index 9dea6d1ed5..a3261f8a32 100644
--- a/extlib/HTMLPurifier/HTMLPurifier.safe-includes.php
+++ b/extlib/HTMLPurifier/HTMLPurifier.safe-includes.php
@@ -131,6 +131,8 @@ require_once $__dir . '/HTMLPurifier/AttrTransform/SafeObject.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/SafeParam.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/ScriptRequired.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/TargetBlank.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoopener.php';
+require_once $__dir . '/HTMLPurifier/AttrTransform/TargetNoreferrer.php';
require_once $__dir . '/HTMLPurifier/AttrTransform/Textarea.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Chameleon.php';
require_once $__dir . '/HTMLPurifier/ChildDef/Custom.php';
@@ -169,6 +171,8 @@ require_once $__dir . '/HTMLPurifier/HTMLModule/StyleAttribute.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tables.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Target.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/TargetBlank.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoopener.php';
+require_once $__dir . '/HTMLPurifier/HTMLModule/TargetNoreferrer.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Text.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/Tidy.php';
require_once $__dir . '/HTMLPurifier/HTMLModule/XMLCommonAttributes.php';
@@ -219,5 +223,6 @@ require_once $__dir . '/HTMLPurifier/URIScheme/https.php';
require_once $__dir . '/HTMLPurifier/URIScheme/mailto.php';
require_once $__dir . '/HTMLPurifier/URIScheme/news.php';
require_once $__dir . '/HTMLPurifier/URIScheme/nntp.php';
+require_once $__dir . '/HTMLPurifier/URIScheme/tel.php';
require_once $__dir . '/HTMLPurifier/VarParser/Flexible.php';
require_once $__dir . '/HTMLPurifier/VarParser/Native.php';
diff --git a/extlib/HTMLPurifier/HTMLPurifier/Arborize.php b/extlib/HTMLPurifier/HTMLPurifier/Arborize.php
index 9e6617be5d..d2e9d22a20 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/Arborize.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/Arborize.php
@@ -19,8 +19,8 @@ class HTMLPurifier_Arborize
if ($token instanceof HTMLPurifier_Token_End) {
$token->start = null; // [MUT]
$r = array_pop($stack);
- assert($r->name === $token->name);
- assert(empty($token->attr));
+ //assert($r->name === $token->name);
+ //assert(empty($token->attr));
$r->endCol = $token->col;
$r->endLine = $token->line;
$r->endArmor = $token->armor;
@@ -32,7 +32,7 @@ class HTMLPurifier_Arborize
$stack[] = $node;
}
}
- assert(count($stack) == 1);
+ //assert(count($stack) == 1);
return $stack[0];
}
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrCollections.php b/extlib/HTMLPurifier/HTMLPurifier/AttrCollections.php
index 4f6c2e39a2..c7b17cf144 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrCollections.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrCollections.php
@@ -21,6 +21,11 @@ class HTMLPurifier_AttrCollections
* @param HTMLPurifier_HTMLModule[] $modules Hash array of HTMLPurifier_HTMLModule members
*/
public function __construct($attr_types, $modules)
+ {
+ $this->doConstruct($attr_types, $modules);
+ }
+
+ public function doConstruct($attr_types, $modules)
{
// load extensions from the modules
foreach ($modules as $module) {
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef.php
index 5ac06522b9..739646fa7c 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrDef.php
@@ -86,7 +86,13 @@ abstract class HTMLPurifier_AttrDef
*/
protected function mungeRgb($string)
{
- return preg_replace('/rgb\((\d+)\s*,\s*(\d+)\s*,\s*(\d+)\)/', 'rgb(\1,\2,\3)', $string);
+ $p = '\s*(\d+(\.\d+)?([%]?))\s*';
+
+ if (preg_match('/(rgba|hsla)\(/', $string)) {
+ return preg_replace('/(rgba|hsla)\('.$p.','.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8,\11)', $string);
+ }
+
+ return preg_replace('/(rgb|hsl)\('.$p.','.$p.','.$p.'\)/', '\1(\2,\5,\8)', $string);
}
/**
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php
index 02c1641fb2..ad2cb90ad1 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS.php
@@ -25,15 +25,42 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
$css = $this->parseCDATA($css);
$definition = $config->getCSSDefinition();
+ $allow_duplicates = $config->get("CSS.AllowDuplicates");
- // we're going to break the spec and explode by semicolons.
- // This is because semicolon rarely appears in escaped form
- // Doing this is generally flaky but fast
- // IT MIGHT APPEAR IN URIs, see HTMLPurifier_AttrDef_CSSURI
- // for details
- $declarations = explode(';', $css);
+ // According to the CSS2.1 spec, the places where a
+ // non-delimiting semicolon can appear are in strings
+ // escape sequences. So here is some dumb hack to
+ // handle quotes.
+ $len = strlen($css);
+ $accum = "";
+ $declarations = array();
+ $quoted = false;
+ for ($i = 0; $i < $len; $i++) {
+ $c = strcspn($css, ";'\"", $i);
+ $accum .= substr($css, $i, $c);
+ $i += $c;
+ if ($i == $len) break;
+ $d = $css[$i];
+ if ($quoted) {
+ $accum .= $d;
+ if ($d == $quoted) {
+ $quoted = false;
+ }
+ } else {
+ if ($d == ";") {
+ $declarations[] = $accum;
+ $accum = "";
+ } else {
+ $accum .= $d;
+ $quoted = $d;
+ }
+ }
+ }
+ if ($accum != "") $declarations[] = $accum;
+
$propvalues = array();
+ $new_declarations = '';
/**
* Name of the current CSS property being validated.
@@ -83,7 +110,11 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
if ($result === false) {
continue;
}
- $propvalues[$property] = $result;
+ if ($allow_duplicates) {
+ $new_declarations .= "$property:$result;";
+ } else {
+ $propvalues[$property] = $result;
+ }
}
$context->destroy('CurrentCSSProperty');
@@ -92,7 +123,6 @@ class HTMLPurifier_AttrDef_CSS extends HTMLPurifier_AttrDef
// slightly inefficient, but it's the only way of getting rid of
// duplicates. Perhaps config to optimize it, but not now.
- $new_declarations = '';
foreach ($propvalues as $prop => $value) {
$new_declarations .= "$prop:$value;";
}
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php
index 16d2a6b98c..d7287a00c2 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/Color.php
@@ -6,6 +6,16 @@
class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
{
+ /**
+ * @type HTMLPurifier_AttrDef_CSS_AlphaValue
+ */
+ protected $alpha;
+
+ public function __construct()
+ {
+ $this->alpha = new HTMLPurifier_AttrDef_CSS_AlphaValue();
+ }
+
/**
* @param string $color
* @param HTMLPurifier_Config $config
@@ -29,59 +39,104 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
return $colors[$lower];
}
- if (strpos($color, 'rgb(') !== false) {
- // rgb literal handling
+ if (preg_match('#(rgb|rgba|hsl|hsla)\(#', $color, $matches) === 1) {
$length = strlen($color);
if (strpos($color, ')') !== $length - 1) {
return false;
}
- $triad = substr($color, 4, $length - 4 - 1);
- $parts = explode(',', $triad);
- if (count($parts) !== 3) {
+
+ // get used function : rgb, rgba, hsl or hsla
+ $function = $matches[1];
+
+ $parameters_size = 3;
+ $alpha_channel = false;
+ if (substr($function, -1) === 'a') {
+ $parameters_size = 4;
+ $alpha_channel = true;
+ }
+
+ /*
+ * Allowed types for values :
+ * parameter_position => [type => max_value]
+ */
+ $allowed_types = array(
+ 1 => array('percentage' => 100, 'integer' => 255),
+ 2 => array('percentage' => 100, 'integer' => 255),
+ 3 => array('percentage' => 100, 'integer' => 255),
+ );
+ $allow_different_types = false;
+
+ if (strpos($function, 'hsl') !== false) {
+ $allowed_types = array(
+ 1 => array('integer' => 360),
+ 2 => array('percentage' => 100),
+ 3 => array('percentage' => 100),
+ );
+ $allow_different_types = true;
+ }
+
+ $values = trim(str_replace($function, '', $color), ' ()');
+
+ $parts = explode(',', $values);
+ if (count($parts) !== $parameters_size) {
return false;
}
- $type = false; // to ensure that they're all the same type
+
+ $type = false;
$new_parts = array();
+ $i = 0;
+
foreach ($parts as $part) {
+ $i++;
$part = trim($part);
+
if ($part === '') {
return false;
}
- $length = strlen($part);
- if ($part[$length - 1] === '%') {
- // handle percents
- if (!$type) {
- $type = 'percentage';
- } elseif ($type !== 'percentage') {
+
+ // different check for alpha channel
+ if ($alpha_channel === true && $i === count($parts)) {
+ $result = $this->alpha->validate($part, $config, $context);
+
+ if ($result === false) {
return false;
}
- $num = (float)substr($part, 0, $length - 1);
- if ($num < 0) {
- $num = 0;
- }
- if ($num > 100) {
- $num = 100;
- }
- $new_parts[] = "$num%";
+
+ $new_parts[] = (string)$result;
+ continue;
+ }
+
+ if (substr($part, -1) === '%') {
+ $current_type = 'percentage';
} 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;
+ $current_type = 'integer';
+ }
+
+ if (!array_key_exists($current_type, $allowed_types[$i])) {
+ return false;
+ }
+
+ if (!$type) {
+ $type = $current_type;
+ }
+
+ if ($allow_different_types === false && $type != $current_type) {
+ return false;
+ }
+
+ $max_value = $allowed_types[$i][$current_type];
+
+ if ($current_type == 'integer') {
+ // Return value between range 0 -> $max_value
+ $new_parts[] = (int)max(min($part, $max_value), 0);
+ } elseif ($current_type == 'percentage') {
+ $new_parts[] = (float)max(min(rtrim($part, '%'), $max_value), 0) . '%';
}
}
- $new_triad = implode(',', $new_parts);
- $color = "rgb($new_triad)";
+
+ $new_values = implode(',', $new_parts);
+
+ $color = $function . '(' . $new_values . ')';
} else {
// hexadecimal handling
if ($color[0] === '#') {
@@ -100,6 +155,7 @@ class HTMLPurifier_AttrDef_CSS_Color extends HTMLPurifier_AttrDef
}
return $color;
}
+
}
// vim: et sw=4 sts=4
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php
index 86101020dc..74e24c8816 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/FontFamily.php
@@ -130,6 +130,8 @@ class HTMLPurifier_AttrDef_CSS_FontFamily extends HTMLPurifier_AttrDef
// . See
// the CSS3 spec for more examples:
//
+ // You can see live samples of these on the Internet:
+ //
// However, most of these fonts have ASCII equivalents:
// for example, 'MS Mincho', and it's considered
// professional to use ASCII font names instead of
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php
index f9434230e2..6617acace5 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/CSS/URI.php
@@ -33,6 +33,9 @@ class HTMLPurifier_AttrDef_CSS_URI extends HTMLPurifier_AttrDef_URI
return false;
}
$uri_string = substr($uri_string, 4);
+ if (strlen($uri_string) == 0) {
+ return false;
+ }
$new_length = strlen($uri_string) - 1;
if ($uri_string[$new_length] != ')') {
return false;
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php
index 3d86efb44c..4ba45610fe 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/HTML/ID.php
@@ -72,18 +72,26 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
// we purposely avoid using regex, hopefully this is faster
- if (ctype_alpha($id)) {
- $result = true;
- } else {
- if (!ctype_alpha(@$id[0])) {
+ if ($config->get('Attr.ID.HTML5') === true) {
+ if (preg_match('/[\t\n\x0b\x0c ]/', $id)) {
return false;
}
- // primitive style of regexps, I suppose
- $trim = trim(
- $id,
- 'A..Za..z0..9:-._'
- );
- $result = ($trim === '');
+ } else {
+ if (ctype_alpha($id)) {
+ // OK
+ } else {
+ if (!ctype_alpha(@$id[0])) {
+ return false;
+ }
+ // primitive style of regexps, I suppose
+ $trim = trim(
+ $id,
+ 'A..Za..z0..9:-._'
+ );
+ if ($trim !== '') {
+ return false;
+ }
+ }
}
$regexp = $config->get('Attr.IDBlacklistRegexp');
@@ -91,14 +99,14 @@ class HTMLPurifier_AttrDef_HTML_ID extends HTMLPurifier_AttrDef
return false;
}
- if (!$this->selector && $result) {
+ if (!$this->selector) {
$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;
+ return $id;
}
}
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php
index e7df800b1e..3b4d186743 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrDef/URI/Host.php
@@ -76,24 +76,33 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
// fairly well supported.
$underscore = $config->get('Core.AllowHostnameUnderscore') ? '_' : '';
+ // Based off of RFC 1738, but amended so that
+ // as per RFC 3696, the top label need only not be all numeric.
// The productions describing this are:
$a = '[a-z]'; // alpha
$an = '[a-z0-9]'; // alphanum
$and = "[a-z0-9-$underscore]"; // alphanum | "-"
// domainlabel = alphanum | alphanum *( alphanum | "-" ) alphanum
- $domainlabel = "$an($and*$an)?";
- // toplabel = alpha | alpha *( alphanum | "-" ) alphanum
- $toplabel = "$a($and*$an)?";
+ $domainlabel = "$an(?:$and*$an)?";
+ // AMENDED as per RFC 3696
+ // toplabel = alphanum | alphanum *( alphanum | "-" ) alphanum
+ // side condition: not all numeric
+ $toplabel = "$an(?:$and*$an)?";
// hostname = *( domainlabel "." ) toplabel [ "." ]
- if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
- return $string;
+ if (preg_match("/^(?:$domainlabel\.)*($toplabel)\.?$/i", $string, $matches)) {
+ if (!ctype_digit($matches[1])) {
+ return $string;
+ }
}
+ // PHP 5.3 and later support this functionality natively
+ if (function_exists('idn_to_ascii')) {
+ $string = idn_to_ascii($string);
+
// If we have Net_IDNA2 support, we can support IRIs by
// punycoding them. (This is the most portable thing to do,
// since otherwise we have to assume browsers support
-
- if ($config->get('Core.EnableIDNA')) {
+ } elseif ($config->get('Core.EnableIDNA')) {
$idna = new Net_IDNA2(array('encoding' => 'utf8', 'overlong' => false, 'strict' => true));
// we need to encode each period separately
$parts = explode('.', $string);
@@ -114,13 +123,14 @@ class HTMLPurifier_AttrDef_URI_Host extends HTMLPurifier_AttrDef
}
}
$string = implode('.', $new_parts);
- if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
- return $string;
- }
} catch (Exception $e) {
// XXX error reporting
}
}
+ // Try again
+ if (preg_match("/^($domainlabel\.)*$toplabel\.?$/i", $string)) {
+ return $string;
+ }
return false;
}
}
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php
index 7df6cb3e1b..235ebb34b6 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/ImgRequired.php
@@ -32,8 +32,7 @@ class HTMLPurifier_AttrTransform_ImgRequired extends HTMLPurifier_AttrTransform
if ($src) {
$alt = $config->get('Attr.DefaultImageAlt');
if ($alt === null) {
- // truncate if the alt is too long
- $attr['alt'] = substr(basename($attr['src']), 0, 40);
+ $attr['alt'] = basename($attr['src']);
} else {
$attr['alt'] = $alt;
}
diff --git a/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/TargetNoopener.php b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/TargetNoopener.php
new file mode 100644
index 0000000000..1db3c6c09e
--- /dev/null
+++ b/extlib/HTMLPurifier/HTMLPurifier/AttrTransform/TargetNoopener.php
@@ -0,0 +1,37 @@
+get('CSS.MaxImgLength');
+ $this->info['min-width'] =
+ $this->info['max-width'] =
+ $this->info['min-height'] =
+ $this->info['max-height'] =
$this->info['width'] =
$this->info['height'] =
$max === null ?
@@ -370,6 +374,19 @@ class HTMLPurifier_CSSDefinition extends HTMLPurifier_Definition
);
$this->info['page-break-inside'] = new HTMLPurifier_AttrDef_Enum(array('auto', 'avoid'));
+ $border_radius = new HTMLPurifier_AttrDef_CSS_Composite(
+ array(
+ new HTMLPurifier_AttrDef_CSS_Percentage(true), // disallow negative
+ new HTMLPurifier_AttrDef_CSS_Length('0') // disallow negative
+ ));
+
+ $this->info['border-top-left-radius'] =
+ $this->info['border-top-right-radius'] =
+ $this->info['border-bottom-right-radius'] =
+ $this->info['border-bottom-left-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 2);
+ // TODO: support SLASH syntax
+ $this->info['border-radius'] = new HTMLPurifier_AttrDef_CSS_Multiple($border_radius, 4);
+
}
/**
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/List.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/List.php
index 891b9f6f5b..4fc70e0efa 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/List.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/List.php
@@ -38,13 +38,19 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
return false;
}
+ // if li is not allowed, delete parent node
+ if (!isset($config->getHTMLDefinition()->info['li'])) {
+ trigger_error("Cannot allow ul/ol without allowing li", E_USER_WARNING);
+ return false;
+ }
+
// the new set of children
$result = array();
// a little sanity check to make sure it's not ALL whitespace
$all_whitespace = true;
- $current_li = false;
+ $current_li = null;
foreach ($children as $node) {
if (!empty($node->is_whitespace)) {
@@ -65,7 +71,7 @@ class HTMLPurifier_ChildDef_List extends HTMLPurifier_ChildDef
// to handle non-list elements; non-list elements should
// not be appended to an existing li; only li created
// for non-list. This distinction is not currently made.
- if ($current_li === false) {
+ if ($current_li === null) {
$current_li = new HTMLPurifier_Node_Element('li');
$result[] = $current_li;
}
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php
index 3e4a0f2182..cb6b3e6cdc 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/ChildDef/Table.php
@@ -203,7 +203,7 @@ class HTMLPurifier_ChildDef_Table extends HTMLPurifier_ChildDef
$current_tr_tbody->children[] = $node;
break;
case '#PCDATA':
- assert($node->is_whitespace);
+ //assert($node->is_whitespace);
if ($current_tr_tbody === null) {
$ret[] = $node;
} else {
diff --git a/extlib/HTMLPurifier/HTMLPurifier/Config.php b/extlib/HTMLPurifier/HTMLPurifier/Config.php
index 2b2db0c264..3648364b30 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/Config.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/Config.php
@@ -21,7 +21,7 @@ class HTMLPurifier_Config
* HTML Purifier's version
* @type string
*/
- public $version = '4.7.0';
+ public $version = '4.9.3';
/**
* Whether or not to automatically finalize
@@ -333,7 +333,7 @@ class HTMLPurifier_Config
}
// Raw type might be negative when using the fully optimized form
- // of stdclass, which indicates allow_null == true
+ // of stdClass, which indicates allow_null == true
$rtype = is_int($def) ? $def : $def->type;
if ($rtype < 0) {
$type = -$rtype;
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php
index bfbb0f92f5..655c0e97ae 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema.php
@@ -24,11 +24,11 @@ class HTMLPurifier_ConfigSchema
*
* array(
* 'Namespace' => array(
- * 'Directive' => new stdclass(),
+ * 'Directive' => new stdClass(),
* )
* )
*
- * The stdclass may have the following properties:
+ * The stdClass may have the following properties:
*
* - If isAlias isn't set:
* - type: Integer type of directive, see HTMLPurifier_VarParser for definitions
@@ -39,8 +39,8 @@ class HTMLPurifier_ConfigSchema
* - 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
+ * 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.
*
@@ -105,7 +105,7 @@ class HTMLPurifier_ConfigSchema
*/
public function add($key, $default, $type, $allow_null)
{
- $obj = new stdclass();
+ $obj = new stdClass();
$obj->type = is_int($type) ? $type : HTMLPurifier_VarParser::$types[$type];
if ($allow_null) {
$obj->allow_null = true;
@@ -152,14 +152,14 @@ class HTMLPurifier_ConfigSchema
*/
public function addAlias($key, $new_key)
{
- $obj = new stdclass;
+ $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.
+ * Replaces any stdClass that only has the type property with type integer.
*/
public function postProcess()
{
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser
index 1e6ccd2275..371e948f1c 100644
Binary files a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser and b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema.ser differ
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
new file mode 100644
index 0000000000..735d4b7a10
--- /dev/null
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Attr.ID.HTML5.txt
@@ -0,0 +1,10 @@
+Attr.ID.HTML5
+TYPE: bool/null
+DEFAULT: null
+VERSION: 4.8.0
+--DESCRIPTION--
+In HTML5, restrictions on the format of the id attribute have been significantly
+relaxed, such that any string is valid so long as it contains no spaces and
+is at least one character. In lieu of a general HTML5 compatibility flag,
+set this configuration directive to true to use the relaxed rules.
+--# vim: et sw=4 sts=4
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
new file mode 100644
index 0000000000..4d054b1f07
--- /dev/null
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/CSS.AllowDuplicates.txt
@@ -0,0 +1,11 @@
+CSS.AllowDuplicates
+TYPE: bool
+DEFAULT: false
+VERSION: 4.8.0
+--DESCRIPTION--
+
+ By default, HTML Purifier removes duplicate CSS properties,
+ like color:red; color:blue. If this is set to
+ true, duplicate properties are allowed.
+
+--# vim: et sw=4 sts=4
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
index b2b83d9ab6..2e0cc81044 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/Cache.SerializerPermissions.txt
@@ -1,5 +1,5 @@
Cache.SerializerPermissions
-TYPE: int
+TYPE: int/null
VERSION: 4.3.0
DEFAULT: 0755
--DESCRIPTION--
@@ -8,4 +8,9 @@ DEFAULT: 0755
Directory permissions of the files and directories created inside
the DefinitionCache/Serializer or other custom serializer path.
+
+ In HTML Purifier 4.8.0, this also supports NULL,
+ which means that no chmod'ing or directory creation shall
+ occur.
+
+ This directive enables aggressive pre-filter removal of
+ script tags. This is not necessary for security,
+ but it can help work around a bug in libxml where embedded
+ HTML elements inside script sections cause the parser to
+ choke. To revert to pre-4.9.0 behavior, set this to false.
+ This directive has no effect if %Core.Trusted is true,
+ %Core.RemoveScriptContents is false, or %Core.HiddenElements
+ does not contain script.
+
+ Prior to HTML Purifier 4.9.0, entities were decoded by performing
+ a global search replace for all entities whose decoded versions
+ did not have special meanings under HTML, and replaced them with
+ their decoded versions. We would match all entities, even if they did
+ not have a trailing semicolon, but only if there weren't any trailing
+ alphanumeric characters.
+
+
+
Original
Text
Attribute
+
¥
¥
¥
+
¥
¥
¥
+
¥a
¥a
¥a
+
¥=
¥=
¥=
+
+
+ In HTML Purifier 4.9.0, we changed the behavior of entity parsing
+ to match entities that had missing trailing semicolons in less
+ cases, to more closely match HTML5 parsing behavior:
+
+
+
Original
Text
Attribute
+
¥
¥
¥
+
¥
¥
¥
+
¥a
¥a
¥a
+
¥=
¥=
¥=
+
+
+ This flag reverts back to pre-HTML Purifier 4.9.0 behavior.
+
+--# vim: et sw=4 sts=4
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
new file mode 100644
index 0000000000..dd514c0def
--- /dev/null
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoopener.txt
@@ -0,0 +1,10 @@
+--# vim: et sw=4 sts=4
+HTML.TargetNoopener
+TYPE: bool
+VERSION: 4.8.0
+DEFAULT: TRUE
+--DESCRIPTION--
+If enabled, noopener rel attributes are added to links which have
+a target attribute associated with them. This prevents malicious
+destinations from overwriting the original window.
+--# vim: et sw=4 sts=4
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
new file mode 100644
index 0000000000..cb5a0b0e5e
--- /dev/null
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/HTML.TargetNoreferrer.txt
@@ -0,0 +1,9 @@
+HTML.TargetNoreferrer
+TYPE: bool
+VERSION: 4.8.0
+DEFAULT: TRUE
+--DESCRIPTION--
+If enabled, noreferrer rel attributes are added to links which have
+a target attribute associated with them. This prevents malicious
+destinations from overwriting the original window.
+--# vim: et sw=4 sts=4
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
index 666635a5ff..eb97307e20 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.AllowedSchemes.txt
@@ -8,6 +8,7 @@ array (
'ftp' => true,
'nntp' => true,
'news' => true,
+ 'tel' => true,
)
--DESCRIPTION--
Whitelist that defines the schemes that a URI is allowed to have. This
diff --git a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
index 728e378cbe..834bc08c0b 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.DefaultScheme.txt
@@ -1,5 +1,5 @@
URI.DefaultScheme
-TYPE: string
+TYPE: string/null
DEFAULT: 'http'
--DESCRIPTION--
@@ -7,4 +7,9 @@ DEFAULT: 'http'
Defines through what scheme the output will be served, in order to
select the proper object validator when no scheme information is present.
+
+
+ Starting with HTML Purifier 4.9.0, the default scheme can be null, in
+ which case we reject all URIs which do not have explicit schemes.
+
--# 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
index 179f94eb03..58c81dcc44 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
+++ b/extlib/HTMLPurifier/HTMLPurifier/ConfigSchema/schema/URI.Munge.txt
@@ -9,75 +9,75 @@ DEFAULT: NULL
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:
- https://searx.laquadrature.net/?q=%s).
-
-
+ 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.
+ 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.
+ 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:
-
-
+
+
-
-
Key
-
Description
-
Example <a href="">
-
+
+
Key
+
Description
+
Example <a href="">
+
-
-
%r
-
1 - The URI embeds a resource (blank) - The URI is merely a link
-
-
-
-
%n
-
The name of the tag this URI came from
-
a
-
-
-
%m
-
The name of the attribute this URI came from
-
href
-
-
-
%p
-
The name of the CSS property this URI came from, or blank if irrelevant
-
-
+
+
%r
+
1 - The URI embeds a resource (blank) - The URI is merely a link
+
+
+
+
%n
+
The name of the tag this URI came from
+
a
+
+
+
%m
+
The name of the attribute this URI came from
+
href
+
+
+
%p
+
The 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
+
+--# vim: et sw=4 sts=4
diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php
index 67bb5b1e69..9aa8ff354f 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache.php
@@ -118,7 +118,7 @@ abstract class HTMLPurifier_DefinitionCache
/**
* Clears all expired (older version or revision) objects from cache
- * @note Be carefuly implementing this method as flush. Flush must
+ * @note Be careful implementing this method as flush. Flush must
* not interfere with other Definition types, and cleanup()
* should not be repeatedly called by userland code.
* @param HTMLPurifier_Config $config
diff --git a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php
index ce268d91b4..952e48d470 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/DefinitionCache/Serializer.php
@@ -97,6 +97,12 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
}
$dir = $this->generateDirectoryPath($config);
$dh = opendir($dir);
+ // Apparently, on some versions of PHP, readdir will return
+ // an empty string if you pass an invalid argument to readdir.
+ // So you need this test. See #49.
+ if (false === $dh) {
+ return false;
+ }
while (false !== ($filename = readdir($dh))) {
if (empty($filename)) {
continue;
@@ -106,6 +112,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
}
unlink($dir . '/' . $filename);
}
+ closedir($dh);
+ return true;
}
/**
@@ -119,6 +127,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
}
$dir = $this->generateDirectoryPath($config);
$dh = opendir($dir);
+ // See #49 (and above).
+ if (false === $dh) {
+ return false;
+ }
while (false !== ($filename = readdir($dh))) {
if (empty($filename)) {
continue;
@@ -131,6 +143,8 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
unlink($dir . '/' . $filename);
}
}
+ closedir($dh);
+ return true;
}
/**
@@ -186,11 +200,9 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
if ($result !== false) {
// set permissions of the new file (no execute)
$chmod = $config->get('Cache.SerializerPermissions');
- if (!$chmod) {
- $chmod = 0644; // invalid config or simpletest
+ if ($chmod !== null) {
+ chmod($file, $chmod & 0666);
}
- $chmod = $chmod & 0666;
- chmod($file, $chmod);
}
return $result;
}
@@ -204,8 +216,10 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
{
$directory = $this->generateDirectoryPath($config);
$chmod = $config->get('Cache.SerializerPermissions');
- if (!$chmod) {
- $chmod = 0755; // invalid config or simpletest
+ if ($chmod === null) {
+ // TODO: This races
+ if (is_dir($directory)) return true;
+ return mkdir($directory);
}
if (!is_dir($directory)) {
$base = $this->generateBaseDirectoryPath($config);
@@ -219,15 +233,16 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
} elseif (!$this->_testPermissions($base, $chmod)) {
return false;
}
- mkdir($directory, $chmod);
- if (!$this->_testPermissions($directory, $chmod)) {
+ if (!mkdir($directory, $chmod)) {
trigger_error(
- 'Base directory ' . $base . ' does not exist,
- please create or change using %Cache.SerializerPath',
+ 'Could not create directory ' . $directory . '',
E_USER_WARNING
);
return false;
}
+ if (!$this->_testPermissions($directory, $chmod)) {
+ return false;
+ }
} elseif (!$this->_testPermissions($directory, $chmod)) {
return false;
}
@@ -256,7 +271,7 @@ class HTMLPurifier_DefinitionCache_Serializer extends HTMLPurifier_DefinitionCac
);
return false;
}
- if (function_exists('posix_getuid')) {
+ if (function_exists('posix_getuid') && $chmod !== null) {
// POSIX system, we can give more specific advice
if (fileowner($dir) === posix_getuid()) {
// we can chmod it ourselves
diff --git a/extlib/HTMLPurifier/HTMLPurifier/Encoder.php b/extlib/HTMLPurifier/HTMLPurifier/Encoder.php
index fef9b58906..b94f175423 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/Encoder.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/Encoder.php
@@ -101,6 +101,14 @@ class HTMLPurifier_Encoder
* It will parse according to UTF-8 and return a valid UTF8 string, with
* non-SGML codepoints excluded.
*
+ * Specifically, it will permit:
+ * \x{9}\x{A}\x{D}\x{20}-\x{7E}\x{A0}-\x{D7FF}\x{E000}-\x{FFFD}\x{10000}-\x{10FFFF}
+ * Source: https://www.w3.org/TR/REC-xml/#NT-Char
+ * Arguably this function should be modernized to the HTML5 set
+ * of allowed characters:
+ * https://www.w3.org/TR/html5/syntax.html#preprocessing-the-input-stream
+ * which simultaneously expand and restrict the set of allowed characters.
+ *
* @param string $str The string to clean
* @param bool $force_php
* @return string
@@ -122,15 +130,12 @@ class HTMLPurifier_Encoder
* 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
@@ -255,6 +260,7 @@ class HTMLPurifier_Encoder
// 7F-9F is not strictly prohibited by XML,
// but it is non-SGML, and thus we don't allow it
(0xA0 <= $mUcs4 && 0xD7FF >= $mUcs4) ||
+ (0xE000 <= $mUcs4 && 0xFFFD >= $mUcs4) ||
(0x10000 <= $mUcs4 && 0x10FFFF >= $mUcs4)
)
) {
diff --git a/extlib/HTMLPurifier/HTMLPurifier/EntityParser.php b/extlib/HTMLPurifier/HTMLPurifier/EntityParser.php
index 61529dcd9d..c372b5a6a6 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/EntityParser.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/EntityParser.php
@@ -16,6 +16,138 @@ class HTMLPurifier_EntityParser
*/
protected $_entity_lookup;
+ /**
+ * Callback regex string for entities in text.
+ * @type string
+ */
+ protected $_textEntitiesRegex;
+
+ /**
+ * Callback regex string for entities in attributes.
+ * @type string
+ */
+ protected $_attrEntitiesRegex;
+
+ /**
+ * Tests if the beginning of a string is a semi-optional regex
+ */
+ protected $_semiOptionalPrefixRegex;
+
+ public function __construct() {
+ // From
+ // http://stackoverflow.com/questions/15532252/why-is-reg-being-rendered-as-without-the-bounding-semicolon
+ $semi_optional = "quot|QUOT|lt|LT|gt|GT|amp|AMP|AElig|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|Iacute|Icirc|Igrave|Iuml|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml";
+
+ // NB: three empty captures to put the fourth match in the right
+ // place
+ $this->_semiOptionalPrefixRegex = "/&()()()($semi_optional)/";
+
+ $this->_textEntitiesRegex =
+ '/&(?:'.
+ // hex
+ '[#]x([a-fA-F0-9]+);?|'.
+ // dec
+ '[#]0*(\d+);?|'.
+ // string (mandatory semicolon)
+ // NB: order matters: match semicolon preferentially
+ '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
+ // string (optional semicolon)
+ "($semi_optional)".
+ ')/';
+
+ $this->_attrEntitiesRegex =
+ '/&(?:'.
+ // hex
+ '[#]x([a-fA-F0-9]+);?|'.
+ // dec
+ '[#]0*(\d+);?|'.
+ // string (mandatory semicolon)
+ // NB: order matters: match semicolon preferentially
+ '([A-Za-z_:][A-Za-z0-9.\-_:]*);|'.
+ // string (optional semicolon)
+ // don't match if trailing is equals or alphanumeric (URL
+ // like)
+ "($semi_optional)(?![=;A-Za-z0-9])".
+ ')/';
+
+ }
+
+ /**
+ * Substitute entities with the parsed equivalents. Use this on
+ * textual data in an HTML document (as opposed to attributes.)
+ *
+ * @param string $string String to have entities parsed.
+ * @return string Parsed string.
+ */
+ public function substituteTextEntities($string)
+ {
+ return preg_replace_callback(
+ $this->_textEntitiesRegex,
+ array($this, 'entityCallback'),
+ $string
+ );
+ }
+
+ /**
+ * Substitute entities with the parsed equivalents. Use this on
+ * attribute contents in documents.
+ *
+ * @param string $string String to have entities parsed.
+ * @return string Parsed string.
+ */
+ public function substituteAttrEntities($string)
+ {
+ return preg_replace_callback(
+ $this->_attrEntitiesRegex,
+ array($this, 'entityCallback'),
+ $string
+ );
+ }
+
+ /**
+ * Callback function for substituteNonSpecialEntities() that does the work.
+ *
+ * @param array $matches PCRE matches array, with 0 the entire match, and
+ * either index 1, 2 or 3 set with a hex value, dec value,
+ * or string (respectively).
+ * @return string Replacement string.
+ */
+
+ protected function entityCallback($matches)
+ {
+ $entity = $matches[0];
+ $hex_part = @$matches[1];
+ $dec_part = @$matches[2];
+ $named_part = empty($matches[3]) ? @$matches[4] : $matches[3];
+ if ($hex_part !== NULL && $hex_part !== "") {
+ return HTMLPurifier_Encoder::unichr(hexdec($hex_part));
+ } elseif ($dec_part !== NULL && $dec_part !== "") {
+ return HTMLPurifier_Encoder::unichr((int) $dec_part);
+ } else {
+ if (!$this->_entity_lookup) {
+ $this->_entity_lookup = HTMLPurifier_EntityLookup::instance();
+ }
+ if (isset($this->_entity_lookup->table[$named_part])) {
+ return $this->_entity_lookup->table[$named_part];
+ } else {
+ // exact match didn't match anything, so test if
+ // any of the semicolon optional match the prefix.
+ // Test that this is an EXACT match is important to
+ // prevent infinite loop
+ if (!empty($matches[3])) {
+ return preg_replace_callback(
+ $this->_semiOptionalPrefixRegex,
+ array($this, 'entityCallback'),
+ $entity
+ );
+ }
+ return $entity;
+ }
+ }
+ }
+
+ // LEGACY CODE BELOW
+
/**
* Callback regex string for parsing entities.
* @type string
@@ -144,7 +276,7 @@ class HTMLPurifier_EntityParser
$entity;
} else {
return isset($this->_special_ent2dec[$matches[3]]) ?
- $this->_special_ent2dec[$matches[3]] :
+ $this->_special_dec2str[$this->_special_ent2dec[$matches[3]]] :
$entity;
}
}
diff --git a/extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php b/extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php
index 08e62c16bf..66f70b0fc0 100644
--- a/extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php
+++ b/extlib/HTMLPurifier/HTMLPurifier/Filter/ExtractStyleBlocks.php
@@ -95,7 +95,10 @@ class HTMLPurifier_Filter_ExtractStyleBlocks extends HTMLPurifier_Filter
if ($tidy !== null) {
$this->_tidy = $tidy;
}
- $html = preg_replace_callback('##isU', array($this, 'styleCallback'), $html);
+ // NB: this must be NON-greedy because if we have
+ //
+ // we must not grab foo
-
-
-
-
Note: Please set $min_cachePath
-in /min/config.php to improve performance.
For the best performance you can serve these files as a pre-defined group with a URI
-like: /min/?g=keyName
-
To do this, add a line like this to /min/groupsConfig.php:
-
-
return array(
- ... your existing groups here ...
-
-);
-
-
Make sure to replace keyName with a unique key for this group.
-
-
-
-
Find URIs on a Page
-
You can use the bookmarklet below to fetch all CSS & Javascript URIs from a page
-on your site. When you active it, this page will open in a new window with a list of
-available URIs to add.
If your CSS files contain @import declarations, Minify will not
-remove them. Therefore, you will want to remove those that point to files already
-in your list, and move any others to the top of the first file in your list
-(imports below any styles will be ignored by browsers as invalid).
-
If you desire, you can use Minify URIs in imports and they will not be touched
-by Minify. E.g. @import "/min/?g=css2";
\r\n" .
- ' ' . base64_encode($raw['prime2']) . "\r\n" .
- ' ' . base64_encode($raw['exponent1']) . "\r\n" .
- ' ' . base64_encode($raw['exponent2']) . "\r\n" .
- ' ' . base64_encode($raw['coefficient']) . "\r\n" .
- ' ' . base64_encode($raw['privateExponent']) . "\r\n" .
- '';
- break;
- case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
- if ($num_primes != 2) {
- return false;
- }
- $key = "PuTTY-User-Key-File-2: ssh-rsa\r\nEncryption: ";
- $encryption = (!empty($this->password) || is_string($this->password)) ? 'aes256-cbc' : 'none';
- $key.= $encryption;
- $key.= "\r\nComment: " . $this->comment . "\r\n";
- $public = pack('Na*Na*Na*',
- strlen('ssh-rsa'), 'ssh-rsa', strlen($raw['publicExponent']), $raw['publicExponent'], strlen($raw['modulus']), $raw['modulus']
- );
- $source = pack('Na*Na*Na*Na*',
- strlen('ssh-rsa'), 'ssh-rsa', strlen($encryption), $encryption,
- strlen($this->comment), $this->comment, strlen($public), $public
- );
- $public = base64_encode($public);
- $key.= "Public-Lines: " . ((strlen($public) + 32) >> 6) . "\r\n";
- $key.= chunk_split($public, 64);
- $private = pack('Na*Na*Na*Na*',
- strlen($raw['privateExponent']), $raw['privateExponent'], strlen($raw['prime1']), $raw['prime1'],
- strlen($raw['prime2']), $raw['prime2'], strlen($raw['coefficient']), $raw['coefficient']
- );
- if (empty($this->password) && !is_string($this->password)) {
- $source.= pack('Na*', strlen($private), $private);
- $hashkey = 'putty-private-key-file-mac-key';
- } else {
- $private.= crypt_random_string(16 - (strlen($private) & 15));
- $source.= pack('Na*', strlen($private), $private);
- if (!class_exists('Crypt_AES')) {
- require_once('Crypt/AES.php');
- }
- $sequence = 0;
- $symkey = '';
- while (strlen($symkey) < 32) {
- $temp = pack('Na*', $sequence++, $this->password);
- $symkey.= pack('H*', sha1($temp));
- }
- $symkey = substr($symkey, 0, 32);
- $crypto = new Crypt_AES();
-
- $crypto->setKey($symkey);
- $crypto->disablePadding();
- $private = $crypto->encrypt($private);
- $hashkey = 'putty-private-key-file-mac-key' . $this->password;
- }
-
- $private = base64_encode($private);
- $key.= 'Private-Lines: ' . ((strlen($private) + 32) >> 6) . "\r\n";
- $key.= chunk_split($private, 64);
- if (!class_exists('Crypt_Hash')) {
- require_once('Crypt/Hash.php');
- }
- $hash = new Crypt_Hash('sha1');
- $hash->setKey(pack('H*', sha1($hashkey)));
- $key.= 'Private-MAC: ' . bin2hex($hash->hash($source)) . "\r\n";
-
- return $key;
- default: // eg. CRYPT_RSA_PRIVATE_FORMAT_PKCS1
- $components = array();
- foreach ($raw as $name => $value) {
- $components[$name] = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($value)), $value);
- }
-
- $RSAPrivateKey = implode('', $components);
-
- if ($num_primes > 2) {
- $OtherPrimeInfos = '';
- for ($i = 3; $i <= $num_primes; $i++) {
- // OtherPrimeInfos ::= SEQUENCE SIZE(1..MAX) OF OtherPrimeInfo
- //
- // OtherPrimeInfo ::= SEQUENCE {
- // prime INTEGER, -- ri
- // exponent INTEGER, -- di
- // coefficient INTEGER -- ti
- // }
- $OtherPrimeInfo = pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($primes[$i]->toBytes(true))), $primes[$i]->toBytes(true));
- $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($exponents[$i]->toBytes(true))), $exponents[$i]->toBytes(true));
- $OtherPrimeInfo.= pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($coefficients[$i]->toBytes(true))), $coefficients[$i]->toBytes(true));
- $OtherPrimeInfos.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfo)), $OtherPrimeInfo);
- }
- $RSAPrivateKey.= pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($OtherPrimeInfos)), $OtherPrimeInfos);
- }
-
- $RSAPrivateKey = pack('Ca*a*', CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($RSAPrivateKey)), $RSAPrivateKey);
-
- if (!empty($this->password) || is_string($this->password)) {
- $iv = crypt_random_string(8);
- $symkey = pack('H*', md5($this->password . $iv)); // symkey is short for symmetric key
- $symkey.= substr(pack('H*', md5($symkey . $this->password . $iv)), 0, 8);
- if (!class_exists('Crypt_TripleDES')) {
- require_once('Crypt/TripleDES.php');
- }
- $des = new Crypt_TripleDES();
- $des->setKey($symkey);
- $des->setIV($iv);
- $iv = strtoupper(bin2hex($iv));
- $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
- "Proc-Type: 4,ENCRYPTED\r\n" .
- "DEK-Info: DES-EDE3-CBC,$iv\r\n" .
- "\r\n" .
- chunk_split(base64_encode($des->encrypt($RSAPrivateKey)), 64) .
- '-----END RSA PRIVATE KEY-----';
- } else {
- $RSAPrivateKey = "-----BEGIN RSA PRIVATE KEY-----\r\n" .
- chunk_split(base64_encode($RSAPrivateKey), 64) .
- '-----END RSA PRIVATE KEY-----';
- }
-
- return $RSAPrivateKey;
- }
- }
-
- /**
- * Convert a public key to the appropriate format
- *
- * @access private
- * @see setPublicKeyFormat()
- * @param String $RSAPrivateKey
- * @return String
- */
- function _convertPublicKey($n, $e)
- {
- $modulus = $n->toBytes(true);
- $publicExponent = $e->toBytes(true);
-
- switch ($this->publicKeyFormat) {
- case CRYPT_RSA_PUBLIC_FORMAT_RAW:
- return array('e' => $e->copy(), 'n' => $n->copy());
- case CRYPT_RSA_PUBLIC_FORMAT_XML:
- return "\r\n" .
- ' ' . base64_encode($modulus) . "\r\n" .
- ' ' . base64_encode($publicExponent) . "\r\n" .
- '';
- break;
- case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
- // from :
- // string "ssh-rsa"
- // mpint e
- // mpint n
- $RSAPublicKey = pack('Na*Na*Na*', strlen('ssh-rsa'), 'ssh-rsa', strlen($publicExponent), $publicExponent, strlen($modulus), $modulus);
- $RSAPublicKey = 'ssh-rsa ' . base64_encode($RSAPublicKey) . ' ' . $this->comment;
-
- return $RSAPublicKey;
- default: // eg. CRYPT_RSA_PUBLIC_FORMAT_PKCS1_RAW or CRYPT_RSA_PUBLIC_FORMAT_PKCS1
- // from :
- // RSAPublicKey ::= SEQUENCE {
- // modulus INTEGER, -- n
- // publicExponent INTEGER -- e
- // }
- $components = array(
- 'modulus' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($modulus)), $modulus),
- 'publicExponent' => pack('Ca*a*', CRYPT_RSA_ASN1_INTEGER, $this->_encodeLength(strlen($publicExponent)), $publicExponent)
- );
-
- $RSAPublicKey = pack('Ca*a*a*',
- CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($components['modulus']) + strlen($components['publicExponent'])),
- $components['modulus'], $components['publicExponent']
- );
-
- if ($this->publicKeyFormat == CRYPT_RSA_PUBLIC_FORMAT_PKCS1) {
- // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption.
- $rsaOID = pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA
- $RSAPublicKey = chr(0) . $RSAPublicKey;
- $RSAPublicKey = chr(3) . $this->_encodeLength(strlen($RSAPublicKey)) . $RSAPublicKey;
-
- $RSAPublicKey = pack('Ca*a*',
- CRYPT_RSA_ASN1_SEQUENCE, $this->_encodeLength(strlen($rsaOID . $RSAPublicKey)), $rsaOID . $RSAPublicKey
- );
- }
-
- $RSAPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" .
- chunk_split(base64_encode($RSAPublicKey), 64) .
- '-----END PUBLIC KEY-----';
-
- return $RSAPublicKey;
- }
- }
-
- /**
- * Break a public or private key down into its constituant components
- *
- * @access private
- * @see _convertPublicKey()
- * @see _convertPrivateKey()
- * @param String $key
- * @param Integer $type
- * @return Array
- */
- function _parseKey($key, $type)
- {
- if ($type != CRYPT_RSA_PUBLIC_FORMAT_RAW && !is_string($key)) {
- return false;
- }
-
- switch ($type) {
- case CRYPT_RSA_PUBLIC_FORMAT_RAW:
- if (!is_array($key)) {
- return false;
- }
- $components = array();
- switch (true) {
- case isset($key['e']):
- $components['publicExponent'] = $key['e']->copy();
- break;
- case isset($key['exponent']):
- $components['publicExponent'] = $key['exponent']->copy();
- break;
- case isset($key['publicExponent']):
- $components['publicExponent'] = $key['publicExponent']->copy();
- break;
- case isset($key[0]):
- $components['publicExponent'] = $key[0]->copy();
- }
- switch (true) {
- case isset($key['n']):
- $components['modulus'] = $key['n']->copy();
- break;
- case isset($key['modulo']):
- $components['modulus'] = $key['modulo']->copy();
- break;
- case isset($key['modulus']):
- $components['modulus'] = $key['modulus']->copy();
- break;
- case isset($key[1]):
- $components['modulus'] = $key[1]->copy();
- }
- return isset($components['modulus']) && isset($components['publicExponent']) ? $components : false;
- case CRYPT_RSA_PRIVATE_FORMAT_PKCS1:
- case CRYPT_RSA_PUBLIC_FORMAT_PKCS1:
- /* Although PKCS#1 proposes a format that public and private keys can use, encrypting them is
- "outside the scope" of PKCS#1. PKCS#1 then refers you to PKCS#12 and PKCS#15 if you're wanting to
- protect private keys, however, that's not what OpenSSL* does. OpenSSL protects private keys by adding
- two new "fields" to the key - DEK-Info and Proc-Type. These fields are discussed here:
-
- http://tools.ietf.org/html/rfc1421#section-4.6.1.1
- http://tools.ietf.org/html/rfc1421#section-4.6.1.3
-
- DES-EDE3-CBC as an algorithm, however, is not discussed anywhere, near as I can tell.
- DES-CBC and DES-EDE are discussed in RFC1423, however, DES-EDE3-CBC isn't, nor is its key derivation
- function. As is, the definitive authority on this encoding scheme isn't the IETF but rather OpenSSL's
- own implementation. ie. the implementation *is* the standard and any bugs that may exist in that
- implementation are part of the standard, as well.
-
- * OpenSSL is the de facto standard. It's utilized by OpenSSH and other projects */
- if (preg_match('#DEK-Info: (.+),(.+)#', $key, $matches)) {
- $iv = pack('H*', trim($matches[2]));
- $symkey = pack('H*', md5($this->password . substr($iv, 0, 8))); // symkey is short for symmetric key
- $symkey.= pack('H*', md5($symkey . $this->password . substr($iv, 0, 8)));
- $ciphertext = preg_replace('#.+(\r|\n|\r\n)\1|[\r\n]|-.+-| #s', '', $key);
- $ciphertext = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $ciphertext) ? base64_decode($ciphertext) : false;
- if ($ciphertext === false) {
- $ciphertext = $key;
- }
- switch ($matches[1]) {
- case 'AES-256-CBC':
- if (!class_exists('Crypt_AES')) {
- require_once('Crypt/AES.php');
- }
- $crypto = new Crypt_AES();
- break;
- case 'AES-128-CBC':
- if (!class_exists('Crypt_AES')) {
- require_once('Crypt/AES.php');
- }
- $symkey = substr($symkey, 0, 16);
- $crypto = new Crypt_AES();
- break;
- case 'DES-EDE3-CFB':
- if (!class_exists('Crypt_TripleDES')) {
- require_once('Crypt/TripleDES.php');
- }
- $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CFB);
- break;
- case 'DES-EDE3-CBC':
- if (!class_exists('Crypt_TripleDES')) {
- require_once('Crypt/TripleDES.php');
- }
- $symkey = substr($symkey, 0, 24);
- $crypto = new Crypt_TripleDES();
- break;
- case 'DES-CBC':
- if (!class_exists('Crypt_DES')) {
- require_once('Crypt/DES.php');
- }
- $crypto = new Crypt_DES();
- break;
- default:
- return false;
- }
- $crypto->setKey($symkey);
- $crypto->setIV($iv);
- $decoded = $crypto->decrypt($ciphertext);
- } else {
- $decoded = preg_replace('#-.+-|[\r\n]| #', '', $key);
- $decoded = preg_match('#^[a-zA-Z\d/+]*={0,2}$#', $decoded) ? base64_decode($decoded) : false;
- }
-
- if ($decoded !== false) {
- $key = $decoded;
- }
-
- $components = array();
-
- if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
- return false;
- }
- if ($this->_decodeLength($key) != strlen($key)) {
- return false;
- }
-
- $tag = ord($this->_string_shift($key));
- /* intended for keys for which OpenSSL's asn1parse returns the following:
-
- 0:d=0 hl=4 l= 631 cons: SEQUENCE
- 4:d=1 hl=2 l= 1 prim: INTEGER :00
- 7:d=1 hl=2 l= 13 cons: SEQUENCE
- 9:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
- 20:d=2 hl=2 l= 0 prim: NULL
- 22:d=1 hl=4 l= 609 prim: OCTET STRING */
-
- if ($tag == CRYPT_RSA_ASN1_INTEGER && substr($key, 0, 3) == "\x01\x00\x30") {
- $this->_string_shift($key, 3);
- $tag = CRYPT_RSA_ASN1_SEQUENCE;
- }
-
- if ($tag == CRYPT_RSA_ASN1_SEQUENCE) {
- /* intended for keys for which OpenSSL's asn1parse returns the following:
-
- 0:d=0 hl=4 l= 290 cons: SEQUENCE
- 4:d=1 hl=2 l= 13 cons: SEQUENCE
- 6:d=2 hl=2 l= 9 prim: OBJECT :rsaEncryption
- 17:d=2 hl=2 l= 0 prim: NULL
- 19:d=1 hl=4 l= 271 prim: BIT STRING */
- $this->_string_shift($key, $this->_decodeLength($key));
- $tag = ord($this->_string_shift($key)); // skip over the BIT STRING / OCTET STRING tag
- $this->_decodeLength($key); // skip over the BIT STRING / OCTET STRING length
- // "The initial octet shall encode, as an unsigned binary integer wtih bit 1 as the least significant bit, the number of
- // unused bits in the final subsequent octet. The number shall be in the range zero to seven."
- // -- http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf (section 8.6.2.2)
- if ($tag == CRYPT_RSA_ASN1_BITSTRING) {
- $this->_string_shift($key);
- }
- if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
- return false;
- }
- if ($this->_decodeLength($key) != strlen($key)) {
- return false;
- }
- $tag = ord($this->_string_shift($key));
- }
- if ($tag != CRYPT_RSA_ASN1_INTEGER) {
- return false;
- }
-
- $length = $this->_decodeLength($key);
- $temp = $this->_string_shift($key, $length);
- if (strlen($temp) != 1 || ord($temp) > 2) {
- $components['modulus'] = new Math_BigInteger($temp, 256);
- $this->_string_shift($key); // skip over CRYPT_RSA_ASN1_INTEGER
- $length = $this->_decodeLength($key);
- $components[$type == CRYPT_RSA_PUBLIC_FORMAT_PKCS1 ? 'publicExponent' : 'privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
-
- return $components;
- }
- if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_INTEGER) {
- return false;
- }
- $length = $this->_decodeLength($key);
- $components['modulus'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['publicExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['privateExponent'] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['exponents'] = array(1 => new Math_BigInteger($this->_string_shift($key, $length), 256));
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($key, $length), 256));
-
- if (!empty($key)) {
- if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
- return false;
- }
- $this->_decodeLength($key);
- while (!empty($key)) {
- if (ord($this->_string_shift($key)) != CRYPT_RSA_ASN1_SEQUENCE) {
- return false;
- }
- $this->_decodeLength($key);
- $key = substr($key, 1);
- $length = $this->_decodeLength($key);
- $components['primes'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['exponents'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- $this->_string_shift($key);
- $length = $this->_decodeLength($key);
- $components['coefficients'][] = new Math_BigInteger($this->_string_shift($key, $length), 256);
- }
- }
-
- return $components;
- case CRYPT_RSA_PUBLIC_FORMAT_OPENSSH:
- $parts = explode(' ', $key, 3);
-
- $key = isset($parts[1]) ? base64_decode($parts[1]) : false;
- if ($key === false) {
- return false;
- }
-
- $comment = isset($parts[2]) ? $parts[2] : false;
-
- $cleanup = substr($key, 0, 11) == "\0\0\0\7ssh-rsa";
-
- if (strlen($key) <= 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($key, 4)));
- $publicExponent = new Math_BigInteger($this->_string_shift($key, $length), -256);
- if (strlen($key) <= 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($key, 4)));
- $modulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
-
- if ($cleanup && strlen($key)) {
- if (strlen($key) <= 4) {
- return false;
- }
- extract(unpack('Nlength', $this->_string_shift($key, 4)));
- $realModulus = new Math_BigInteger($this->_string_shift($key, $length), -256);
- return strlen($key) ? false : array(
- 'modulus' => $realModulus,
- 'publicExponent' => $modulus,
- 'comment' => $comment
- );
- } else {
- return strlen($key) ? false : array(
- 'modulus' => $modulus,
- 'publicExponent' => $publicExponent,
- 'comment' => $comment
- );
- }
- // http://www.w3.org/TR/xmldsig-core/#sec-RSAKeyValue
- // http://en.wikipedia.org/wiki/XML_Signature
- case CRYPT_RSA_PRIVATE_FORMAT_XML:
- case CRYPT_RSA_PUBLIC_FORMAT_XML:
- $this->components = array();
-
- $xml = xml_parser_create('UTF-8');
- xml_set_object($xml, $this);
- xml_set_element_handler($xml, '_start_element_handler', '_stop_element_handler');
- xml_set_character_data_handler($xml, '_data_handler');
- // add to account for "dangling" tags like ... that are sometimes added
- if (!xml_parse($xml, '' . $key . '')) {
- return false;
- }
-
- return isset($this->components['modulus']) && isset($this->components['publicExponent']) ? $this->components : false;
- // from PuTTY's SSHPUBK.C
- case CRYPT_RSA_PRIVATE_FORMAT_PUTTY:
- $components = array();
- $key = preg_split('#\r\n|\r|\n#', $key);
- $type = trim(preg_replace('#PuTTY-User-Key-File-2: (.+)#', '$1', $key[0]));
- if ($type != 'ssh-rsa') {
- return false;
- }
- $encryption = trim(preg_replace('#Encryption: (.+)#', '$1', $key[1]));
- $comment = trim(preg_replace('#Comment: (.+)#', '$1', $key[2]));
-
- $publicLength = trim(preg_replace('#Public-Lines: (\d+)#', '$1', $key[3]));
- $public = base64_decode(implode('', array_map('trim', array_slice($key, 4, $publicLength))));
- $public = substr($public, 11);
- extract(unpack('Nlength', $this->_string_shift($public, 4)));
- $components['publicExponent'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
- extract(unpack('Nlength', $this->_string_shift($public, 4)));
- $components['modulus'] = new Math_BigInteger($this->_string_shift($public, $length), -256);
-
- $privateLength = trim(preg_replace('#Private-Lines: (\d+)#', '$1', $key[$publicLength + 4]));
- $private = base64_decode(implode('', array_map('trim', array_slice($key, $publicLength + 5, $privateLength))));
-
- switch ($encryption) {
- case 'aes256-cbc':
- if (!class_exists('Crypt_AES')) {
- require_once('Crypt/AES.php');
- }
- $symkey = '';
- $sequence = 0;
- while (strlen($symkey) < 32) {
- $temp = pack('Na*', $sequence++, $this->password);
- $symkey.= pack('H*', sha1($temp));
- }
- $symkey = substr($symkey, 0, 32);
- $crypto = new Crypt_AES();
- }
-
- if ($encryption != 'none') {
- $crypto->setKey($symkey);
- $crypto->disablePadding();
- $private = $crypto->decrypt($private);
- if ($private === false) {
- return false;
- }
- }
-
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['privateExponent'] = new Math_BigInteger($this->_string_shift($private, $length), -256);
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['primes'] = array(1 => new Math_BigInteger($this->_string_shift($private, $length), -256));
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['primes'][] = new Math_BigInteger($this->_string_shift($private, $length), -256);
-
- $temp = $components['primes'][1]->subtract($this->one);
- $components['exponents'] = array(1 => $components['publicExponent']->modInverse($temp));
- $temp = $components['primes'][2]->subtract($this->one);
- $components['exponents'][] = $components['publicExponent']->modInverse($temp);
-
- extract(unpack('Nlength', $this->_string_shift($private, 4)));
- if (strlen($private) < $length) {
- return false;
- }
- $components['coefficients'] = array(2 => new Math_BigInteger($this->_string_shift($private, $length), -256));
-
- return $components;
- }
- }
-
- /**
- * Returns the key size
- *
- * More specifically, this returns the size of the modulo in bits.
- *
- * @access public
- * @return Integer
- */
- function getSize()
- {
- return !isset($this->modulus) ? 0 : strlen($this->modulus->toBits());
- }
-
- /**
- * Start Element Handler
- *
- * Called by xml_set_element_handler()
- *
- * @access private
- * @param Resource $parser
- * @param String $name
- * @param Array $attribs
- */
- function _start_element_handler($parser, $name, $attribs)
- {
- //$name = strtoupper($name);
- switch ($name) {
- case 'MODULUS':
- $this->current = &$this->components['modulus'];
- break;
- case 'EXPONENT':
- $this->current = &$this->components['publicExponent'];
- break;
- case 'P':
- $this->current = &$this->components['primes'][1];
- break;
- case 'Q':
- $this->current = &$this->components['primes'][2];
- break;
- case 'DP':
- $this->current = &$this->components['exponents'][1];
- break;
- case 'DQ':
- $this->current = &$this->components['exponents'][2];
- break;
- case 'INVERSEQ':
- $this->current = &$this->components['coefficients'][2];
- break;
- case 'D':
- $this->current = &$this->components['privateExponent'];
- }
- $this->current = '';
- }
-
- /**
- * Stop Element Handler
- *
- * Called by xml_set_element_handler()
- *
- * @access private
- * @param Resource $parser
- * @param String $name
- */
- function _stop_element_handler($parser, $name)
- {
- //$name = strtoupper($name);
- if ($name == 'RSAKEYVALUE') {
- return;
- }
- $this->current = new Math_BigInteger(base64_decode($this->current), 256);
- unset($this->current);
- }
-
- /**
- * Data Handler
- *
- * Called by xml_set_character_data_handler()
- *
- * @access private
- * @param Resource $parser
- * @param String $data
- */
- function _data_handler($parser, $data)
- {
- if (!isset($this->current) || is_object($this->current)) {
- return;
- }
- $this->current.= trim($data);
- }
-
- /**
- * Loads a public or private key
- *
- * Returns true on success and false on failure (ie. an incorrect password was provided or the key was malformed)
- *
- * @access public
- * @param String $key
- * @param Integer $type optional
- */
- function loadKey($key, $type = false)
- {
- if ($type === false) {
- $types = array(
- CRYPT_RSA_PUBLIC_FORMAT_RAW,
- CRYPT_RSA_PRIVATE_FORMAT_PKCS1,
- CRYPT_RSA_PRIVATE_FORMAT_XML,
- CRYPT_RSA_PRIVATE_FORMAT_PUTTY,
- CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
- );
- foreach ($types as $type) {
- $components = $this->_parseKey($key, $type);
- if ($components !== false) {
- break;
- }
- }
-
- } else {
- $components = $this->_parseKey($key, $type);
- }
-
- if ($components === false) {
- return false;
- }
-
- if (isset($components['comment']) && $components['comment'] !== false) {
- $this->comment = $components['comment'];
- }
- $this->modulus = $components['modulus'];
- $this->k = strlen($this->modulus->toBytes());
- $this->exponent = isset($components['privateExponent']) ? $components['privateExponent'] : $components['publicExponent'];
- if (isset($components['primes'])) {
- $this->primes = $components['primes'];
- $this->exponents = $components['exponents'];
- $this->coefficients = $components['coefficients'];
- $this->publicExponent = $components['publicExponent'];
- } else {
- $this->primes = array();
- $this->exponents = array();
- $this->coefficients = array();
- $this->publicExponent = false;
- }
-
- return true;
- }
-
- /**
- * Sets the password
- *
- * Private keys can be encrypted with a password. To unset the password, pass in the empty string or false.
- * Or rather, pass in $password such that empty($password) && !is_string($password) is true.
- *
- * @see createKey()
- * @see loadKey()
- * @access public
- * @param String $password
- */
- function setPassword($password = false)
- {
- $this->password = $password;
- }
-
- /**
- * Defines the public key
- *
- * Some private key formats define the public exponent and some don't. Those that don't define it are problematic when
- * used in certain contexts. For example, in SSH-2, RSA authentication works by sending the public key along with a
- * message signed by the private key to the server. The SSH-2 server looks the public key up in an index of public keys
- * and if it's present then proceeds to verify the signature. Problem is, if your private key doesn't include the public
- * exponent this won't work unless you manually add the public exponent.
- *
- * Do note that when a new key is loaded the index will be cleared.
- *
- * Returns true on success, false on failure
- *
- * @see getPublicKey()
- * @access public
- * @param String $key optional
- * @param Integer $type optional
- * @return Boolean
- */
- function setPublicKey($key = false, $type = false)
- {
- if ($key === false && !empty($this->modulus)) {
- $this->publicExponent = $this->exponent;
- return true;
- }
-
- if ($type === false) {
- $types = array(
- CRYPT_RSA_PUBLIC_FORMAT_RAW,
- CRYPT_RSA_PUBLIC_FORMAT_PKCS1,
- CRYPT_RSA_PUBLIC_FORMAT_XML,
- CRYPT_RSA_PUBLIC_FORMAT_OPENSSH
- );
- foreach ($types as $type) {
- $components = $this->_parseKey($key, $type);
- if ($components !== false) {
- break;
- }
- }
- } else {
- $components = $this->_parseKey($key, $type);
- }
-
- if ($components === false) {
- return false;
- }
-
- if (empty($this->modulus) || !$this->modulus->equals($components['modulus'])) {
- $this->modulus = $components['modulus'];
- $this->exponent = $this->publicExponent = $components['publicExponent'];
- return true;
- }
-
- $this->publicExponent = $components['publicExponent'];
-
- return true;
- }
-
- /**
- * Returns the public key
- *
- * The public key is only returned under two circumstances - if the private key had the public key embedded within it
- * or if the public key was set via setPublicKey(). If the currently loaded key is supposed to be the public key this
- * function won't return it since this library, for the most part, doesn't distinguish between public and private keys.
- *
- * @see getPublicKey()
- * @access public
- * @param String $key
- * @param Integer $type optional
- */
- function getPublicKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
- {
- if (empty($this->modulus) || empty($this->publicExponent)) {
- return false;
- }
-
- $oldFormat = $this->publicKeyFormat;
- $this->publicKeyFormat = $type;
- $temp = $this->_convertPublicKey($this->modulus, $this->publicExponent);
- $this->publicKeyFormat = $oldFormat;
- return $temp;
- }
-
- /**
- * Returns the private key
- *
- * The private key is only returned if the currently loaded key contains the constituent prime numbers.
- *
- * @see getPublicKey()
- * @access public
- * @param String $key
- * @param Integer $type optional
- */
- function getPrivateKey($type = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
- {
- if (empty($this->primes)) {
- return false;
- }
-
- $oldFormat = $this->privateKeyFormat;
- $this->privateKeyFormat = $type;
- $temp = $this->_convertPrivateKey($this->modulus, $this->publicExponent, $this->exponent, $this->primes, $this->exponents, $this->coefficients);
- $this->privateKeyFormat = $oldFormat;
- return $temp;
- }
-
- /**
- * Returns a minimalistic private key
- *
- * Returns the private key without the prime number constituants. Structurally identical to a public key that
- * hasn't been set as the public key
- *
- * @see getPrivateKey()
- * @access private
- * @param String $key
- * @param Integer $type optional
- */
- function _getPrivatePublicKey($mode = CRYPT_RSA_PUBLIC_FORMAT_PKCS1)
- {
- if (empty($this->modulus) || empty($this->exponent)) {
- return false;
- }
-
- $oldFormat = $this->publicKeyFormat;
- $this->publicKeyFormat = $mode;
- $temp = $this->_convertPublicKey($this->modulus, $this->exponent);
- $this->publicKeyFormat = $oldFormat;
- return $temp;
- }
-
- /**
- * __toString() magic method
- *
- * @access public
- */
- function __toString()
- {
- $key = $this->getPrivateKey($this->privateKeyFormat);
- if ($key !== false) {
- return $key;
- }
- $key = $this->_getPrivatePublicKey($this->publicKeyFormat);
- return $key !== false ? $key : '';
- }
-
- /**
- * Generates the smallest and largest numbers requiring $bits bits
- *
- * @access private
- * @param Integer $bits
- * @return Array
- */
- function _generateMinMax($bits)
- {
- $bytes = $bits >> 3;
- $min = str_repeat(chr(0), $bytes);
- $max = str_repeat(chr(0xFF), $bytes);
- $msb = $bits & 7;
- if ($msb) {
- $min = chr(1 << ($msb - 1)) . $min;
- $max = chr((1 << $msb) - 1) . $max;
- } else {
- $min[0] = chr(0x80);
- }
-
- return array(
- 'min' => new Math_BigInteger($min, 256),
- 'max' => new Math_BigInteger($max, 256)
- );
- }
-
- /**
- * DER-decode the length
- *
- * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
- * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
- *
- * @access private
- * @param String $string
- * @return Integer
- */
- function _decodeLength(&$string)
- {
- $length = ord($this->_string_shift($string));
- if ( $length & 0x80 ) { // definite length, long form
- $length&= 0x7F;
- $temp = $this->_string_shift($string, $length);
- list(, $length) = unpack('N', substr(str_pad($temp, 4, chr(0), STR_PAD_LEFT), -4));
- }
- return $length;
- }
-
- /**
- * DER-encode the length
- *
- * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See
- * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information.
- *
- * @access private
- * @param Integer $length
- * @return String
- */
- function _encodeLength($length)
- {
- if ($length <= 0x7F) {
- return chr($length);
- }
-
- $temp = ltrim(pack('N', $length), chr(0));
- return pack('Ca*', 0x80 | strlen($temp), $temp);
- }
-
- /**
- * String Shift
- *
- * Inspired by array_shift
- *
- * @param String $string
- * @param optional Integer $index
- * @return String
- * @access private
- */
- function _string_shift(&$string, $index = 1)
- {
- $substr = substr($string, 0, $index);
- $string = substr($string, $index);
- return $substr;
- }
-
- /**
- * Determines the private key format
- *
- * @see createKey()
- * @access public
- * @param Integer $format
- */
- function setPrivateKeyFormat($format)
- {
- $this->privateKeyFormat = $format;
- }
-
- /**
- * Determines the public key format
- *
- * @see createKey()
- * @access public
- * @param Integer $format
- */
- function setPublicKeyFormat($format)
- {
- $this->publicKeyFormat = $format;
- }
-
- /**
- * Determines which hashing function should be used
- *
- * Used with signature production / verification and (if the encryption mode is CRYPT_RSA_ENCRYPTION_OAEP) encryption and
- * decryption. If $hash isn't supported, sha1 is used.
- *
- * @access public
- * @param String $hash
- */
- function setHash($hash)
- {
- // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
- switch ($hash) {
- case 'md2':
- case 'md5':
- case 'sha1':
- case 'sha256':
- case 'sha384':
- case 'sha512':
- $this->hash = new Crypt_Hash($hash);
- $this->hashName = $hash;
- break;
- default:
- $this->hash = new Crypt_Hash('sha1');
- $this->hashName = 'sha1';
- }
- $this->hLen = $this->hash->getLength();
- }
-
- /**
- * Determines which hashing function should be used for the mask generation function
- *
- * The mask generation function is used by CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_SIGNATURE_PSS and although it's
- * best if Hash and MGFHash are set to the same thing this is not a requirement.
- *
- * @access public
- * @param String $hash
- */
- function setMGFHash($hash)
- {
- // Crypt_Hash supports algorithms that PKCS#1 doesn't support. md5-96 and sha1-96, for example.
- switch ($hash) {
- case 'md2':
- case 'md5':
- case 'sha1':
- case 'sha256':
- case 'sha384':
- case 'sha512':
- $this->mgfHash = new Crypt_Hash($hash);
- break;
- default:
- $this->mgfHash = new Crypt_Hash('sha1');
- }
- $this->mgfHLen = $this->mgfHash->getLength();
- }
-
- /**
- * Determines the salt length
- *
- * To quote from {@link http://tools.ietf.org/html/rfc3447#page-38 RFC3447#page-38}:
- *
- * Typical salt lengths in octets are hLen (the length of the output
- * of the hash function Hash) and 0.
- *
- * @access public
- * @param Integer $format
- */
- function setSaltLength($sLen)
- {
- $this->sLen = $sLen;
- }
-
- /**
- * Integer-to-Octet-String primitive
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-4.1 RFC3447#section-4.1}.
- *
- * @access private
- * @param Math_BigInteger $x
- * @param Integer $xLen
- * @return String
- */
- function _i2osp($x, $xLen)
- {
- $x = $x->toBytes();
- if (strlen($x) > $xLen) {
- user_error('Integer too large');
- return false;
- }
- return str_pad($x, $xLen, chr(0), STR_PAD_LEFT);
- }
-
- /**
- * Octet-String-to-Integer primitive
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-4.2 RFC3447#section-4.2}.
- *
- * @access private
- * @param String $x
- * @return Math_BigInteger
- */
- function _os2ip($x)
- {
- return new Math_BigInteger($x, 256);
- }
-
- /**
- * Exponentiate with or without Chinese Remainder Theorem
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.2}.
- *
- * @access private
- * @param Math_BigInteger $x
- * @return Math_BigInteger
- */
- function _exponentiate($x)
- {
- if (empty($this->primes) || empty($this->coefficients) || empty($this->exponents)) {
- return $x->modPow($this->exponent, $this->modulus);
- }
-
- $num_primes = count($this->primes);
-
- if (defined('CRYPT_RSA_DISABLE_BLINDING')) {
- $m_i = array(
- 1 => $x->modPow($this->exponents[1], $this->primes[1]),
- 2 => $x->modPow($this->exponents[2], $this->primes[2])
- );
- $h = $m_i[1]->subtract($m_i[2]);
- $h = $h->multiply($this->coefficients[2]);
- list(, $h) = $h->divide($this->primes[1]);
- $m = $m_i[2]->add($h->multiply($this->primes[2]));
-
- $r = $this->primes[1];
- for ($i = 3; $i <= $num_primes; $i++) {
- $m_i = $x->modPow($this->exponents[$i], $this->primes[$i]);
-
- $r = $r->multiply($this->primes[$i - 1]);
-
- $h = $m_i->subtract($m);
- $h = $h->multiply($this->coefficients[$i]);
- list(, $h) = $h->divide($this->primes[$i]);
-
- $m = $m->add($r->multiply($h));
- }
- } else {
- $smallest = $this->primes[1];
- for ($i = 2; $i <= $num_primes; $i++) {
- if ($smallest->compare($this->primes[$i]) > 0) {
- $smallest = $this->primes[$i];
- }
- }
-
- $one = new Math_BigInteger(1);
-
- $r = $one->random($one, $smallest->subtract($one));
-
- $m_i = array(
- 1 => $this->_blind($x, $r, 1),
- 2 => $this->_blind($x, $r, 2)
- );
- $h = $m_i[1]->subtract($m_i[2]);
- $h = $h->multiply($this->coefficients[2]);
- list(, $h) = $h->divide($this->primes[1]);
- $m = $m_i[2]->add($h->multiply($this->primes[2]));
-
- $r = $this->primes[1];
- for ($i = 3; $i <= $num_primes; $i++) {
- $m_i = $this->_blind($x, $r, $i);
-
- $r = $r->multiply($this->primes[$i - 1]);
-
- $h = $m_i->subtract($m);
- $h = $h->multiply($this->coefficients[$i]);
- list(, $h) = $h->divide($this->primes[$i]);
-
- $m = $m->add($r->multiply($h));
- }
- }
-
- return $m;
- }
-
- /**
- * Performs RSA Blinding
- *
- * Protects against timing attacks by employing RSA Blinding.
- * Returns $x->modPow($this->exponents[$i], $this->primes[$i])
- *
- * @access private
- * @param Math_BigInteger $x
- * @param Math_BigInteger $r
- * @param Integer $i
- * @return Math_BigInteger
- */
- function _blind($x, $r, $i)
- {
- $x = $x->multiply($r->modPow($this->publicExponent, $this->primes[$i]));
- $x = $x->modPow($this->exponents[$i], $this->primes[$i]);
-
- $r = $r->modInverse($this->primes[$i]);
- $x = $x->multiply($r);
- list(, $x) = $x->divide($this->primes[$i]);
-
- return $x;
- }
-
- /**
- * Performs blinded RSA equality testing
- *
- * Protects against a particular type of timing attack described.
- *
- * See {@link http://codahale.com/a-lesson-in-timing-attacks/ A Lesson In Timing Attacks (or, Don't use MessageDigest.isEquals)}
- *
- * Thanks for the heads up singpolyma!
- *
- * @access private
- * @param String $x
- * @param String $y
- * @return Boolean
- */
- function _equals($x, $y)
- {
- if (strlen($x) != strlen($y)) {
- return false;
- }
-
- $result = 0;
- for ($i = 0; $i < strlen($x); $i++) {
- $result |= ord($x[$i]) ^ ord($y[$i]);
- }
-
- return $result == 0;
- }
-
- /**
- * RSAEP
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.1 RFC3447#section-5.1.1}.
- *
- * @access private
- * @param Math_BigInteger $m
- * @return Math_BigInteger
- */
- function _rsaep($m)
- {
- if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
- user_error('Message representative out of range');
- return false;
- }
- return $this->_exponentiate($m);
- }
-
- /**
- * RSADP
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.1.2 RFC3447#section-5.1.2}.
- *
- * @access private
- * @param Math_BigInteger $c
- * @return Math_BigInteger
- */
- function _rsadp($c)
- {
- if ($c->compare($this->zero) < 0 || $c->compare($this->modulus) > 0) {
- user_error('Ciphertext representative out of range');
- return false;
- }
- return $this->_exponentiate($c);
- }
-
- /**
- * RSASP1
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.1 RFC3447#section-5.2.1}.
- *
- * @access private
- * @param Math_BigInteger $m
- * @return Math_BigInteger
- */
- function _rsasp1($m)
- {
- if ($m->compare($this->zero) < 0 || $m->compare($this->modulus) > 0) {
- user_error('Message representative out of range');
- return false;
- }
- return $this->_exponentiate($m);
- }
-
- /**
- * RSAVP1
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-5.2.2 RFC3447#section-5.2.2}.
- *
- * @access private
- * @param Math_BigInteger $s
- * @return Math_BigInteger
- */
- function _rsavp1($s)
- {
- if ($s->compare($this->zero) < 0 || $s->compare($this->modulus) > 0) {
- user_error('Signature representative out of range');
- return false;
- }
- return $this->_exponentiate($s);
- }
-
- /**
- * MGF1
- *
- * See {@link http://tools.ietf.org/html/rfc3447#appendix-B.2.1 RFC3447#appendix-B.2.1}.
- *
- * @access private
- * @param String $mgfSeed
- * @param Integer $mgfLen
- * @return String
- */
- function _mgf1($mgfSeed, $maskLen)
- {
- // if $maskLen would yield strings larger than 4GB, PKCS#1 suggests a "Mask too long" error be output.
-
- $t = '';
- $count = ceil($maskLen / $this->mgfHLen);
- for ($i = 0; $i < $count; $i++) {
- $c = pack('N', $i);
- $t.= $this->mgfHash->hash($mgfSeed . $c);
- }
-
- return substr($t, 0, $maskLen);
- }
-
- /**
- * RSAES-OAEP-ENCRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.1 RFC3447#section-7.1.1} and
- * {http://en.wikipedia.org/wiki/Optimal_Asymmetric_Encryption_Padding OAES}.
- *
- * @access private
- * @param String $m
- * @param String $l
- * @return String
- */
- function _rsaes_oaep_encrypt($m, $l = '')
- {
- $mLen = strlen($m);
-
- // Length checking
-
- // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- if ($mLen > $this->k - 2 * $this->hLen - 2) {
- user_error('Message too long');
- return false;
- }
-
- // EME-OAEP encoding
-
- $lHash = $this->hash->hash($l);
- $ps = str_repeat(chr(0), $this->k - $mLen - 2 * $this->hLen - 2);
- $db = $lHash . $ps . chr(1) . $m;
- $seed = crypt_random_string($this->hLen);
- $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
- $maskedDB = $db ^ $dbMask;
- $seedMask = $this->_mgf1($maskedDB, $this->hLen);
- $maskedSeed = $seed ^ $seedMask;
- $em = chr(0) . $maskedSeed . $maskedDB;
-
- // RSA encryption
-
- $m = $this->_os2ip($em);
- $c = $this->_rsaep($m);
- $c = $this->_i2osp($c, $this->k);
-
- // Output the ciphertext C
-
- return $c;
- }
-
- /**
- * RSAES-OAEP-DECRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.1.2 RFC3447#section-7.1.2}. The fact that the error
- * messages aren't distinguishable from one another hinders debugging, but, to quote from RFC3447#section-7.1.2:
- *
- * Note. Care must be taken to ensure that an opponent cannot
- * distinguish the different error conditions in Step 3.g, whether by
- * error message or timing, or, more generally, learn partial
- * information about the encoded message EM. Otherwise an opponent may
- * be able to obtain useful information about the decryption of the
- * ciphertext C, leading to a chosen-ciphertext attack such as the one
- * observed by Manger [36].
- *
- * As for $l... to quote from {@link http://tools.ietf.org/html/rfc3447#page-17 RFC3447#page-17}:
- *
- * Both the encryption and the decryption operations of RSAES-OAEP take
- * the value of a label L as input. In this version of PKCS #1, L is
- * the empty string; other uses of the label are outside the scope of
- * this document.
- *
- * @access private
- * @param String $c
- * @param String $l
- * @return String
- */
- function _rsaes_oaep_decrypt($c, $l = '')
- {
- // Length checking
-
- // if $l is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- if (strlen($c) != $this->k || $this->k < 2 * $this->hLen + 2) {
- user_error('Decryption error');
- return false;
- }
-
- // RSA decryption
-
- $c = $this->_os2ip($c);
- $m = $this->_rsadp($c);
- if ($m === false) {
- user_error('Decryption error');
- return false;
- }
- $em = $this->_i2osp($m, $this->k);
-
- // EME-OAEP decoding
-
- $lHash = $this->hash->hash($l);
- $y = ord($em[0]);
- $maskedSeed = substr($em, 1, $this->hLen);
- $maskedDB = substr($em, $this->hLen + 1);
- $seedMask = $this->_mgf1($maskedDB, $this->hLen);
- $seed = $maskedSeed ^ $seedMask;
- $dbMask = $this->_mgf1($seed, $this->k - $this->hLen - 1);
- $db = $maskedDB ^ $dbMask;
- $lHash2 = substr($db, 0, $this->hLen);
- $m = substr($db, $this->hLen);
- if ($lHash != $lHash2) {
- user_error('Decryption error');
- return false;
- }
- $m = ltrim($m, chr(0));
- if (ord($m[0]) != 1) {
- user_error('Decryption error');
- return false;
- }
-
- // Output the message M
-
- return substr($m, 1);
- }
-
- /**
- * RSAES-PKCS1-V1_5-ENCRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.1 RFC3447#section-7.2.1}.
- *
- * @access private
- * @param String $m
- * @return String
- */
- function _rsaes_pkcs1_v1_5_encrypt($m)
- {
- $mLen = strlen($m);
-
- // Length checking
-
- if ($mLen > $this->k - 11) {
- user_error('Message too long');
- return false;
- }
-
- // EME-PKCS1-v1_5 encoding
-
- $psLen = $this->k - $mLen - 3;
- $ps = '';
- while (strlen($ps) != $psLen) {
- $temp = crypt_random_string($psLen - strlen($ps));
- $temp = str_replace("\x00", '', $temp);
- $ps.= $temp;
- }
- $type = 2;
- // see the comments of _rsaes_pkcs1_v1_5_decrypt() to understand why this is being done
- if (defined('CRYPT_RSA_PKCS15_COMPAT') && (!isset($this->publicExponent) || $this->exponent !== $this->publicExponent)) {
- $type = 1;
- // "The padding string PS shall consist of k-3-||D|| octets. ... for block type 01, they shall have value FF"
- $ps = str_repeat("\xFF", $psLen);
- }
- $em = chr(0) . chr($type) . $ps . chr(0) . $m;
-
- // RSA encryption
- $m = $this->_os2ip($em);
- $c = $this->_rsaep($m);
- $c = $this->_i2osp($c, $this->k);
-
- // Output the ciphertext C
-
- return $c;
- }
-
- /**
- * RSAES-PKCS1-V1_5-DECRYPT
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-7.2.2 RFC3447#section-7.2.2}.
- *
- * For compatability purposes, this function departs slightly from the description given in RFC3447.
- * The reason being that RFC2313#section-8.1 (PKCS#1 v1.5) states that ciphertext's encrypted by the
- * private key should have the second byte set to either 0 or 1 and that ciphertext's encrypted by the
- * public key should have the second byte set to 2. In RFC3447 (PKCS#1 v2.1), the second byte is supposed
- * to be 2 regardless of which key is used. For compatability purposes, we'll just check to make sure the
- * second byte is 2 or less. If it is, we'll accept the decrypted string as valid.
- *
- * As a consequence of this, a private key encrypted ciphertext produced with Crypt_RSA may not decrypt
- * with a strictly PKCS#1 v1.5 compliant RSA implementation. Public key encrypted ciphertext's should but
- * not private key encrypted ciphertext's.
- *
- * @access private
- * @param String $c
- * @return String
- */
- function _rsaes_pkcs1_v1_5_decrypt($c)
- {
- // Length checking
-
- if (strlen($c) != $this->k) { // or if k < 11
- user_error('Decryption error');
- return false;
- }
-
- // RSA decryption
-
- $c = $this->_os2ip($c);
- $m = $this->_rsadp($c);
-
- if ($m === false) {
- user_error('Decryption error');
- return false;
- }
- $em = $this->_i2osp($m, $this->k);
-
- // EME-PKCS1-v1_5 decoding
-
- if (ord($em[0]) != 0 || ord($em[1]) > 2) {
- user_error('Decryption error');
- return false;
- }
-
- $ps = substr($em, 2, strpos($em, chr(0), 2) - 2);
- $m = substr($em, strlen($ps) + 3);
-
- if (strlen($ps) < 8) {
- user_error('Decryption error');
- return false;
- }
-
- // Output M
-
- return $m;
- }
-
- /**
- * EMSA-PSS-ENCODE
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.1 RFC3447#section-9.1.1}.
- *
- * @access private
- * @param String $m
- * @param Integer $emBits
- */
- function _emsa_pss_encode($m, $emBits)
- {
- // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8)
- $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
-
- $mHash = $this->hash->hash($m);
- if ($emLen < $this->hLen + $sLen + 2) {
- user_error('Encoding error');
- return false;
- }
-
- $salt = crypt_random_string($sLen);
- $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
- $h = $this->hash->hash($m2);
- $ps = str_repeat(chr(0), $emLen - $sLen - $this->hLen - 2);
- $db = $ps . chr(1) . $salt;
- $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
- $maskedDB = $db ^ $dbMask;
- $maskedDB[0] = ~chr(0xFF << ($emBits & 7)) & $maskedDB[0];
- $em = $maskedDB . $h . chr(0xBC);
-
- return $em;
- }
-
- /**
- * EMSA-PSS-VERIFY
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-9.1.2 RFC3447#section-9.1.2}.
- *
- * @access private
- * @param String $m
- * @param String $em
- * @param Integer $emBits
- * @return String
- */
- function _emsa_pss_verify($m, $em, $emBits)
- {
- // if $m is larger than two million terrabytes and you're using sha1, PKCS#1 suggests a "Label too long" error
- // be output.
-
- $emLen = ($emBits + 1) >> 3; // ie. ceil($emBits / 8);
- $sLen = $this->sLen == false ? $this->hLen : $this->sLen;
-
- $mHash = $this->hash->hash($m);
- if ($emLen < $this->hLen + $sLen + 2) {
- return false;
- }
-
- if ($em[strlen($em) - 1] != chr(0xBC)) {
- return false;
- }
-
- $maskedDB = substr($em, 0, -$this->hLen - 1);
- $h = substr($em, -$this->hLen - 1, $this->hLen);
- $temp = chr(0xFF << ($emBits & 7));
- if ((~$maskedDB[0] & $temp) != $temp) {
- return false;
- }
- $dbMask = $this->_mgf1($h, $emLen - $this->hLen - 1);
- $db = $maskedDB ^ $dbMask;
- $db[0] = ~chr(0xFF << ($emBits & 7)) & $db[0];
- $temp = $emLen - $this->hLen - $sLen - 2;
- if (substr($db, 0, $temp) != str_repeat(chr(0), $temp) || ord($db[$temp]) != 1) {
- return false;
- }
- $salt = substr($db, $temp + 1); // should be $sLen long
- $m2 = "\0\0\0\0\0\0\0\0" . $mHash . $salt;
- $h2 = $this->hash->hash($m2);
- return $this->_equals($h, $h2);
- }
-
- /**
- * RSASSA-PSS-SIGN
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.1 RFC3447#section-8.1.1}.
- *
- * @access private
- * @param String $m
- * @return String
- */
- function _rsassa_pss_sign($m)
- {
- // EMSA-PSS encoding
-
- $em = $this->_emsa_pss_encode($m, 8 * $this->k - 1);
-
- // RSA signature
-
- $m = $this->_os2ip($em);
- $s = $this->_rsasp1($m);
- $s = $this->_i2osp($s, $this->k);
-
- // Output the signature S
-
- return $s;
- }
-
- /**
- * RSASSA-PSS-VERIFY
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.1.2 RFC3447#section-8.1.2}.
- *
- * @access private
- * @param String $m
- * @param String $s
- * @return String
- */
- function _rsassa_pss_verify($m, $s)
- {
- // Length checking
-
- if (strlen($s) != $this->k) {
- user_error('Invalid signature');
- return false;
- }
-
- // RSA verification
-
- $modBits = 8 * $this->k;
-
- $s2 = $this->_os2ip($s);
- $m2 = $this->_rsavp1($s2);
- if ($m2 === false) {
- user_error('Invalid signature');
- return false;
- }
- $em = $this->_i2osp($m2, $modBits >> 3);
- if ($em === false) {
- user_error('Invalid signature');
- return false;
- }
-
- // EMSA-PSS verification
-
- return $this->_emsa_pss_verify($m, $em, $modBits - 1);
- }
-
- /**
- * EMSA-PKCS1-V1_5-ENCODE
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-9.2 RFC3447#section-9.2}.
- *
- * @access private
- * @param String $m
- * @param Integer $emLen
- * @return String
- */
- function _emsa_pkcs1_v1_5_encode($m, $emLen)
- {
- $h = $this->hash->hash($m);
- if ($h === false) {
- return false;
- }
-
- // see http://tools.ietf.org/html/rfc3447#page-43
- switch ($this->hashName) {
- case 'md2':
- $t = pack('H*', '3020300c06082a864886f70d020205000410');
- break;
- case 'md5':
- $t = pack('H*', '3020300c06082a864886f70d020505000410');
- break;
- case 'sha1':
- $t = pack('H*', '3021300906052b0e03021a05000414');
- break;
- case 'sha256':
- $t = pack('H*', '3031300d060960864801650304020105000420');
- break;
- case 'sha384':
- $t = pack('H*', '3041300d060960864801650304020205000430');
- break;
- case 'sha512':
- $t = pack('H*', '3051300d060960864801650304020305000440');
- }
- $t.= $h;
- $tLen = strlen($t);
-
- if ($emLen < $tLen + 11) {
- user_error('Intended encoded message length too short');
- return false;
- }
-
- $ps = str_repeat(chr(0xFF), $emLen - $tLen - 3);
-
- $em = "\0\1$ps\0$t";
-
- return $em;
- }
-
- /**
- * RSASSA-PKCS1-V1_5-SIGN
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.1 RFC3447#section-8.2.1}.
- *
- * @access private
- * @param String $m
- * @return String
- */
- function _rsassa_pkcs1_v1_5_sign($m)
- {
- // EMSA-PKCS1-v1_5 encoding
-
- $em = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
- if ($em === false) {
- user_error('RSA modulus too short');
- return false;
- }
-
- // RSA signature
-
- $m = $this->_os2ip($em);
- $s = $this->_rsasp1($m);
- $s = $this->_i2osp($s, $this->k);
-
- // Output the signature S
-
- return $s;
- }
-
- /**
- * RSASSA-PKCS1-V1_5-VERIFY
- *
- * See {@link http://tools.ietf.org/html/rfc3447#section-8.2.2 RFC3447#section-8.2.2}.
- *
- * @access private
- * @param String $m
- * @return String
- */
- function _rsassa_pkcs1_v1_5_verify($m, $s)
- {
- // Length checking
-
- if (strlen($s) != $this->k) {
- user_error('Invalid signature');
- return false;
- }
-
- // RSA verification
-
- $s = $this->_os2ip($s);
- $m2 = $this->_rsavp1($s);
- if ($m2 === false) {
- user_error('Invalid signature');
- return false;
- }
- $em = $this->_i2osp($m2, $this->k);
- if ($em === false) {
- user_error('Invalid signature');
- return false;
- }
-
- // EMSA-PKCS1-v1_5 encoding
-
- $em2 = $this->_emsa_pkcs1_v1_5_encode($m, $this->k);
- if ($em2 === false) {
- user_error('RSA modulus too short');
- return false;
- }
-
- // Compare
- return $this->_equals($em, $em2);
- }
-
- /**
- * Set Encryption Mode
- *
- * Valid values include CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1.
- *
- * @access public
- * @param Integer $mode
- */
- function setEncryptionMode($mode)
- {
- $this->encryptionMode = $mode;
- }
-
- /**
- * Set Signature Mode
- *
- * Valid values include CRYPT_RSA_SIGNATURE_PSS and CRYPT_RSA_SIGNATURE_PKCS1
- *
- * @access public
- * @param Integer $mode
- */
- function setSignatureMode($mode)
- {
- $this->signatureMode = $mode;
- }
-
- /**
- * Set public key comment.
- *
- * @access public
- * @param String $comment
- */
- function setComment($comment)
- {
- $this->comment = $comment;
- }
-
- /**
- * Get public key comment.
- *
- * @access public
- * @return String
- */
- function getComment()
- {
- return $this->comment;
- }
-
- /**
- * Encryption
- *
- * Both CRYPT_RSA_ENCRYPTION_OAEP and CRYPT_RSA_ENCRYPTION_PKCS1 both place limits on how long $plaintext can be.
- * If $plaintext exceeds those limits it will be broken up so that it does and the resultant ciphertext's will
- * be concatenated together.
- *
- * @see decrypt()
- * @access public
- * @param String $plaintext
- * @return String
- */
- function encrypt($plaintext)
- {
- switch ($this->encryptionMode) {
- case CRYPT_RSA_ENCRYPTION_PKCS1:
- $length = $this->k - 11;
- if ($length <= 0) {
- return false;
- }
-
- $plaintext = str_split($plaintext, $length);
- $ciphertext = '';
- foreach ($plaintext as $m) {
- $ciphertext.= $this->_rsaes_pkcs1_v1_5_encrypt($m);
- }
- return $ciphertext;
- //case CRYPT_RSA_ENCRYPTION_OAEP:
- default:
- $length = $this->k - 2 * $this->hLen - 2;
- if ($length <= 0) {
- return false;
- }
-
- $plaintext = str_split($plaintext, $length);
- $ciphertext = '';
- foreach ($plaintext as $m) {
- $ciphertext.= $this->_rsaes_oaep_encrypt($m);
- }
- return $ciphertext;
- }
- }
-
- /**
- * Decryption
- *
- * @see encrypt()
- * @access public
- * @param String $plaintext
- * @return String
- */
- function decrypt($ciphertext)
- {
- if ($this->k <= 0) {
- return false;
- }
-
- $ciphertext = str_split($ciphertext, $this->k);
- $ciphertext[count($ciphertext) - 1] = str_pad($ciphertext[count($ciphertext) - 1], $this->k, chr(0), STR_PAD_LEFT);
-
- $plaintext = '';
-
- switch ($this->encryptionMode) {
- case CRYPT_RSA_ENCRYPTION_PKCS1:
- $decrypt = '_rsaes_pkcs1_v1_5_decrypt';
- break;
- //case CRYPT_RSA_ENCRYPTION_OAEP:
- default:
- $decrypt = '_rsaes_oaep_decrypt';
- }
-
- foreach ($ciphertext as $c) {
- $temp = $this->$decrypt($c);
- if ($temp === false) {
- return false;
- }
- $plaintext.= $temp;
- }
-
- return $plaintext;
- }
-
- /**
- * Create a signature
- *
- * @see verify()
- * @access public
- * @param String $message
- * @return String
- */
- function sign($message)
- {
- if (empty($this->modulus) || empty($this->exponent)) {
- return false;
- }
-
- switch ($this->signatureMode) {
- case CRYPT_RSA_SIGNATURE_PKCS1:
- return $this->_rsassa_pkcs1_v1_5_sign($message);
- //case CRYPT_RSA_SIGNATURE_PSS:
- default:
- return $this->_rsassa_pss_sign($message);
- }
- }
-
- /**
- * Verifies a signature
- *
- * @see sign()
- * @access public
- * @param String $message
- * @param String $signature
- * @return Boolean
- */
- function verify($message, $signature)
- {
- if (empty($this->modulus) || empty($this->exponent)) {
- return false;
- }
-
- switch ($this->signatureMode) {
- case CRYPT_RSA_SIGNATURE_PKCS1:
- return $this->_rsassa_pkcs1_v1_5_verify($message, $signature);
- //case CRYPT_RSA_SIGNATURE_PSS:
- default:
- return $this->_rsassa_pss_verify($message, $signature);
- }
- }
-}
diff --git a/plugins/OStatus/extlib/phpseclib/Crypt/Random.php b/plugins/OStatus/extlib/phpseclib/Crypt/Random.php
deleted file mode 100644
index 948512da7f..0000000000
--- a/plugins/OStatus/extlib/phpseclib/Crypt/Random.php
+++ /dev/null
@@ -1,249 +0,0 @@
-
- *
- *
- *
- * LICENSE: 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.
- *
- * @category Crypt
- * @package Crypt_Random
- * @author Jim Wigginton
- * @copyright MMVII Jim Wigginton
- * @license http://www.opensource.org/licenses/mit-license.html MIT License
- * @link http://phpseclib.sourceforge.net
- */
-
-/**
- * "Is Windows" test
- *
- * @access private
- */
-define('CRYPT_RANDOM_IS_WINDOWS', strtoupper(substr(PHP_OS, 0, 3)) === 'WIN');
-
-/**
- * Generate a random string.
- *
- * Although microoptimizations are generally discouraged as they impair readability this function is ripe with
- * microoptimizations because this function has the potential of being called a huge number of times.
- * eg. for RSA key generation.
- *
- * @param Integer $length
- * @return String
- * @access public
- */
-function crypt_random_string($length)
-{
- if (CRYPT_RANDOM_IS_WINDOWS) {
- // method 1. prior to PHP 5.3 this would call rand() on windows hence the function_exists('class_alias') call.
- // ie. class_alias is a function that was introduced in PHP 5.3
- if (function_exists('mcrypt_create_iv') && function_exists('class_alias')) {
- return mcrypt_create_iv($length);
- }
- // method 2. openssl_random_pseudo_bytes was introduced in PHP 5.3.0 but prior to PHP 5.3.4 there was,
- // to quote , "possible blocking behavior". as of 5.3.4
- // openssl_random_pseudo_bytes and mcrypt_create_iv do the exact same thing on Windows. ie. they both
- // call php_win32_get_random_bytes():
- //
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/openssl/openssl.c#L5008
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1392
- //
- // php_win32_get_random_bytes() is defined thusly:
- //
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/win32/winutil.c#L80
- //
- // we're calling it, all the same, in the off chance that the mcrypt extension is not available
- if (function_exists('openssl_random_pseudo_bytes') && version_compare(PHP_VERSION, '5.3.4', '>=')) {
- return openssl_random_pseudo_bytes($length);
- }
- } else {
- // method 1. the fastest
- if (function_exists('openssl_random_pseudo_bytes')) {
- return openssl_random_pseudo_bytes($length);
- }
- // method 2
- static $fp = true;
- if ($fp === true) {
- // warning's will be output unles the error suppression operator is used. errors such as
- // "open_basedir restriction in effect", "Permission denied", "No such file or directory", etc.
- $fp = @fopen('/dev/urandom', 'rb');
- }
- if ($fp !== true && $fp !== false) { // surprisingly faster than !is_bool() or is_resource()
- return fread($fp, $length);
- }
- // method 3. pretty much does the same thing as method 2 per the following url:
- // https://github.com/php/php-src/blob/7014a0eb6d1611151a286c0ff4f2238f92c120d6/ext/mcrypt/mcrypt.c#L1391
- // surprisingly slower than method 2. maybe that's because mcrypt_create_iv does a bunch of error checking that we're
- // not doing. regardless, this'll only be called if this PHP script couldn't open /dev/urandom due to open_basedir
- // restrictions or some such
- if (function_exists('mcrypt_create_iv')) {
- return mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
- }
- }
- // at this point we have no choice but to use a pure-PHP CSPRNG
-
- // cascade entropy across multiple PHP instances by fixing the session and collecting all
- // environmental variables, including the previous session data and the current session
- // data.
- //
- // mt_rand seeds itself by looking at the PID and the time, both of which are (relatively)
- // easy to guess at. linux uses mouse clicks, keyboard timings, etc, as entropy sources, but
- // PHP isn't low level to be able to use those as sources and on a web server there's not likely
- // going to be a ton of keyboard or mouse action. web servers do have one thing that we can use
- // however. a ton of people visiting the website. obviously you don't want to base your seeding
- // soley on parameters a potential attacker sends but (1) not everything in $_SERVER is controlled
- // by the user and (2) this isn't just looking at the data sent by the current user - it's based
- // on the data sent by all users. one user requests the page and a hash of their info is saved.
- // another user visits the page and the serialization of their data is utilized along with the
- // server envirnment stuff and a hash of the previous http request data (which itself utilizes
- // a hash of the session data before that). certainly an attacker should be assumed to have
- // full control over his own http requests. he, however, is not going to have control over
- // everyone's http requests.
- static $crypto = false, $v;
- if ($crypto === false) {
- // save old session data
- $old_session_id = session_id();
- $old_use_cookies = ini_get('session.use_cookies');
- $old_session_cache_limiter = session_cache_limiter();
- if (isset($_SESSION)) {
- $_OLD_SESSION = $_SESSION;
- }
- if ($old_session_id != '') {
- session_write_close();
- }
-
- session_id(1);
- ini_set('session.use_cookies', 0);
- session_cache_limiter('');
- session_start();
-
- $v = $seed = $_SESSION['seed'] = pack('H*', sha1(
- serialize($_SERVER) .
- serialize($_POST) .
- serialize($_GET) .
- serialize($_COOKIE) .
- serialize($GLOBALS) .
- serialize($_SESSION) .
- serialize($_OLD_SESSION)
- ));
- if (!isset($_SESSION['count'])) {
- $_SESSION['count'] = 0;
- }
- $_SESSION['count']++;
-
- session_write_close();
-
- // restore old session data
- if ($old_session_id != '') {
- session_id($old_session_id);
- session_start();
- ini_set('session.use_cookies', $old_use_cookies);
- session_cache_limiter($old_session_cache_limiter);
- } else {
- if (isset($_OLD_SESSION)) {
- $_SESSION = $_OLD_SESSION;
- unset($_OLD_SESSION);
- } else {
- unset($_SESSION);
- }
- }
-
- // in SSH2 a shared secret and an exchange hash are generated through the key exchange process.
- // the IV client to server is the hash of that "nonce" with the letter A and for the encryption key it's the letter C.
- // if the hash doesn't produce enough a key or an IV that's long enough concat successive hashes of the
- // original hash and the current hash. we'll be emulating that. for more info see the following URL:
- //
- // http://tools.ietf.org/html/rfc4253#section-7.2
- //
- // see the is_string($crypto) part for an example of how to expand the keys
- $key = pack('H*', sha1($seed . 'A'));
- $iv = pack('H*', sha1($seed . 'C'));
-
- // ciphers are used as per the nist.gov link below. also, see this link:
- //
- // http://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator#Designs_based_on_cryptographic_primitives
- switch (true) {
- case class_exists('Crypt_AES'):
- $crypto = new Crypt_AES(CRYPT_AES_MODE_CTR);
- break;
- case class_exists('Crypt_TripleDES'):
- $crypto = new Crypt_TripleDES(CRYPT_DES_MODE_CTR);
- break;
- case class_exists('Crypt_DES'):
- $crypto = new Crypt_DES(CRYPT_DES_MODE_CTR);
- break;
- case class_exists('Crypt_RC4'):
- $crypto = new Crypt_RC4();
- break;
- default:
- $crypto = $seed;
- return crypt_random_string($length);
- }
-
- $crypto->setKey($key);
- $crypto->setIV($iv);
- $crypto->enableContinuousBuffer();
- }
-
- if (is_string($crypto)) {
- // the following is based off of ANSI X9.31:
- //
- // http://csrc.nist.gov/groups/STM/cavp/documents/rng/931rngext.pdf
- //
- // OpenSSL uses that same standard for it's random numbers:
- //
- // http://www.opensource.apple.com/source/OpenSSL/OpenSSL-38/openssl/fips-1.0/rand/fips_rand.c
- // (do a search for "ANS X9.31 A.2.4")
- //
- // ANSI X9.31 recommends ciphers be used and phpseclib does use them if they're available (see
- // later on in the code) but if they're not we'll use sha1
- $result = '';
- while (strlen($result) < $length) { // each loop adds 20 bytes
- // microtime() isn't packed as "densely" as it could be but then neither is that the idea.
- // the idea is simply to ensure that each "block" has a unique element to it.
- $i = pack('H*', sha1(microtime()));
- $r = pack('H*', sha1($i ^ $v));
- $v = pack('H*', sha1($r ^ $i));
- $result.= $r;
- }
- return substr($result, 0, $length);
- }
-
- //return $crypto->encrypt(str_repeat("\0", $length));
-
- $result = '';
- while (strlen($result) < $length) {
- $i = $crypto->encrypt(microtime());
- $r = $crypto->encrypt($i ^ $v);
- $v = $crypto->encrypt($r ^ $i);
- $result.= $r;
- }
- return substr($result, 0, $length);
-}
diff --git a/plugins/OStatus/extlib/phpseclib/Crypt/Rijndael.php b/plugins/OStatus/extlib/phpseclib/Crypt/Rijndael.php
deleted file mode 100644
index c63e0ff7e3..0000000000
--- a/plugins/OStatus/extlib/phpseclib/Crypt/Rijndael.php
+++ /dev/null
@@ -1,1374 +0,0 @@
-
- * setKey('abcdefghijklmnop');
- *
- * $size = 10 * 1024;
- * $plaintext = '';
- * for ($i = 0; $i < $size; $i++) {
- * $plaintext.= 'a';
- * }
- *
- * echo $rijndael->decrypt($rijndael->encrypt($plaintext));
- * ?>
- *
- *
- * LICENSE: 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.
- *
- * @category Crypt
- * @package Crypt_Rijndael
- * @author Jim Wigginton
- * @copyright MMVIII Jim Wigginton
- * @license http://www.opensource.org/licenses/mit-license.html MIT License
- * @link http://phpseclib.sourceforge.net
- */
-
-/**
- * Include Crypt_Base
- *
- * Base cipher class
- */
-if (!class_exists('Crypt_Base')) {
- require_once('Base.php');
-}
-
-/**#@+
- * @access public
- * @see Crypt_Rijndael::encrypt()
- * @see Crypt_Rijndael::decrypt()
- */
-/**
- * Encrypt / decrypt using the Counter mode.
- *
- * Set to -1 since that's what Crypt/Random.php uses to index the CTR mode.
- *
- * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Counter_.28CTR.29
- */
-define('CRYPT_RIJNDAEL_MODE_CTR', CRYPT_MODE_CTR);
-/**
- * Encrypt / decrypt using the Electronic Code Book mode.
- *
- * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Electronic_codebook_.28ECB.29
- */
-define('CRYPT_RIJNDAEL_MODE_ECB', CRYPT_MODE_ECB);
-/**
- * Encrypt / decrypt using the Code Book Chaining mode.
- *
- * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher-block_chaining_.28CBC.29
- */
-define('CRYPT_RIJNDAEL_MODE_CBC', CRYPT_MODE_CBC);
-/**
- * Encrypt / decrypt using the Cipher Feedback mode.
- *
- * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Cipher_feedback_.28CFB.29
- */
-define('CRYPT_RIJNDAEL_MODE_CFB', CRYPT_MODE_CFB);
-/**
- * Encrypt / decrypt using the Cipher Feedback mode.
- *
- * @link http://en.wikipedia.org/wiki/Block_cipher_modes_of_operation#Output_feedback_.28OFB.29
- */
-define('CRYPT_RIJNDAEL_MODE_OFB', CRYPT_MODE_OFB);
-/**#@-*/
-
-/**#@+
- * @access private
- * @see Crypt_Rijndael::Crypt_Rijndael()
- */
-/**
- * Toggles the internal implementation
- */
-define('CRYPT_RIJNDAEL_MODE_INTERNAL', CRYPT_MODE_INTERNAL);
-/**
- * Toggles the mcrypt implementation
- */
-define('CRYPT_RIJNDAEL_MODE_MCRYPT', CRYPT_MODE_MCRYPT);
-/**#@-*/
-
-/**
- * Pure-PHP implementation of Rijndael.
- *
- * @author Jim Wigginton
- * @version 0.1.0
- * @access public
- * @package Crypt_Rijndael
- */
-class Crypt_Rijndael extends Crypt_Base {
- /**
- * The default password key_size used by setPassword()
- *
- * @see Crypt_Base::password_key_size
- * @see Crypt_Base::setPassword()
- * @var Integer
- * @access private
- */
- var $password_key_size = 16;
-
- /**
- * The namespace used by the cipher for its constants.
- *
- * @see Crypt_Base::const_namespace
- * @var String
- * @access private
- */
- var $const_namespace = 'RIJNDAEL';
-
- /**
- * The mcrypt specific name of the cipher
- *
- * Mcrypt is useable for 128/192/256-bit $block_size/$key_size. For 160/224 not.
- * Crypt_Rijndael determines automatically whether mcrypt is useable
- * or not for the current $block_size/$key_size.
- * In case of, $cipher_name_mcrypt will be set dynamicaly at run time accordingly.
- *
- * @see Crypt_Base::cipher_name_mcrypt
- * @see Crypt_Base::engine
- * @see _setupEngine()
- * @var String
- * @access private
- */
- var $cipher_name_mcrypt = 'rijndael-128';
-
- /**
- * The default salt used by setPassword()
- *
- * @see Crypt_Base::password_default_salt
- * @see Crypt_Base::setPassword()
- * @var String
- * @access private
- */
- var $password_default_salt = 'phpseclib';
-
- /**
- * Has the key length explicitly been set or should it be derived from the key, itself?
- *
- * @see setKeyLength()
- * @var Boolean
- * @access private
- */
- var $explicit_key_length = false;
-
- /**
- * The Key Schedule
- *
- * @see _setup()
- * @var Array
- * @access private
- */
- var $w;
-
- /**
- * The Inverse Key Schedule
- *
- * @see _setup()
- * @var Array
- * @access private
- */
- var $dw;
-
- /**
- * The Block Length divided by 32
- *
- * @see setBlockLength()
- * @var Integer
- * @access private
- * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4. Exists in conjunction with $block_size
- * because the encryption / decryption / key schedule creation requires this number and not $block_size. We could
- * derive this from $block_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
- * of that, we'll just precompute it once.
- *
- */
- var $Nb = 4;
-
- /**
- * The Key Length
- *
- * @see setKeyLength()
- * @var Integer
- * @access private
- * @internal The max value is 256 / 8 = 32, the min value is 128 / 8 = 16. Exists in conjunction with $Nk
- * because the encryption / decryption / key schedule creation requires this number and not $key_size. We could
- * derive this from $key_size or vice versa, but that'd mean we'd have to do multiple shift operations, so in lieu
- * of that, we'll just precompute it once.
- */
- var $key_size = 16;
-
- /**
- * The Key Length divided by 32
- *
- * @see setKeyLength()
- * @var Integer
- * @access private
- * @internal The max value is 256 / 32 = 8, the min value is 128 / 32 = 4
- */
- var $Nk = 4;
-
- /**
- * The Number of Rounds
- *
- * @var Integer
- * @access private
- * @internal The max value is 14, the min value is 10.
- */
- var $Nr;
-
- /**
- * Shift offsets
- *
- * @var Array
- * @access private
- */
- var $c;
-
- /**
- * Holds the last used key- and block_size information
- *
- * @var Array
- * @access private
- */
- var $kl;
-
- /**
- * Precomputed mixColumns table
- *
- * According to (section 5.2.1),
- * precomputed tables can be used in the mixColumns phase. in that example, they're assigned t0...t3, so
- * those are the names we'll use.
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $t0 = array(
- 0xC66363A5, 0xF87C7C84, 0xEE777799, 0xF67B7B8D, 0xFFF2F20D, 0xD66B6BBD, 0xDE6F6FB1, 0x91C5C554,
- 0x60303050, 0x02010103, 0xCE6767A9, 0x562B2B7D, 0xE7FEFE19, 0xB5D7D762, 0x4DABABE6, 0xEC76769A,
- 0x8FCACA45, 0x1F82829D, 0x89C9C940, 0xFA7D7D87, 0xEFFAFA15, 0xB25959EB, 0x8E4747C9, 0xFBF0F00B,
- 0x41ADADEC, 0xB3D4D467, 0x5FA2A2FD, 0x45AFAFEA, 0x239C9CBF, 0x53A4A4F7, 0xE4727296, 0x9BC0C05B,
- 0x75B7B7C2, 0xE1FDFD1C, 0x3D9393AE, 0x4C26266A, 0x6C36365A, 0x7E3F3F41, 0xF5F7F702, 0x83CCCC4F,
- 0x6834345C, 0x51A5A5F4, 0xD1E5E534, 0xF9F1F108, 0xE2717193, 0xABD8D873, 0x62313153, 0x2A15153F,
- 0x0804040C, 0x95C7C752, 0x46232365, 0x9DC3C35E, 0x30181828, 0x379696A1, 0x0A05050F, 0x2F9A9AB5,
- 0x0E070709, 0x24121236, 0x1B80809B, 0xDFE2E23D, 0xCDEBEB26, 0x4E272769, 0x7FB2B2CD, 0xEA75759F,
- 0x1209091B, 0x1D83839E, 0x582C2C74, 0x341A1A2E, 0x361B1B2D, 0xDC6E6EB2, 0xB45A5AEE, 0x5BA0A0FB,
- 0xA45252F6, 0x763B3B4D, 0xB7D6D661, 0x7DB3B3CE, 0x5229297B, 0xDDE3E33E, 0x5E2F2F71, 0x13848497,
- 0xA65353F5, 0xB9D1D168, 0x00000000, 0xC1EDED2C, 0x40202060, 0xE3FCFC1F, 0x79B1B1C8, 0xB65B5BED,
- 0xD46A6ABE, 0x8DCBCB46, 0x67BEBED9, 0x7239394B, 0x944A4ADE, 0x984C4CD4, 0xB05858E8, 0x85CFCF4A,
- 0xBBD0D06B, 0xC5EFEF2A, 0x4FAAAAE5, 0xEDFBFB16, 0x864343C5, 0x9A4D4DD7, 0x66333355, 0x11858594,
- 0x8A4545CF, 0xE9F9F910, 0x04020206, 0xFE7F7F81, 0xA05050F0, 0x783C3C44, 0x259F9FBA, 0x4BA8A8E3,
- 0xA25151F3, 0x5DA3A3FE, 0x804040C0, 0x058F8F8A, 0x3F9292AD, 0x219D9DBC, 0x70383848, 0xF1F5F504,
- 0x63BCBCDF, 0x77B6B6C1, 0xAFDADA75, 0x42212163, 0x20101030, 0xE5FFFF1A, 0xFDF3F30E, 0xBFD2D26D,
- 0x81CDCD4C, 0x180C0C14, 0x26131335, 0xC3ECEC2F, 0xBE5F5FE1, 0x359797A2, 0x884444CC, 0x2E171739,
- 0x93C4C457, 0x55A7A7F2, 0xFC7E7E82, 0x7A3D3D47, 0xC86464AC, 0xBA5D5DE7, 0x3219192B, 0xE6737395,
- 0xC06060A0, 0x19818198, 0x9E4F4FD1, 0xA3DCDC7F, 0x44222266, 0x542A2A7E, 0x3B9090AB, 0x0B888883,
- 0x8C4646CA, 0xC7EEEE29, 0x6BB8B8D3, 0x2814143C, 0xA7DEDE79, 0xBC5E5EE2, 0x160B0B1D, 0xADDBDB76,
- 0xDBE0E03B, 0x64323256, 0x743A3A4E, 0x140A0A1E, 0x924949DB, 0x0C06060A, 0x4824246C, 0xB85C5CE4,
- 0x9FC2C25D, 0xBDD3D36E, 0x43ACACEF, 0xC46262A6, 0x399191A8, 0x319595A4, 0xD3E4E437, 0xF279798B,
- 0xD5E7E732, 0x8BC8C843, 0x6E373759, 0xDA6D6DB7, 0x018D8D8C, 0xB1D5D564, 0x9C4E4ED2, 0x49A9A9E0,
- 0xD86C6CB4, 0xAC5656FA, 0xF3F4F407, 0xCFEAEA25, 0xCA6565AF, 0xF47A7A8E, 0x47AEAEE9, 0x10080818,
- 0x6FBABAD5, 0xF0787888, 0x4A25256F, 0x5C2E2E72, 0x381C1C24, 0x57A6A6F1, 0x73B4B4C7, 0x97C6C651,
- 0xCBE8E823, 0xA1DDDD7C, 0xE874749C, 0x3E1F1F21, 0x964B4BDD, 0x61BDBDDC, 0x0D8B8B86, 0x0F8A8A85,
- 0xE0707090, 0x7C3E3E42, 0x71B5B5C4, 0xCC6666AA, 0x904848D8, 0x06030305, 0xF7F6F601, 0x1C0E0E12,
- 0xC26161A3, 0x6A35355F, 0xAE5757F9, 0x69B9B9D0, 0x17868691, 0x99C1C158, 0x3A1D1D27, 0x279E9EB9,
- 0xD9E1E138, 0xEBF8F813, 0x2B9898B3, 0x22111133, 0xD26969BB, 0xA9D9D970, 0x078E8E89, 0x339494A7,
- 0x2D9B9BB6, 0x3C1E1E22, 0x15878792, 0xC9E9E920, 0x87CECE49, 0xAA5555FF, 0x50282878, 0xA5DFDF7A,
- 0x038C8C8F, 0x59A1A1F8, 0x09898980, 0x1A0D0D17, 0x65BFBFDA, 0xD7E6E631, 0x844242C6, 0xD06868B8,
- 0x824141C3, 0x299999B0, 0x5A2D2D77, 0x1E0F0F11, 0x7BB0B0CB, 0xA85454FC, 0x6DBBBBD6, 0x2C16163A
- );
-
- /**
- * Precomputed mixColumns table
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $t1 = array(
- 0xA5C66363, 0x84F87C7C, 0x99EE7777, 0x8DF67B7B, 0x0DFFF2F2, 0xBDD66B6B, 0xB1DE6F6F, 0x5491C5C5,
- 0x50603030, 0x03020101, 0xA9CE6767, 0x7D562B2B, 0x19E7FEFE, 0x62B5D7D7, 0xE64DABAB, 0x9AEC7676,
- 0x458FCACA, 0x9D1F8282, 0x4089C9C9, 0x87FA7D7D, 0x15EFFAFA, 0xEBB25959, 0xC98E4747, 0x0BFBF0F0,
- 0xEC41ADAD, 0x67B3D4D4, 0xFD5FA2A2, 0xEA45AFAF, 0xBF239C9C, 0xF753A4A4, 0x96E47272, 0x5B9BC0C0,
- 0xC275B7B7, 0x1CE1FDFD, 0xAE3D9393, 0x6A4C2626, 0x5A6C3636, 0x417E3F3F, 0x02F5F7F7, 0x4F83CCCC,
- 0x5C683434, 0xF451A5A5, 0x34D1E5E5, 0x08F9F1F1, 0x93E27171, 0x73ABD8D8, 0x53623131, 0x3F2A1515,
- 0x0C080404, 0x5295C7C7, 0x65462323, 0x5E9DC3C3, 0x28301818, 0xA1379696, 0x0F0A0505, 0xB52F9A9A,
- 0x090E0707, 0x36241212, 0x9B1B8080, 0x3DDFE2E2, 0x26CDEBEB, 0x694E2727, 0xCD7FB2B2, 0x9FEA7575,
- 0x1B120909, 0x9E1D8383, 0x74582C2C, 0x2E341A1A, 0x2D361B1B, 0xB2DC6E6E, 0xEEB45A5A, 0xFB5BA0A0,
- 0xF6A45252, 0x4D763B3B, 0x61B7D6D6, 0xCE7DB3B3, 0x7B522929, 0x3EDDE3E3, 0x715E2F2F, 0x97138484,
- 0xF5A65353, 0x68B9D1D1, 0x00000000, 0x2CC1EDED, 0x60402020, 0x1FE3FCFC, 0xC879B1B1, 0xEDB65B5B,
- 0xBED46A6A, 0x468DCBCB, 0xD967BEBE, 0x4B723939, 0xDE944A4A, 0xD4984C4C, 0xE8B05858, 0x4A85CFCF,
- 0x6BBBD0D0, 0x2AC5EFEF, 0xE54FAAAA, 0x16EDFBFB, 0xC5864343, 0xD79A4D4D, 0x55663333, 0x94118585,
- 0xCF8A4545, 0x10E9F9F9, 0x06040202, 0x81FE7F7F, 0xF0A05050, 0x44783C3C, 0xBA259F9F, 0xE34BA8A8,
- 0xF3A25151, 0xFE5DA3A3, 0xC0804040, 0x8A058F8F, 0xAD3F9292, 0xBC219D9D, 0x48703838, 0x04F1F5F5,
- 0xDF63BCBC, 0xC177B6B6, 0x75AFDADA, 0x63422121, 0x30201010, 0x1AE5FFFF, 0x0EFDF3F3, 0x6DBFD2D2,
- 0x4C81CDCD, 0x14180C0C, 0x35261313, 0x2FC3ECEC, 0xE1BE5F5F, 0xA2359797, 0xCC884444, 0x392E1717,
- 0x5793C4C4, 0xF255A7A7, 0x82FC7E7E, 0x477A3D3D, 0xACC86464, 0xE7BA5D5D, 0x2B321919, 0x95E67373,
- 0xA0C06060, 0x98198181, 0xD19E4F4F, 0x7FA3DCDC, 0x66442222, 0x7E542A2A, 0xAB3B9090, 0x830B8888,
- 0xCA8C4646, 0x29C7EEEE, 0xD36BB8B8, 0x3C281414, 0x79A7DEDE, 0xE2BC5E5E, 0x1D160B0B, 0x76ADDBDB,
- 0x3BDBE0E0, 0x56643232, 0x4E743A3A, 0x1E140A0A, 0xDB924949, 0x0A0C0606, 0x6C482424, 0xE4B85C5C,
- 0x5D9FC2C2, 0x6EBDD3D3, 0xEF43ACAC, 0xA6C46262, 0xA8399191, 0xA4319595, 0x37D3E4E4, 0x8BF27979,
- 0x32D5E7E7, 0x438BC8C8, 0x596E3737, 0xB7DA6D6D, 0x8C018D8D, 0x64B1D5D5, 0xD29C4E4E, 0xE049A9A9,
- 0xB4D86C6C, 0xFAAC5656, 0x07F3F4F4, 0x25CFEAEA, 0xAFCA6565, 0x8EF47A7A, 0xE947AEAE, 0x18100808,
- 0xD56FBABA, 0x88F07878, 0x6F4A2525, 0x725C2E2E, 0x24381C1C, 0xF157A6A6, 0xC773B4B4, 0x5197C6C6,
- 0x23CBE8E8, 0x7CA1DDDD, 0x9CE87474, 0x213E1F1F, 0xDD964B4B, 0xDC61BDBD, 0x860D8B8B, 0x850F8A8A,
- 0x90E07070, 0x427C3E3E, 0xC471B5B5, 0xAACC6666, 0xD8904848, 0x05060303, 0x01F7F6F6, 0x121C0E0E,
- 0xA3C26161, 0x5F6A3535, 0xF9AE5757, 0xD069B9B9, 0x91178686, 0x5899C1C1, 0x273A1D1D, 0xB9279E9E,
- 0x38D9E1E1, 0x13EBF8F8, 0xB32B9898, 0x33221111, 0xBBD26969, 0x70A9D9D9, 0x89078E8E, 0xA7339494,
- 0xB62D9B9B, 0x223C1E1E, 0x92158787, 0x20C9E9E9, 0x4987CECE, 0xFFAA5555, 0x78502828, 0x7AA5DFDF,
- 0x8F038C8C, 0xF859A1A1, 0x80098989, 0x171A0D0D, 0xDA65BFBF, 0x31D7E6E6, 0xC6844242, 0xB8D06868,
- 0xC3824141, 0xB0299999, 0x775A2D2D, 0x111E0F0F, 0xCB7BB0B0, 0xFCA85454, 0xD66DBBBB, 0x3A2C1616
- );
-
- /**
- * Precomputed mixColumns table
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $t2 = array(
- 0x63A5C663, 0x7C84F87C, 0x7799EE77, 0x7B8DF67B, 0xF20DFFF2, 0x6BBDD66B, 0x6FB1DE6F, 0xC55491C5,
- 0x30506030, 0x01030201, 0x67A9CE67, 0x2B7D562B, 0xFE19E7FE, 0xD762B5D7, 0xABE64DAB, 0x769AEC76,
- 0xCA458FCA, 0x829D1F82, 0xC94089C9, 0x7D87FA7D, 0xFA15EFFA, 0x59EBB259, 0x47C98E47, 0xF00BFBF0,
- 0xADEC41AD, 0xD467B3D4, 0xA2FD5FA2, 0xAFEA45AF, 0x9CBF239C, 0xA4F753A4, 0x7296E472, 0xC05B9BC0,
- 0xB7C275B7, 0xFD1CE1FD, 0x93AE3D93, 0x266A4C26, 0x365A6C36, 0x3F417E3F, 0xF702F5F7, 0xCC4F83CC,
- 0x345C6834, 0xA5F451A5, 0xE534D1E5, 0xF108F9F1, 0x7193E271, 0xD873ABD8, 0x31536231, 0x153F2A15,
- 0x040C0804, 0xC75295C7, 0x23654623, 0xC35E9DC3, 0x18283018, 0x96A13796, 0x050F0A05, 0x9AB52F9A,
- 0x07090E07, 0x12362412, 0x809B1B80, 0xE23DDFE2, 0xEB26CDEB, 0x27694E27, 0xB2CD7FB2, 0x759FEA75,
- 0x091B1209, 0x839E1D83, 0x2C74582C, 0x1A2E341A, 0x1B2D361B, 0x6EB2DC6E, 0x5AEEB45A, 0xA0FB5BA0,
- 0x52F6A452, 0x3B4D763B, 0xD661B7D6, 0xB3CE7DB3, 0x297B5229, 0xE33EDDE3, 0x2F715E2F, 0x84971384,
- 0x53F5A653, 0xD168B9D1, 0x00000000, 0xED2CC1ED, 0x20604020, 0xFC1FE3FC, 0xB1C879B1, 0x5BEDB65B,
- 0x6ABED46A, 0xCB468DCB, 0xBED967BE, 0x394B7239, 0x4ADE944A, 0x4CD4984C, 0x58E8B058, 0xCF4A85CF,
- 0xD06BBBD0, 0xEF2AC5EF, 0xAAE54FAA, 0xFB16EDFB, 0x43C58643, 0x4DD79A4D, 0x33556633, 0x85941185,
- 0x45CF8A45, 0xF910E9F9, 0x02060402, 0x7F81FE7F, 0x50F0A050, 0x3C44783C, 0x9FBA259F, 0xA8E34BA8,
- 0x51F3A251, 0xA3FE5DA3, 0x40C08040, 0x8F8A058F, 0x92AD3F92, 0x9DBC219D, 0x38487038, 0xF504F1F5,
- 0xBCDF63BC, 0xB6C177B6, 0xDA75AFDA, 0x21634221, 0x10302010, 0xFF1AE5FF, 0xF30EFDF3, 0xD26DBFD2,
- 0xCD4C81CD, 0x0C14180C, 0x13352613, 0xEC2FC3EC, 0x5FE1BE5F, 0x97A23597, 0x44CC8844, 0x17392E17,
- 0xC45793C4, 0xA7F255A7, 0x7E82FC7E, 0x3D477A3D, 0x64ACC864, 0x5DE7BA5D, 0x192B3219, 0x7395E673,
- 0x60A0C060, 0x81981981, 0x4FD19E4F, 0xDC7FA3DC, 0x22664422, 0x2A7E542A, 0x90AB3B90, 0x88830B88,
- 0x46CA8C46, 0xEE29C7EE, 0xB8D36BB8, 0x143C2814, 0xDE79A7DE, 0x5EE2BC5E, 0x0B1D160B, 0xDB76ADDB,
- 0xE03BDBE0, 0x32566432, 0x3A4E743A, 0x0A1E140A, 0x49DB9249, 0x060A0C06, 0x246C4824, 0x5CE4B85C,
- 0xC25D9FC2, 0xD36EBDD3, 0xACEF43AC, 0x62A6C462, 0x91A83991, 0x95A43195, 0xE437D3E4, 0x798BF279,
- 0xE732D5E7, 0xC8438BC8, 0x37596E37, 0x6DB7DA6D, 0x8D8C018D, 0xD564B1D5, 0x4ED29C4E, 0xA9E049A9,
- 0x6CB4D86C, 0x56FAAC56, 0xF407F3F4, 0xEA25CFEA, 0x65AFCA65, 0x7A8EF47A, 0xAEE947AE, 0x08181008,
- 0xBAD56FBA, 0x7888F078, 0x256F4A25, 0x2E725C2E, 0x1C24381C, 0xA6F157A6, 0xB4C773B4, 0xC65197C6,
- 0xE823CBE8, 0xDD7CA1DD, 0x749CE874, 0x1F213E1F, 0x4BDD964B, 0xBDDC61BD, 0x8B860D8B, 0x8A850F8A,
- 0x7090E070, 0x3E427C3E, 0xB5C471B5, 0x66AACC66, 0x48D89048, 0x03050603, 0xF601F7F6, 0x0E121C0E,
- 0x61A3C261, 0x355F6A35, 0x57F9AE57, 0xB9D069B9, 0x86911786, 0xC15899C1, 0x1D273A1D, 0x9EB9279E,
- 0xE138D9E1, 0xF813EBF8, 0x98B32B98, 0x11332211, 0x69BBD269, 0xD970A9D9, 0x8E89078E, 0x94A73394,
- 0x9BB62D9B, 0x1E223C1E, 0x87921587, 0xE920C9E9, 0xCE4987CE, 0x55FFAA55, 0x28785028, 0xDF7AA5DF,
- 0x8C8F038C, 0xA1F859A1, 0x89800989, 0x0D171A0D, 0xBFDA65BF, 0xE631D7E6, 0x42C68442, 0x68B8D068,
- 0x41C38241, 0x99B02999, 0x2D775A2D, 0x0F111E0F, 0xB0CB7BB0, 0x54FCA854, 0xBBD66DBB, 0x163A2C16
- );
-
- /**
- * Precomputed mixColumns table
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $t3 = array(
- 0x6363A5C6, 0x7C7C84F8, 0x777799EE, 0x7B7B8DF6, 0xF2F20DFF, 0x6B6BBDD6, 0x6F6FB1DE, 0xC5C55491,
- 0x30305060, 0x01010302, 0x6767A9CE, 0x2B2B7D56, 0xFEFE19E7, 0xD7D762B5, 0xABABE64D, 0x76769AEC,
- 0xCACA458F, 0x82829D1F, 0xC9C94089, 0x7D7D87FA, 0xFAFA15EF, 0x5959EBB2, 0x4747C98E, 0xF0F00BFB,
- 0xADADEC41, 0xD4D467B3, 0xA2A2FD5F, 0xAFAFEA45, 0x9C9CBF23, 0xA4A4F753, 0x727296E4, 0xC0C05B9B,
- 0xB7B7C275, 0xFDFD1CE1, 0x9393AE3D, 0x26266A4C, 0x36365A6C, 0x3F3F417E, 0xF7F702F5, 0xCCCC4F83,
- 0x34345C68, 0xA5A5F451, 0xE5E534D1, 0xF1F108F9, 0x717193E2, 0xD8D873AB, 0x31315362, 0x15153F2A,
- 0x04040C08, 0xC7C75295, 0x23236546, 0xC3C35E9D, 0x18182830, 0x9696A137, 0x05050F0A, 0x9A9AB52F,
- 0x0707090E, 0x12123624, 0x80809B1B, 0xE2E23DDF, 0xEBEB26CD, 0x2727694E, 0xB2B2CD7F, 0x75759FEA,
- 0x09091B12, 0x83839E1D, 0x2C2C7458, 0x1A1A2E34, 0x1B1B2D36, 0x6E6EB2DC, 0x5A5AEEB4, 0xA0A0FB5B,
- 0x5252F6A4, 0x3B3B4D76, 0xD6D661B7, 0xB3B3CE7D, 0x29297B52, 0xE3E33EDD, 0x2F2F715E, 0x84849713,
- 0x5353F5A6, 0xD1D168B9, 0x00000000, 0xEDED2CC1, 0x20206040, 0xFCFC1FE3, 0xB1B1C879, 0x5B5BEDB6,
- 0x6A6ABED4, 0xCBCB468D, 0xBEBED967, 0x39394B72, 0x4A4ADE94, 0x4C4CD498, 0x5858E8B0, 0xCFCF4A85,
- 0xD0D06BBB, 0xEFEF2AC5, 0xAAAAE54F, 0xFBFB16ED, 0x4343C586, 0x4D4DD79A, 0x33335566, 0x85859411,
- 0x4545CF8A, 0xF9F910E9, 0x02020604, 0x7F7F81FE, 0x5050F0A0, 0x3C3C4478, 0x9F9FBA25, 0xA8A8E34B,
- 0x5151F3A2, 0xA3A3FE5D, 0x4040C080, 0x8F8F8A05, 0x9292AD3F, 0x9D9DBC21, 0x38384870, 0xF5F504F1,
- 0xBCBCDF63, 0xB6B6C177, 0xDADA75AF, 0x21216342, 0x10103020, 0xFFFF1AE5, 0xF3F30EFD, 0xD2D26DBF,
- 0xCDCD4C81, 0x0C0C1418, 0x13133526, 0xECEC2FC3, 0x5F5FE1BE, 0x9797A235, 0x4444CC88, 0x1717392E,
- 0xC4C45793, 0xA7A7F255, 0x7E7E82FC, 0x3D3D477A, 0x6464ACC8, 0x5D5DE7BA, 0x19192B32, 0x737395E6,
- 0x6060A0C0, 0x81819819, 0x4F4FD19E, 0xDCDC7FA3, 0x22226644, 0x2A2A7E54, 0x9090AB3B, 0x8888830B,
- 0x4646CA8C, 0xEEEE29C7, 0xB8B8D36B, 0x14143C28, 0xDEDE79A7, 0x5E5EE2BC, 0x0B0B1D16, 0xDBDB76AD,
- 0xE0E03BDB, 0x32325664, 0x3A3A4E74, 0x0A0A1E14, 0x4949DB92, 0x06060A0C, 0x24246C48, 0x5C5CE4B8,
- 0xC2C25D9F, 0xD3D36EBD, 0xACACEF43, 0x6262A6C4, 0x9191A839, 0x9595A431, 0xE4E437D3, 0x79798BF2,
- 0xE7E732D5, 0xC8C8438B, 0x3737596E, 0x6D6DB7DA, 0x8D8D8C01, 0xD5D564B1, 0x4E4ED29C, 0xA9A9E049,
- 0x6C6CB4D8, 0x5656FAAC, 0xF4F407F3, 0xEAEA25CF, 0x6565AFCA, 0x7A7A8EF4, 0xAEAEE947, 0x08081810,
- 0xBABAD56F, 0x787888F0, 0x25256F4A, 0x2E2E725C, 0x1C1C2438, 0xA6A6F157, 0xB4B4C773, 0xC6C65197,
- 0xE8E823CB, 0xDDDD7CA1, 0x74749CE8, 0x1F1F213E, 0x4B4BDD96, 0xBDBDDC61, 0x8B8B860D, 0x8A8A850F,
- 0x707090E0, 0x3E3E427C, 0xB5B5C471, 0x6666AACC, 0x4848D890, 0x03030506, 0xF6F601F7, 0x0E0E121C,
- 0x6161A3C2, 0x35355F6A, 0x5757F9AE, 0xB9B9D069, 0x86869117, 0xC1C15899, 0x1D1D273A, 0x9E9EB927,
- 0xE1E138D9, 0xF8F813EB, 0x9898B32B, 0x11113322, 0x6969BBD2, 0xD9D970A9, 0x8E8E8907, 0x9494A733,
- 0x9B9BB62D, 0x1E1E223C, 0x87879215, 0xE9E920C9, 0xCECE4987, 0x5555FFAA, 0x28287850, 0xDFDF7AA5,
- 0x8C8C8F03, 0xA1A1F859, 0x89898009, 0x0D0D171A, 0xBFBFDA65, 0xE6E631D7, 0x4242C684, 0x6868B8D0,
- 0x4141C382, 0x9999B029, 0x2D2D775A, 0x0F0F111E, 0xB0B0CB7B, 0x5454FCA8, 0xBBBBD66D, 0x16163A2C
- );
-
- /**
- * Precomputed invMixColumns table
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $dt0 = array(
- 0x51F4A750, 0x7E416553, 0x1A17A4C3, 0x3A275E96, 0x3BAB6BCB, 0x1F9D45F1, 0xACFA58AB, 0x4BE30393,
- 0x2030FA55, 0xAD766DF6, 0x88CC7691, 0xF5024C25, 0x4FE5D7FC, 0xC52ACBD7, 0x26354480, 0xB562A38F,
- 0xDEB15A49, 0x25BA1B67, 0x45EA0E98, 0x5DFEC0E1, 0xC32F7502, 0x814CF012, 0x8D4697A3, 0x6BD3F9C6,
- 0x038F5FE7, 0x15929C95, 0xBF6D7AEB, 0x955259DA, 0xD4BE832D, 0x587421D3, 0x49E06929, 0x8EC9C844,
- 0x75C2896A, 0xF48E7978, 0x99583E6B, 0x27B971DD, 0xBEE14FB6, 0xF088AD17, 0xC920AC66, 0x7DCE3AB4,
- 0x63DF4A18, 0xE51A3182, 0x97513360, 0x62537F45, 0xB16477E0, 0xBB6BAE84, 0xFE81A01C, 0xF9082B94,
- 0x70486858, 0x8F45FD19, 0x94DE6C87, 0x527BF8B7, 0xAB73D323, 0x724B02E2, 0xE31F8F57, 0x6655AB2A,
- 0xB2EB2807, 0x2FB5C203, 0x86C57B9A, 0xD33708A5, 0x302887F2, 0x23BFA5B2, 0x02036ABA, 0xED16825C,
- 0x8ACF1C2B, 0xA779B492, 0xF307F2F0, 0x4E69E2A1, 0x65DAF4CD, 0x0605BED5, 0xD134621F, 0xC4A6FE8A,
- 0x342E539D, 0xA2F355A0, 0x058AE132, 0xA4F6EB75, 0x0B83EC39, 0x4060EFAA, 0x5E719F06, 0xBD6E1051,
- 0x3E218AF9, 0x96DD063D, 0xDD3E05AE, 0x4DE6BD46, 0x91548DB5, 0x71C45D05, 0x0406D46F, 0x605015FF,
- 0x1998FB24, 0xD6BDE997, 0x894043CC, 0x67D99E77, 0xB0E842BD, 0x07898B88, 0xE7195B38, 0x79C8EEDB,
- 0xA17C0A47, 0x7C420FE9, 0xF8841EC9, 0x00000000, 0x09808683, 0x322BED48, 0x1E1170AC, 0x6C5A724E,
- 0xFD0EFFFB, 0x0F853856, 0x3DAED51E, 0x362D3927, 0x0A0FD964, 0x685CA621, 0x9B5B54D1, 0x24362E3A,
- 0x0C0A67B1, 0x9357E70F, 0xB4EE96D2, 0x1B9B919E, 0x80C0C54F, 0x61DC20A2, 0x5A774B69, 0x1C121A16,
- 0xE293BA0A, 0xC0A02AE5, 0x3C22E043, 0x121B171D, 0x0E090D0B, 0xF28BC7AD, 0x2DB6A8B9, 0x141EA9C8,
- 0x57F11985, 0xAF75074C, 0xEE99DDBB, 0xA37F60FD, 0xF701269F, 0x5C72F5BC, 0x44663BC5, 0x5BFB7E34,
- 0x8B432976, 0xCB23C6DC, 0xB6EDFC68, 0xB8E4F163, 0xD731DCCA, 0x42638510, 0x13972240, 0x84C61120,
- 0x854A247D, 0xD2BB3DF8, 0xAEF93211, 0xC729A16D, 0x1D9E2F4B, 0xDCB230F3, 0x0D8652EC, 0x77C1E3D0,
- 0x2BB3166C, 0xA970B999, 0x119448FA, 0x47E96422, 0xA8FC8CC4, 0xA0F03F1A, 0x567D2CD8, 0x223390EF,
- 0x87494EC7, 0xD938D1C1, 0x8CCAA2FE, 0x98D40B36, 0xA6F581CF, 0xA57ADE28, 0xDAB78E26, 0x3FADBFA4,
- 0x2C3A9DE4, 0x5078920D, 0x6A5FCC9B, 0x547E4662, 0xF68D13C2, 0x90D8B8E8, 0x2E39F75E, 0x82C3AFF5,
- 0x9F5D80BE, 0x69D0937C, 0x6FD52DA9, 0xCF2512B3, 0xC8AC993B, 0x10187DA7, 0xE89C636E, 0xDB3BBB7B,
- 0xCD267809, 0x6E5918F4, 0xEC9AB701, 0x834F9AA8, 0xE6956E65, 0xAAFFE67E, 0x21BCCF08, 0xEF15E8E6,
- 0xBAE79BD9, 0x4A6F36CE, 0xEA9F09D4, 0x29B07CD6, 0x31A4B2AF, 0x2A3F2331, 0xC6A59430, 0x35A266C0,
- 0x744EBC37, 0xFC82CAA6, 0xE090D0B0, 0x33A7D815, 0xF104984A, 0x41ECDAF7, 0x7FCD500E, 0x1791F62F,
- 0x764DD68D, 0x43EFB04D, 0xCCAA4D54, 0xE49604DF, 0x9ED1B5E3, 0x4C6A881B, 0xC12C1FB8, 0x4665517F,
- 0x9D5EEA04, 0x018C355D, 0xFA877473, 0xFB0B412E, 0xB3671D5A, 0x92DBD252, 0xE9105633, 0x6DD64713,
- 0x9AD7618C, 0x37A10C7A, 0x59F8148E, 0xEB133C89, 0xCEA927EE, 0xB761C935, 0xE11CE5ED, 0x7A47B13C,
- 0x9CD2DF59, 0x55F2733F, 0x1814CE79, 0x73C737BF, 0x53F7CDEA, 0x5FFDAA5B, 0xDF3D6F14, 0x7844DB86,
- 0xCAAFF381, 0xB968C43E, 0x3824342C, 0xC2A3405F, 0x161DC372, 0xBCE2250C, 0x283C498B, 0xFF0D9541,
- 0x39A80171, 0x080CB3DE, 0xD8B4E49C, 0x6456C190, 0x7BCB8461, 0xD532B670, 0x486C5C74, 0xD0B85742
- );
-
- /**
- * Precomputed invMixColumns table
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $dt1 = array(
- 0x5051F4A7, 0x537E4165, 0xC31A17A4, 0x963A275E, 0xCB3BAB6B, 0xF11F9D45, 0xABACFA58, 0x934BE303,
- 0x552030FA, 0xF6AD766D, 0x9188CC76, 0x25F5024C, 0xFC4FE5D7, 0xD7C52ACB, 0x80263544, 0x8FB562A3,
- 0x49DEB15A, 0x6725BA1B, 0x9845EA0E, 0xE15DFEC0, 0x02C32F75, 0x12814CF0, 0xA38D4697, 0xC66BD3F9,
- 0xE7038F5F, 0x9515929C, 0xEBBF6D7A, 0xDA955259, 0x2DD4BE83, 0xD3587421, 0x2949E069, 0x448EC9C8,
- 0x6A75C289, 0x78F48E79, 0x6B99583E, 0xDD27B971, 0xB6BEE14F, 0x17F088AD, 0x66C920AC, 0xB47DCE3A,
- 0x1863DF4A, 0x82E51A31, 0x60975133, 0x4562537F, 0xE0B16477, 0x84BB6BAE, 0x1CFE81A0, 0x94F9082B,
- 0x58704868, 0x198F45FD, 0x8794DE6C, 0xB7527BF8, 0x23AB73D3, 0xE2724B02, 0x57E31F8F, 0x2A6655AB,
- 0x07B2EB28, 0x032FB5C2, 0x9A86C57B, 0xA5D33708, 0xF2302887, 0xB223BFA5, 0xBA02036A, 0x5CED1682,
- 0x2B8ACF1C, 0x92A779B4, 0xF0F307F2, 0xA14E69E2, 0xCD65DAF4, 0xD50605BE, 0x1FD13462, 0x8AC4A6FE,
- 0x9D342E53, 0xA0A2F355, 0x32058AE1, 0x75A4F6EB, 0x390B83EC, 0xAA4060EF, 0x065E719F, 0x51BD6E10,
- 0xF93E218A, 0x3D96DD06, 0xAEDD3E05, 0x464DE6BD, 0xB591548D, 0x0571C45D, 0x6F0406D4, 0xFF605015,
- 0x241998FB, 0x97D6BDE9, 0xCC894043, 0x7767D99E, 0xBDB0E842, 0x8807898B, 0x38E7195B, 0xDB79C8EE,
- 0x47A17C0A, 0xE97C420F, 0xC9F8841E, 0x00000000, 0x83098086, 0x48322BED, 0xAC1E1170, 0x4E6C5A72,
- 0xFBFD0EFF, 0x560F8538, 0x1E3DAED5, 0x27362D39, 0x640A0FD9, 0x21685CA6, 0xD19B5B54, 0x3A24362E,
- 0xB10C0A67, 0x0F9357E7, 0xD2B4EE96, 0x9E1B9B91, 0x4F80C0C5, 0xA261DC20, 0x695A774B, 0x161C121A,
- 0x0AE293BA, 0xE5C0A02A, 0x433C22E0, 0x1D121B17, 0x0B0E090D, 0xADF28BC7, 0xB92DB6A8, 0xC8141EA9,
- 0x8557F119, 0x4CAF7507, 0xBBEE99DD, 0xFDA37F60, 0x9FF70126, 0xBC5C72F5, 0xC544663B, 0x345BFB7E,
- 0x768B4329, 0xDCCB23C6, 0x68B6EDFC, 0x63B8E4F1, 0xCAD731DC, 0x10426385, 0x40139722, 0x2084C611,
- 0x7D854A24, 0xF8D2BB3D, 0x11AEF932, 0x6DC729A1, 0x4B1D9E2F, 0xF3DCB230, 0xEC0D8652, 0xD077C1E3,
- 0x6C2BB316, 0x99A970B9, 0xFA119448, 0x2247E964, 0xC4A8FC8C, 0x1AA0F03F, 0xD8567D2C, 0xEF223390,
- 0xC787494E, 0xC1D938D1, 0xFE8CCAA2, 0x3698D40B, 0xCFA6F581, 0x28A57ADE, 0x26DAB78E, 0xA43FADBF,
- 0xE42C3A9D, 0x0D507892, 0x9B6A5FCC, 0x62547E46, 0xC2F68D13, 0xE890D8B8, 0x5E2E39F7, 0xF582C3AF,
- 0xBE9F5D80, 0x7C69D093, 0xA96FD52D, 0xB3CF2512, 0x3BC8AC99, 0xA710187D, 0x6EE89C63, 0x7BDB3BBB,
- 0x09CD2678, 0xF46E5918, 0x01EC9AB7, 0xA8834F9A, 0x65E6956E, 0x7EAAFFE6, 0x0821BCCF, 0xE6EF15E8,
- 0xD9BAE79B, 0xCE4A6F36, 0xD4EA9F09, 0xD629B07C, 0xAF31A4B2, 0x312A3F23, 0x30C6A594, 0xC035A266,
- 0x37744EBC, 0xA6FC82CA, 0xB0E090D0, 0x1533A7D8, 0x4AF10498, 0xF741ECDA, 0x0E7FCD50, 0x2F1791F6,
- 0x8D764DD6, 0x4D43EFB0, 0x54CCAA4D, 0xDFE49604, 0xE39ED1B5, 0x1B4C6A88, 0xB8C12C1F, 0x7F466551,
- 0x049D5EEA, 0x5D018C35, 0x73FA8774, 0x2EFB0B41, 0x5AB3671D, 0x5292DBD2, 0x33E91056, 0x136DD647,
- 0x8C9AD761, 0x7A37A10C, 0x8E59F814, 0x89EB133C, 0xEECEA927, 0x35B761C9, 0xEDE11CE5, 0x3C7A47B1,
- 0x599CD2DF, 0x3F55F273, 0x791814CE, 0xBF73C737, 0xEA53F7CD, 0x5B5FFDAA, 0x14DF3D6F, 0x867844DB,
- 0x81CAAFF3, 0x3EB968C4, 0x2C382434, 0x5FC2A340, 0x72161DC3, 0x0CBCE225, 0x8B283C49, 0x41FF0D95,
- 0x7139A801, 0xDE080CB3, 0x9CD8B4E4, 0x906456C1, 0x617BCB84, 0x70D532B6, 0x74486C5C, 0x42D0B857
- );
-
- /**
- * Precomputed invMixColumns table
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $dt2 = array(
- 0xA75051F4, 0x65537E41, 0xA4C31A17, 0x5E963A27, 0x6BCB3BAB, 0x45F11F9D, 0x58ABACFA, 0x03934BE3,
- 0xFA552030, 0x6DF6AD76, 0x769188CC, 0x4C25F502, 0xD7FC4FE5, 0xCBD7C52A, 0x44802635, 0xA38FB562,
- 0x5A49DEB1, 0x1B6725BA, 0x0E9845EA, 0xC0E15DFE, 0x7502C32F, 0xF012814C, 0x97A38D46, 0xF9C66BD3,
- 0x5FE7038F, 0x9C951592, 0x7AEBBF6D, 0x59DA9552, 0x832DD4BE, 0x21D35874, 0x692949E0, 0xC8448EC9,
- 0x896A75C2, 0x7978F48E, 0x3E6B9958, 0x71DD27B9, 0x4FB6BEE1, 0xAD17F088, 0xAC66C920, 0x3AB47DCE,
- 0x4A1863DF, 0x3182E51A, 0x33609751, 0x7F456253, 0x77E0B164, 0xAE84BB6B, 0xA01CFE81, 0x2B94F908,
- 0x68587048, 0xFD198F45, 0x6C8794DE, 0xF8B7527B, 0xD323AB73, 0x02E2724B, 0x8F57E31F, 0xAB2A6655,
- 0x2807B2EB, 0xC2032FB5, 0x7B9A86C5, 0x08A5D337, 0x87F23028, 0xA5B223BF, 0x6ABA0203, 0x825CED16,
- 0x1C2B8ACF, 0xB492A779, 0xF2F0F307, 0xE2A14E69, 0xF4CD65DA, 0xBED50605, 0x621FD134, 0xFE8AC4A6,
- 0x539D342E, 0x55A0A2F3, 0xE132058A, 0xEB75A4F6, 0xEC390B83, 0xEFAA4060, 0x9F065E71, 0x1051BD6E,
- 0x8AF93E21, 0x063D96DD, 0x05AEDD3E, 0xBD464DE6, 0x8DB59154, 0x5D0571C4, 0xD46F0406, 0x15FF6050,
- 0xFB241998, 0xE997D6BD, 0x43CC8940, 0x9E7767D9, 0x42BDB0E8, 0x8B880789, 0x5B38E719, 0xEEDB79C8,
- 0x0A47A17C, 0x0FE97C42, 0x1EC9F884, 0x00000000, 0x86830980, 0xED48322B, 0x70AC1E11, 0x724E6C5A,
- 0xFFFBFD0E, 0x38560F85, 0xD51E3DAE, 0x3927362D, 0xD9640A0F, 0xA621685C, 0x54D19B5B, 0x2E3A2436,
- 0x67B10C0A, 0xE70F9357, 0x96D2B4EE, 0x919E1B9B, 0xC54F80C0, 0x20A261DC, 0x4B695A77, 0x1A161C12,
- 0xBA0AE293, 0x2AE5C0A0, 0xE0433C22, 0x171D121B, 0x0D0B0E09, 0xC7ADF28B, 0xA8B92DB6, 0xA9C8141E,
- 0x198557F1, 0x074CAF75, 0xDDBBEE99, 0x60FDA37F, 0x269FF701, 0xF5BC5C72, 0x3BC54466, 0x7E345BFB,
- 0x29768B43, 0xC6DCCB23, 0xFC68B6ED, 0xF163B8E4, 0xDCCAD731, 0x85104263, 0x22401397, 0x112084C6,
- 0x247D854A, 0x3DF8D2BB, 0x3211AEF9, 0xA16DC729, 0x2F4B1D9E, 0x30F3DCB2, 0x52EC0D86, 0xE3D077C1,
- 0x166C2BB3, 0xB999A970, 0x48FA1194, 0x642247E9, 0x8CC4A8FC, 0x3F1AA0F0, 0x2CD8567D, 0x90EF2233,
- 0x4EC78749, 0xD1C1D938, 0xA2FE8CCA, 0x0B3698D4, 0x81CFA6F5, 0xDE28A57A, 0x8E26DAB7, 0xBFA43FAD,
- 0x9DE42C3A, 0x920D5078, 0xCC9B6A5F, 0x4662547E, 0x13C2F68D, 0xB8E890D8, 0xF75E2E39, 0xAFF582C3,
- 0x80BE9F5D, 0x937C69D0, 0x2DA96FD5, 0x12B3CF25, 0x993BC8AC, 0x7DA71018, 0x636EE89C, 0xBB7BDB3B,
- 0x7809CD26, 0x18F46E59, 0xB701EC9A, 0x9AA8834F, 0x6E65E695, 0xE67EAAFF, 0xCF0821BC, 0xE8E6EF15,
- 0x9BD9BAE7, 0x36CE4A6F, 0x09D4EA9F, 0x7CD629B0, 0xB2AF31A4, 0x23312A3F, 0x9430C6A5, 0x66C035A2,
- 0xBC37744E, 0xCAA6FC82, 0xD0B0E090, 0xD81533A7, 0x984AF104, 0xDAF741EC, 0x500E7FCD, 0xF62F1791,
- 0xD68D764D, 0xB04D43EF, 0x4D54CCAA, 0x04DFE496, 0xB5E39ED1, 0x881B4C6A, 0x1FB8C12C, 0x517F4665,
- 0xEA049D5E, 0x355D018C, 0x7473FA87, 0x412EFB0B, 0x1D5AB367, 0xD25292DB, 0x5633E910, 0x47136DD6,
- 0x618C9AD7, 0x0C7A37A1, 0x148E59F8, 0x3C89EB13, 0x27EECEA9, 0xC935B761, 0xE5EDE11C, 0xB13C7A47,
- 0xDF599CD2, 0x733F55F2, 0xCE791814, 0x37BF73C7, 0xCDEA53F7, 0xAA5B5FFD, 0x6F14DF3D, 0xDB867844,
- 0xF381CAAF, 0xC43EB968, 0x342C3824, 0x405FC2A3, 0xC372161D, 0x250CBCE2, 0x498B283C, 0x9541FF0D,
- 0x017139A8, 0xB3DE080C, 0xE49CD8B4, 0xC1906456, 0x84617BCB, 0xB670D532, 0x5C74486C, 0x5742D0B8
- );
-
- /**
- * Precomputed invMixColumns table
- *
- * @see Crypt_Rijndael:_encryptBlock()
- * @see Crypt_Rijndael:_decryptBlock()
- * @var Array
- * @access private
- */
- var $dt3 = array(
- 0xF4A75051, 0x4165537E, 0x17A4C31A, 0x275E963A, 0xAB6BCB3B, 0x9D45F11F, 0xFA58ABAC, 0xE303934B,
- 0x30FA5520, 0x766DF6AD, 0xCC769188, 0x024C25F5, 0xE5D7FC4F, 0x2ACBD7C5, 0x35448026, 0x62A38FB5,
- 0xB15A49DE, 0xBA1B6725, 0xEA0E9845, 0xFEC0E15D, 0x2F7502C3, 0x4CF01281, 0x4697A38D, 0xD3F9C66B,
- 0x8F5FE703, 0x929C9515, 0x6D7AEBBF, 0x5259DA95, 0xBE832DD4, 0x7421D358, 0xE0692949, 0xC9C8448E,
- 0xC2896A75, 0x8E7978F4, 0x583E6B99, 0xB971DD27, 0xE14FB6BE, 0x88AD17F0, 0x20AC66C9, 0xCE3AB47D,
- 0xDF4A1863, 0x1A3182E5, 0x51336097, 0x537F4562, 0x6477E0B1, 0x6BAE84BB, 0x81A01CFE, 0x082B94F9,
- 0x48685870, 0x45FD198F, 0xDE6C8794, 0x7BF8B752, 0x73D323AB, 0x4B02E272, 0x1F8F57E3, 0x55AB2A66,
- 0xEB2807B2, 0xB5C2032F, 0xC57B9A86, 0x3708A5D3, 0x2887F230, 0xBFA5B223, 0x036ABA02, 0x16825CED,
- 0xCF1C2B8A, 0x79B492A7, 0x07F2F0F3, 0x69E2A14E, 0xDAF4CD65, 0x05BED506, 0x34621FD1, 0xA6FE8AC4,
- 0x2E539D34, 0xF355A0A2, 0x8AE13205, 0xF6EB75A4, 0x83EC390B, 0x60EFAA40, 0x719F065E, 0x6E1051BD,
- 0x218AF93E, 0xDD063D96, 0x3E05AEDD, 0xE6BD464D, 0x548DB591, 0xC45D0571, 0x06D46F04, 0x5015FF60,
- 0x98FB2419, 0xBDE997D6, 0x4043CC89, 0xD99E7767, 0xE842BDB0, 0x898B8807, 0x195B38E7, 0xC8EEDB79,
- 0x7C0A47A1, 0x420FE97C, 0x841EC9F8, 0x00000000, 0x80868309, 0x2BED4832, 0x1170AC1E, 0x5A724E6C,
- 0x0EFFFBFD, 0x8538560F, 0xAED51E3D, 0x2D392736, 0x0FD9640A, 0x5CA62168, 0x5B54D19B, 0x362E3A24,
- 0x0A67B10C, 0x57E70F93, 0xEE96D2B4, 0x9B919E1B, 0xC0C54F80, 0xDC20A261, 0x774B695A, 0x121A161C,
- 0x93BA0AE2, 0xA02AE5C0, 0x22E0433C, 0x1B171D12, 0x090D0B0E, 0x8BC7ADF2, 0xB6A8B92D, 0x1EA9C814,
- 0xF1198557, 0x75074CAF, 0x99DDBBEE, 0x7F60FDA3, 0x01269FF7, 0x72F5BC5C, 0x663BC544, 0xFB7E345B,
- 0x4329768B, 0x23C6DCCB, 0xEDFC68B6, 0xE4F163B8, 0x31DCCAD7, 0x63851042, 0x97224013, 0xC6112084,
- 0x4A247D85, 0xBB3DF8D2, 0xF93211AE, 0x29A16DC7, 0x9E2F4B1D, 0xB230F3DC, 0x8652EC0D, 0xC1E3D077,
- 0xB3166C2B, 0x70B999A9, 0x9448FA11, 0xE9642247, 0xFC8CC4A8, 0xF03F1AA0, 0x7D2CD856, 0x3390EF22,
- 0x494EC787, 0x38D1C1D9, 0xCAA2FE8C, 0xD40B3698, 0xF581CFA6, 0x7ADE28A5, 0xB78E26DA, 0xADBFA43F,
- 0x3A9DE42C, 0x78920D50, 0x5FCC9B6A, 0x7E466254, 0x8D13C2F6, 0xD8B8E890, 0x39F75E2E, 0xC3AFF582,
- 0x5D80BE9F, 0xD0937C69, 0xD52DA96F, 0x2512B3CF, 0xAC993BC8, 0x187DA710, 0x9C636EE8, 0x3BBB7BDB,
- 0x267809CD, 0x5918F46E, 0x9AB701EC, 0x4F9AA883, 0x956E65E6, 0xFFE67EAA, 0xBCCF0821, 0x15E8E6EF,
- 0xE79BD9BA, 0x6F36CE4A, 0x9F09D4EA, 0xB07CD629, 0xA4B2AF31, 0x3F23312A, 0xA59430C6, 0xA266C035,
- 0x4EBC3774, 0x82CAA6FC, 0x90D0B0E0, 0xA7D81533, 0x04984AF1, 0xECDAF741, 0xCD500E7F, 0x91F62F17,
- 0x4DD68D76, 0xEFB04D43, 0xAA4D54CC, 0x9604DFE4, 0xD1B5E39E, 0x6A881B4C, 0x2C1FB8C1, 0x65517F46,
- 0x5EEA049D, 0x8C355D01, 0x877473FA, 0x0B412EFB, 0x671D5AB3, 0xDBD25292, 0x105633E9, 0xD647136D,
- 0xD7618C9A, 0xA10C7A37, 0xF8148E59, 0x133C89EB, 0xA927EECE, 0x61C935B7, 0x1CE5EDE1, 0x47B13C7A,
- 0xD2DF599C, 0xF2733F55, 0x14CE7918, 0xC737BF73, 0xF7CDEA53, 0xFDAA5B5F, 0x3D6F14DF, 0x44DB8678,
- 0xAFF381CA, 0x68C43EB9, 0x24342C38, 0xA3405FC2, 0x1DC37216, 0xE2250CBC, 0x3C498B28, 0x0D9541FF,
- 0xA8017139, 0x0CB3DE08, 0xB4E49CD8, 0x56C19064, 0xCB84617B, 0x32B670D5, 0x6C5C7448, 0xB85742D0
- );
-
- /**
- * The SubByte S-Box
- *
- * @see Crypt_Rijndael::_encryptBlock()
- * @var Array
- * @access private
- */
- var $sbox = array(
- 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,
- 0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,
- 0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,
- 0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,
- 0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,
- 0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,
- 0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,
- 0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,
- 0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,
- 0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,
- 0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,
- 0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,
- 0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,
- 0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,
- 0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,
- 0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16
- );
-
- /**
- * The inverse SubByte S-Box
- *
- * @see Crypt_Rijndael::_decryptBlock()
- * @var Array
- * @access private
- */
- var $isbox = array(
- 0x52, 0x09, 0x6A, 0xD5, 0x30, 0x36, 0xA5, 0x38, 0xBF, 0x40, 0xA3, 0x9E, 0x81, 0xF3, 0xD7, 0xFB,
- 0x7C, 0xE3, 0x39, 0x82, 0x9B, 0x2F, 0xFF, 0x87, 0x34, 0x8E, 0x43, 0x44, 0xC4, 0xDE, 0xE9, 0xCB,
- 0x54, 0x7B, 0x94, 0x32, 0xA6, 0xC2, 0x23, 0x3D, 0xEE, 0x4C, 0x95, 0x0B, 0x42, 0xFA, 0xC3, 0x4E,
- 0x08, 0x2E, 0xA1, 0x66, 0x28, 0xD9, 0x24, 0xB2, 0x76, 0x5B, 0xA2, 0x49, 0x6D, 0x8B, 0xD1, 0x25,
- 0x72, 0xF8, 0xF6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xD4, 0xA4, 0x5C, 0xCC, 0x5D, 0x65, 0xB6, 0x92,
- 0x6C, 0x70, 0x48, 0x50, 0xFD, 0xED, 0xB9, 0xDA, 0x5E, 0x15, 0x46, 0x57, 0xA7, 0x8D, 0x9D, 0x84,
- 0x90, 0xD8, 0xAB, 0x00, 0x8C, 0xBC, 0xD3, 0x0A, 0xF7, 0xE4, 0x58, 0x05, 0xB8, 0xB3, 0x45, 0x06,
- 0xD0, 0x2C, 0x1E, 0x8F, 0xCA, 0x3F, 0x0F, 0x02, 0xC1, 0xAF, 0xBD, 0x03, 0x01, 0x13, 0x8A, 0x6B,
- 0x3A, 0x91, 0x11, 0x41, 0x4F, 0x67, 0xDC, 0xEA, 0x97, 0xF2, 0xCF, 0xCE, 0xF0, 0xB4, 0xE6, 0x73,
- 0x96, 0xAC, 0x74, 0x22, 0xE7, 0xAD, 0x35, 0x85, 0xE2, 0xF9, 0x37, 0xE8, 0x1C, 0x75, 0xDF, 0x6E,
- 0x47, 0xF1, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7, 0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B,
- 0xFC, 0x56, 0x3E, 0x4B, 0xC6, 0xD2, 0x79, 0x20, 0x9A, 0xDB, 0xC0, 0xFE, 0x78, 0xCD, 0x5A, 0xF4,
- 0x1F, 0xDD, 0xA8, 0x33, 0x88, 0x07, 0xC7, 0x31, 0xB1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xEC, 0x5F,
- 0x60, 0x51, 0x7F, 0xA9, 0x19, 0xB5, 0x4A, 0x0D, 0x2D, 0xE5, 0x7A, 0x9F, 0x93, 0xC9, 0x9C, 0xEF,
- 0xA0, 0xE0, 0x3B, 0x4D, 0xAE, 0x2A, 0xF5, 0xB0, 0xC8, 0xEB, 0xBB, 0x3C, 0x83, 0x53, 0x99, 0x61,
- 0x17, 0x2B, 0x04, 0x7E, 0xBA, 0x77, 0xD6, 0x26, 0xE1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0C, 0x7D
- );
-
- /**
- * Default Constructor.
- *
- * Determines whether or not the mcrypt extension should be used.
- *
- * $mode could be:
- *
- * - CRYPT_RIJNDAEL_MODE_ECB
- *
- * - CRYPT_RIJNDAEL_MODE_CBC
- *
- * - CRYPT_RIJNDAEL_MODE_CTR
- *
- * - CRYPT_RIJNDAEL_MODE_CFB
- *
- * - CRYPT_RIJNDAEL_MODE_OFB
- *
- * If not explictly set, CRYPT_RIJNDAEL_MODE_CBC will be used.
- *
- * @see Crypt_Base::Crypt_Base()
- * @param optional Integer $mode
- * @access public
- */
- function Crypt_Rijndael($mode = CRYPT_RIJNDAEL_MODE_CBC)
- {
- parent::Crypt_Base($mode);
- }
-
- /**
- * Sets the key.
- *
- * Keys can be of any length. Rijndael, itself, requires the use of a key that's between 128-bits and 256-bits long and
- * whose length is a multiple of 32. If the key is less than 256-bits and the key length isn't set, we round the length
- * up to the closest valid key length, padding $key with null bytes. If the key is more than 256-bits, we trim the
- * excess bits.
- *
- * If the key is not explicitly set, it'll be assumed to be all null bytes.
- *
- * Note: 160/224-bit keys must explicitly set by setKeyLength(), otherwise they will be round/pad up to 192/256 bits.
- *
- * @see Crypt_Base:setKey()
- * @see setKeyLength()
- * @access public
- * @param String $key
- */
- function setKey($key)
- {
- parent::setKey($key);
-
- if (!$this->explicit_key_length) {
- $length = strlen($key);
- switch (true) {
- case $length <= 16:
- $this->key_size = 16;
- break;
- case $length <= 24:
- $this->key_size = 24;
- break;
- default:
- $this->key_size = 32;
- }
- $this->_setupEngine();
- }
- }
-
- /**
- * Sets the key length
- *
- * Valid key lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
- * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
- *
- * Note: phpseclib extends Rijndael (and AES) for using 160- and 224-bit keys but they are officially not defined
- * and the most (if not all) implementations are not able using 160/224-bit keys but round/pad them up to
- * 192/256 bits as, for example, mcrypt will do.
- *
- * That said, if you want be compatible with other Rijndael and AES implementations,
- * you should not setKeyLength(160) or setKeyLength(224).
- *
- * Additional: In case of 160- and 224-bit keys, phpseclib will/can, for that reason, not use
- * the mcrypt php extention, even if available.
- * This results then in slower encryption.
- *
- * @access public
- * @param Integer $length
- */
- function setKeyLength($length)
- {
- switch (true) {
- case $length == 160:
- $this->key_size = 20;
- break;
- case $length == 224:
- $this->key_size = 28;
- break;
- case $length <= 128:
- $this->key_size = 16;
- break;
- case $length <= 192:
- $this->key_size = 24;
- break;
- default:
- $this->key_size = 32;
- }
-
- $this->explicit_key_length = true;
- $this->changed = true;
- $this->_setupEngine();
- }
-
- /**
- * Sets the block length
- *
- * Valid block lengths are 128, 160, 192, 224, and 256. If the length is less than 128, it will be rounded up to
- * 128. If the length is greater than 128 and invalid, it will be rounded down to the closest valid amount.
- *
- * @access public
- * @param Integer $length
- */
- function setBlockLength($length)
- {
- $length >>= 5;
- if ($length > 8) {
- $length = 8;
- } else if ($length < 4) {
- $length = 4;
- }
- $this->Nb = $length;
- $this->block_size = $length << 2;
- $this->changed = true;
- $this->_setupEngine();
- }
-
- /**
- * Setup the fastest possible $engine
- *
- * Determines if the mcrypt (MODE_MCRYPT) $engine available
- * and usable for the current $block_size and $key_size.
- *
- * If not, the slower MODE_INTERNAL $engine will be set.
- *
- * @see setKey()
- * @see setKeyLength()
- * @see setBlockLength()
- * @access private
- */
- function _setupEngine()
- {
- if (constant('CRYPT_' . $this->const_namespace . '_MODE') == CRYPT_MODE_INTERNAL) {
- // No mcrypt support at all for rijndael
- return;
- }
-
- // The required mcrypt module name for the current $block_size of rijndael
- $cipher_name_mcrypt = 'rijndael-' . ($this->block_size << 3);
-
- // Determining the availibility/usability of $cipher_name_mcrypt
- switch (true) {
- case $this->key_size % 8: // mcrypt is not usable for 160/224-bit keys, only for 128/192/256-bit keys
- case !in_array($cipher_name_mcrypt, mcrypt_list_algorithms()): // $cipher_name_mcrypt is not available for the current $block_size
- $engine = CRYPT_MODE_INTERNAL;
- break;
- default:
- $engine = CRYPT_MODE_MCRYPT;
- }
-
- if ($this->engine == $engine && $this->cipher_name_mcrypt == $cipher_name_mcrypt) {
- // allready set, so we not unnecessary close $this->enmcrypt/demcrypt/ecb
- return;
- }
-
- // Set the $engine
- $this->engine = $engine;
- $this->cipher_name_mcrypt = $cipher_name_mcrypt;
-
- if ($this->enmcrypt) {
- // Closing the current mcrypt resource(s). _mcryptSetup() will, if needed,
- // (re)open them with the module named in $this->cipher_name_mcrypt
- mcrypt_module_close($this->enmcrypt);
- mcrypt_module_close($this->demcrypt);
- $this->enmcrypt = null;
- $this->demcrypt = null;
-
- if ($this->ecb) {
- mcrypt_module_close($this->ecb);
- $this->ecb = null;
- }
- }
- }
-
- /**
- * Setup the CRYPT_MODE_MCRYPT $engine
- *
- * @see Crypt_Base::_setupMcrypt()
- * @access private
- */
- function _setupMcrypt()
- {
- $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0");
- parent::_setupMcrypt();
- }
-
- /**
- * Encrypts a block
- *
- * @access private
- * @param String $in
- * @return String
- */
- function _encryptBlock($in)
- {
- static $t0, $t1, $t2, $t3, $sbox;
- if (!$t0) {
- for ($i = 0; $i < 256; ++$i) {
- $t0[] = (int)$this->t0[$i];
- $t1[] = (int)$this->t1[$i];
- $t2[] = (int)$this->t2[$i];
- $t3[] = (int)$this->t3[$i];
- $sbox[] = (int)$this->sbox[$i];
- }
- }
-
- $state = array();
- $words = unpack('N*', $in);
-
- $c = $this->c;
- $w = $this->w;
- $Nb = $this->Nb;
- $Nr = $this->Nr;
-
- // addRoundKey
- $i = -1;
- foreach ($words as $word) {
- $state[] = $word ^ $w[0][++$i];
- }
-
- // fips-197.pdf#page=19, "Figure 5. Pseudo Code for the Cipher", states that this loop has four components -
- // subBytes, shiftRows, mixColumns, and addRoundKey. fips-197.pdf#page=30, "Implementation Suggestions Regarding
- // Various Platforms" suggests that performs enhanced implementations are described in Rijndael-ammended.pdf.
- // Rijndael-ammended.pdf#page=20, "Implementation aspects / 32-bit processor", discusses such an optimization.
- // Unfortunately, the description given there is not quite correct. Per aes.spec.v316.pdf#page=19 [1],
- // equation (7.4.7) is supposed to use addition instead of subtraction, so we'll do that here, as well.
-
- // [1] http://fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.v316.pdf
- $temp = array();
- for ($round = 1; $round < $Nr; ++$round) {
- $i = 0; // $c[0] == 0
- $j = $c[1];
- $k = $c[2];
- $l = $c[3];
-
- while ($i < $Nb) {
- $temp[$i] = $t0[$state[$i] >> 24 & 0x000000FF] ^
- $t1[$state[$j] >> 16 & 0x000000FF] ^
- $t2[$state[$k] >> 8 & 0x000000FF] ^
- $t3[$state[$l] & 0x000000FF] ^
- $w[$round][$i];
- ++$i;
- $j = ($j + 1) % $Nb;
- $k = ($k + 1) % $Nb;
- $l = ($l + 1) % $Nb;
- }
- $state = $temp;
- }
-
- // subWord
- for ($i = 0; $i < $Nb; ++$i) {
- $state[$i] = $sbox[$state[$i] & 0x000000FF] |
- ($sbox[$state[$i] >> 8 & 0x000000FF] << 8) |
- ($sbox[$state[$i] >> 16 & 0x000000FF] << 16) |
- ($sbox[$state[$i] >> 24 & 0x000000FF] << 24);
- }
-
- // shiftRows + addRoundKey
- $i = 0; // $c[0] == 0
- $j = $c[1];
- $k = $c[2];
- $l = $c[3];
- while ($i < $Nb) {
- $temp[$i] = ($state[$i] & 0xFF000000) ^
- ($state[$j] & 0x00FF0000) ^
- ($state[$k] & 0x0000FF00) ^
- ($state[$l] & 0x000000FF) ^
- $w[$Nr][$i];
- ++$i;
- $j = ($j + 1) % $Nb;
- $k = ($k + 1) % $Nb;
- $l = ($l + 1) % $Nb;
- }
-
- switch ($Nb) {
- case 8:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
- case 7:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
- case 6:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
- case 5:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
- default:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
- }
- }
-
- /**
- * Decrypts a block
- *
- * @access private
- * @param String $in
- * @return String
- */
- function _decryptBlock($in)
- {
- static $dt0, $dt1, $dt2, $dt3, $isbox;
- if (!$dt0) {
- for ($i = 0; $i < 256; ++$i) {
- $dt0[] = (int)$this->dt0[$i];
- $dt1[] = (int)$this->dt1[$i];
- $dt2[] = (int)$this->dt2[$i];
- $dt3[] = (int)$this->dt3[$i];
- $isbox[] = (int)$this->isbox[$i];
- }
- }
-
- $state = array();
- $words = unpack('N*', $in);
-
- $c = $this->c;
- $dw = $this->dw;
- $Nb = $this->Nb;
- $Nr = $this->Nr;
-
- // addRoundKey
- $i = -1;
- foreach ($words as $word) {
- $state[] = $word ^ $dw[$Nr][++$i];
- }
-
- $temp = array();
- for ($round = $Nr - 1; $round > 0; --$round) {
- $i = 0; // $c[0] == 0
- $j = $Nb - $c[1];
- $k = $Nb - $c[2];
- $l = $Nb - $c[3];
-
- while ($i < $Nb) {
- $temp[$i] = $dt0[$state[$i] >> 24 & 0x000000FF] ^
- $dt1[$state[$j] >> 16 & 0x000000FF] ^
- $dt2[$state[$k] >> 8 & 0x000000FF] ^
- $dt3[$state[$l] & 0x000000FF] ^
- $dw[$round][$i];
- ++$i;
- $j = ($j + 1) % $Nb;
- $k = ($k + 1) % $Nb;
- $l = ($l + 1) % $Nb;
- }
- $state = $temp;
- }
-
- // invShiftRows + invSubWord + addRoundKey
- $i = 0; // $c[0] == 0
- $j = $Nb - $c[1];
- $k = $Nb - $c[2];
- $l = $Nb - $c[3];
-
- while ($i < $Nb) {
- $word = ($state[$i] & 0xFF000000) |
- ($state[$j] & 0x00FF0000) |
- ($state[$k] & 0x0000FF00) |
- ($state[$l] & 0x000000FF);
-
- $temp[$i] = $dw[0][$i] ^ ($isbox[$word & 0x000000FF] |
- ($isbox[$word >> 8 & 0x000000FF] << 8) |
- ($isbox[$word >> 16 & 0x000000FF] << 16) |
- ($isbox[$word >> 24 & 0x000000FF] << 24));
- ++$i;
- $j = ($j + 1) % $Nb;
- $k = ($k + 1) % $Nb;
- $l = ($l + 1) % $Nb;
- }
-
- switch ($Nb) {
- case 8:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6], $temp[7]);
- case 7:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5], $temp[6]);
- case 6:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4], $temp[5]);
- case 5:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3], $temp[4]);
- default:
- return pack('N*', $temp[0], $temp[1], $temp[2], $temp[3]);
- }
- }
-
- /**
- * Setup the key (expansion)
- *
- * @see Crypt_Base::_setupKey()
- * @access private
- */
- function _setupKey()
- {
- // Each number in $rcon is equal to the previous number multiplied by two in Rijndael's finite field.
- // See http://en.wikipedia.org/wiki/Finite_field_arithmetic#Multiplicative_inverse
- static $rcon = array(0,
- 0x01000000, 0x02000000, 0x04000000, 0x08000000, 0x10000000,
- 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000,
- 0x6C000000, 0xD8000000, 0xAB000000, 0x4D000000, 0x9A000000,
- 0x2F000000, 0x5E000000, 0xBC000000, 0x63000000, 0xC6000000,
- 0x97000000, 0x35000000, 0x6A000000, 0xD4000000, 0xB3000000,
- 0x7D000000, 0xFA000000, 0xEF000000, 0xC5000000, 0x91000000
- );
-
- $this->key = str_pad(substr($this->key, 0, $this->key_size), $this->key_size, "\0");
-
- if (isset($this->kl['key']) && $this->key === $this->kl['key'] && $this->key_size === $this->kl['key_size'] && $this->block_size === $this->kl['block_size']) {
- // already expanded
- return;
- }
- $this->kl = array('key' => $this->key, 'key_size' => $this->key_size, 'block_size' => $this->block_size);
-
- $this->Nk = $this->key_size >> 2;
- // see Rijndael-ammended.pdf#page=44
- $this->Nr = max($this->Nk, $this->Nb) + 6;
-
- // shift offsets for Nb = 5, 7 are defined in Rijndael-ammended.pdf#page=44,
- // "Table 8: Shift offsets in Shiftrow for the alternative block lengths"
- // shift offsets for Nb = 4, 6, 8 are defined in Rijndael-ammended.pdf#page=14,
- // "Table 2: Shift offsets for different block lengths"
- switch ($this->Nb) {
- case 4:
- case 5:
- case 6:
- $this->c = array(0, 1, 2, 3);
- break;
- case 7:
- $this->c = array(0, 1, 2, 4);
- break;
- case 8:
- $this->c = array(0, 1, 3, 4);
- }
-
- $w = array_values(unpack('N*words', $this->key));
-
- $length = $this->Nb * ($this->Nr + 1);
- for ($i = $this->Nk; $i < $length; $i++) {
- $temp = $w[$i - 1];
- if ($i % $this->Nk == 0) {
- // according to , "the size of an integer is platform-dependent".
- // on a 32-bit machine, it's 32-bits, and on a 64-bit machine, it's 64-bits. on a 32-bit machine,
- // 0xFFFFFFFF << 8 == 0xFFFFFF00, but on a 64-bit machine, it equals 0xFFFFFFFF00. as such, doing 'and'
- // with 0xFFFFFFFF (or 0xFFFFFF00) on a 32-bit machine is unnecessary, but on a 64-bit machine, it is.
- $temp = (($temp << 8) & 0xFFFFFF00) | (($temp >> 24) & 0x000000FF); // rotWord
- $temp = $this->_subWord($temp) ^ $rcon[$i / $this->Nk];
- } else if ($this->Nk > 6 && $i % $this->Nk == 4) {
- $temp = $this->_subWord($temp);
- }
- $w[$i] = $w[$i - $this->Nk] ^ $temp;
- }
-
- // convert the key schedule from a vector of $Nb * ($Nr + 1) length to a matrix with $Nr + 1 rows and $Nb columns
- // and generate the inverse key schedule. more specifically,
- // according to (section 5.3.3),
- // "The key expansion for the Inverse Cipher is defined as follows:
- // 1. Apply the Key Expansion.
- // 2. Apply InvMixColumn to all Round Keys except the first and the last one."
- // also, see fips-197.pdf#page=27, "5.3.5 Equivalent Inverse Cipher"
- $temp = $this->w = $this->dw = array();
- for ($i = $row = $col = 0; $i < $length; $i++, $col++) {
- if ($col == $this->Nb) {
- if ($row == 0) {
- $this->dw[0] = $this->w[0];
- } else {
- // subWord + invMixColumn + invSubWord = invMixColumn
- $j = 0;
- while ($j < $this->Nb) {
- $dw = $this->_subWord($this->w[$row][$j]);
- $temp[$j] = $this->dt0[$dw >> 24 & 0x000000FF] ^
- $this->dt1[$dw >> 16 & 0x000000FF] ^
- $this->dt2[$dw >> 8 & 0x000000FF] ^
- $this->dt3[$dw & 0x000000FF];
- $j++;
- }
- $this->dw[$row] = $temp;
- }
-
- $col = 0;
- $row++;
- }
- $this->w[$row][$col] = $w[$i];
- }
-
- $this->dw[$row] = $this->w[$row];
-
- // In case of $this->use_inline_crypt === true we have to use 1-dim key arrays (both ascending)
- if ($this->use_inline_crypt) {
- $this->dw = array_reverse($this->dw);
- $w = array_pop($this->w);
- $dw = array_pop($this->dw);
- foreach ($this->w as $r => $wr) {
- foreach ($wr as $c => $wc) {
- $w[] = $wc;
- $dw[] = $this->dw[$r][$c];
- }
- }
- $this->w = $w;
- $this->dw = $dw;
- }
- }
-
- /**
- * Performs S-Box substitutions
- *
- * @access private
- * @param Integer $word
- */
- function _subWord($word)
- {
- $sbox = $this->sbox;
-
- return $sbox[$word & 0x000000FF] |
- ($sbox[$word >> 8 & 0x000000FF] << 8) |
- ($sbox[$word >> 16 & 0x000000FF] << 16) |
- ($sbox[$word >> 24 & 0x000000FF] << 24);
- }
-
- /**
- * Setup the performance-optimized function for de/encrypt()
- *
- * @see Crypt_Base::_setupInlineCrypt()
- * @access private
- */
- function _setupInlineCrypt()
- {
- // Note: _setupInlineCrypt() will be called only if $this->changed === true
- // So here we are'nt under the same heavy timing-stress as we are in _de/encryptBlock() or de/encrypt().
- // However...the here generated function- $code, stored as php callback in $this->inline_crypt, must work as fast as even possible.
-
- $lambda_functions =& Crypt_Rijndael::_getLambdaFunctions();
-
- // The first 10 generated $lambda_functions will use the key-words hardcoded for better performance.
- // For memory reason we limit those ultra-optimized functions.
- // After that, we use pure (extracted) integer vars for the key-words which is faster than accessing them via array.
- if (count($lambda_functions) < 10) {
- $w = $this->w;
- $dw = $this->dw;
- $init_encrypt = '';
- $init_decrypt = '';
- } else {
- for ($i = 0, $cw = count($this->w); $i < $cw; ++$i) {
- $w[] = '$w[' . $i . ']';
- $dw[] = '$dw[' . $i . ']';
- }
- $init_encrypt = '$w = $self->w;';
- $init_decrypt = '$dw = $self->dw;';
- }
-
- $code_hash = md5(str_pad("Crypt_Rijndael, {$this->mode}, {$this->block_size}, ", 32, "\0") . implode(',', $w));
-
- if (!isset($lambda_functions[$code_hash])) {
- $Nr = $this->Nr;
- $Nb = $this->Nb;
- $c = $this->c;
-
- // Generating encrypt code:
- $init_encrypt.= '
- static $t0, $t1, $t2, $t3, $sbox;
- if (!$t0) {
- for ($i = 0; $i < 256; ++$i) {
- $t0[$i] = (int)$self->t0[$i];
- $t1[$i] = (int)$self->t1[$i];
- $t2[$i] = (int)$self->t2[$i];
- $t3[$i] = (int)$self->t3[$i];
- $sbox[$i] = (int)$self->sbox[$i];
- }
- }
- ';
-
- $s = 'e';
- $e = 's';
- $wc = $Nb - 1;
-
- // Preround: addRoundKey
- $encrypt_block = '$in = unpack("N*", $in);'."\n";
- for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$w[++$wc].";\n";
- }
-
- // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
- for ($round = 1; $round < $Nr; ++$round) {
- list($s, $e) = array($e, $s);
- for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block.=
- '$'.$e.$i.' =
- $t0[($'.$s.$i .' >> 24) & 0xff] ^
- $t1[($'.$s.(($i + $c[1]) % $Nb).' >> 16) & 0xff] ^
- $t2[($'.$s.(($i + $c[2]) % $Nb).' >> 8) & 0xff] ^
- $t3[ $'.$s.(($i + $c[3]) % $Nb).' & 0xff] ^
- '.$w[++$wc].";\n";
- }
- }
-
- // Finalround: subWord + shiftRows + addRoundKey
- for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block.=
- '$'.$e.$i.' =
- $sbox[ $'.$e.$i.' & 0xff] |
- ($sbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
- ($sbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
- ($sbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
- }
- $encrypt_block .= '$in = pack("N*"'."\n";
- for ($i = 0; $i < $Nb; ++$i) {
- $encrypt_block.= ',
- ($'.$e.$i .' & 0xFF000000) ^
- ($'.$e.(($i + $c[1]) % $Nb).' & 0x00FF0000) ^
- ($'.$e.(($i + $c[2]) % $Nb).' & 0x0000FF00) ^
- ($'.$e.(($i + $c[3]) % $Nb).' & 0x000000FF) ^
- '.$w[$i]."\n";
- }
- $encrypt_block .= ');';
-
- // Generating decrypt code:
- $init_decrypt.= '
- static $dt0, $dt1, $dt2, $dt3, $isbox;
- if (!$dt0) {
- for ($i = 0; $i < 256; ++$i) {
- $dt0[$i] = (int)$self->dt0[$i];
- $dt1[$i] = (int)$self->dt1[$i];
- $dt2[$i] = (int)$self->dt2[$i];
- $dt3[$i] = (int)$self->dt3[$i];
- $isbox[$i] = (int)$self->isbox[$i];
- }
- }
- ';
-
- $s = 'e';
- $e = 's';
- $wc = $Nb - 1;
-
- // Preround: addRoundKey
- $decrypt_block = '$in = unpack("N*", $in);'."\n";
- for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block .= '$s'.$i.' = $in['.($i + 1).'] ^ '.$dw[++$wc].';'."\n";
- }
-
- // Mainrounds: shiftRows + subWord + mixColumns + addRoundKey
- for ($round = 1; $round < $Nr; ++$round) {
- list($s, $e) = array($e, $s);
- for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block.=
- '$'.$e.$i.' =
- $dt0[($'.$s.$i .' >> 24) & 0xff] ^
- $dt1[($'.$s.(($Nb + $i - $c[1]) % $Nb).' >> 16) & 0xff] ^
- $dt2[($'.$s.(($Nb + $i - $c[2]) % $Nb).' >> 8) & 0xff] ^
- $dt3[ $'.$s.(($Nb + $i - $c[3]) % $Nb).' & 0xff] ^
- '.$dw[++$wc].";\n";
- }
- }
-
- // Finalround: subWord + shiftRows + addRoundKey
- for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block.=
- '$'.$e.$i.' =
- $isbox[ $'.$e.$i.' & 0xff] |
- ($isbox[($'.$e.$i.' >> 8) & 0xff] << 8) |
- ($isbox[($'.$e.$i.' >> 16) & 0xff] << 16) |
- ($isbox[($'.$e.$i.' >> 24) & 0xff] << 24);'."\n";
- }
- $decrypt_block .= '$in = pack("N*"'."\n";
- for ($i = 0; $i < $Nb; ++$i) {
- $decrypt_block.= ',
- ($'.$e.$i. ' & 0xFF000000) ^
- ($'.$e.(($Nb + $i - $c[1]) % $Nb).' & 0x00FF0000) ^
- ($'.$e.(($Nb + $i - $c[2]) % $Nb).' & 0x0000FF00) ^
- ($'.$e.(($Nb + $i - $c[3]) % $Nb).' & 0x000000FF) ^
- '.$dw[$i]."\n";
- }
- $decrypt_block .= ');';
-
- $lambda_functions[$code_hash] = $this->_createInlineCryptFunction(
- array(
- 'init_crypt' => '',
- 'init_encrypt' => $init_encrypt,
- 'init_decrypt' => $init_decrypt,
- 'encrypt_block' => $encrypt_block,
- 'decrypt_block' => $decrypt_block
- )
- );
- }
- $this->inline_crypt = $lambda_functions[$code_hash];
- }
-}
-
-// vim: ts=4:sw=4:et:
-// vim6: fdl=1:
diff --git a/plugins/OStatus/lib/feeddbexception.php b/plugins/OStatus/lib/feeddbexception.php
new file mode 100644
index 0000000000..dd0fc97cd6
--- /dev/null
+++ b/plugins/OStatus/lib/feeddbexception.php
@@ -0,0 +1,12 @@
+obj = $obj;
+ }
+}
diff --git a/plugins/OStatus/lib/feeddiscovery.php b/plugins/OStatus/lib/feeddiscovery.php
index 02c3ce0212..6baa07c0ea 100644
--- a/plugins/OStatus/lib/feeddiscovery.php
+++ b/plugins/OStatus/lib/feeddiscovery.php
@@ -94,7 +94,7 @@ class FeedDiscovery
}
/**
- * Get the referenced PuSH hub link from an Atom feed.
+ * Get the referenced WebSub hub link from an Atom feed.
*
* @return mixed string or false
*/
diff --git a/plugins/OStatus/lib/feedsubbadpushsignatureexception.php b/plugins/OStatus/lib/feedsubbadpushsignatureexception.php
new file mode 100644
index 0000000000..450831e1cf
--- /dev/null
+++ b/plugins/OStatus/lib/feedsubbadpushsignatureexception.php
@@ -0,0 +1,5 @@
+
*/
@@ -46,7 +46,7 @@ class HubConfQueueHandler extends QueueHandler
try {
$sub->verify($mode, $token);
} catch (Exception $e) {
- common_log(LOG_ERR, "Failed PuSH $mode verify to $sub->callback for $sub->topic: " .
+ common_log(LOG_ERR, "Failed WebSub $mode verify to $sub->callback for $sub->topic: " .
$e->getMessage());
// @fixme schedule retry?
// @fixme just kill it?
diff --git a/plugins/OStatus/lib/huboutqueuehandler.php b/plugins/OStatus/lib/huboutqueuehandler.php
index 590f2e3b25..91ac30e650 100644
--- a/plugins/OStatus/lib/huboutqueuehandler.php
+++ b/plugins/OStatus/lib/huboutqueuehandler.php
@@ -17,12 +17,10 @@
* along with this program. If not, see .
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
- * Send a raw PuSH atom update from our internal hub.
+ * Send a raw WebSub push atom update from our internal hub.
* @package Hub
* @author Brion Vibber
*/
@@ -35,20 +33,45 @@ class HubOutQueueHandler extends QueueHandler
function handle($data)
{
- $sub = $data['sub'];
+ assert(array_key_exists('atom', $data));
+ assert(is_string($data['atom']));
$atom = $data['atom'];
- $retries = $data['retries'];
+ assert(array_key_exists('retries', $data));
+ $retries = intval($data['retries']);
+
+ if (array_key_exists('topic', $data) && array_key_exists('callback', $data)) {
+ assert(is_string($data['topic']));
+ assert(is_string($data['callback']));
+
+ $sub = HubSub::getByHashkey($data['topic'], $data['callback']);
+ } elseif (array_key_exists('sub', $data)) {
+ // queue behaviour changed 2017-07-09 to store topic/callback instead of sub object
+ common_debug('Legacy behaviour of storing HubSub objects found, this should go away when all objects are handled...');
+ $sub = $data['sub'];
+ } else {
+ throw new ServerException('No HubSub object available with queue item data.');
+ }
assert($sub instanceof HubSub);
- assert(is_string($atom));
try {
- $sub->push($atom);
+ $success = $sub->push($atom);
+ // The reason I split these up is because I want to see how the algorithm acts in practice.
+ if ($success) {
+ common_debug('WebSub push completed successfully!');
+ } else {
+ common_debug('WebSub push failed with an HTTP error.');
+ }
+ if ($sub->getErrors()>0) {
+ common_debug('Resetting WebSub push error count following successful reset.');
+ $sub->resetErrors();
+ }
} catch (AlreadyFulfilledException $e) {
- common_log(LOG_INFO, "Failed PuSH to $sub->callback for $sub->topic (".get_class($e)."): " . $e->getMessage());
+ // Probably doesn't happen anymore since commit 74a60ab963b5ce1ed95bd81f935a44c573cd0264
+ common_log(LOG_INFO, "Failed WebSub push to $sub->callback for $sub->topic (".get_class($e)."): " . $e->getMessage());
} catch (Exception $e) {
$retries--;
- $msg = "Failed PuSH to $sub->callback for $sub->topic (".get_class($e)."): " . $e->getMessage();
+ $msg = "Failed WebSub push to $sub->callback for $sub->topic (".get_class($e)."): " . $e->getMessage();
if ($retries > 0) {
common_log(LOG_INFO, "$msg; scheduling for $retries more tries");
@@ -56,6 +79,7 @@ class HubOutQueueHandler extends QueueHandler
// after a delay, use it.
$sub->distribute($atom, $retries);
} else {
+ $sub->incrementErrors($e->getMessage());
common_log(LOG_ERR, "$msg; discarding");
}
}
diff --git a/plugins/OStatus/lib/hubprepqueuehandler.php b/plugins/OStatus/lib/hubprepqueuehandler.php
index f6abfcf30d..f11ca42e62 100644
--- a/plugins/OStatus/lib/hubprepqueuehandler.php
+++ b/plugins/OStatus/lib/hubprepqueuehandler.php
@@ -22,7 +22,7 @@ if (!defined('STATUSNET')) {
}
/**
- * When we have a large batch of PuSH consumers, we break the data set
+ * When we have a large batch of WebSub consumers, we break the data set
* into smaller chunks. Enqueue final destinations...
*
* @package Hub
@@ -67,14 +67,14 @@ class HubPrepQueueHandler extends QueueHandler
$callback = array_shift($pushCallbacks);
$sub = HubSub::getByHashkey($topic, $callback);
if (!$sub) {
- common_log(LOG_ERR, "Skipping PuSH delivery for deleted(?) consumer $callback on $topic");
+ common_log(LOG_ERR, "Skipping WebSub delivery for deleted(?) consumer $callback on $topic");
continue;
}
$sub->distribute($atom);
}
} catch (Exception $e) {
- common_log(LOG_ERR, "Exception during PuSH batch out: " .
+ common_log(LOG_ERR, "Exception during WebSub batch out: " .
$e->getMessage() .
" prepping $topic to $callback");
}
diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php
index 27ff0de5bf..48b88a2d5f 100644
--- a/plugins/OStatus/lib/magicenvelope.php
+++ b/plugins/OStatus/lib/magicenvelope.php
@@ -97,7 +97,7 @@ class MagicEnvelope
throw new ServerException(sprintf('No public key found for profile (id==%d)', $profile->id));
}
- assert($magicsig->publicKey instanceof Crypt_RSA);
+ assert($magicsig->publicKey instanceof \phpseclib\Crypt\RSA);
return $magicsig;
}
@@ -203,7 +203,7 @@ class MagicEnvelope
$magicsig = Magicsig::generate($this->actor->getUser());
}
assert($magicsig instanceof Magicsig);
- assert($magicsig->privateKey instanceof Crypt_RSA);
+ assert($magicsig->privateKey instanceof \phpseclib\Crypt\RSA);
// Prepare text and metadata for signing
$this->data = Magicsig::base64_url_encode($text);
diff --git a/plugins/OStatus/lib/ostatusqueuehandler.php b/plugins/OStatus/lib/ostatusqueuehandler.php
index 2358176ec2..3ad6b3b2ab 100644
--- a/plugins/OStatus/lib/ostatusqueuehandler.php
+++ b/plugins/OStatus/lib/ostatusqueuehandler.php
@@ -22,7 +22,7 @@ if (!defined('STATUSNET')) {
}
/**
- * Prepare PuSH and Salmon distributions for an outgoing message.
+ * Prepare WebSub and Salmon distributions for an outgoing message.
*
* @package OStatusPlugin
* @author Brion Vibber
@@ -30,7 +30,7 @@ if (!defined('STATUSNET')) {
class OStatusQueueHandler extends QueueHandler
{
// If we have more than this many subscribing sites on a single feed,
- // break up the PuSH distribution into smaller batches which will be
+ // break up the WebSub distribution into smaller batches which will be
// rolled into the queue progressively. This reduces disruption to
// other, shorter activities being enqueued while we work.
const MAX_UNBATCHED = 50;
@@ -132,7 +132,7 @@ class OStatusQueueHandler extends QueueHandler
{
if ($this->user) {
common_debug("OSTATUS [{$this->notice->getID()}]: pushing feed for local user {$this->user->getID()}");
- // For local posts, ping the PuSH hub to update their feed.
+ // For local posts, ping the WebSub hub to update their feed.
// http://identi.ca/api/statuses/user_timeline/1.atom
$feed = common_local_url('ApiTimelineUser',
array('id' => $this->user->id,
@@ -144,7 +144,7 @@ class OStatusQueueHandler extends QueueHandler
function pushGroup(User_group $group)
{
common_debug("OSTATUS [{$this->notice->getID()}]: pushing group '{$group->getNickname()}' profile_id={$group->profile_id}");
- // For a local group, ping the PuSH hub to update its feed.
+ // For a local group, ping the WebSub hub to update its feed.
// Updates may come from either a local or a remote user.
$feed = common_local_url('ApiTimelineGroup',
array('id' => $group->getID(),
@@ -155,7 +155,7 @@ class OStatusQueueHandler extends QueueHandler
function pushPeopletag($ptag)
{
common_debug("OSTATUS [{$this->notice->getID()}]: pushing peopletag '{$ptag->id}'");
- // For a local people tag, ping the PuSH hub to update its feed.
+ // For a local people tag, ping the WebSub hub to update its feed.
// Updates may come from either a local or a remote user.
$feed = common_local_url('ApiTimelineList',
array('id' => $ptag->id,
@@ -183,11 +183,37 @@ class OStatusQueueHandler extends QueueHandler
*/
function pushFeed($feed, $callback)
{
+ // NOTE: external hub pings will not be fixed by
+ // our legacy_http thing!
$hub = common_config('ostatus', 'hub');
if ($hub) {
$this->pushFeedExternal($feed, $hub);
}
+ // If we used to be http but now are https, see if we find an http entry for this feed URL
+ // and then upgrade it. This self-healing feature needs to be enabled manually in config.
+ // This code is based on a patch by @hannes2peer@quitter.se
+ if (common_config('fix', 'legacy_http') && parse_url($feed, PHP_URL_SCHEME) === 'https') {
+ common_log(LOG_DEBUG, "OSTATUS [{$this->notice->getID()}]: Searching for http scheme instead for HubSub feed topic: "._ve($feed));
+ $http_feed = str_replace('https://', 'http://', $feed);
+ $sub = new HubSub();
+ $sub->topic = $http_feed;
+ // If we find it we upgrade the rows in the hubsub table.
+ if ($sub->find()) {
+ common_log(LOG_INFO, "OSTATUS [{$this->notice->getID()}]: Found topic with http scheme for "._ve($feed).", will update the rows to use https instead!");
+ // we found an http:// URL but we use https:// now
+ // so let's update the rows to reflect on this!
+ while ($sub->fetch()) {
+ common_debug("OSTATUS [{$this->notice->getID()}]: Changing topic URL to https for feed callback "._ve($sub->callback));
+ $orig = clone($sub);
+ $sub->topic = $feed;
+ // hashkey column will be set automagically in HubSub->onUpdateKeys through updateWithKeys
+ $sub->updateWithKeys($orig);
+ unset($orig);
+ }
+ }
+ }
+
$sub = new HubSub();
$sub->topic = $feed;
if ($sub->find()) {
@@ -195,9 +221,8 @@ class OStatusQueueHandler extends QueueHandler
$atom = call_user_func_array($callback, $args);
$this->pushFeedInternal($atom, $sub);
} else {
- common_log(LOG_INFO, "No PuSH subscribers for $feed");
+ common_log(LOG_INFO, "OSTATUS [{$this->notice->getID()}]: No WebSub subscribers for $feed");
}
- return true;
}
/**
@@ -206,7 +231,7 @@ class OStatusQueueHandler extends QueueHandler
* Not guaranteed safe in an environment with database replication.
*
* @param string $feed feed topic URI
- * @param string $hub PuSH hub URI
+ * @param string $hub WebSub hub URI
* @fixme can consolidate pings for user & group posts
*/
function pushFeedExternal($feed, $hub)
@@ -217,15 +242,15 @@ class OStatusQueueHandler extends QueueHandler
'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");
+ common_log(LOG_INFO, "WebSub ping to hub $hub for $feed ok");
return true;
} else {
- common_log(LOG_ERR, "PuSH ping to hub $hub for $feed failed with HTTP " .
+ common_log(LOG_ERR, "WebSub 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());
+ common_log(LOG_ERR, "WebSub ping to hub $hub for $feed failed: " . $e->getMessage());
return false;
}
}
@@ -240,7 +265,7 @@ class OStatusQueueHandler extends QueueHandler
*/
function pushFeedInternal($atom, $sub)
{
- common_log(LOG_INFO, "Preparing $sub->N PuSH distribution(s) for $sub->topic");
+ common_log(LOG_INFO, "Preparing $sub->N WebSub distribution(s) for $sub->topic");
$n = 0;
$batch = array();
while ($sub->fetch()) {
diff --git a/plugins/OStatus/lib/pushinqueuehandler.php b/plugins/OStatus/lib/pushinqueuehandler.php
index ac8a6c8429..4946186cf4 100644
--- a/plugins/OStatus/lib/pushinqueuehandler.php
+++ b/plugins/OStatus/lib/pushinqueuehandler.php
@@ -17,12 +17,10 @@
* along with this program. If not, see .
*/
-if (!defined('STATUSNET')) {
- exit(1);
-}
+if (!defined('GNUSOCIAL')) { exit(1); }
/**
- * Process a feed distribution POST from a PuSH hub.
+ * Process a feed distribution POST from a WebSub (previously PuSH) hub.
* @package FeedSub
* @author Brion Vibber
*/
@@ -41,15 +39,17 @@ class PushInQueueHandler extends QueueHandler
$post = $data['post'];
$hmac = $data['hmac'];
- $feedsub = FeedSub::getKV('id', $feedsub_id);
- if ($feedsub instanceof FeedSub) {
- try {
- $feedsub->receive($post, $hmac);
- } catch(Exception $e) {
- common_log(LOG_ERR, "Exception during PuSH input processing for $feedsub->uri: " . $e->getMessage());
+ try {
+ $feedsub = FeedSub::getByID($feedsub_id);
+ $feedsub->receive($post, $hmac);
+ } catch(NoResultException $e) {
+ common_log(LOG_INFO, "Discarding POST to unknown feed subscription id {$feedsub_id}");
+ } catch(Exception $e) {
+ if (is_null($feedsub)) {
+ common_log(LOG_ERR, "Exception "._ve(get_class($e))." during WebSub push input processing where FeedSub->receive returned null!" . _ve($e->getMessage()));
+ } else {
+ common_log(LOG_ERR, "Exception "._ve(get_class($e))." during WebSub push input processing for {$feedsub->getUri()}: " . _ve($e->getMessage()));
}
- } else {
- common_log(LOG_ERR, "Discarding POST to unknown feed subscription id $feedsub_id");
}
return true;
}
diff --git a/plugins/OStatus/lib/pushrenewqueuehandler.php b/plugins/OStatus/lib/pushrenewqueuehandler.php
index d79cbe503f..31df9b5f63 100644
--- a/plugins/OStatus/lib/pushrenewqueuehandler.php
+++ b/plugins/OStatus/lib/pushrenewqueuehandler.php
@@ -39,7 +39,7 @@ class PushRenewQueueHandler extends QueueHandler
common_log(LOG_INFO, "Renewing feed subscription\n\tExp.: {$feedsub->sub_end}\n\tFeed: {$feedsub->uri}\n\tHub: {$feedsub->huburi}");
$feedsub->renew();
} catch(Exception $e) {
- common_log(LOG_ERR, "Exception during PuSH renew processing for $feedsub->uri: " . $e->getMessage());
+ common_log(LOG_ERR, "Exception during WebSub renew processing for $feedsub->uri: " . $e->getMessage());
}
} else {
common_log(LOG_ERR, "Discarding renew for unknown feed subscription id $feedsub_id");
diff --git a/plugins/OStatus/lib/salmonaction.php b/plugins/OStatus/lib/salmonaction.php
index d1293728d2..4d2144c300 100644
--- a/plugins/OStatus/lib/salmonaction.php
+++ b/plugins/OStatus/lib/salmonaction.php
@@ -31,6 +31,8 @@ class SalmonAction extends Action
protected $oprofile = null; // Ostatus_profile of the actor
protected $actor = null; // Profile object of the actor
+ var $format = 'text'; // error messages will be printed in plaintext
+
var $xml = null;
var $activity = null;
var $target = null;
@@ -43,7 +45,7 @@ class SalmonAction extends Action
if (!isset($_SERVER['CONTENT_TYPE'])) {
// TRANS: Client error. Do not translate "Content-type"
- $this->clientError(_m('Salmon requires a Content-type header.'));
+ throw new ClientException(_m('Salmon requires a Content-type header.'));
}
$envxml = null;
switch ($_SERVER['CONTENT_TYPE']) {
@@ -83,6 +85,8 @@ class SalmonAction extends Action
// Cryptographic verification test, throws exception on failure
$magic_env->verify($this->actor);
+ common_debug('Salmon slap is carrying activity URI=='._ve($this->activity->id));
+
return true;
}
diff --git a/plugins/OStatus/scripts/renew-feeds.php b/plugins/OStatus/scripts/renew-feeds.php
index 8f8ac3ee1d..d63f448827 100755
--- a/plugins/OStatus/scripts/renew-feeds.php
+++ b/plugins/OStatus/scripts/renew-feeds.php
@@ -37,7 +37,11 @@ try {
while ($sub->fetch()) {
echo "Renewing feed subscription\n\tExp.: {$sub->sub_end}\n\tFeed: {$sub->uri}\n\tHub: {$sub->huburi}\n";
- $sub->renew();
+ try {
+ $sub->renew();
+ } catch (Exception $e) {
+ echo "FAILED: {$e->getMessage()}\n";
+ }
}
echo "Done!";
diff --git a/plugins/OStatus/scripts/resub-feed.php b/plugins/OStatus/scripts/resub-feed.php
index 37b09883db..48db9f4e6a 100755
--- a/plugins/OStatus/scripts/resub-feed.php
+++ b/plugins/OStatus/scripts/resub-feed.php
@@ -25,7 +25,7 @@ $shortoptions = 'u';
$helptext = <<importFeed()
+
// Fetch the URL
try {
- $xml = HTTPClient::quickGet($feedurl, 'text/html,application/xhtml+xml');
+ $xml = HTTPClient::quickGet($feedurl, 'application/atom+xml');
} catch (Exception $e) {
echo sprintf("Could not fetch feedurl %s (%d).\n", $e->getMessage(), $e->getCode());
exit(1);
diff --git a/plugins/OStatus/scripts/update-profile-data.php b/plugins/OStatus/scripts/update-profile-data.php
index 97d44e3842..5b3e00e9fc 100755
--- a/plugins/OStatus/scripts/update-profile-data.php
+++ b/plugins/OStatus/scripts/update-profile-data.php
@@ -44,44 +44,34 @@ function showProfileInfo(Ostatus_profile $oprofile) {
echo "group\n";
} else {
$profile = $oprofile->localProfile();
- try {
- foreach (array('nickname', 'fullname', 'bio', 'homepage', 'location') as $field) {
- print " $field: {$profile->$field}\n";
- }
- } catch (NoProfileException $e) {
- print "local profile not found";
+ foreach (array('nickname', 'fullname', 'bio', 'homepage', 'location') as $field) {
+ print " $field: {$profile->$field}\n";
}
}
echo "\n";
}
-function fixProfile($uri) {
- $oprofile = Ostatus_profile::getKV('uri', $uri);
-
- if (!$oprofile) {
- print "No OStatus remote profile known for URI $uri\n";
- return false;
- }
-
+function fixProfile(Ostatus_profile $oprofile) {
echo "Before:\n";
showProfileInfo($oprofile);
$feedurl = $oprofile->feeduri;
- $client = new HttpClient();
+ $client = new HTTPClient();
$response = $client->get($feedurl);
if ($response->isOk()) {
echo "Updating profile from feed: $feedurl\n";
$dom = new DOMDocument();
if ($dom->loadXML($response->getBody())) {
- $feed = $dom->documentElement;
- $entries = $dom->getElementsByTagNameNS(Activity::ATOM, 'entry');
- if ($entries->length) {
- $entry = $entries->item(0);
- $activity = new Activity($entry, $feed);
- $oprofile->checkAuthorship($activity);
+ if ($dom->documentElement->tagName !== 'feed') {
+ echo " (no element in feed URL response; skipping)\n";
+ return false;
+ }
+ $actorObj = ActivityUtils::getFeedAuthor($dom->documentElement);
+ if ($actorObj) {
+ $oprofile->updateFromActivityObject($actorObj);
echo " (ok)\n";
} else {
- echo " (no entry; skipping)\n";
+ echo " (no author on feed; skipping)\n";
return false;
}
} else {
@@ -106,7 +96,7 @@ if (have_option('all')) {
echo "Found $oprofile->N profiles:\n\n";
while ($oprofile->fetch()) {
try {
- $ok = fixProfile($oprofile->uri) && $ok;
+ $ok = fixProfile($oprofile) && $ok;
} catch (Exception $e) {
$ok = false;
echo "Failed on URI=="._ve($oprofile->uri).": {$e->getMessage()}\n";
@@ -120,7 +110,7 @@ if (have_option('all')) {
echo "Found $oprofile->N matching profiles:\n\n";
while ($oprofile->fetch()) {
try {
- $ok = fixProfile($oprofile->uri) && $ok;
+ $ok = fixProfile($oprofile) && $ok;
} catch (Exception $e) {
$ok = false;
echo "Failed on URI=="._ve($oprofile->uri).": {$e->getMessage()}\n";
@@ -128,8 +118,15 @@ if (have_option('all')) {
}
} else if (!empty($args[0]) && $validate->uri($args[0])) {
$uri = $args[0];
+ $oprofile = Ostatus_profile::getKV('uri', $uri);
+
+ if (!$oprofile instanceof Ostatus_profile) {
+ print "No OStatus remote profile known for URI $uri\n";
+ return false;
+ }
+
try {
- $ok = fixProfile($oprofile->uri) && $ok;
+ $ok = fixProfile($oprofile) && $ok;
} catch (Exception $e) {
$ok = false;
echo "Failed on URI=="._ve($oprofile->uri).": {$e->getMessage()}\n";
diff --git a/plugins/OStatus/scripts/update-profile.php b/plugins/OStatus/scripts/update-profile.php
index c5d9aa897c..758c5e6e12 100755
--- a/plugins/OStatus/scripts/update-profile.php
+++ b/plugins/OStatus/scripts/update-profile.php
@@ -24,7 +24,7 @@ $helptext = <<assertTrue($this->pub->hasSubscriber($this->sub->getProfileUri()));
$name = $this->sub->username;
- $post = $this->pub->post("Regular post, which $name should get via PuSH");
+ $post = $this->pub->post("Regular post, which $name should get via WebSub");
$this->sub->assertReceived($post);
}
diff --git a/plugins/Oembed/OembedPlugin.php b/plugins/Oembed/OembedPlugin.php
index 44e4ac9318..3ccca20538 100644
--- a/plugins/Oembed/OembedPlugin.php
+++ b/plugins/Oembed/OembedPlugin.php
@@ -102,7 +102,7 @@ class OembedPlugin extends Plugin
array(),
array('format'=>'json', 'url'=>
common_local_url('attachment',
- array('attachment' => $action->attachment->id)))),
+ array('attachment' => $action->attachment->getID())))),
'title'=>'oEmbed'),null);
$action->element('link',array('rel'=>'alternate',
'type'=>'text/xml+oembed',
@@ -111,7 +111,7 @@ class OembedPlugin extends Plugin
array(),
array('format'=>'xml','url'=>
common_local_url('attachment',
- array('attachment' => $action->attachment->id)))),
+ array('attachment' => $action->attachment->getID())))),
'title'=>'oEmbed'),null);
break;
case 'shownotice':
@@ -143,6 +143,11 @@ class OembedPlugin extends Plugin
return true;
}
+ public function onEndShowStylesheets(Action $action) {
+ $action->cssLink($this->path('css/oembed.css'));
+ return true;
+ }
+
/**
* Save embedding information for a File, if applicable.
*
@@ -154,9 +159,9 @@ class OembedPlugin extends Plugin
*/
public function onEndFileSaveNew(File $file)
{
- $fo = File_oembed::getKV('file_id', $file->id);
+ $fo = File_oembed::getKV('file_id', $file->getID());
if ($fo instanceof File_oembed) {
- common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file {$file->id}", __FILE__);
+ common_log(LOG_WARNING, "Strangely, a File_oembed object exists for new file {$file->getID()}", __FILE__);
return true;
}
@@ -169,18 +174,20 @@ class OembedPlugin extends Plugin
if ($oembed_data === false) {
throw new Exception('Did not get oEmbed data from URL');
}
+ $file->setTitle($oembed_data->title);
} catch (Exception $e) {
+ common_log(LOG_WARNING, sprintf(__METHOD__.': %s thrown when getting oEmbed data: %s', get_class($e), _ve($e->getMessage())));
return true;
}
- File_oembed::saveNew($oembed_data, $file->id);
+ File_oembed::saveNew($oembed_data, $file->getID());
}
return true;
}
public function onEndShowAttachmentLink(HTMLOutputter $out, File $file)
{
- $oembed = File_oembed::getKV('file_id', $file->id);
+ $oembed = File_oembed::getKV('file_id', $file->getID());
if (empty($oembed->author_name) && empty($oembed->provider)) {
return true;
}
@@ -212,7 +219,7 @@ class OembedPlugin extends Plugin
{
// Never treat generic HTML links as an enclosure type!
// But if we have oEmbed info, we'll consider it golden.
- $oembed = File_oembed::getKV('file_id', $file->id);
+ $oembed = File_oembed::getKV('file_id', $file->getID());
if (!$oembed instanceof File_oembed || !in_array($oembed->type, array('photo', 'video'))) {
return true;
}
@@ -224,6 +231,70 @@ class OembedPlugin extends Plugin
}
return true;
}
+
+ public function onStartShowAttachmentRepresentation(HTMLOutputter $out, File $file)
+ {
+ try {
+ $oembed = File_oembed::getByFile($file);
+ } catch (NoResultException $e) {
+ return true;
+ }
+
+ // Show thumbnail as usual if it's a photo.
+ if ($oembed->type === 'photo') {
+ return true;
+ }
+
+ $out->elementStart('article', ['class'=>'h-entry oembed']);
+ $out->elementStart('header');
+ try {
+ $thumb = $file->getThumbnail(128, 128);
+ $out->element('img', $thumb->getHtmlAttrs(['class'=>'u-photo oembed']));
+ unset($thumb);
+ } catch (Exception $e) {
+ $out->element('div', ['class'=>'error'], $e->getMessage());
+ }
+ $out->elementStart('h5', ['class'=>'p-name oembed']);
+ $out->element('a', ['class'=>'u-url', 'href'=>$file->getUrl()], common_strip_html($oembed->title));
+ $out->elementEnd('h5');
+ $out->elementStart('div', ['class'=>'p-author oembed']);
+ if (!empty($oembed->author_name)) {
+ // TRANS: text before the author name of oEmbed attachment representation
+ // FIXME: The whole "By x from y" should be i18n because of different language constructions.
+ $out->text(_('By '));
+ $attrs = ['class'=>'h-card p-author'];
+ if (!empty($oembed->author_url)) {
+ $attrs['href'] = $oembed->author_url;
+ $tag = 'a';
+ } else {
+ $tag = 'span';
+ }
+ $out->element($tag, $attrs, $oembed->author_name);
+ }
+ if (!empty($oembed->provider)) {
+ // TRANS: text between the oEmbed author name and provider url
+ // FIXME: The whole "By x from y" should be i18n because of different language constructions.
+ $out->text(_(' from '));
+ $attrs = ['class'=>'h-card'];
+ if (!empty($oembed->provider_url)) {
+ $attrs['href'] = $oembed->provider_url;
+ $tag = 'a';
+ } else {
+ $tag = 'span';
+ }
+ $out->element($tag, $attrs, $oembed->provider);
+ }
+ $out->elementEnd('div');
+ $out->elementEnd('header');
+ $out->elementStart('div', ['class'=>'p-summary oembed']);
+ $out->raw(common_purify($oembed->html));
+ $out->elementEnd('div');
+ $out->elementStart('footer');
+ $out->elementEnd('footer');
+ $out->elementEnd('article');
+
+ return false;
+ }
public function onShowUnsupportedAttachmentRepresentation(HTMLOutputter $out, File $file)
{
@@ -261,6 +332,7 @@ class OembedPlugin extends Plugin
// All our remote Oembed images lack a local filename property in the File object
if (!is_null($file->filename)) {
+ common_debug(sprintf('Filename of file id==%d is not null (%s), so nothing oEmbed should handle.', $file->getID(), _ve($file->filename)));
return true;
}
@@ -271,6 +343,7 @@ class OembedPlugin extends Plugin
$thumbnail = File_thumbnail::byFile($file);
} catch (NoResultException $e) {
// Not Oembed data, or at least nothing we either can or want to use.
+ common_debug('No oEmbed data found for file id=='.$file->getID());
return true;
}
@@ -278,6 +351,9 @@ class OembedPlugin extends Plugin
$this->storeRemoteFileThumbnail($thumbnail);
} catch (AlreadyFulfilledException $e) {
// aw yiss!
+ } catch (Exception $e) {
+ common_debug(sprintf('oEmbed encountered an exception (%s) for file id==%d: %s', get_class($e), $file->getID(), _ve($e->getMessage())));
+ throw $e;
}
$imgPath = $thumbnail->getPath();
@@ -308,25 +384,53 @@ class OembedPlugin extends Plugin
protected function storeRemoteFileThumbnail(File_thumbnail $thumbnail)
{
if (!empty($thumbnail->filename) && file_exists($thumbnail->getPath())) {
- throw new AlreadyFulfilledException(sprintf('A thumbnail seems to already exist for remote file with id==%u', $thumbnail->file_id));
+ throw new AlreadyFulfilledException(sprintf('A thumbnail seems to already exist for remote file with id==%u', $thumbnail->getFileId()));
}
- $url = $thumbnail->getUrl();
- $this->checkWhitelist($url);
+ $remoteUrl = $thumbnail->getUrl();
+ $this->checkWhitelist($remoteUrl);
- // First we download the file to memory and test whether it's actually an image file
+ $http = new HTTPClient();
+ // First see if it's too large for us
+ common_debug(__METHOD__ . ': '.sprintf('Performing HEAD request for remote file id==%u to avoid unnecessarily downloading too large files. URL: %s', $thumbnail->getFileId(), $remoteUrl));
+ $head = $http->head($remoteUrl);
+ if (!$head->isOk()) {
+ common_log(LOG_WARNING, 'HEAD request returned HTTP failure, so we will abort now and delete the thumbnail object.');
+ $thumbnail->delete();
+ return false;
+ } else {
+ common_debug('HEAD request returned HTTP success, so we will continue.');
+ }
+ $remoteUrl = $head->getEffectiveUrl(); // to avoid going through redirects again
+
+ $headers = $head->getHeader();
+ $filesize = isset($headers['content-length']) ? $headers['content-length'] : null;
+
+ // FIXME: I just copied some checks from StoreRemoteMedia, maybe we should have other checks for thumbnails? Or at least embed into some class somewhere.
+ if (empty($filesize)) {
+ // file size not specified on remote server
+ common_debug(sprintf('%s: Ignoring remote thumbnail because we did not get a content length for thumbnail for file id==%u', __CLASS__, $thumbnail->getFileId()));
+ return true;
+ } elseif ($filesize > common_config('attachments', 'file_quota')) {
+ // file too big according to site configuration
+ common_debug(sprintf('%s: Skip downloading remote thumbnail because content length (%u) is larger than file_quota (%u) for file id==%u', __CLASS__, intval($filesize), common_config('attachments', 'file_quota'), $thumbnail->getFileId()));
+ return true;
+ }
+
+ // Then we download the file to memory and test whether it's actually an image file
// FIXME: To support remote video/whatever files, this needs reworking.
- common_debug(sprintf('Downloading remote thumbnail for file id==%u with thumbnail URL: %s', $thumbnail->file_id, $url));
- $imgData = HTTPClient::quickGet($url);
+ common_debug(sprintf('Downloading remote thumbnail for file id==%u (should be size %u) with effective URL: %s', $thumbnail->getFileId(), $filesize, _ve($remoteUrl)));
+ $imgData = HTTPClient::quickGet($remoteUrl);
$info = @getimagesizefromstring($imgData);
if ($info === false) {
- throw new UnsupportedMediaException(_('Remote file format was not identified as an image.'), $url);
+ throw new UnsupportedMediaException(_('Remote file format was not identified as an image.'), $remoteUrl);
} elseif (!$info[0] || !$info[1]) {
throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
}
- // We'll trust sha256 (File::FILEHASH_ALG) not to have collision issues any time soon :)
- $filename = hash(File::FILEHASH_ALG, $imgData) . '.' . common_supported_mime_to_ext($info['mime']);
+ $ext = File::guessMimeExtension($info['mime']);
+
+ $filename = sprintf('oembed-%d.%s', $thumbnail->getFileId(), $ext);
$fullpath = File_thumbnail::path($filename);
// Write the file to disk. Throw Exception on failure
if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) {
diff --git a/plugins/Oembed/README b/plugins/Oembed/README
index be8c09f7a6..b5e1aeae1f 100644
--- a/plugins/Oembed/README
+++ b/plugins/Oembed/README
@@ -1 +1,29 @@
-It's really called oEmbed.
+The Oembed plugin for using and representing oEmbed data.
+
+See: http://www.oembed.com/
+
+Installation
+============
+This plugin is enabled by default
+
+Settings
+========
+width: Maximum width of the thumbnail in pixels.
+height: Maximum height of the thumbnail in pixels.
+show_html: Whether to show HTML oEmbed data.
+domain_whitelist: Array of regular expressions. Always escape your dots and end your strings.
+check_whitelist: Whether to check the domain_whitelist.
+
+Example
+=======
+$config['thumbnail']['width'] = 42;
+$config['thumbnail']['height'] = 42;
+$config['attachments']['show_html'] = true;
+addPlugin('Oembed', array(
+ 'domain_whitelist' => array(
+ '^i\d*\.ytimg\.com$' => 'YouTube',
+ '^i\d*\.vimeocdn\.com$' => 'Vimeo'
+ ),
+ 'check_whitelist' => true
+));
+
diff --git a/plugins/Oembed/actions/oembed.php b/plugins/Oembed/actions/oembed.php
index af181ad586..564e492e4a 100644
--- a/plugins/Oembed/actions/oembed.php
+++ b/plugins/Oembed/actions/oembed.php
@@ -44,21 +44,20 @@ class OembedAction extends Action
parent::handle();
$url = $this->trimmed('url');
- if (substr(strtolower($url),0,strlen(common_root_url())) !== strtolower(common_root_url())) {
+ $tls = parse_url($url, PHP_URL_SCHEME) == 'https';
+ $root_url = common_root_url($tls);
+
+ if (substr(strtolower($url),0,mb_strlen($root_url)) !== strtolower($root_url)) {
// TRANS: Error message displaying attachments. %s is the site's base URL.
- $this->clientError(sprintf(_('oEmbed data will only be provided for %s URLs.'), common_root_url()), 400);
+ throw new ClientException(sprintf(_('oEmbed data will only be provided for %s URLs.'), $root_url));
}
- $path = substr($url,strlen(common_root_url()));
+ $path = substr($url,strlen($root_url));
$r = Router::get();
+ // $r->map will throw ClientException 404 if it fails to find a mapping
$proxy_args = $r->map($path);
- if (!$proxy_args) {
- // TRANS: Client error displayed in oEmbed action when path not found.
- // TRANS: %s is a path.
- $this->clientError(sprintf(_('"%s" not found.'),$path), 404);
- }
$oembed=array();
$oembed['version']='1.0';
@@ -68,18 +67,12 @@ class OembedAction extends Action
switch ($proxy_args['action']) {
case 'shownotice':
$oembed['type']='link';
- $id = $proxy_args['notice'];
- $notice = Notice::getKV($id);
- if(empty($notice)){
- // TRANS: Client error displayed in oEmbed action when notice not found.
- // TRANS: %s is a notice.
- $this->clientError(sprintf(_("Notice %s not found."),$id), 404);
+ try {
+ $notice = Notice::getByID($proxy_args['notice']);
+ } catch (NoResultException $e) {
+ throw new ClientException($e->getMessage(), 404);
}
$profile = $notice->getProfile();
- if (empty($profile)) {
- // TRANS: Server error displayed in oEmbed action when notice has not profile.
- $this->serverError(_('Notice has no profile.'), 500);
- }
$authorname = $profile->getFancyName();
// TRANS: oEmbed title. %1$s is the author name, %2$s is the creation date.
$oembed['title'] = sprintf(_('%1$s\'s status on %2$s'),
@@ -91,26 +84,25 @@ class OembedAction extends Action
$oembed['html']=$notice->getRendered();
// maybe add thumbnail
- $attachments = $notice->attachments();
- if (!empty($attachments)) {
- foreach ($attachments as $attachment) {
- if(is_object($attachment)) {
- try {
- $thumb = $attachment->getThumbnail();
- } catch (ServerException $e) {
- //
- }
- try {
- $thumb_url = File_thumbnail::url($thumb->filename);
- $oembed['thumbnail_url'] = $thumb_url;
- break; // only first one
- } catch (ClientException $e) {
- //
- }
- }
- }
- }
-
+ foreach ($notice->attachments() as $attachment) {
+ if (!$attachment instanceof File) {
+ common_debug('ATTACHMENTS array entry from notice id=='._ve($notice->getID()).' is something else than a File dataobject: '._ve($attachment));
+ continue;
+ }
+ try {
+ $thumb = $attachment->getThumbnail();
+ $thumb_url = File_thumbnail::url($thumb->filename);
+ $oembed['thumbnail_url'] = $thumb_url;
+ break; // only first one
+ } catch (UseFileAsThumbnailException $e) {
+ $oembed['thumbnail_url'] = $attachment->getUrl();
+ break; // we're happy with that
+ } catch (ServerException $e) {
+ //
+ } catch (ClientException $e) {
+ //
+ }
+ }
break;
case 'attachment':
@@ -256,4 +248,4 @@ class OembedAction extends Action
{
return true;
}
-}
\ No newline at end of file
+}
diff --git a/plugins/Oembed/classes/File_oembed.php b/plugins/Oembed/classes/File_oembed.php
index 246a74fea0..e47fee6abe 100644
--- a/plugins/Oembed/classes/File_oembed.php
+++ b/plugins/Oembed/classes/File_oembed.php
@@ -120,15 +120,13 @@ class File_oembed extends Managed_DataObject
$file_oembed->url = $data->url;
$given_url = File_redirection::_canonUrl($file_oembed->url);
if (! empty($given_url)){
- $file = File::getKV('url', $given_url);
- if ($file instanceof File) {
+ try {
+ $file = File::getByUrl($given_url);
$file_oembed->mimetype = $file->mimetype;
- } else {
- $redir = File_redirection::where($given_url);
- if (empty($redir->file_id)) {
- $f = $redir->getFile();
- $file_oembed->mimetype = $f->mimetype;
- } else {
+ } catch (NoResultException $e) {
+ // File_redirection::where argument 'discover' is false to avoid loops
+ $redir = File_redirection::where($given_url, false);
+ if (!empty($redir->file_id)) {
$file_id = $redir->file_id;
}
}
diff --git a/plugins/Oembed/css/oembed.css b/plugins/Oembed/css/oembed.css
new file mode 100644
index 0000000000..9750f02779
--- /dev/null
+++ b/plugins/Oembed/css/oembed.css
@@ -0,0 +1,15 @@
+.u-photo.oembed {
+ float: left;
+ margin-bottom: 1ex;
+ margin-right: 1em;
+}
+
+.p-author.oembed {
+ font-style: italic;
+}
+
+.p-summary.oembed {
+ line-height: 1.25em;
+ max-height: 5em;
+ overflow: auto;
+}
diff --git a/plugins/OfflineBackup/OfflineBackupPlugin.php b/plugins/OfflineBackup/OfflineBackupPlugin.php
index 1d7a17ca2c..b1c69558ea 100644
--- a/plugins/OfflineBackup/OfflineBackupPlugin.php
+++ b/plugins/OfflineBackup/OfflineBackupPlugin.php
@@ -76,7 +76,7 @@ class OfflineBackupPlugin extends Plugin
$versions[] = array('name' => 'OfflineBackup',
'version' => GNUSOCIAL_VERSION,
'author' => 'Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:OfflineBackup',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/OfflineBackup',
'rawdescription' =>
// TRANS: Plugin description.
_m('Backup user data in offline queue and email when ready.'));
diff --git a/plugins/OfflineBackup/README b/plugins/OfflineBackup/README
new file mode 100644
index 0000000000..17537f3561
--- /dev/null
+++ b/plugins/OfflineBackup/README
@@ -0,0 +1,16 @@
+The OfflineBackup plugin backups user data in offline queue and email when
+ready.
+
+Installation
+============
+add "addPlugin('OfflineBackup');"
+to the bottom of your config.php
+
+Settings
+========
+none
+
+Example
+=======
+addPlugin('OfflineBackup');
+
diff --git a/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php b/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php
index 06c75f7a93..89c7cbabfd 100644
--- a/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php
+++ b/plugins/OpenExternalLinkTarget/OpenExternalLinkTargetPlugin.php
@@ -54,7 +54,7 @@ class OpenExternalLinkTargetPlugin extends Plugin
$versions[] = array('name' => 'OpenExternalLinkTarget',
'version' => GNUSOCIAL_VERSION,
'author' => 'Sarven Capadisli',
- 'homepage' => 'http://status.net/wiki/Plugin:OpenExternalLinkTarget',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/OpenExternalLinkTarget',
'rawdescription' =>
// TRANS: Plugin description.
_m('Opens external links (i.e. with rel=external) on a new window or tab.'));
diff --git a/plugins/OpenExternalLinkTarget/README b/plugins/OpenExternalLinkTarget/README
new file mode 100644
index 0000000000..6d4db6437a
--- /dev/null
+++ b/plugins/OpenExternalLinkTarget/README
@@ -0,0 +1,15 @@
+The OpenExternalLinkTarget plugin opens external links in a new window or tab.
+
+Installation
+============
+add "addPlugin('OpenExternalLinkTarget');"
+to the bottom of your config.php
+
+Settings
+========
+none
+
+Example
+=======
+addPlugin('OpenExternalLinkTarget');
+
diff --git a/plugins/OpenID/OpenIDPlugin.php b/plugins/OpenID/OpenIDPlugin.php
index c6f2d75a2b..c6d247caf3 100644
--- a/plugins/OpenID/OpenIDPlugin.php
+++ b/plugins/OpenID/OpenIDPlugin.php
@@ -597,7 +597,7 @@ class OpenIDPlugin extends Plugin
$versions[] = array('name' => 'OpenID',
'version' => GNUSOCIAL_VERSION,
'author' => 'Evan Prodromou, Craig Andrews',
- 'homepage' => 'http://status.net/wiki/Plugin:OpenID',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/OpenID',
'rawdescription' =>
// TRANS: Plugin description.
_m('Use OpenID to login to the site.'));
diff --git a/plugins/OpenID/README b/plugins/OpenID/README
new file mode 100644
index 0000000000..17114a1dc6
--- /dev/null
+++ b/plugins/OpenID/README
@@ -0,0 +1,21 @@
+The OpenID plugin allows users to use OpenID to login.
+
+See: http://openid.net/
+
+Installation
+============
+This plugin is enabled by default
+
+Settings
+========
+openidonly: Whether we only allow logins through OpenID.
+trusted_provider: URL to the OpenID provider.
+append_username: Whether to append the username at the end of the OpenID URL
+
+Example
+=======
+$config['site']['openidonly'] = true;
+$config['openid']['trusted_provider'] = "http://example.org";
+$config['openid']['append_username'] = true;
+addPlugin('OpenID');
+
diff --git a/plugins/OpenID/actions/finishaddopenid.php b/plugins/OpenID/actions/finishaddopenid.php
index 5182e50778..e3e100c9e4 100644
--- a/plugins/OpenID/actions/finishaddopenid.php
+++ b/plugins/OpenID/actions/finishaddopenid.php
@@ -58,9 +58,9 @@ class FinishaddopenidAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (!common_logged_in()) {
// TRANS: Error message displayed when trying to perform an action that requires a logged in user.
$this->clientError(_m('Not logged in.'));
diff --git a/plugins/OpenID/actions/finishopenidlogin.php b/plugins/OpenID/actions/finishopenidlogin.php
index 3a99988fb5..f91d10d252 100644
--- a/plugins/OpenID/actions/finishopenidlogin.php
+++ b/plugins/OpenID/actions/finishopenidlogin.php
@@ -29,9 +29,9 @@ class FinishopenidloginAction extends Action
var $username = null;
var $message = null;
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (common_is_real_login()) {
// TRANS: Client error message trying to log on with OpenID while already logged on.
$this->clientError(_m('Already logged in.'));
diff --git a/plugins/OpenID/actions/openidlogin.php b/plugins/OpenID/actions/openidlogin.php
index 86ebcae2e1..b06189e2fd 100644
--- a/plugins/OpenID/actions/openidlogin.php
+++ b/plugins/OpenID/actions/openidlogin.php
@@ -25,9 +25,9 @@ require_once INSTALLDIR.'/plugins/OpenID/openid.php';
class OpenidloginAction extends Action
{
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (common_is_real_login()) {
// TRANS: Client error message trying to log on with OpenID while already logged on.
$this->clientError(_m('Already logged in.'));
diff --git a/plugins/OpenID/actions/openidserver.php b/plugins/OpenID/actions/openidserver.php
index d4bb6e25f4..6cc3061edd 100644
--- a/plugins/OpenID/actions/openidserver.php
+++ b/plugins/OpenID/actions/openidserver.php
@@ -50,16 +50,16 @@ class OpenidserverAction extends Action
{
var $oserver;
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
$this->oserver = oid_server();
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$request = $this->oserver->decodeRequest();
if (in_array($request->mode, array('checkid_immediate',
'checkid_setup'))) {
diff --git a/plugins/OpenID/actions/openidtrust.php b/plugins/OpenID/actions/openidtrust.php
index 3f8a3a93b7..734b35bae2 100644
--- a/plugins/OpenID/actions/openidtrust.php
+++ b/plugins/OpenID/actions/openidtrust.php
@@ -51,7 +51,7 @@ class OpenidtrustAction extends Action
return _m('OpenID Identity Verification');
}
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
common_ensure_session();
@@ -71,9 +71,9 @@ class OpenidtrustAction extends Action
return true;
}
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if($_SERVER['REQUEST_METHOD'] == 'POST'){
$this->handleSubmit();
}else{
diff --git a/plugins/OpenID/classes/User_openid.php b/plugins/OpenID/classes/User_openid.php
index 7e53d8ec95..2221a9e78a 100644
--- a/plugins/OpenID/classes/User_openid.php
+++ b/plugins/OpenID/classes/User_openid.php
@@ -45,6 +45,16 @@ class User_openid extends Managed_DataObject
);
}
+ public function getID()
+ {
+ if (!isset($this->user_id)) {
+ throw new Exception('No ID set.');
+ } elseif (empty($this->user_id)) {
+ throw new Exception('Empty ID for object! (not inserted yet?).');
+ }
+ return intval($this->user_id);
+ }
+
static function hasOpenID($user_id)
{
$oid = new User_openid();
diff --git a/plugins/OpenID/openid.php b/plugins/OpenID/openid.php
index 8ec138ffb6..25811d88b2 100644
--- a/plugins/OpenID/openid.php
+++ b/plugins/OpenID/openid.php
@@ -360,9 +360,9 @@ class AutosubmitAction extends Action
var $form_html = null;
var $form_id = null;
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
diff --git a/plugins/OpenX/OpenXPlugin.php b/plugins/OpenX/OpenXPlugin.php
index 8d80197be3..f0c78905aa 100644
--- a/plugins/OpenX/OpenXPlugin.php
+++ b/plugins/OpenX/OpenXPlugin.php
@@ -204,7 +204,7 @@ ENDOFSCRIPT;
$versions[] = array('name' => 'OpenX',
'version' => GNUSOCIAL_VERSION,
'author' => 'Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:OpenX',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/OpenX',
'rawdescription' =>
// TRANS: Plugin description.
_m('Plugin for OpenX Ad Server.'));
diff --git a/plugins/OpenX/README b/plugins/OpenX/README
new file mode 100644
index 0000000000..6b05e8e4a5
--- /dev/null
+++ b/plugins/OpenX/README
@@ -0,0 +1,26 @@
+The OpenX plugin enables support for OpenX Ad Server.
+
+See: http://www.openx.org/
+
+Installation
+============
+add "addPlugin('OpenX');"
+to the bottom of your config.php
+
+Settings
+========
+mediumRectangle:
+rectangle:
+leaderboard:
+wideSkyscraper:
+adScript:
+
+Example
+=======
+$config['openx']['mediumRectangle'] = '';
+$config['openx']['rectangle'] = '';
+$config['openx']['leaderboard'] = '';
+$config['openx']['wideSkyscraper'] = '';
+$config['openx']['adScript'] = '';
+addPlugin('OpenX');
+
diff --git a/plugins/Orbited/OrbitedPlugin.php b/plugins/Orbited/OrbitedPlugin.php
index 5abd27e9ee..e007eebe7e 100644
--- a/plugins/Orbited/OrbitedPlugin.php
+++ b/plugins/Orbited/OrbitedPlugin.php
@@ -164,7 +164,7 @@ class OrbitedPlugin extends RealtimePlugin
$versions[] = array('name' => 'Orbited',
'version' => GNUSOCIAL_VERSION,
'author' => 'Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:Orbited',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Orbited',
'rawdescription' =>
// TRANS: Plugin description.
_m('Plugin to make updates using Orbited and STOMP.'));
diff --git a/plugins/Orbited/README b/plugins/Orbited/README
new file mode 100644
index 0000000000..20d0598e24
--- /dev/null
+++ b/plugins/Orbited/README
@@ -0,0 +1,37 @@
+The Orbited plugin enables "real time" updates using Orbited + STOMP
+
+See:
+* https://pypi.python.org/pypi/orbited
+* https://en.wikipedia.org/wiki/Streaming_Text_Oriented_Messaging_Protocol
+
+Installation
+============
+add "addPlugin('Orbited');"
+to the bottom of your config.php
+
+Settings
+========
+webserver:
+webport:
+channelbase:
+stompserver:
+stompport:
+username:
+password:
+webuser:
+webpass:
+
+Example
+=======
+addPlugin('Orbited', array(
+ 'webserver' => '',
+ 'webport' => '',
+ 'channelbase' => '',
+ 'stompserver' => '',
+ 'stompport' => '',
+ 'username' => '',
+ 'password' => '',
+ 'webuser' => '',
+ 'webpass' => ''
+));
+
diff --git a/plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php b/plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php
index 31f62cc2ca..adc87e66ee 100644
--- a/plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php
+++ b/plugins/PiwikAnalytics/PiwikAnalyticsPlugin.php
@@ -108,7 +108,7 @@ ENDOFPIWIK;
$versions[] = array('name' => 'PiwikAnalytics',
'version' => GNUSOCIAL_VERSION,
'author' => 'Tobias Diekershoff, Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:Piwik',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Piwik',
'rawdescription' =>
// TRANS: Plugin description.
_m('Use Piwik Open Source web analytics software.'));
diff --git a/plugins/PiwikAnalytics/README b/plugins/PiwikAnalytics/README
new file mode 100644
index 0000000000..6d47ee89ff
--- /dev/null
+++ b/plugins/PiwikAnalytics/README
@@ -0,0 +1,23 @@
+The PiwikAnalytics plugin adds JavaScript that sends various traffic details
+to a Piwik server to track web access.
+
+See:
+* http://piwik.org/
+
+Installation
+============
+add "addPlugin('PiwikAnalytics');"
+to the bottom of your config.php
+
+Settings
+========
+piwikroot: The root installation URL of the Piwik instance WITHOUT the protocol
+piwikId: The ID provided by the Pwiki instance.
+
+Example
+=======
+addPlugin('PiwikAnalytics', array(
+ 'piwikroot' => 'example.org/piwik/',
+ 'piwikId' => 'PIWIK_ID'
+));
+
diff --git a/plugins/Poll/PollPlugin.php b/plugins/Poll/PollPlugin.php
index 53a491ef47..4f74e82a35 100644
--- a/plugins/Poll/PollPlugin.php
+++ b/plugins/Poll/PollPlugin.php
@@ -125,7 +125,7 @@ class PollPlugin extends MicroAppPlugin
$versions[] = array('name' => 'Poll',
'version' => self::VERSION,
'author' => 'Brion Vibber',
- 'homepage' => 'http://status.net/wiki/Plugin:Poll',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Poll',
'rawdescription' =>
// TRANS: Plugin description.
_m('Simple extension for supporting basic polls.'));
diff --git a/plugins/PostDebug/PostDebugPlugin.php b/plugins/PostDebug/PostDebugPlugin.php
index 120feee8fe..9f87494405 100644
--- a/plugins/PostDebug/PostDebugPlugin.php
+++ b/plugins/PostDebug/PostDebugPlugin.php
@@ -53,7 +53,7 @@ class PostDebugPlugin extends Plugin
$versions[] = array('name' => 'PostDebug',
'version' => GNUSOCIAL_VERSION,
'author' => 'Brion Vibber',
- 'homepage' => 'http://status.net/wiki/Plugin:PostDebug',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/PostDebug',
'rawdescription' =>
// TRANS: Plugin description.
_m('Debugging tool to record request details on POST.'));
diff --git a/plugins/PostDebug/README b/plugins/PostDebug/README
new file mode 100644
index 0000000000..61a69725d3
--- /dev/null
+++ b/plugins/PostDebug/README
@@ -0,0 +1,18 @@
+The PostDebug plugin records detailed data on POSTs requests.
+
+Installation
+============
+add "addPlugin('PostDebug');"
+to the bottom of your config.php
+
+Settings
+========
+dir: The directory where the log file will be saved
+
+Example
+=======
+
+addPlugin('PostDebug', array(
+ 'dir' => '/tmp'
+));
+
diff --git a/plugins/PtitUrl/PtitUrlPlugin.php b/plugins/PtitUrl/PtitUrlPlugin.php
index 0e23a5f64e..031604b57f 100644
--- a/plugins/PtitUrl/PtitUrlPlugin.php
+++ b/plugins/PtitUrl/PtitUrlPlugin.php
@@ -62,7 +62,7 @@ class PtitUrlPlugin extends UrlShortenerPlugin
$versions[] = array('name' => sprintf('PtitUrl (%s)', $this->shortenerName),
'version' => GNUSOCIAL_VERSION,
'author' => 'Craig Andrews',
- 'homepage' => 'http://status.net/wiki/Plugin:PtitUrl',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/PtitUrl',
'rawdescription' =>
// TRANS: Plugin description. %1$s is the URL shortening service base URL (for example "bit.ly").
sprintf(_m('Uses %1$s URL-shortener service.'),
diff --git a/plugins/PtitUrl/README b/plugins/PtitUrl/README
new file mode 100644
index 0000000000..507b9ecc26
--- /dev/null
+++ b/plugins/PtitUrl/README
@@ -0,0 +1,18 @@
+The PtitUrl plugin shortens URLS via a PtitUrl URL-shortener service
+
+Installation
+============
+add "addPlugin('PtitUrl');"
+to the bottom of your config.php
+
+Settings
+========
+serviceUrl: The URL to the PtitUrl instance
+
+Example
+=======
+
+addPlugin('PtitUrl', array(
+ 'serviceUrl' => 'http://example.org'
+));
+
diff --git a/plugins/QnA/QnAPlugin.php b/plugins/QnA/QnAPlugin.php
index e8c983eb9a..6780637009 100644
--- a/plugins/QnA/QnAPlugin.php
+++ b/plugins/QnA/QnAPlugin.php
@@ -130,7 +130,7 @@ class QnAPlugin extends MicroAppPlugin
'name' => 'QnA',
'version' => GNUSOCIAL_VERSION,
'author' => 'Zach Copley',
- 'homepage' => 'http://status.net/wiki/Plugin:QnA',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/QnA',
'description' =>
// TRANS: Plugin description.
_m('Question and Answers micro-app.')
diff --git a/plugins/QnA/README b/plugins/QnA/README
new file mode 100644
index 0000000000..c85b8d910d
--- /dev/null
+++ b/plugins/QnA/README
@@ -0,0 +1,10 @@
+The QnA plugin enables Questions and Answers type of notices
+
+Installation
+============
+This plugin is enabled by default
+
+Settings
+========
+none
+
diff --git a/plugins/Qvitter b/plugins/Qvitter
new file mode 160000
index 0000000000..2be23b4038
--- /dev/null
+++ b/plugins/Qvitter
@@ -0,0 +1 @@
+Subproject commit 2be23b403879d2f04a00f401ead47f698ddfdff3
diff --git a/plugins/QvitterPlus b/plugins/QvitterPlus
new file mode 160000
index 0000000000..0e98b2f2ea
--- /dev/null
+++ b/plugins/QvitterPlus
@@ -0,0 +1 @@
+Subproject commit 0e98b2f2ea5dee9de86cc2a6333ed30b85de5369
diff --git a/plugins/RSSCloud/RSSCloudPlugin.php b/plugins/RSSCloud/RSSCloudPlugin.php
index 0fff0947a2..3a8a83749c 100644
--- a/plugins/RSSCloud/RSSCloudPlugin.php
+++ b/plugins/RSSCloud/RSSCloudPlugin.php
@@ -203,7 +203,7 @@ class RSSCloudPlugin extends Plugin
$versions[] = array('name' => 'RSSCloud',
'version' => RSSCLOUDPLUGIN_VERSION,
'author' => 'Zach Copley',
- 'homepage' => 'http://status.net/wiki/Plugin:RSSCloud',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/RSSCloud',
'rawdescription' =>
// TRANS: Plugin description.
_m('The RSSCloud plugin enables your StatusNet instance to publish ' .
diff --git a/plugins/RSSCloud/actions/loggingaggregator.php b/plugins/RSSCloud/actions/loggingaggregator.php
index 824fa9ee9c..7ef9e65cbf 100644
--- a/plugins/RSSCloud/actions/loggingaggregator.php
+++ b/plugins/RSSCloud/actions/loggingaggregator.php
@@ -57,7 +57,7 @@ class LoggingAggregatorAction extends Action
*
* @return boolean false if user doesn't exist
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -77,9 +77,9 @@ class LoggingAggregatorAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if (empty($this->url)) {
// TRANS: Form validation error displayed when a URL parameter is missing.
diff --git a/plugins/RSSCloud/actions/rsscloudrequestnotify.php b/plugins/RSSCloud/actions/rsscloudrequestnotify.php
index 7fd6da0509..1d294fef68 100644
--- a/plugins/RSSCloud/actions/rsscloudrequestnotify.php
+++ b/plugins/RSSCloud/actions/rsscloudrequestnotify.php
@@ -50,7 +50,7 @@ class RSSCloudRequestNotifyAction extends Action
*
* @return boolean false if user doesn't exist
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -82,9 +82,9 @@ class RSSCloudRequestNotifyAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
if ($_SERVER['REQUEST_METHOD'] != 'POST') {
// TRANS: Form validation error displayed when POST is not used.
diff --git a/plugins/Realtime/RealtimePlugin.php b/plugins/Realtime/RealtimePlugin.php
index 172e9272af..8b7767ac15 100644
--- a/plugins/Realtime/RealtimePlugin.php
+++ b/plugins/Realtime/RealtimePlugin.php
@@ -311,7 +311,7 @@ class RealtimePlugin extends Plugin
return false; // No default processing
}
- function noticeAsJson($notice)
+ function noticeAsJson(Notice $notice)
{
// FIXME: this code should be abstracted to a neutral third
// party, like Notice::asJson(). I'm not sure of the ethics
@@ -347,7 +347,7 @@ class RealtimePlugin extends Plugin
return $arr;
}
- function getNoticeTags($notice)
+ function getNoticeTags(Notice $notice)
{
$tags = null;
diff --git a/plugins/RegisterThrottle/README b/plugins/RegisterThrottle/README
new file mode 100644
index 0000000000..049bfd77c6
--- /dev/null
+++ b/plugins/RegisterThrottle/README
@@ -0,0 +1,24 @@
+The RegisterThrottle plugin throttles registration by IP address
+
+Installation
+============
+This plugin is enabled by default on public instances, otherwise it can be
+enabled by adding "addPlugin('RegisterThrottle');" to the bottom of your
+config.php
+
+Settings
+========
+regLimits: Array of time spans in seconds to limits. Default is 3 registrations per hour, 5 per day, 10 per week.
+silenced: Disallow registration if a silenced user has registered from this IP address
+
+Example
+=======
+addPlugin('RegisterThrottle', array(
+ 'regLimits' => array(
+ 604800 => 10, // per week
+ 86400 => 5, // per day
+ 3600 => 3 // per hour
+ ),
+ 'silenced' => true
+));
+
diff --git a/plugins/RegisterThrottle/RegisterThrottlePlugin.php b/plugins/RegisterThrottle/RegisterThrottlePlugin.php
index 552420d8f6..5b999a4370 100644
--- a/plugins/RegisterThrottle/RegisterThrottlePlugin.php
+++ b/plugins/RegisterThrottle/RegisterThrottlePlugin.php
@@ -60,6 +60,14 @@ class RegisterThrottlePlugin extends Plugin
*/
public $silenced = true;
+ /**
+ * Auto-silence all other users from the same registration_ip
+ * as the one being silenced. Caution: Many users may come from
+ * the same IP (even entire countries) without having any sort
+ * of relevant connection for moderation.
+ */
+ public $auto_silence_by_ip = false;
+
/**
* Whether we're enabled; prevents recursion.
*/
@@ -233,7 +241,7 @@ class RegisterThrottlePlugin extends Plugin
$versions[] = array('name' => 'RegisterThrottle',
'version' => GNUSOCIAL_VERSION,
'author' => 'Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:RegisterThrottle',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/RegisterThrottle',
'description' =>
// TRANS: Plugin description.
_m('Throttles excessive registration from a single IP address.'));
@@ -300,15 +308,15 @@ class RegisterThrottlePlugin extends Plugin
return true;
}
- if ($role != Profile_role::SILENCED) {
+ if ($role !== Profile_role::SILENCED) {
return true;
}
- if (!$this->silenced) {
+ if (!$this->auto_silence_by_ip) {
return true;
}
- $ri = Registration_ip::getKV('user_id', $profile->id);
+ $ri = Registration_ip::getKV('user_id', $profile->getID());
if (empty($ri)) {
return true;
@@ -317,13 +325,13 @@ class RegisterThrottlePlugin extends Plugin
$ids = Registration_ip::usersByIP($ri->ipaddress);
foreach ($ids as $id) {
- if ($id == $profile->id) {
+ if ($id == $profile->getID()) {
continue;
}
- $other = Profile::getKV('id', $id);
-
- if (empty($other)) {
+ try {
+ $other = Profile::getByID($id);
+ } catch (NoResultException $e) {
continue;
}
@@ -331,6 +339,11 @@ class RegisterThrottlePlugin extends Plugin
continue;
}
+ // 'enabled' here is used to prevent recursion, since
+ // we'll end up in this function again on ->silence()
+ // though I actually think it doesn't matter since we
+ // do this in onEndGrantRole and that means the above
+ // $other->isSilenced() test should've 'continue'd...
$old = self::$enabled;
self::$enabled = false;
$other->silence();
diff --git a/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php b/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
index 734cabbcd5..6ca13b9097 100644
--- a/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
+++ b/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
@@ -221,7 +221,7 @@ class RequireValidatedEmailPlugin extends Plugin
'Evan Prodromou, '.
'Brion Vibber',
'homepage' =>
- 'http://status.net/wiki/Plugin:RequireValidatedEmail',
+ 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/RequireValidatedEmail',
'rawdescription' =>
// TRANS: Plugin description.
_m('Disables posting without a validated email address.'));
diff --git a/plugins/RequireValidatedEmail/scripts/registerbyemail.php b/plugins/RequireValidatedEmail/scripts/registerbyemail.php
index 4d2000ab0f..c7b4a2af9e 100755
--- a/plugins/RequireValidatedEmail/scripts/registerbyemail.php
+++ b/plugins/RequireValidatedEmail/scripts/registerbyemail.php
@@ -73,8 +73,4 @@ $url = common_local_url('confirmfirstemail',
print "$url\n";
-mail_confirm_address($user,
- $confirm->code,
- $user->nickname,
- $email,
- $url);
+$confirm->sendConfirmation(['url'=>$url]);
diff --git a/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php b/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php
index 9a85a974ae..47f304414e 100644
--- a/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php
+++ b/plugins/ReverseUsernameAuthentication/ReverseUsernameAuthenticationPlugin.php
@@ -62,7 +62,7 @@ class ReverseUsernameAuthenticationPlugin extends AuthenticationPlugin
$versions[] = array('name' => 'Reverse Username Authentication',
'version' => GNUSOCIAL_VERSION,
'author' => 'Craig Andrews',
- 'homepage' => 'http://status.net/wiki/Plugin:ReverseUsernameAuthentication',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/ReverseUsernameAuthentication',
'rawdescription' =>
// TRANS: Plugin description.
_m('The Reverse Username Authentication plugin allows for StatusNet to handle authentication by checking if the provided password is the same as the reverse of the username.'));
diff --git a/plugins/SQLProfile/README b/plugins/SQLProfile/README
new file mode 100644
index 0000000000..03b42b3262
--- /dev/null
+++ b/plugins/SQLProfile/README
@@ -0,0 +1,17 @@
+The SQLProfile plugin watches for poorly indexed DB queries.
+
+Installation
+============
+add "addPlugin('SQLProfile');"
+to the bottom of your config.php
+
+Settings
+========
+none
+
+Note: entries are logged at the LOG_DEBUG level.
+
+Example
+=======
+addPlugin('SQLProfile');
+
diff --git a/plugins/SQLProfile/SQLProfilePlugin.php b/plugins/SQLProfile/SQLProfilePlugin.php
index a7912844c2..3baa91e494 100644
--- a/plugins/SQLProfile/SQLProfilePlugin.php
+++ b/plugins/SQLProfile/SQLProfilePlugin.php
@@ -36,7 +36,7 @@ class SQLProfilePlugin extends Plugin
$versions[] = array('name' => 'SQLProfile',
'version' => GNUSOCIAL_VERSION,
'author' => 'Brion Vibber',
- 'homepage' => 'http://status.net/wiki/Plugin:SQLProfile',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/SQLProfile',
'rawdescription' =>
// TRANS: Plugin description.
_m('Debug tool to watch for poorly indexed DB queries.'));
diff --git a/plugins/SQLStats/README b/plugins/SQLStats/README
new file mode 100644
index 0000000000..0f840b1af5
--- /dev/null
+++ b/plugins/SQLStats/README
@@ -0,0 +1,18 @@
+The SQLStats plugin logs statistics on performed SQL queries.
+
+Installation
+============
+add "addPlugin('SQLStats');"
+to the bottom of your config.php
+
+Settings
+========
+verbose: whether to be verbose or not
+
+Note: entries are logged at the LOG_INFO level.
+
+Example
+=======
+$config['sqlstats']['verbose'] = false;
+addPlugin('SQLStats');
+
diff --git a/plugins/SQLStats/SQLStatsPlugin.php b/plugins/SQLStats/SQLStatsPlugin.php
index f67ed03012..bd83ac0a32 100644
--- a/plugins/SQLStats/SQLStatsPlugin.php
+++ b/plugins/SQLStats/SQLStatsPlugin.php
@@ -39,7 +39,7 @@ class SQLStatsPlugin extends Plugin
$versions[] = array('name' => 'SQLStats',
'version' => GNUSOCIAL_VERSION,
'author' => 'Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:SQLStats',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/SQLStats',
'rawdescription' =>
// TRANS: Plugin decription.
_m('Debug tool to watch for poorly indexed DB queries.'));
diff --git a/plugins/Sample/README b/plugins/Sample/README
new file mode 100644
index 0000000000..ff1188d8af
--- /dev/null
+++ b/plugins/Sample/README
@@ -0,0 +1,19 @@
+The Sample plugin shows best practices for development of GNU social plugins.
+
+It adds a "Hello" menu item to the default menu and tracks how many times it
+has greeted each user.
+
+Installation
+============
+add "addPlugin('Sample');"
+to the bottom of your config.php
+
+Settings
+========
+none
+
+Example
+=======
+
+addPlugin('Sample');
+
diff --git a/plugins/Sample/SamplePlugin.php b/plugins/Sample/SamplePlugin.php
index 80c46d17c4..4f8351cc08 100644
--- a/plugins/Sample/SamplePlugin.php
+++ b/plugins/Sample/SamplePlugin.php
@@ -227,7 +227,7 @@ class SamplePlugin extends Plugin
$versions[] = array('name' => 'Sample',
'version' => GNUSOCIAL_VERSION,
'author' => 'Brion Vibber, Evan Prodromou',
- 'homepage' => 'http://status.net/wiki/Plugin:Sample',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/Sample',
'rawdescription' =>
// TRANS: Plugin description.
_m('A sample plugin to show basics of development for new hackers.'));
diff --git a/plugins/Sample/actions/hello.php b/plugins/Sample/actions/hello.php
index da5682b332..37bae3a4dd 100644
--- a/plugins/Sample/actions/hello.php
+++ b/plugins/Sample/actions/hello.php
@@ -66,7 +66,7 @@ class HelloAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
@@ -91,9 +91,9 @@ class HelloAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
- parent::handle($args);
+ parent::handle();
$this->showPage();
}
diff --git a/plugins/SearchSub/README b/plugins/SearchSub/README
new file mode 100644
index 0000000000..7e9e693a8e
--- /dev/null
+++ b/plugins/SearchSub/README
@@ -0,0 +1,10 @@
+The SearchSub plugin allows following all messages with a given search.
+
+Installation
+============
+This plugin is enabled by default
+
+Settings
+========
+none
+
diff --git a/plugins/SearchSub/SearchSubPlugin.php b/plugins/SearchSub/SearchSubPlugin.php
index 57984d0e01..06d644235e 100644
--- a/plugins/SearchSub/SearchSubPlugin.php
+++ b/plugins/SearchSub/SearchSubPlugin.php
@@ -93,7 +93,7 @@ class SearchSubPlugin extends Plugin
$versions[] = array('name' => 'SearchSub',
'version' => self::VERSION,
'author' => 'Brion Vibber',
- 'homepage' => 'http://status.net/wiki/Plugin:SearchSub',
+ 'homepage' => 'https://git.gnu.io/gnu/gnu-social/tree/master/plugins/SearchSub',
'rawdescription' =>
// TRANS: Plugin description.
_m('Plugin to allow following all messages with a given search.'));
diff --git a/plugins/SearchSub/actions/searchsub.php b/plugins/SearchSub/actions/searchsub.php
index 586f2252f6..98c218b8e5 100644
--- a/plugins/SearchSub/actions/searchsub.php
+++ b/plugins/SearchSub/actions/searchsub.php
@@ -63,7 +63,7 @@ class SearchsubAction extends Action
*
* @return boolean success flag
*/
- function prepare($args)
+ function prepare(array $args = array())
{
parent::prepare($args);
if ($this->boolean('ajax')) {
@@ -118,7 +118,7 @@ class SearchsubAction extends Action
*
* @return void
*/
- function handle($args)
+ function handle()
{
// Throws exception on error
diff --git a/plugins/SearchSub/actions/searchsubs.php b/plugins/SearchSub/actions/searchsubs.php
index fd89075032..2f9b8db8a7 100644
--- a/plugins/SearchSub/actions/searchsubs.php
+++ b/plugins/SearchSub/actions/searchsubs.php
@@ -136,7 +136,7 @@ class SearchSubsAction extends GalleryAction
class SearchSubscriptionsList extends SubscriptionList
{
- function newListItem($searchsub)
+ function newListItem(Profile $searchsub)
{
return new SearchSubscriptionsListItem($searchsub, $this->owner, $this->action);
}
diff --git a/plugins/SearchSub/actions/searchunsub.php b/plugins/SearchSub/actions/searchunsub.php
index a6ce0f07c5..1bc3a1c95b 100644
--- a/plugins/SearchSub/actions/searchunsub.php
+++ b/plugins/SearchSub/actions/searchunsub.php
@@ -62,7 +62,7 @@ class SearchunsubAction extends SearchsubAction
*
* @return void
*/
- function handle($args)
+ function handle()
{
// Throws exception on error
diff --git a/plugins/SensitiveContent/LICENSE b/plugins/SensitiveContent/LICENSE
new file mode 100644
index 0000000000..a871fcfd0f
--- /dev/null
+++ b/plugins/SensitiveContent/LICENSE
@@ -0,0 +1,662 @@
+ GNU AFFERO GENERAL PUBLIC LICENSE
+ Version 3, 19 November 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU Affero General Public License is a free, copyleft license for
+software and other kinds of works, specifically designed to ensure
+cooperation with the community in the case of network server software.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+our General Public Licenses are intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ Developers that use our General Public Licenses protect your rights
+with two steps: (1) assert copyright on the software, and (2) offer
+you this License which gives you legal permission to copy, distribute
+and/or modify the software.
+
+ A secondary benefit of defending all users' freedom is that
+improvements made in alternate versions of the program, if they
+receive widespread use, become available for other developers to
+incorporate. Many developers of free software are heartened and
+encouraged by the resulting cooperation. However, in the case of
+software used on network servers, this result may fail to come about.
+The GNU General Public License permits making a modified version and
+letting the public access it on a server without ever releasing its
+source code to the public.
+
+ The GNU Affero General Public License is designed specifically to
+ensure that, in such cases, the modified source code becomes available
+to the community. It requires the operator of a network server to
+provide the source code of the modified version running there to the
+users of that server. Therefore, public use of a modified version, on
+a publicly accessible server, gives the public access to the source
+code of the modified version.
+
+ An older license, called the Affero General Public License and
+published by Affero, was designed to accomplish similar goals. This is
+a different license, not a version of the Affero GPL, but Affero has
+released a new version of the Affero GPL which permits relicensing under
+this license.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU Affero General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Remote Network Interaction; Use with the GNU General Public License.
+
+ Notwithstanding any other provision of this License, if you modify the
+Program, your modified version must prominently offer all users
+interacting with it remotely through a computer network (if your version
+supports such interaction) an opportunity to receive the Corresponding
+Source of your version by providing access to the Corresponding Source
+from a network server at no charge, through some standard or customary
+means of facilitating copying of software. This Corresponding Source
+shall include the Corresponding Source for any work covered by version 3
+of the GNU General Public License that is incorporated pursuant to the
+following paragraph.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the work with which it is combined will remain governed by version
+3 of the GNU General Public License.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU Affero General Public License from time to time. Such new versions
+will be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU Affero General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU Affero General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU Affero General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If your software can interact with users remotely through a computer
+network, you should also make sure that it provides a way for users to
+get its source. For example, if your program is a web application, its
+interface could display a "Source" link that leads users to an archive
+of the code. There are many ways you could offer source, and different
+solutions will be better for different programs; see section 13 for the
+specific requirements.
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU AGPL, see
+.
+
diff --git a/plugins/SensitiveContent/README.md b/plugins/SensitiveContent/README.md
new file mode 100644
index 0000000000..9de210d381
--- /dev/null
+++ b/plugins/SensitiveContent/README.md
@@ -0,0 +1,41 @@
+# "Sensitive" Content Plugin for GNU Social
+
+## About
+
+WARNING: THIS IS ALPHA CODE, IT IS PRERELEASE AND SHOULD ONLY BE INSTALLED TO
+HELP TEST OR YOU ARE WILLING TO TAKE RISKS.
+
+Create user option to allow a user to hide #NSFW-hashtagged notices behind a
+blocker image until clicked.
+
+Works for both vanilla GNUSocial and with the Qvitter plugin.
+
+## Install
+
+- Move the project directory to ${GNU_SOCIAL}/plugins
+- Add addPlugin('SensitiveContent'); to your config.php
+
+if you want to customize the blocker image, add a line to your config.php:
+
+ $config['site']['sensitivecontent']['blockerimage'] = "/path/to/image.jpg";
+
+## Usage
+
+Individual users must go to their Settings page. A new sidebar menu item "Sensitive Content"
+will be available. User checks or unchecks the checkbox on this page, and presses save.
+
+
+If you have GNU Social open in other browser tabs, refresh them. If you are using Qvitter, also
+refresh, but because Qvitter caches notices on the client side, only new sensitive images will
+be hidden, it will not apply to notices retroactively unless you clear your browser cache.
+
+## License
+
+GNU Affero License
+
+## Thanks
+
+Thanks in particular to Hannes and Qvitter because looking at his code helped me a lot.
+
+A tiny bit of content was taken from Qvitter to enhance Qvitter with this functionality.
+
diff --git a/plugins/SensitiveContent/SensitiveContentPlugin.php b/plugins/SensitiveContent/SensitiveContentPlugin.php
new file mode 100644
index 0000000000..8eb7c4a6ef
--- /dev/null
+++ b/plugins/SensitiveContent/SensitiveContentPlugin.php
@@ -0,0 +1,274 @@
+ 'Sensitive Content',
+ 'version' => self::VERSION,
+ 'author' => 'MoonMan',
+ 'homepage' => 'https://gitgud.io/ShitposterClub/SensitiveContent/',
+ 'description' =>
+ _m('Mark, hide/show sensitive notices like on Twitter.'));
+ return true;
+ }
+
+ static function settings($setting)
+ {
+ $settings['blockerimage'] = Plugin::staticPath('SensitiveContent', '').'img/blocker.png';
+
+ $configphpsettings = common_config('site','sensitivecontent') ?: array();
+ foreach($configphpsettings as $configphpsetting=>$value) {
+ $settings[$configphpsetting] = $value;
+ }
+
+ if(isset($settings[$setting])) {
+ return $settings[$setting];
+ }
+ else FALSE;
+ }
+
+ function onNoticeSimpleStatusArray($notice, &$twitter_status, $scoped)
+ {
+ $twitter_status['tags'] = $notice->getTags();
+ }
+
+ function onTwitterUserArray($profile, &$twitter_user, $scoped)
+ {
+ if ($scoped instanceof Profile && $scoped->sameAs($profile)) {
+ $twitter_user['hide_sensitive'] = $this->getHideSensitive($scoped);
+ }
+ }
+
+ public function onRouterInitialized(URLMapper $m)
+ {
+ $m->connect('settings/sensitivecontent',
+ array('action' => 'sensitivecontentsettings'));
+ }
+
+
+ function onEndAccountSettingsNav($action)
+ {
+ $action->menuItem(common_local_url('sensitivecontentsettings'),
+ _m('MENU', 'Sensitive Content'),
+ _m('Settings for display of sensitive content.'));
+
+ return true;
+ }
+
+
+ public function onQvitterEndShowHeadElements(Action $action)
+ {
+ $blocker = static::settings('blockerimage');
+ common_log( LOG_DEBUG, "SENSITIVECONTENT " . $blocker );
+
+
+ $styles = <<style($styles);
+ }
+
+ function onQvitterEndShowScripts(Action $action)
+ {
+ $action->script( Plugin::staticPath('SensitiveContent', '').'js/sensitivecontent.js' );
+ }
+
+ function onEndShowStyles(Action $action)
+ {
+ $blocker = static::settings('blockerimage');
+
+ $styles = << footer > .attachments > .inline-attachment > .attachment-wrapper > .sensitive-blocker {
+ display: none;
+}
+
+html[data-hidesensitive='true'] .tagcontainer.data-tag-nsfw > footer > .attachments > .inline-attachment > .attachment-wrapper > .sensitive-blocker {
+display: block;
+width: 100%;
+height: 100%;
+position: absolute;
+z-index: 100;
+/*background-color: #d4baba;*/
+background-color: black;
+background-image: url($blocker);
+background-repeat: no-repeat;
+background-position: center center;
+background-size: contain;
+transition: opacity 1s ease-in-out;
+}
+
+html[data-hidesensitive='true'] .tagcontainer.data-tag-nsfw > footer > .attachments > .inline-attachment > .attachment-wrapper > .sensitive-blocker.reveal {
+ opacity: 0;
+}
+
+EOB;
+
+ $action->style($styles);
+ }
+
+ function onStartShowAttachmentRepresentation($out, $file)
+ {
+ $profile = Profile::current();
+
+ if (!is_null($profile) && $profile instanceof Profile)
+ {
+ $hidesensitive = $this->getHideSensitive($profile);
+ }
+ else
+ {
+ $hidesensitive = false;
+ }
+
+
+ $classes = "sensitive-blocker"; //'sensitive-blocker';
+
+ $out->elementStart('div', array(
+ 'class'=>'attachment-wrapper',
+ 'style'=>'height: ' . $file->getThumbnail()->height . 'px; width: ' . $file->getThumbnail()->width . 'px;'
+ )); /*needs height of thumb*/
+ $out->elementStart('div', array(
+ 'class'=>$classes,
+ 'onclick'=>'toggleSpoiler(event)',
+ 'style'=>'height: ' . $file->getThumbnail()->height . 'px; width: ' . $file->getThumbnail()->width . 'px;'
+ ));
+ $out->raw(' ');
+ $out->elementEnd('div');
+ }
+
+ function onEndShowAttachmentRepresentation($out, $file)
+ {
+ $out->elementEnd('div');
+ }
+
+ function onEndShowScripts(Action $action)
+ {
+ $profile = $action->getScoped();
+ if (!is_null($profile) && $profile instanceof Profile)
+ {
+ $hidesensitive = $this->getHideSensitive($profile) ? "true" : "false";
+ }
+ else
+ {
+ $hidesensitive = "false";
+ }
+
+ $inline = <<inlineScript($inline);
+ }
+
+ function onEndOpenNoticeListItemElement(NoticeListItem $nli)
+ {
+ $rawtags = $nli->getNotice()->getTags();
+ $classes = "tagcontainer";
+
+ foreach($rawtags as $tag)
+ {
+ $classes = $classes . ' data-tag-' . $tag;
+ }
+
+
+ $nli->elementStart('span', array('class' => $classes));
+ //$nli->elementEnd('span');
+ }
+
+ function onStartCloseNoticeListItemElement(NoticeListItem $nli)
+ {
+ $nli->elementEnd('span');
+ }
+
+ function onStartHtmlElement($action, &$attrs) {
+ $profile = Profile::current();
+
+ if (!is_null($profile) && $profile instanceof Profile)
+ {
+ $hidesensitive = $this->getHideSensitive($profile);
+ }
+ else
+ {
+ $hidesensitive = false;
+ }
+
+
+ $attrs = array_merge($attrs,
+ array('data-hidesensitive' => ($hidesensitive ? "true" : "false"))
+ );
+ }
+
+
+ function getHideSensitive($profile) {
+ $c = Cache::instance();
+
+ /*
+ if (!empty($c)) {
+ $hidesensitive = $c->get(Cache::key('profile:hide_sensitive:'.$profile->id));
+ if (is_numeric($hidesensitive)) {
+ return (boolean) $hidesensitive;
+ }
+ else return FALSE;
+ }
+ */
+
+ $hidesensitive = $profile->getPref('MoonMan', 'hide_sensitive', '0');
+
+ if (!empty($c)) {
+ //not using it yet.
+ $c->set(Cache::key('profile:hide_sensitive:'.$profile->id), $hidesensitive);
+ }
+
+ //common_log(LOG_DEBUG, "SENSITIVECONTENT hidesensitive? id " . $profile->id . " value " . (boolean)$hidesensitive );
+
+ if (is_null($hidesensitive)) {
+ return FALSE;
+ } else
+ if (is_numeric($hidesensitive)) {
+ return (boolean) $hidesensitive;
+ }
+ else return FALSE;
+ }
+
+}
diff --git a/plugins/SensitiveContent/actions/sensitivecontentsettings.php b/plugins/SensitiveContent/actions/sensitivecontentsettings.php
new file mode 100644
index 0000000000..482a893958
--- /dev/null
+++ b/plugins/SensitiveContent/actions/sensitivecontentsettings.php
@@ -0,0 +1,51 @@
+scoped->getUser();
+
+ $this->elementStart('form', array('method' => 'post',
+ 'id' => 'sensitivecontent',
+ 'class' => 'form_settings',
+ 'action' => common_local_url('sensitivecontentsettings')));
+
+ $this->elementStart('fieldset');
+ $this->hidden('token', common_session_token());
+ $this->elementStart('ul', 'form_data');
+
+ $this->elementStart('li');
+ $this->checkbox('hidesensitive', _('Hide attachments in posts hashtagged #NSFW'),
+ ($this->arg('hidesensitive')) ?
+ $this->boolean('hidesensitive') : $this->scoped->getPref('MoonMan','hide_sensitive',0));
+ $this->elementEnd('li');
+
+
+ $this->elementEnd('ul');
+ $this->submit('save', _m('BUTTON','Save'));
+
+ $this->elementEnd('fieldset');
+ $this->elementEnd('form');
+ }
+
+ function doPost()
+ {
+ $hidesensitive = $this->booleanintstring('hidesensitive');
+ $this->scoped->setPref('MoonMan','hide_sensitive', $hidesensitive);
+ return _('Settings saved.');
+ }
+}
diff --git a/plugins/SensitiveContent/img/blocker.png b/plugins/SensitiveContent/img/blocker.png
new file mode 100644
index 0000000000..6866ce4d4a
Binary files /dev/null and b/plugins/SensitiveContent/img/blocker.png differ
diff --git a/plugins/SensitiveContent/js/sensitivecontent.js b/plugins/SensitiveContent/js/sensitivecontent.js
new file mode 100644
index 0000000000..3c9d6584fd
--- /dev/null
+++ b/plugins/SensitiveContent/js/sensitivecontent.js
@@ -0,0 +1,228 @@
+window.buildAttachmentHTML = function(attachments){
+ var attachmentHTML = '';
+ var oembedHTML = '';
+ var quotedNotices = [];
+ var attachmentNum = 0;
+ var oembedNum = 0;
+ var urlsToHide = [];
+ if(typeof attachments != "undefined") {
+ $.each(attachments, function(){
+
+ // quoted notices
+ if(typeof this.quoted_notice != 'undefined') {
+
+ var quotedContent = this.quoted_notice.content;
+
+ // quoted notice's attachments' thumb urls
+ var quotedAttachmentsHTML = '';
+ var quotedAttachmentsHTMLbefore = '';
+ var quotedAttachmentsHTMLafter = '';
+ if(typeof this.quoted_notice.attachments != 'undefined' && this.quoted_notice.attachments.length > 0) {
+ quotedAttachmentsHTML += '