Merge branch '0.9.x' into 1.0.x
Conflicts: actions/imsettings.php lib/jabber.php Made a quick attempt to merge the new JID validation into the XmppPlugin, have not had a chance to test that version live yet. Should also move over the test cases.
This commit is contained in:
commit
2f4438fe24
26
README
26
README
@ -942,6 +942,26 @@ stomp_password: password for connecting to the stomp server; defaults
|
|||||||
to null.
|
to null.
|
||||||
|
|
||||||
stomp_persistent: keep items across queue server restart, if enabled.
|
stomp_persistent: keep items across queue server restart, if enabled.
|
||||||
|
Under ActiveMQ, the server configuration determines if and how
|
||||||
|
persistent storage is actually saved.
|
||||||
|
|
||||||
|
If using a message queue server other than ActiveMQ, you may
|
||||||
|
need to disable this if it does not support persistence.
|
||||||
|
|
||||||
|
stomp_transactions: use transactions to aid in error detection.
|
||||||
|
A broken transaction will be seen quickly, allowing a message
|
||||||
|
to be redelivered immediately if a daemon crashes.
|
||||||
|
|
||||||
|
If using a message queue server other than ActiveMQ, you may
|
||||||
|
need to disable this if it does not support transactions.
|
||||||
|
|
||||||
|
stomp_acks: send acknowledgements to aid in flow control.
|
||||||
|
An acknowledgement of successful processing tells the server
|
||||||
|
we're ready for more and can help keep things moving smoothly.
|
||||||
|
|
||||||
|
This should *not* be turned off when running with ActiveMQ, but
|
||||||
|
if using another message queue server that does not support
|
||||||
|
acknowledgements you might need to disable this.
|
||||||
|
|
||||||
softlimit: an absolute or relative "soft memory limit"; daemons will
|
softlimit: an absolute or relative "soft memory limit"; daemons will
|
||||||
restart themselves gracefully when they find they've hit
|
restart themselves gracefully when they find they've hit
|
||||||
@ -970,6 +990,12 @@ max_retries: for stomp, drop messages after N failed attempts to process.
|
|||||||
dead_letter_dir: for stomp, optional directory to dump data on failed
|
dead_letter_dir: for stomp, optional directory to dump data on failed
|
||||||
queue processing events after discarding them.
|
queue processing events after discarding them.
|
||||||
|
|
||||||
|
stomp_no_transactions: for stomp, the server does not support transactions,
|
||||||
|
so do not try to user them. This is needed for http://www.morbidq.com/.
|
||||||
|
|
||||||
|
stomp_no_acks: for stomp, the server does not support acknowledgements.
|
||||||
|
so do not try to user them. This is needed for http://www.morbidq.com/.
|
||||||
|
|
||||||
license
|
license
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
@ -66,7 +66,13 @@ class ApiGroupListAction extends ApiBareAuthAction
|
|||||||
{
|
{
|
||||||
parent::prepare($args);
|
parent::prepare($args);
|
||||||
|
|
||||||
$this->user = $this->getTargetUser($id);
|
$this->user = $this->getTargetUser(null);
|
||||||
|
|
||||||
|
if (empty($this->user)) {
|
||||||
|
$this->clientError(_('No such user.'), 404, $this->format);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
$this->groups = $this->getGroups();
|
$this->groups = $this->getGroups();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -86,11 +92,6 @@ class ApiGroupListAction extends ApiBareAuthAction
|
|||||||
{
|
{
|
||||||
parent::handle($args);
|
parent::handle($args);
|
||||||
|
|
||||||
if (empty($this->user)) {
|
|
||||||
$this->clientError(_('No such user.'), 404, $this->format);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$sitename = common_config('site', 'name');
|
$sitename = common_config('site', 'name');
|
||||||
$title = sprintf(_("%s's groups"), $this->user->nickname);
|
$title = sprintf(_("%s's groups"), $this->user->nickname);
|
||||||
$taguribase = TagURI::base();
|
$taguribase = TagURI::base();
|
||||||
@ -100,7 +101,7 @@ class ApiGroupListAction extends ApiBareAuthAction
|
|||||||
array('nickname' => $this->user->nickname)
|
array('nickname' => $this->user->nickname)
|
||||||
);
|
);
|
||||||
$subtitle = sprintf(
|
$subtitle = sprintf(
|
||||||
_("Groups %1$s is a member of on %2$s."),
|
_("Groups %1\$s is a member of on %2\$s."),
|
||||||
$this->user->nickname,
|
$this->user->nickname,
|
||||||
$sitename
|
$sitename
|
||||||
);
|
);
|
||||||
|
@ -66,7 +66,7 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction
|
|||||||
{
|
{
|
||||||
parent::prepare($args);
|
parent::prepare($args);
|
||||||
|
|
||||||
$this->user = $this->getTargetUser($id);
|
$this->user = $this->getTargetUser(null);
|
||||||
$this->groups = $this->getGroups();
|
$this->groups = $this->getGroups();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -137,11 +137,18 @@ class ApiGroupListAllAction extends ApiPrivateAuthAction
|
|||||||
$qry = 'SELECT user_group.* '.
|
$qry = 'SELECT user_group.* '.
|
||||||
'from user_group join local_group on user_group.id = local_group.group_id '.
|
'from user_group join local_group on user_group.id = local_group.group_id '.
|
||||||
'order by created desc ';
|
'order by created desc ';
|
||||||
|
$offset = intval($this->page - 1) * intval($this->count);
|
||||||
|
$limit = intval($this->count);
|
||||||
|
if (common_config('db', 'type') == 'pgsql') {
|
||||||
|
$qry .= ' LIMIT ' . $limit . ' OFFSET ' . $offset;
|
||||||
|
} else {
|
||||||
|
$qry .= ' LIMIT ' . $offset . ', ' . $limit;
|
||||||
|
}
|
||||||
$group = new User_group();
|
$group = new User_group();
|
||||||
|
|
||||||
$group->query($qry);
|
$group->query($qry);
|
||||||
|
|
||||||
|
$groups = array();
|
||||||
while ($group->fetch()) {
|
while ($group->fetch()) {
|
||||||
$groups[] = clone($group);
|
$groups[] = clone($group);
|
||||||
}
|
}
|
||||||
|
@ -126,7 +126,7 @@ class FoafAction extends Action
|
|||||||
$this->element('geo:lat', null, $location->lat);
|
$this->element('geo:lat', null, $location->lat);
|
||||||
}
|
}
|
||||||
if ($location->lon) {
|
if ($location->lon) {
|
||||||
$this->element('geo:long', null, $location->lat);
|
$this->element('geo:long', null, $location->lon);
|
||||||
}
|
}
|
||||||
if ($location->getURL()) {
|
if ($location->getURL()) {
|
||||||
$this->element('page', array('rdf:resource'=>$location->getURL()));
|
$this->element('page', array('rdf:resource'=>$location->getURL()));
|
||||||
|
@ -164,19 +164,15 @@ class NewnoticeAction extends Action
|
|||||||
$replyto = 'false';
|
$replyto = 'false';
|
||||||
}
|
}
|
||||||
|
|
||||||
$uploads = array();
|
$upload = null;
|
||||||
foreach($_FILES as $name => $value) {
|
$upload = MediaFile::fromUpload('attach');
|
||||||
if(substr($name, 0, 6) == "attach") {
|
|
||||||
$upload = MediaFile::fromUpload($name);
|
|
||||||
if (isset($upload)) {
|
if (isset($upload)) {
|
||||||
|
|
||||||
$content_shortened .= ' ' . $upload->shortUrl();
|
$content_shortened .= ' ' . $upload->shortUrl();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (Notice::contentTooLong($content_shortened)) {
|
if (Notice::contentTooLong($content_shortened)) {
|
||||||
foreach($uploads as $upload) {
|
|
||||||
$upload->delete();
|
$upload->delete();
|
||||||
}
|
|
||||||
$this->clientError(
|
$this->clientError(
|
||||||
sprintf(
|
sprintf(
|
||||||
_('Max notice size is %d chars, including attachment URL.'),
|
_('Max notice size is %d chars, including attachment URL.'),
|
||||||
@ -184,23 +180,32 @@ class NewnoticeAction extends Action
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$options = array('reply_to' => ($replyto == 'false') ? null : $replyto);
|
$options = array('reply_to' => ($replyto == 'false') ? null : $replyto);
|
||||||
|
|
||||||
if ($user->shareLocation() && $this->arg('notice_data-geo')) {
|
if ($user->shareLocation()) {
|
||||||
|
// use browser data if checked; otherwise profile data
|
||||||
|
if ($this->arg('notice_data-geo')) {
|
||||||
$locOptions = Notice::locationOptions($this->trimmed('lat'),
|
$locOptions = Notice::locationOptions($this->trimmed('lat'),
|
||||||
$this->trimmed('lon'),
|
$this->trimmed('lon'),
|
||||||
$this->trimmed('location_id'),
|
$this->trimmed('location_id'),
|
||||||
$this->trimmed('location_ns'),
|
$this->trimmed('location_ns'),
|
||||||
$user->getProfile());
|
$user->getProfile());
|
||||||
|
} else {
|
||||||
|
$locOptions = Notice::locationOptions(null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
$user->getProfile());
|
||||||
|
}
|
||||||
|
|
||||||
$options = array_merge($options, $locOptions);
|
$options = array_merge($options, $locOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
$notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
|
$notice = Notice::saveNew($user->id, $content_shortened, 'web', $options);
|
||||||
|
|
||||||
foreach($uploads as $upload) {
|
if (isset($upload)) {
|
||||||
$upload->attachToNotice($notice);
|
$upload->attachToNotice($notice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ class TagAction extends Action
|
|||||||
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
|
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE,
|
||||||
$this->page, 'tag', array('tag' => $this->tag));
|
$this->page, 'tag', array('tag' => $this->tag));
|
||||||
|
|
||||||
Event::handle('EndTagShowContent', array($this))
|
Event::handle('EndTagShowContent', array($this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,12 +96,23 @@ class Inbox extends Memcached_DataObject
|
|||||||
$inbox = new Inbox();
|
$inbox = new Inbox();
|
||||||
|
|
||||||
$inbox->user_id = $user_id;
|
$inbox->user_id = $user_id;
|
||||||
$inbox->notice_ids = call_user_func_array('pack', array_merge(array('N*'), $ids));
|
$inbox->pack($ids);
|
||||||
$inbox->fake = true;
|
$inbox->fake = true;
|
||||||
|
|
||||||
return $inbox;
|
return $inbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append the given notice to the given user's inbox.
|
||||||
|
* Caching updates are managed for the inbox itself.
|
||||||
|
*
|
||||||
|
* If the notice is already in this inbox, the second
|
||||||
|
* add will be silently dropped.
|
||||||
|
*
|
||||||
|
* @param int @user_id
|
||||||
|
* @param int $notice_id
|
||||||
|
* @return boolean success
|
||||||
|
*/
|
||||||
static function insertNotice($user_id, $notice_id)
|
static function insertNotice($user_id, $notice_id)
|
||||||
{
|
{
|
||||||
$inbox = DB_DataObject::staticGet('inbox', 'user_id', $user_id);
|
$inbox = DB_DataObject::staticGet('inbox', 'user_id', $user_id);
|
||||||
@ -114,6 +125,13 @@ class Inbox extends Memcached_DataObject
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ids = $inbox->unpack();
|
||||||
|
if (in_array(intval($notice_id), $ids)) {
|
||||||
|
// Already in there, we probably re-ran some inbox adds
|
||||||
|
// due to an error. Skip the dupe silently.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
$result = $inbox->query(sprintf('UPDATE inbox '.
|
$result = $inbox->query(sprintf('UPDATE inbox '.
|
||||||
'set notice_ids = concat(cast(0x%08x as binary(4)), '.
|
'set notice_ids = concat(cast(0x%08x as binary(4)), '.
|
||||||
'substr(notice_ids, 1, %d)) '.
|
'substr(notice_ids, 1, %d)) '.
|
||||||
@ -150,7 +168,7 @@ class Inbox extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$ids = unpack('N*', $inbox->notice_ids);
|
$ids = $inbox->unpack();
|
||||||
|
|
||||||
if (!empty($since_id)) {
|
if (!empty($since_id)) {
|
||||||
$newids = array();
|
$newids = array();
|
||||||
@ -229,4 +247,21 @@ class Inbox extends Memcached_DataObject
|
|||||||
}
|
}
|
||||||
return new ArrayWrapper($items);
|
return new ArrayWrapper($items);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a list of integer notice_ids into a packed blob in this object.
|
||||||
|
* @param array $ids list of integer notice_ids
|
||||||
|
*/
|
||||||
|
protected function pack(array $ids)
|
||||||
|
{
|
||||||
|
$this->notice_ids = call_user_func_array('pack', array_merge(array('N*'), $ids));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array of integer notice_ids
|
||||||
|
*/
|
||||||
|
protected function unpack()
|
||||||
|
{
|
||||||
|
return unpack('N*', $this->notice_ids);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -225,6 +225,36 @@ class Profile extends Memcached_DataObject
|
|||||||
{
|
{
|
||||||
$notice = new Notice();
|
$notice = new Notice();
|
||||||
|
|
||||||
|
// Temporary hack until notice_profile_id_idx is updated
|
||||||
|
// to (profile_id, id) instead of (profile_id, created, id).
|
||||||
|
// It's been falling back to PRIMARY instead, which is really
|
||||||
|
// very inefficient for a profile that hasn't posted in a few
|
||||||
|
// months. Even though forcing the index will cause a filesort,
|
||||||
|
// it's usually going to be better.
|
||||||
|
if (common_config('db', 'type') == 'mysql') {
|
||||||
|
$index = '';
|
||||||
|
$query =
|
||||||
|
"select id from notice force index (notice_profile_id_idx) ".
|
||||||
|
"where profile_id=" . $notice->escape($this->id);
|
||||||
|
|
||||||
|
if ($since_id != 0) {
|
||||||
|
$query .= " and id > $since_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($max_id != 0) {
|
||||||
|
$query .= " and id < $max_id";
|
||||||
|
}
|
||||||
|
|
||||||
|
$query .= ' order by id DESC';
|
||||||
|
|
||||||
|
if (!is_null($offset)) {
|
||||||
|
$query .= " LIMIT $limit OFFSET $offset";
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice->query($query);
|
||||||
|
} else {
|
||||||
|
$index = '';
|
||||||
|
|
||||||
$notice->profile_id = $this->id;
|
$notice->profile_id = $this->id;
|
||||||
|
|
||||||
$notice->selectAdd();
|
$notice->selectAdd();
|
||||||
@ -244,13 +274,14 @@ class Profile extends Memcached_DataObject
|
|||||||
$notice->limit($offset, $limit);
|
$notice->limit($offset, $limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$notice->find();
|
||||||
|
}
|
||||||
|
|
||||||
$ids = array();
|
$ids = array();
|
||||||
|
|
||||||
if ($notice->find()) {
|
|
||||||
while ($notice->fetch()) {
|
while ($notice->fetch()) {
|
||||||
$ids[] = $notice->id;
|
$ids[] = $notice->id;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $ids;
|
return $ids;
|
||||||
}
|
}
|
||||||
@ -577,11 +608,41 @@ class Profile extends Memcached_DataObject
|
|||||||
{
|
{
|
||||||
$sub = new Subscription();
|
$sub = new Subscription();
|
||||||
$sub->subscriber = $this->id;
|
$sub->subscriber = $this->id;
|
||||||
$sub->delete();
|
|
||||||
|
$sub->find();
|
||||||
|
|
||||||
|
while ($sub->fetch()) {
|
||||||
|
$other = Profile::staticGet('id', $sub->subscribed);
|
||||||
|
if (empty($other)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($other->id == $this->id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Subscription::cancel($this, $other);
|
||||||
|
}
|
||||||
|
|
||||||
$subd = new Subscription();
|
$subd = new Subscription();
|
||||||
$subd->subscribed = $this->id;
|
$subd->subscribed = $this->id;
|
||||||
$subd->delete();
|
$subd->find();
|
||||||
|
|
||||||
|
while ($subd->fetch()) {
|
||||||
|
$other = Profile::staticGet('id', $subd->subscriber);
|
||||||
|
if (empty($other)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if ($other->id == $this->id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Subscription::cancel($other, $this);
|
||||||
|
}
|
||||||
|
|
||||||
|
$self = new Subscription();
|
||||||
|
|
||||||
|
$self->subscriber = $this->id;
|
||||||
|
$self->subscribed = $this->id;
|
||||||
|
|
||||||
|
$self->delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
function _deleteMessages()
|
function _deleteMessages()
|
||||||
|
@ -520,8 +520,8 @@ class User extends Memcached_DataObject
|
|||||||
common_log(LOG_WARNING,
|
common_log(LOG_WARNING,
|
||||||
sprintf(
|
sprintf(
|
||||||
"Profile ID %d (%s) tried to block his or herself.",
|
"Profile ID %d (%s) tried to block his or herself.",
|
||||||
$profile->id,
|
$this->id,
|
||||||
$profile->nickname
|
$this->nickname
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
@ -543,13 +543,7 @@ class User extends Memcached_DataObject
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel their subscription, if it exists
|
Subscription::cancel($other, $this->getProfile());
|
||||||
|
|
||||||
$otherUser = User::staticGet('id', $other->id);
|
|
||||||
|
|
||||||
if (!empty($otherUser)) {
|
|
||||||
subs_unsubscribe_to($otherUser, $this->getProfile());
|
|
||||||
}
|
|
||||||
|
|
||||||
$block->query('COMMIT');
|
$block->query('COMMIT');
|
||||||
|
|
||||||
|
100
extlib/Net/IDNA.php
Normal file
100
extlib/Net/IDNA.php
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
// {{{ license
|
||||||
|
|
||||||
|
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4 foldmethod=marker: */
|
||||||
|
//
|
||||||
|
// +----------------------------------------------------------------------+
|
||||||
|
// | This library is free software; you can redistribute it and/or modify |
|
||||||
|
// | it under the terms of the GNU Lesser General Public License as |
|
||||||
|
// | published by the Free Software Foundation; either version 2.1 of the |
|
||||||
|
// | License, or (at your option) any later version. |
|
||||||
|
// | |
|
||||||
|
// | This library is distributed in the hope that it will be useful, but |
|
||||||
|
// | WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
|
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
||||||
|
// | Lesser General Public License for more details. |
|
||||||
|
// | |
|
||||||
|
// | You should have received a copy of the GNU Lesser General Public |
|
||||||
|
// | License along with this library; if not, write to the Free Software |
|
||||||
|
// | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 |
|
||||||
|
// | USA. |
|
||||||
|
// +----------------------------------------------------------------------+
|
||||||
|
//
|
||||||
|
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encode/decode Internationalized Domain Names.
|
||||||
|
* Factory class to get correct implementation either for php4 or php5.
|
||||||
|
*
|
||||||
|
* @author Markus Nix <mnix@docuverse.de>
|
||||||
|
* @author Matthias Sommerfeld <mso@phlylabs.de>
|
||||||
|
* @package Net
|
||||||
|
* @version $Id: IDNA.php 284681 2009-07-24 04:24:27Z clockwerx $
|
||||||
|
*/
|
||||||
|
|
||||||
|
class Net_IDNA
|
||||||
|
{
|
||||||
|
// {{{ factory
|
||||||
|
/**
|
||||||
|
* Attempts to return a concrete IDNA instance for either php4 or php5.
|
||||||
|
*
|
||||||
|
* @param array $params Set of paramaters
|
||||||
|
* @return object IDNA The newly created concrete Log instance, or an
|
||||||
|
* false on an error.
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
function getInstance($params = array())
|
||||||
|
{
|
||||||
|
$version = explode( '.', phpversion() );
|
||||||
|
$handler = ((int)$version[0] > 4) ? 'php5' : 'php4';
|
||||||
|
$class = 'Net_IDNA_' . $handler;
|
||||||
|
$classfile = 'Net/IDNA/' . $handler . '.php';
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attempt to include our version of the named class, but don't treat
|
||||||
|
* a failure as fatal. The caller may have already included their own
|
||||||
|
* version of the named class.
|
||||||
|
*/
|
||||||
|
@include_once $classfile;
|
||||||
|
|
||||||
|
/* If the class exists, return a new instance of it. */
|
||||||
|
if (class_exists($class)) {
|
||||||
|
return new $class($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// {{{ singleton
|
||||||
|
/**
|
||||||
|
* Attempts to return a concrete IDNA instance for either php4 or php5,
|
||||||
|
* only creating a new instance if no IDNA instance with the same
|
||||||
|
* parameters currently exists.
|
||||||
|
*
|
||||||
|
* @param array $params Set of paramaters
|
||||||
|
* @return object IDNA The newly created concrete Log instance, or an
|
||||||
|
* false on an error.
|
||||||
|
* @access public
|
||||||
|
*/
|
||||||
|
function singleton($params = array())
|
||||||
|
{
|
||||||
|
static $instances;
|
||||||
|
if (!isset($instances)) {
|
||||||
|
$instances = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$signature = serialize($params);
|
||||||
|
if (!isset($instances[$signature])) {
|
||||||
|
$instances[$signature] = Net_IDNA::getInstance($params);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $instances[$signature];
|
||||||
|
}
|
||||||
|
// }}}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
3269
extlib/Net/IDNA/php5.php
Normal file
3269
extlib/Net/IDNA/php5.php
Normal file
File diff suppressed because it is too large
Load Diff
@ -904,7 +904,7 @@ function registerInitialUser($nickname, $password, $email, $adminUpdates)
|
|||||||
|
|
||||||
if (class_exists('Ostatus_profile') && $adminUpdates) {
|
if (class_exists('Ostatus_profile') && $adminUpdates) {
|
||||||
try {
|
try {
|
||||||
$oprofile = Ostatus_profile::ensureProfile('http://update.status.net/');
|
$oprofile = Ostatus_profile::ensureProfileURL('http://update.status.net/');
|
||||||
Subscription::start($user->getProfile(), $oprofile->localProfile());
|
Subscription::start($user->getProfile(), $oprofile->localProfile());
|
||||||
updateStatus("Set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
|
updateStatus("Set up subscription to <a href='http://update.status.net/'>update@status.net</a>.");
|
||||||
} catch (Exception $e) {
|
} catch (Exception $e) {
|
||||||
|
51
js/util.js
51
js/util.js
@ -30,8 +30,7 @@ var SN = { // StatusNet
|
|||||||
CounterBlackout: false,
|
CounterBlackout: false,
|
||||||
MaxLength: 140,
|
MaxLength: 140,
|
||||||
PatternUsername: /^[0-9a-zA-Z\-_.]*$/,
|
PatternUsername: /^[0-9a-zA-Z\-_.]*$/,
|
||||||
HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307],
|
HTTP20x30x: [200, 201, 202, 203, 204, 205, 206, 300, 301, 302, 303, 304, 305, 306, 307]
|
||||||
UploadCounter: 0
|
|
||||||
},
|
},
|
||||||
|
|
||||||
S: { // Selector
|
S: { // Selector
|
||||||
@ -173,7 +172,6 @@ var SN = { // StatusNet
|
|||||||
FormNoticeXHR: function(form) {
|
FormNoticeXHR: function(form) {
|
||||||
SN.C.I.NoticeDataGeo = {};
|
SN.C.I.NoticeDataGeo = {};
|
||||||
form.append('<input type="hidden" name="ajax" value="1"/>');
|
form.append('<input type="hidden" name="ajax" value="1"/>');
|
||||||
|
|
||||||
form.ajaxForm({
|
form.ajaxForm({
|
||||||
dataType: 'xml',
|
dataType: 'xml',
|
||||||
timeout: '60000',
|
timeout: '60000',
|
||||||
@ -230,10 +228,9 @@ var SN = { // StatusNet
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
|
if (parseInt(xhr.status) === 0 || jQuery.inArray(parseInt(xhr.status), SN.C.I.HTTP20x30x) >= 0) {
|
||||||
form.resetForm();
|
form
|
||||||
SN.U.NoticeClearAttachments(form);
|
.resetForm()
|
||||||
SN.C.I.UploadCounter = 0;
|
.find('#'+SN.C.S.NoticeDataAttachSelected).remove();
|
||||||
SN.U.NoticeNewAttachment($('fieldset', form));
|
|
||||||
SN.U.FormNoticeEnhancements(form);
|
SN.U.FormNoticeEnhancements(form);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -290,9 +287,8 @@ var SN = { // StatusNet
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
form.resetForm();
|
form.resetForm();
|
||||||
SN.U.NoticeClearAttachments(form);
|
form.find('#'+SN.C.S.NoticeInReplyTo).val('');
|
||||||
SN.C.I.UploadCounter = 0;
|
form.find('#'+SN.C.S.NoticeDataAttachSelected).remove();
|
||||||
SN.U.NoticeNewAttachment($('fieldset', form));
|
|
||||||
SN.U.FormNoticeEnhancements(form);
|
SN.U.FormNoticeEnhancements(form);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -314,11 +310,6 @@ var SN = { // StatusNet
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
NoticeClearAttachments: function(form) {
|
|
||||||
$('input:file', form).remove();
|
|
||||||
$('div[class=' + SN.C.S.Success + ']', form).remove();
|
|
||||||
},
|
|
||||||
|
|
||||||
NoticeReply: function() {
|
NoticeReply: function() {
|
||||||
if ($('#'+SN.C.S.NoticeDataText).length > 0 && $('#content .notice_reply').length > 0) {
|
if ($('#'+SN.C.S.NoticeDataText).length > 0 && $('#content .notice_reply').length > 0) {
|
||||||
$('#content .notice').each(function() { SN.U.NoticeReplyTo($(this)); });
|
$('#content .notice').each(function() { SN.U.NoticeReplyTo($(this)); });
|
||||||
@ -477,32 +468,26 @@ var SN = { // StatusNet
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
NoticeDataAttach: function(NDANum) {
|
NoticeDataAttach: function() {
|
||||||
NDA = $('#'+SN.C.S.NoticeDataAttach+NDANum);
|
NDA = $('#'+SN.C.S.NoticeDataAttach);
|
||||||
NDA.change(function() {
|
NDA.change(function() {
|
||||||
S = '<div id="'+SN.C.S.NoticeDataAttachSelected+SN.C.I.UploadCounter+'" class="'+SN.C.S.Success+'"><code>'+$(this).val()+'</code> <button class="close">×</button></div>';
|
S = '<div id="'+SN.C.S.NoticeDataAttachSelected+'" class="'+SN.C.S.Success+'"><code>'+$(this).val()+'</code> <button class="close">×</button></div>';
|
||||||
|
NDAS = $('#'+SN.C.S.NoticeDataAttachSelected);
|
||||||
|
if (NDAS.length > 0) {
|
||||||
|
NDAS.replaceWith(S);
|
||||||
|
}
|
||||||
|
else {
|
||||||
$('#'+SN.C.S.FormNotice).append(S);
|
$('#'+SN.C.S.FormNotice).append(S);
|
||||||
|
}
|
||||||
$('#'+SN.C.S.NoticeDataAttachSelected+SN.C.I.UploadCounter+' button').click(function(){
|
$('#'+SN.C.S.NoticeDataAttachSelected+' button').click(function(){
|
||||||
$('#'+this.parentNode.getAttribute("id")).remove();
|
$('#'+SN.C.S.NoticeDataAttachSelected).remove();
|
||||||
$('#'+this.parentNode.getAttribute("id").replace("_selected", "")).remove();
|
|
||||||
NDA.val('');
|
NDA.val('');
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
SN.C.I.UploadCounter++;
|
|
||||||
NDA.attr('style', 'display: none;');
|
|
||||||
SN.U.NoticeNewAttachment(NDA.parent());
|
|
||||||
$('#notice_data-attach-label').attr('for', SN.C.S.NoticeDataAttach+SN.C.I.UploadCounter);
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
NoticeNewAttachment: function(parent) {
|
|
||||||
NEWFILE = '<input id="'+SN.C.S.NoticeDataAttach+SN.C.I.UploadCounter+'" class="attach" type="file" name="attach'+SN.C.I.UploadCounter+'" title="'+NoticeAttachment_text.AttachFile+'"/>';
|
|
||||||
parent.append(NEWFILE);
|
|
||||||
SN.U.NoticeDataAttach(SN.C.I.UploadCounter);
|
|
||||||
},
|
|
||||||
|
|
||||||
NoticeLocationAttach: function() {
|
NoticeLocationAttach: function() {
|
||||||
var NLat = $('#'+SN.C.S.NoticeLat).val();
|
var NLat = $('#'+SN.C.S.NoticeLat).val();
|
||||||
var NLon = $('#'+SN.C.S.NoticeLon).val();
|
var NLon = $('#'+SN.C.S.NoticeLon).val();
|
||||||
@ -735,7 +720,7 @@ var SN = { // StatusNet
|
|||||||
SN.U.FormNoticeEnhancements($(this));
|
SN.U.FormNoticeEnhancements($(this));
|
||||||
});
|
});
|
||||||
|
|
||||||
SN.U.NoticeDataAttach("");
|
SN.U.NoticeDataAttach();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -318,7 +318,6 @@ class ApiAction extends Action
|
|||||||
$twitter_group['url']=$group->permalink();
|
$twitter_group['url']=$group->permalink();
|
||||||
$twitter_group['nickname']=$group->nickname;
|
$twitter_group['nickname']=$group->nickname;
|
||||||
$twitter_group['fullname']=$group->fullname;
|
$twitter_group['fullname']=$group->fullname;
|
||||||
$twitter_group['homepage_url']=$group->homepage_url;
|
|
||||||
$twitter_group['original_logo']=$group->original_logo;
|
$twitter_group['original_logo']=$group->original_logo;
|
||||||
$twitter_group['homepage_logo']=$group->homepage_logo;
|
$twitter_group['homepage_logo']=$group->homepage_logo;
|
||||||
$twitter_group['stream_logo']=$group->stream_logo;
|
$twitter_group['stream_logo']=$group->stream_logo;
|
||||||
|
@ -88,6 +88,8 @@ $default =
|
|||||||
'stomp_username' => null,
|
'stomp_username' => null,
|
||||||
'stomp_password' => null,
|
'stomp_password' => null,
|
||||||
'stomp_persistent' => true, // keep items across queue server restart, if persistence is enabled
|
'stomp_persistent' => true, // keep items across queue server restart, if persistence is enabled
|
||||||
|
'stomp_transactions' => true, // use STOMP transactions to aid in detecting failures (supported by ActiveMQ, but not by all)
|
||||||
|
'stomp_acks' => true, // send acknowledgements after successful processing (supported by ActiveMQ, but not by all)
|
||||||
'stomp_manual_failover' => true, // if multiple servers are listed, treat them as separate (enqueue on one randomly, listen on all)
|
'stomp_manual_failover' => true, // if multiple servers are listed, treat them as separate (enqueue on one randomly, listen on all)
|
||||||
'monitor' => null, // URL to monitor ping endpoint (work in progress)
|
'monitor' => null, // URL to monitor ping endpoint (work in progress)
|
||||||
'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully
|
'softlimit' => '90%', // total size or % of memory_limit at which to restart queue threads gracefully
|
||||||
|
@ -189,14 +189,10 @@ class NoticeForm extends Form
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (common_config('attachments', 'uploads')) {
|
if (common_config('attachments', 'uploads')) {
|
||||||
$this->out->element('label', array('id' => 'notice_data-attach-label',
|
$this->out->element('label', array('for' => 'notice_data-attach'),_('Attach'));
|
||||||
'class' => 'attach-label',
|
|
||||||
'for' => 'notice_data-attach'),
|
|
||||||
_('Attach'));
|
|
||||||
$this->out->element('input', array('id' => 'notice_data-attach',
|
$this->out->element('input', array('id' => 'notice_data-attach',
|
||||||
'class' => 'attach',
|
|
||||||
'type' => 'file',
|
'type' => 'file',
|
||||||
'name' => 'attach0',
|
'name' => 'attach',
|
||||||
'title' => _('Attach a file')));
|
'title' => _('Attach a file')));
|
||||||
$this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
|
$this->out->hidden('MAX_FILE_SIZE', common_config('attachments', 'file_quota'));
|
||||||
}
|
}
|
||||||
@ -216,10 +212,8 @@ class NoticeForm extends Form
|
|||||||
$this->out->checkbox('notice_data-geo', _('Share my location'), true);
|
$this->out->checkbox('notice_data-geo', _('Share my location'), true);
|
||||||
$this->out->elementEnd('div');
|
$this->out->elementEnd('div');
|
||||||
$this->out->inlineScript(' var NoticeDataGeo_text = {'.
|
$this->out->inlineScript(' var NoticeDataGeo_text = {'.
|
||||||
'ShareDisable: ' .json_encode(_('Do not share my location')).','.
|
'ShareDisable: "'._('Do not share my location').'",'.
|
||||||
'ErrorTimeout: ' .json_encode(_('Sorry, retrieving your geo location is taking longer than expected, please try again later')).
|
'ErrorTimeout: "'._('Sorry, retrieving your geo location is taking longer than expected, please try again later').'"'.
|
||||||
'} ; var NoticeAttachment_text = {'.
|
|
||||||
'AttachFile: ' . json_encode(_('Attach a file')) .
|
|
||||||
'}');
|
'}');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -340,8 +340,9 @@ class NoticeListItem extends Widget
|
|||||||
|
|
||||||
function showNickname()
|
function showNickname()
|
||||||
{
|
{
|
||||||
$this->out->element('span', array('class' => 'nickname fn'),
|
$this->out->raw('<span class="nickname fn">' .
|
||||||
$this->profile->nickname);
|
htmlspecialchars($this->profile->nickname) .
|
||||||
|
'</span>');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,7 +39,8 @@ class StompQueueManager extends QueueManager
|
|||||||
protected $base;
|
protected $base;
|
||||||
protected $control;
|
protected $control;
|
||||||
|
|
||||||
protected $useTransactions = true;
|
protected $useTransactions;
|
||||||
|
protected $useAcks;
|
||||||
|
|
||||||
protected $sites = array();
|
protected $sites = array();
|
||||||
protected $subscriptions = array();
|
protected $subscriptions = array();
|
||||||
@ -64,6 +65,8 @@ class StompQueueManager extends QueueManager
|
|||||||
$this->base = common_config('queue', 'queue_basename');
|
$this->base = common_config('queue', 'queue_basename');
|
||||||
$this->control = common_config('queue', 'control_channel');
|
$this->control = common_config('queue', 'control_channel');
|
||||||
$this->breakout = common_config('queue', 'breakout');
|
$this->breakout = common_config('queue', 'breakout');
|
||||||
|
$this->useTransactions = common_config('queue', 'stomp_transactions');
|
||||||
|
$this->useAcks = common_config('queue', 'stomp_acks');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -703,6 +706,7 @@ class StompQueueManager extends QueueManager
|
|||||||
|
|
||||||
protected function ack($idx, $frame)
|
protected function ack($idx, $frame)
|
||||||
{
|
{
|
||||||
|
if ($this->useAcks) {
|
||||||
if ($this->useTransactions) {
|
if ($this->useTransactions) {
|
||||||
if (empty($this->transaction[$idx])) {
|
if (empty($this->transaction[$idx])) {
|
||||||
throw new Exception("Tried to ack but not in a transaction");
|
throw new Exception("Tried to ack but not in a transaction");
|
||||||
@ -712,6 +716,7 @@ class StompQueueManager extends QueueManager
|
|||||||
$this->cons[$idx]->ack($frame);
|
$this->cons[$idx]->ack($frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function commit($idx)
|
protected function commit($idx)
|
||||||
{
|
{
|
||||||
|
49
lib/util.php
49
lib/util.php
@ -1388,6 +1388,55 @@ function common_valid_tag($tag)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if given domain or address literal is valid
|
||||||
|
* eg for use in JIDs and URLs. Does not check if the domain
|
||||||
|
* exists!
|
||||||
|
*
|
||||||
|
* @param string $domain
|
||||||
|
* @return boolean valid or not
|
||||||
|
*/
|
||||||
|
function common_valid_domain($domain)
|
||||||
|
{
|
||||||
|
$octet = "(?:25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])";
|
||||||
|
$ipv4 = "(?:$octet(?:\.$octet){3})";
|
||||||
|
if (preg_match("/^$ipv4$/u", $domain)) return true;
|
||||||
|
|
||||||
|
$group = "(?:[0-9a-f]{1,4})";
|
||||||
|
$ipv6 = "(?:\[($group(?::$group){0,7})?(::)?($group(?::$group){0,7})?\])"; // http://tools.ietf.org/html/rfc3513#section-2.2
|
||||||
|
|
||||||
|
if (preg_match("/^$ipv6$/ui", $domain, $matches)) {
|
||||||
|
$before = explode(":", $matches[1]);
|
||||||
|
$zeroes = $matches[2];
|
||||||
|
$after = explode(":", $matches[3]);
|
||||||
|
if ($zeroes) {
|
||||||
|
$min = 0;
|
||||||
|
$max = 7;
|
||||||
|
} else {
|
||||||
|
$min = 1;
|
||||||
|
$max = 8;
|
||||||
|
}
|
||||||
|
$explicit = count($before) + count($after);
|
||||||
|
if ($explicit < $min || $explicit > $max) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
require_once "Net/IDNA.php";
|
||||||
|
$idn = Net_IDNA::getInstance();
|
||||||
|
$domain = $idn->encode($domain);
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$subdomain = "(?:[a-z0-9][a-z0-9-]*)"; // @fixme
|
||||||
|
$fqdn = "(?:$subdomain(?:\.$subdomain)*\.?)";
|
||||||
|
|
||||||
|
return preg_match("/^$fqdn$/ui", $domain);
|
||||||
|
}
|
||||||
|
|
||||||
/* Following functions are copied from MediaWiki GlobalFunctions.php
|
/* Following functions are copied from MediaWiki GlobalFunctions.php
|
||||||
* and written by Evan Prodromou. */
|
* and written by Evan Prodromou. */
|
||||||
|
|
||||||
|
@ -55,6 +55,8 @@ class GeonamesPlugin extends Plugin
|
|||||||
public $username = null;
|
public $username = null;
|
||||||
public $token = null;
|
public $token = null;
|
||||||
public $expiry = 7776000; // 90-day expiry
|
public $expiry = 7776000; // 90-day expiry
|
||||||
|
public $cachePrefix = null; // Optional shared memcache prefix override
|
||||||
|
// to share lookups between local instances.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* convert a name into a Location object
|
* convert a name into a Location object
|
||||||
@ -408,9 +410,14 @@ class GeonamesPlugin extends Plugin
|
|||||||
|
|
||||||
function cacheKey($attrs)
|
function cacheKey($attrs)
|
||||||
{
|
{
|
||||||
return common_cache_key('geonames:'.
|
$key = 'geonames:' .
|
||||||
implode(',', array_keys($attrs)) . ':'.
|
implode(',', array_keys($attrs)) . ':'.
|
||||||
common_keyize(implode(',', array_values($attrs))));
|
common_keyize(implode(',', array_values($attrs)));
|
||||||
|
if ($this->cachePrefix) {
|
||||||
|
return $this->cachePrefix . ':' . $key;
|
||||||
|
} else {
|
||||||
|
return common_cache_key($key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function wsUrl($method, $params)
|
function wsUrl($method, $params)
|
||||||
|
@ -1685,7 +1685,11 @@ class Ostatus_profile extends Memcached_DataObject
|
|||||||
*/
|
*/
|
||||||
function saveHTMLFile($title, $rendered)
|
function saveHTMLFile($title, $rendered)
|
||||||
{
|
{
|
||||||
$final = sprintf("<!DOCTYPE html>\n<html><head><title>%s</title></head>".
|
$final = sprintf("<!DOCTYPE html>\n" .
|
||||||
|
'<html><head>' .
|
||||||
|
'<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">' .
|
||||||
|
'<title>%s</title>' .
|
||||||
|
'</head>' .
|
||||||
'<body>%s</body></html>',
|
'<body>%s</body></html>',
|
||||||
htmlspecialchars($title),
|
htmlspecialchars($title),
|
||||||
$rendered);
|
$rendered);
|
||||||
|
@ -66,17 +66,198 @@ class XmppPlugin extends ImPlugin
|
|||||||
return _m('XMPP/Jabber/GTalk');
|
return _m('XMPP/Jabber/GTalk');
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalize($screenname)
|
/**
|
||||||
|
* Splits a Jabber ID (JID) into node, domain, and resource portions.
|
||||||
|
*
|
||||||
|
* Based on validation routine submitted by:
|
||||||
|
* @copyright 2009 Patrick Georgi <patrick@georgi-clan.de>
|
||||||
|
* @license Licensed under ISC-L, which is compatible with everything else that keeps the copyright notice intact.
|
||||||
|
*
|
||||||
|
* @param string $jid string to check
|
||||||
|
*
|
||||||
|
* @return array with "node", "domain", and "resource" indices
|
||||||
|
* @throws Exception if input is not valid
|
||||||
|
*/
|
||||||
|
|
||||||
|
protected function splitJid($jid)
|
||||||
{
|
{
|
||||||
if (preg_match("/(?:([^\@]+)\@)?([^\/]+)(?:\/(.*))?$/", $screenname, $matches)) {
|
$chars = '';
|
||||||
$node = $matches[1];
|
/* the following definitions come from stringprep, Appendix C,
|
||||||
$server = $matches[2];
|
which is used in its entirety by nodeprop, Chapter 5, "Prohibited Output" */
|
||||||
return strtolower($node.'@'.$server);
|
/* C1.1 ASCII space characters */
|
||||||
|
$chars .= "\x{20}";
|
||||||
|
/* C1.2 Non-ASCII space characters */
|
||||||
|
$chars .= "\x{a0}\x{1680}\x{2000}-\x{200b}\x{202f}\x{205f}\x{3000a}";
|
||||||
|
/* C2.1 ASCII control characters */
|
||||||
|
$chars .= "\x{00}-\x{1f}\x{7f}";
|
||||||
|
/* C2.2 Non-ASCII control characters */
|
||||||
|
$chars .= "\x{80}-\x{9f}\x{6dd}\x{70f}\x{180e}\x{200c}\x{200d}\x{2028}\x{2029}\x{2060}-\x{2063}\x{206a}-\x{206f}\x{feff}\x{fff9}-\x{fffc}\x{1d173}-\x{1d17a}";
|
||||||
|
/* C3 - Private Use */
|
||||||
|
$chars .= "\x{e000}-\x{f8ff}\x{f0000}-\x{ffffd}\x{100000}-\x{10fffd}";
|
||||||
|
/* C4 - Non-character code points */
|
||||||
|
$chars .= "\x{fdd0}-\x{fdef}\x{fffe}\x{ffff}\x{1fffe}\x{1ffff}\x{2fffe}\x{2ffff}\x{3fffe}\x{3ffff}\x{4fffe}\x{4ffff}\x{5fffe}\x{5ffff}\x{6fffe}\x{6ffff}\x{7fffe}\x{7ffff}\x{8fffe}\x{8ffff}\x{9fffe}\x{9ffff}\x{afffe}\x{affff}\x{bfffe}\x{bffff}\x{cfffe}\x{cffff}\x{dfffe}\x{dffff}\x{efffe}\x{effff}\x{ffffe}\x{fffff}\x{10fffe}\x{10ffff}";
|
||||||
|
/* C5 - Surrogate codes */
|
||||||
|
$chars .= "\x{d800}-\x{dfff}";
|
||||||
|
/* C6 - Inappropriate for plain text */
|
||||||
|
$chars .= "\x{fff9}-\x{fffd}";
|
||||||
|
/* C7 - Inappropriate for canonical representation */
|
||||||
|
$chars .= "\x{2ff0}-\x{2ffb}";
|
||||||
|
/* C8 - Change display properties or are deprecated */
|
||||||
|
$chars .= "\x{340}\x{341}\x{200e}\x{200f}\x{202a}-\x{202e}\x{206a}-\x{206f}";
|
||||||
|
/* C9 - Tagging characters */
|
||||||
|
$chars .= "\x{e0001}\x{e0020}-\x{e007f}";
|
||||||
|
|
||||||
|
/* Nodeprep forbids some more characters */
|
||||||
|
$nodeprepchars = $chars;
|
||||||
|
$nodeprepchars .= "\x{22}\x{26}\x{27}\x{2f}\x{3a}\x{3c}\x{3e}\x{40}";
|
||||||
|
|
||||||
|
$parts = explode("/", $jid, 2);
|
||||||
|
if (count($parts) > 1) {
|
||||||
|
$resource = $parts[1];
|
||||||
|
if ($resource == '') {
|
||||||
|
// Warning: empty resource isn't legit.
|
||||||
|
// But if we're normalizing, we may as well take it...
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
|
$resource = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$node = explode("@", $parts[0]);
|
||||||
|
if ((count($node) > 2) || (count($node) == 0)) {
|
||||||
|
throw new Exception("Invalid JID: too many @s");
|
||||||
|
} else if (count($node) == 1) {
|
||||||
|
$domain = $node[0];
|
||||||
|
$node = null;
|
||||||
|
} else {
|
||||||
|
$domain = $node[1];
|
||||||
|
$node = $node[0];
|
||||||
|
if ($node == '') {
|
||||||
|
throw new Exception("Invalid JID: @ but no node");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Length limits per http://xmpp.org/rfcs/rfc3920.html#addressing
|
||||||
|
if ($node !== null) {
|
||||||
|
if (strlen($node) > 1023) {
|
||||||
|
throw new Exception("Invalid JID: node too long.");
|
||||||
|
}
|
||||||
|
if (preg_match("/[".$nodeprepchars."]/u", $node)) {
|
||||||
|
throw new Exception("Invalid JID node '$node'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($domain) > 1023) {
|
||||||
|
throw new Exception("Invalid JID: domain too long.");
|
||||||
|
}
|
||||||
|
if (!common_valid_domain($domain)) {
|
||||||
|
throw new Exception("Invalid JID domain name '$domain'");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($resource !== null) {
|
||||||
|
if (strlen($resource) > 1023) {
|
||||||
|
throw new Exception("Invalid JID: resource too long.");
|
||||||
|
}
|
||||||
|
if (preg_match("/[".$chars."]/u", $resource)) {
|
||||||
|
throw new Exception("Invalid JID resource '$resource'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return array('node' => is_null($node) ? null : mb_strtolower($node),
|
||||||
|
'domain' => is_null($domain) ? null : mb_strtolower($domain),
|
||||||
|
'resource' => $resource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a string is a syntactically valid Jabber ID (JID),
|
||||||
|
* either with or without a resource.
|
||||||
|
*
|
||||||
|
* Note that a bare domain can be a valid JID.
|
||||||
|
*
|
||||||
|
* @param string $jid string to check
|
||||||
|
* @param bool $check_domain whether we should validate that domain...
|
||||||
|
*
|
||||||
|
* @return boolean whether the string is a valid JID
|
||||||
|
*/
|
||||||
|
protected function validateFullJid($jid, $check_domain=false)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$parts = $this->splitJid($jid);
|
||||||
|
if ($check_domain) {
|
||||||
|
if (!$this->checkDomain($parts['domain'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $parts['resource'] !== ''; // missing or present; empty ain't kosher
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a string is a syntactically valid base Jabber ID (JID).
|
||||||
|
* A base JID won't include a resource specifier on the end; since we
|
||||||
|
* take it off when reading input we can't really use them reliably
|
||||||
|
* to direct outgoing messages yet (sorry guys!)
|
||||||
|
*
|
||||||
|
* Note that a bare domain can be a valid JID.
|
||||||
|
*
|
||||||
|
* @param string $jid string to check
|
||||||
|
* @param bool $check_domain whether we should validate that domain...
|
||||||
|
*
|
||||||
|
* @return boolean whether the string is a valid JID
|
||||||
|
*/
|
||||||
|
protected function validateBaseJid($jid, $check_domain=false)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$parts = $this->splitJid($jid);
|
||||||
|
if ($check_domain) {
|
||||||
|
if (!$this->checkDomain($parts['domain'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ($parts['resource'] === null); // missing; empty ain't kosher
|
||||||
|
} catch (Exception $e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalizes a Jabber ID for comparison, dropping the resource component if any.
|
||||||
|
*
|
||||||
|
* @param string $jid JID to check
|
||||||
|
* @param bool $check_domain if true, reject if the domain isn't findable
|
||||||
|
*
|
||||||
|
* @return string an equivalent JID in normalized (lowercase) form
|
||||||
|
*/
|
||||||
|
|
||||||
|
function normalize($jid)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$parts = $this->splitJid($jid);
|
||||||
|
if ($parts['node'] !== null) {
|
||||||
|
return $parts['node'] . '@' . $parts['domain'];
|
||||||
|
} else {
|
||||||
|
return $parts['domain'];
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if this domain's got some legit DNS record
|
||||||
|
*/
|
||||||
|
protected function checkDomain($domain)
|
||||||
|
{
|
||||||
|
if (checkdnsrr("_xmpp-server._tcp." . $domain, "SRV")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (checkdnsrr($domain, "ANY")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function daemon_screenname()
|
function daemon_screenname()
|
||||||
{
|
{
|
||||||
$ret = $this->user . '@' . $this->server;
|
$ret = $this->user . '@' . $this->server;
|
||||||
@ -90,8 +271,7 @@ class XmppPlugin extends ImPlugin
|
|||||||
|
|
||||||
function validate($screenname)
|
function validate($screenname)
|
||||||
{
|
{
|
||||||
// Cheap but effective
|
return $this->validateBaseJid($screenname, common_config('email', 'check_domain'));
|
||||||
return Validate::email($screenname);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
146
tests/JidValidateTest.php
Normal file
146
tests/JidValidateTest.php
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
if (isset($_SERVER) && array_key_exists('REQUEST_METHOD', $_SERVER)) {
|
||||||
|
print "This script must be run from the command line\n";
|
||||||
|
exit();
|
||||||
|
}
|
||||||
|
|
||||||
|
define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
|
||||||
|
define('STATUSNET', true);
|
||||||
|
define('LACONICA', true);
|
||||||
|
|
||||||
|
mb_internal_encoding('UTF-8'); // @fixme this probably belongs in common.php?
|
||||||
|
|
||||||
|
require_once INSTALLDIR . '/lib/common.php';
|
||||||
|
require_once INSTALLDIR . '/lib/jabber.php';
|
||||||
|
|
||||||
|
class JidValidateTest extends PHPUnit_Framework_TestCase
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @dataProvider validationCases
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function testValidate($jid, $validFull, $validBase)
|
||||||
|
{
|
||||||
|
$this->assertEquals($validFull, jabber_valid_full_jid($jid), "validating as full or base JID");
|
||||||
|
|
||||||
|
$this->assertEquals($validBase, jabber_valid_base_jid($jid), "validating as base JID only");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider normalizationCases
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public function testNormalize($jid, $expected)
|
||||||
|
{
|
||||||
|
$this->assertEquals($expected, jabber_normalize_jid($jid));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider domainCheckCases()
|
||||||
|
*/
|
||||||
|
public function testDomainCheck($domain, $expected, $note)
|
||||||
|
{
|
||||||
|
$this->assertEquals($expected, jabber_check_domain($domain), $note);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function validationCases()
|
||||||
|
{
|
||||||
|
$long1023 = "long1023" . str_repeat('x', 1023 - 8);
|
||||||
|
$long1024 = "long1024" . str_repeat('x', 1024 - 8);
|
||||||
|
return array(
|
||||||
|
// Our own test cases for standard things & those mentioned in bug reports
|
||||||
|
// (jid, valid_full, valid_base)
|
||||||
|
array('user@example.com', true, true),
|
||||||
|
array('user@example.com/resource', true, false),
|
||||||
|
array('user with spaces@example.com', false, false), // not kosher
|
||||||
|
|
||||||
|
array('user.@example.com', true, true), // "common in intranets"
|
||||||
|
array('example.com', true, true),
|
||||||
|
array('example.com/resource', true, false),
|
||||||
|
array('jabchat', true, true),
|
||||||
|
|
||||||
|
array("$long1023@$long1023/$long1023", true, false), // max 1023 "bytes" per portion per spec. Do they really mean bytes though?
|
||||||
|
array("$long1024@$long1023/$long1023", false, false),
|
||||||
|
array("$long1023@$long1024/$long1023", false, false),
|
||||||
|
array("$long1023@$long1023/$long1024", false, false),
|
||||||
|
|
||||||
|
// Borrowed from test_jabber_jutil.c in libpurple
|
||||||
|
array("gmail.com", true, true),
|
||||||
|
array("gmail.com/Test", true, false),
|
||||||
|
array("gmail.com/Test@", true, false),
|
||||||
|
array("gmail.com/@", true, false),
|
||||||
|
array("gmail.com/Test@alkjaweflkj", true, false),
|
||||||
|
array("mark.doliner@gmail.com", true, true),
|
||||||
|
array("mark.doliner@gmail.com/Test12345", true, false),
|
||||||
|
array("mark.doliner@gmail.com/Test@12345", true, false),
|
||||||
|
array("mark.doliner@gmail.com/Te/st@12@//345", true, false),
|
||||||
|
array("わいど@conference.jabber.org", true, true),
|
||||||
|
array("まりるーむ@conference.jabber.org", true, true),
|
||||||
|
array("mark.doliner@gmail.com/まりるーむ", true, false),
|
||||||
|
array("mark.doliner@gmail/stuff.org", true, false),
|
||||||
|
array("stuart@nödåtXäYZ.se", true, true),
|
||||||
|
array("stuart@nödåtXäYZ.se/まりるーむ", true, false),
|
||||||
|
array("mark.doliner@わいど.org", true, true),
|
||||||
|
array("nick@まつ.おおかみ.net", true, true),
|
||||||
|
array("paul@10.0.42.230/s", true, false),
|
||||||
|
array("paul@[::1]", true, true), /* IPv6 */
|
||||||
|
array("paul@[2001:470:1f05:d58::2]", true, true),
|
||||||
|
array("paul@[2001:470:1f05:d58::2]/foo", true, false),
|
||||||
|
array("pa=ul@10.0.42.230", true, true),
|
||||||
|
array("pa,ul@10.0.42.230", true, true),
|
||||||
|
|
||||||
|
array("@gmail.com", false, false),
|
||||||
|
array("@@gmail.com", false, false),
|
||||||
|
array("mark.doliner@@gmail.com/Test12345", false, false),
|
||||||
|
array("mark@doliner@gmail.com/Test12345", false, false),
|
||||||
|
array("@gmail.com/Test@12345", false, false),
|
||||||
|
array("/Test@12345", false, false),
|
||||||
|
array("mark.doliner@", false, false),
|
||||||
|
array("mark.doliner/", false, false),
|
||||||
|
array("mark.doliner@gmail_stuff.org", false, false),
|
||||||
|
array("mark.doliner@gmail[stuff.org", false, false),
|
||||||
|
array("mark.doliner@gmail\\stuff.org", false, false),
|
||||||
|
array("paul@[::1]124", false, false),
|
||||||
|
array("paul@2[::1]124/as", false, false),
|
||||||
|
array("paul@まつ.おおかみ/\x01", false, false),
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC 3454 Section 6 reads, in part,
|
||||||
|
* "If a string contains any RandALCat character, the
|
||||||
|
* string MUST NOT contain any LCat character."
|
||||||
|
* The character is U+066D (ARABIC FIVE POINTED STAR).
|
||||||
|
*/
|
||||||
|
// Leaving this one commented out for the moment
|
||||||
|
// as it shouldn't hurt anything for our purposes.
|
||||||
|
//array("foo@example.com/٭simplexe٭", false, false)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function normalizationCases()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
// Borrowed from test_jabber_jutil.c in libpurple
|
||||||
|
array('PaUL@DaRkRain42.org', 'paul@darkrain42.org'),
|
||||||
|
array('PaUL@DaRkRain42.org/', 'paul@darkrain42.org'),
|
||||||
|
array('PaUL@DaRkRain42.org/resource', 'paul@darkrain42.org'),
|
||||||
|
|
||||||
|
// Also adapted from libpurple tests...
|
||||||
|
array('Ф@darkrain42.org', 'ф@darkrain42.org'),
|
||||||
|
array('paul@Өarkrain.org', 'paul@өarkrain.org'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static public function domainCheckCases()
|
||||||
|
{
|
||||||
|
return array(
|
||||||
|
array('gmail.com', true, 'known SRV record'),
|
||||||
|
array('jabber.org', true, 'known SRV record'),
|
||||||
|
array('status.net', true, 'known SRV record'),
|
||||||
|
array('status.leuksman.com', true, 'known no SRV record but valid domain'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -550,19 +550,19 @@ float:left;
|
|||||||
font-size:1.3em;
|
font-size:1.3em;
|
||||||
margin-bottom:7px;
|
margin-bottom:7px;
|
||||||
}
|
}
|
||||||
.form_notice .attach-label,
|
.form_notice label[for=notice_data-attach],
|
||||||
.form_notice .attach {
|
.form_notice #notice_data-attach {
|
||||||
position:absolute;
|
position:absolute;
|
||||||
top:25px;
|
top:25px;
|
||||||
right:10.5%;
|
right:10.5%;
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
}
|
}
|
||||||
.form_notice .attach-label {
|
.form_notice label[for=notice_data-attach] {
|
||||||
text-indent:-9999px;
|
text-indent:-9999px;
|
||||||
width:16px;
|
width:16px;
|
||||||
height:16px;
|
height:16px;
|
||||||
}
|
}
|
||||||
.form_notice .attach {
|
.form_notice #notice_data-attach {
|
||||||
padding:0;
|
padding:0;
|
||||||
height:16px;
|
height:16px;
|
||||||
}
|
}
|
||||||
@ -606,7 +606,7 @@ width:81.5%;
|
|||||||
margin-bottom:0;
|
margin-bottom:0;
|
||||||
line-height:1.618;
|
line-height:1.618;
|
||||||
}
|
}
|
||||||
.form_notice code {
|
.form_notice #notice_data-attach_selected code {
|
||||||
float:left;
|
float:left;
|
||||||
width:80%;
|
width:80%;
|
||||||
display:block;
|
display:block;
|
||||||
@ -614,7 +614,7 @@ overflow:auto;
|
|||||||
margin-right:2.5%;
|
margin-right:2.5%;
|
||||||
font-size:1.1em;
|
font-size:1.1em;
|
||||||
}
|
}
|
||||||
.form_notice button.close {
|
.form_notice #notice_data-attach_selected button.close {
|
||||||
float:right;
|
float:right;
|
||||||
font-size:0.8em;
|
font-size:0.8em;
|
||||||
}
|
}
|
||||||
|
@ -154,15 +154,14 @@ color:#333333;
|
|||||||
.entity_actions .dialogbox input {
|
.entity_actions .dialogbox input {
|
||||||
color:#000000;
|
color:#000000;
|
||||||
}
|
}
|
||||||
.form_notice .attach-label {
|
.form_notice label[for=notice_data-attach] {
|
||||||
background-position:0 -328px;
|
background-position:0 -328px;
|
||||||
}
|
}
|
||||||
.form_notice .attach {
|
.form_notice #notice_data-attach {
|
||||||
position: absolute;
|
|
||||||
opacity:0;
|
opacity:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form_notice .attach-label,
|
.form_notice label[for=notice_data-attach],
|
||||||
#export_data li a.rss,
|
#export_data li a.rss,
|
||||||
#export_data li a.atom,
|
#export_data li a.atom,
|
||||||
#export_data li a.foaf,
|
#export_data li a.foaf,
|
||||||
|
Loading…
Reference in New Issue
Block a user