summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvan Prodromou <evan@status.net>2010-03-10 14:13:02 -0500
committerEvan Prodromou <evan@status.net>2010-03-10 14:13:02 -0500
commita2cc26ba8a21aa3614ebfc4a77c0bec9ffb2655d (patch)
tree76eae27ba51ae642db93f3db0a3a83fda573a352
parent3fba9a16f5e908b62e486d0e0df02fcb6e0a7796 (diff)
parent69b2f19b6fef793aa607c6d8f4590b93e2565626 (diff)
Merge branch 'testing' of git@gitorious.org:statusnet/mainline into testing
-rw-r--r--actions/apistatusnetconfig.php2
-rw-r--r--actions/otp.php2
-rw-r--r--install.php30
-rw-r--r--lib/action.php3
-rw-r--r--lib/apiauth.php8
-rw-r--r--lib/channel.php19
-rw-r--r--lib/command.php355
-rw-r--r--lib/default.php2
-rw-r--r--lib/jabber.php34
-rw-r--r--lib/queued_xmpp.php18
-rw-r--r--lib/util.php43
-rw-r--r--lib/xmppmanager.php1
-rw-r--r--plugins/OStatus/OStatusPlugin.php80
-rw-r--r--plugins/OStatus/classes/HubSub.php2
-rw-r--r--plugins/OStatus/lib/feeddiscovery.php2
-rw-r--r--plugins/RSSCloud/RSSCloudNotifier.php2
-rw-r--r--plugins/RSSCloud/RSSCloudRequestNotify.php7
-rw-r--r--plugins/RequireValidatedEmail/README2
-rw-r--r--plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php23
-rw-r--r--plugins/RequireValidatedEmail/locale/RequireValidatedEmail.po31
-rwxr-xr-xscripts/command.php80
-rw-r--r--scripts/flushsite.php45
22 files changed, 595 insertions, 196 deletions
diff --git a/actions/apistatusnetconfig.php b/actions/apistatusnetconfig.php
index 296376d19..51400dfc9 100644
--- a/actions/apistatusnetconfig.php
+++ b/actions/apistatusnetconfig.php
@@ -52,7 +52,7 @@ class ApiStatusnetConfigAction extends ApiAction
var $keys = array(
'site' => array('name', 'server', 'theme', 'path', 'fancy', 'language',
'email', 'broughtby', 'broughtbyurl', 'closed',
- 'inviteonly', 'private'),
+ 'inviteonly', 'private','textlimit'),
'license' => array('url', 'title', 'image'),
'nickname' => array('featured'),
'throttle' => array('enabled', 'count', 'timespan'),
diff --git a/actions/otp.php b/actions/otp.php
index acf84aee8..1e06603d4 100644
--- a/actions/otp.php
+++ b/actions/otp.php
@@ -126,6 +126,8 @@ class OtpAction extends Action
$this->lt->delete();
$this->lt = null;
+ common_real_login(true);
+
if ($this->rememberme) {
common_rememberme($this->user);
}
diff --git a/install.php b/install.php
index 7fece8999..9a7e27fa2 100644
--- a/install.php
+++ b/install.php
@@ -308,7 +308,7 @@ function checkPrereqs()
printf('<p class="error">PHP is linked to a version of the PCRE library ' .
'that does not support Unicode properties. ' .
'If you are running Red Hat Enterprise Linux / ' .
- 'CentOS 5.3 or earlier, see <a href="' .
+ 'CentOS 5.4 or earlier, see <a href="' .
'http://status.net/wiki/Red_Hat_Enterprise_Linux#PCRE_library' .
'">our documentation page</a> on fixing this.</p>');
$pass = false;
@@ -483,6 +483,7 @@ function showForm()
$dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
}
}
+
echo<<<E_O_T
</ul>
</dd>
@@ -559,6 +560,11 @@ function showForm()
<input id="admin_email" name="admin_email" value="{$post->value('admin_email')}" />
<p class="form_guide">Optional email address for the initial StatusNet user (administrator)</p>
</li>
+ <li>
+ <label for="admin_updates">Subscribe to announcements</label>
+ <input type="checkbox" id="admin_updates" name="admin_updates" value="true" checked="checked" />
+ <p class="form_guide">Release and security feed from <a href="http://update.status.net/">update@status.net</a> (recommended)</p>
+ </li>
</ul>
</fieldset>
<input type="submit" name="submit" class="submit" value="Submit" />
@@ -583,10 +589,11 @@ function handlePost()
$sitename = $_POST['sitename'];
$fancy = !empty($_POST['fancy']);
- $adminNick = $_POST['admin_nickname'];
+ $adminNick = strtolower($_POST['admin_nickname']);
$adminPass = $_POST['admin_password'];
$adminPass2 = $_POST['admin_password2'];
$adminEmail = $_POST['admin_email'];
+ $adminUpdates = $_POST['admin_updates'];
$server = $_SERVER['HTTP_HOST'];
$path = substr(dirname($_SERVER['PHP_SELF']), 1);
@@ -623,6 +630,19 @@ STR;
updateStatus("No initial StatusNet user nickname specified.", true);
$fail = true;
}
+ if ($adminNick && !preg_match('/^[0-9a-z]{1,64}$/', $adminNick)) {
+ updateStatus('The user nickname "' . htmlspecialchars($adminNick) .
+ '" is invalid; should be plain letters and numbers no longer than 64 characters.', true);
+ $fail = true;
+ }
+ // @fixme hardcoded list; should use User::allowed_nickname()
+ // if/when it's safe to have loaded the infrastructure here
+ $blacklist = array('main', 'admin', 'twitter', 'settings', 'rsd.xml', 'favorited', 'featured', 'favoritedrss', 'featuredrss', 'rss', 'getfile', 'api', 'groups', 'group', 'peopletag', 'tag', 'user', 'message', 'conversation', 'bookmarklet', 'notice', 'attachment', 'search', 'index.php', 'doc', 'opensearch', 'robots.txt', 'xd_receiver.html', 'facebook');
+ if (in_array($adminNick, $blacklist)) {
+ updateStatus('The user nickname "' . htmlspecialchars($adminNick) .
+ '" is reserved.', true);
+ $fail = true;
+ }
if (empty($adminPass)) {
updateStatus("No initial StatusNet user password specified.", true);
@@ -657,7 +677,7 @@ STR;
}
// Okay, cross fingers and try to register an initial user
- if (registerInitialUser($adminNick, $adminPass, $adminEmail)) {
+ if (registerInitialUser($adminNick, $adminPass, $adminEmail, $adminUpdates)) {
updateStatus(
"An initial user with the administrator role has been created."
);
@@ -854,7 +874,7 @@ function runDbScript($filename, $conn, $type = 'mysqli')
return true;
}
-function registerInitialUser($nickname, $password, $email)
+function registerInitialUser($nickname, $password, $email, $adminUpdates)
{
define('STATUSNET', true);
define('LACONICA', true); // compatibility
@@ -882,7 +902,7 @@ function registerInitialUser($nickname, $password, $email)
// Attempt to do a remote subscribe to update@status.net
// Will fail if instance is on a private network.
- if (class_exists('Ostatus_profile')) {
+ if (class_exists('Ostatus_profile') && $adminUpdates) {
try {
$oprofile = Ostatus_profile::ensureProfile('http://update.status.net/');
Subscription::start($user->getProfile(), $oprofile->localProfile());
diff --git a/lib/action.php b/lib/action.php
index 816086d20..9884f529c 100644
--- a/lib/action.php
+++ b/lib/action.php
@@ -767,11 +767,14 @@ class Action extends HTMLOutputter // lawsuit
{
$this->element('dt', array('id' => 'site_statusnet_license'), _('StatusNet software license'));
$this->elementStart('dd', null);
+ // @fixme drop the final spaces in the messages when at good spot
+ // to let translations get updated.
if (common_config('site', 'broughtby')) {
$instr = _('**%%site.name%%** is a microblogging service brought to you by [%%site.broughtby%%](%%site.broughtbyurl%%). ');
} else {
$instr = _('**%%site.name%%** is a microblogging service. ');
}
+ $instr .= ' ';
$instr .= sprintf(_('It runs the [StatusNet](http://status.net/) microblogging software, version %s, available under the [GNU Affero General Public License](http://www.fsf.org/licensing/licenses/agpl-3.0.html).'), STATUSNET_VERSION);
$output = common_markup_to_html($instr);
$this->raw($output);
diff --git a/lib/apiauth.php b/lib/apiauth.php
index 5090871cf..32502399f 100644
--- a/lib/apiauth.php
+++ b/lib/apiauth.php
@@ -235,9 +235,13 @@ class ApiAuthAction extends ApiAction
{
$this->basicAuthProcessHeader();
- $realm = common_config('site', 'name') . ' API';
+ $realm = common_config('api', 'realm');
- if (!isset($this->auth_user_nickname) && $required) {
+ if (empty($realm)) {
+ $realm = common_config('site', 'name') . ' API';
+ }
+
+ if (empty($this->auth_user_nickname) && $required) {
header('WWW-Authenticate: Basic realm="' . $realm . '"');
// show error if the user clicks 'cancel'
diff --git a/lib/channel.php b/lib/channel.php
index 3cd168786..689bca0be 100644
--- a/lib/channel.php
+++ b/lib/channel.php
@@ -47,6 +47,25 @@ class Channel
}
}
+class CLIChannel extends Channel
+{
+ function source()
+ {
+ return 'cli';
+ }
+
+ function output($user, $text)
+ {
+ $site = common_config('site', 'name');
+ print "[{$user->nickname}@{$site}] $text\n";
+ }
+
+ function error($user, $text)
+ {
+ $this->output($user, $text);
+ }
+}
+
class XMPPChannel extends Channel
{
diff --git a/lib/command.php b/lib/command.php
index ea7b60372..9d550550f 100644
--- a/lib/command.php
+++ b/lib/command.php
@@ -1,7 +1,7 @@
<?php
/*
* StatusNet - the distributed open-source microblogging tool
- * Copyright (C) 2008, 2009, StatusNet, Inc.
+ * Copyright (C) 2008, 2009, 2010 StatusNet, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -31,15 +31,147 @@ class Command
$this->user = $user;
}
- function execute($channel)
+ /**
+ * Execute the command and send success or error results
+ * back via the given communications channel.
+ *
+ * @param Channel
+ */
+ public function execute($channel)
+ {
+ try {
+ $this->handle($channel);
+ } catch (CommandException $e) {
+ $channel->error($this->user, $e->getMessage());
+ } catch (Exception $e) {
+ common_log(LOG_ERR, "Error handling " . get_class($this) . ": " . $e->getMessage());
+ $channel->error($this->user, $e->getMessage());
+ }
+ }
+
+
+ /**
+ * Override this with the meat!
+ *
+ * An error to send back to the user may be sent by throwing
+ * a CommandException with a formatted message.
+ *
+ * @param Channel
+ * @throws CommandException
+ */
+ function handle($channel)
{
return false;
}
+
+ /**
+ * Look up a notice from an argument, by poster's name to get last post
+ * or notice_id prefixed with #.
+ *
+ * @return Notice
+ * @throws CommandException
+ */
+ function getNotice($arg)
+ {
+ $notice = null;
+ if (Event::handle('StartCommandGetNotice', array($this, $arg, &$notice))) {
+ if(substr($this->other,0,1)=='#'){
+ // A specific notice_id #123
+
+ $notice = Notice::staticGet(substr($arg,1));
+ if (!$notice) {
+ throw new CommandException(_('Notice with that id does not exist'));
+ }
+ }
+
+ if (Validate::uri($this->other)) {
+ // A specific notice by URI lookup
+ $notice = Notice::staticGet('uri', $arg);
+ }
+
+ if (!$notice) {
+ // Local or remote profile name to get their last notice.
+ // May throw an exception and report 'no such user'
+ $recipient = $this->getProfile($arg);
+
+ $notice = $recipient->getCurrentNotice();
+ if (!$notice) {
+ throw new CommandException(_('User has no last notice'));
+ }
+ }
+ }
+ Event::handle('EndCommandGetNotice', array($this, $arg, &$notice));
+ if (!$notice) {
+ throw new CommandException(_('Notice with that id does not exist'));
+ }
+ return $notice;
+ }
+
+ /**
+ * Look up a local or remote profile by nickname.
+ *
+ * @return Profile
+ * @throws CommandException
+ */
+ function getProfile($arg)
+ {
+ $profile = null;
+ if (Event::handle('StartCommandGetProfile', array($this, $arg, &$profile))) {
+ $profile =
+ common_relative_profile($this->user, common_canonical_nickname($arg));
+ }
+ Event::handle('EndCommandGetProfile', array($this, $arg, &$profile));
+ if (!$profile) {
+ throw new CommandException(sprintf(_('Could not find a user with nickname %s'), $arg));
+ }
+ return $profile;
+ }
+
+ /**
+ * Get a local user by name
+ * @return User
+ * @throws CommandException
+ */
+ function getUser($arg)
+ {
+ $user = null;
+ if (Event::handle('StartCommandGetUser', array($this, $arg, &$user))) {
+ $user = User::staticGet('nickname', $arg);
+ }
+ Event::handle('EndCommandGetUser', array($this, $arg, &$user));
+ if (!$user){
+ throw new CommandException(sprintf(_('Could not find a local user with nickname %s'),
+ $arg));
+ }
+ return $user;
+ }
+
+ /**
+ * Get a local or remote group by name.
+ * @return User_group
+ * @throws CommandException
+ */
+ function getGroup($arg)
+ {
+ $group = null;
+ if (Event::handle('StartCommandGetGroup', array($this, $arg, &$group))) {
+ $group = User_group::getForNickname($arg, $this->user->getProfile());
+ }
+ Event::handle('EndCommandGetGroup', array($this, $arg, &$group));
+ if (!$group) {
+ throw new CommandException(_('No such group.'));
+ }
+ return $group;
+ }
+}
+
+class CommandException extends Exception
+{
}
class UnimplementedCommand extends Command
{
- function execute($channel)
+ function handle($channel)
{
$channel->error($this->user, _("Sorry, this command is not yet implemented."));
}
@@ -81,24 +213,20 @@ class NudgeCommand extends Command
parent::__construct($user);
$this->other = $other;
}
- function execute($channel)
+
+ function handle($channel)
{
- $recipient = User::staticGet('nickname', $this->other);
- if(! $recipient){
- $channel->error($this->user, sprintf(_('Could not find a user with nickname %s'),
- $this->other));
- }else{
- if ($recipient->id == $this->user->id) {
- $channel->error($this->user, _('It does not make a lot of sense to nudge yourself!'));
- }else{
- if ($recipient->email && $recipient->emailnotifynudge) {
- mail_notify_nudge($this->user, $recipient);
- }
- // XXX: notify by IM
- // XXX: notify by SMS
- $channel->output($this->user, sprintf(_('Nudge sent to %s'),
- $recipient->nickname));
+ $recipient = $this->getUser($this->other);
+ if ($recipient->id == $this->user->id) {
+ throw new CommandException(_('It does not make a lot of sense to nudge yourself!'));
+ } else {
+ if ($recipient->email && $recipient->emailnotifynudge) {
+ mail_notify_nudge($this->user, $recipient);
}
+ // XXX: notify by IM
+ // XXX: notify by SMS
+ $channel->output($this->user, sprintf(_('Nudge sent to %s'),
+ $recipient->nickname));
}
}
}
@@ -115,7 +243,7 @@ class InviteCommand extends UnimplementedCommand
class StatsCommand extends Command
{
- function execute($channel)
+ function handle($channel)
{
$profile = $this->user->getProfile();
@@ -142,34 +270,9 @@ class FavCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
- if(substr($this->other,0,1)=='#'){
- //favoriting a specific notice_id
-
- $notice = Notice::staticGet(substr($this->other,1));
- if (!$notice) {
- $channel->error($this->user, _('Notice with that id does not exist'));
- return;
- }
- $recipient = $notice->getProfile();
- }else{
- //favoriting a given user's last notice
-
- $recipient =
- common_relative_profile($this->user, common_canonical_nickname($this->other));
-
- if (!$recipient) {
- $channel->error($this->user, _('No such user.'));
- return;
- }
- $notice = $recipient->getCurrentNotice();
- if (!$notice) {
- $channel->error($this->user, _('User has no last notice'));
- return;
- }
- }
-
+ $notice = $this->getNotice($this->other);
$fave = Fave::addNew($this->user, $notice);
if (!$fave) {
@@ -177,7 +280,10 @@ class FavCommand extends Command
return;
}
- $other = User::staticGet('id', $recipient->id);
+ // @fixme favorite notification should be triggered
+ // at a lower level
+
+ $other = User::staticGet('id', $notice->profile_id);
if ($other && $other->id != $user->id) {
if ($other->email && $other->emailnotifyfav) {
@@ -191,6 +297,7 @@ class FavCommand extends Command
}
}
+
class JoinCommand extends Command
{
var $other = null;
@@ -201,17 +308,10 @@ class JoinCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
-
- $nickname = common_canonical_nickname($this->other);
- $group = User_group::staticGet('nickname', $nickname);
- $cur = $this->user;
-
- if (!$group) {
- $channel->error($cur, _('No such group.'));
- return;
- }
+ $group = $this->getGroup($this->other);
+ $cur = $this->user;
if ($cur->isMember($group)) {
$channel->error($cur, _('You are already a member of that group'));
@@ -249,12 +349,10 @@ class DropCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
-
- $nickname = common_canonical_nickname($this->other);
- $group = User_group::staticGet('nickname', $nickname);
- $cur = $this->user;
+ $group = $this->getGroup($this->other);
+ $cur = $this->user;
if (!$group) {
$channel->error($cur, _('No such group.'));
@@ -293,15 +391,9 @@ class WhoisCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
- $recipient =
- common_relative_profile($this->user, common_canonical_nickname($this->other));
-
- if (!$recipient) {
- $channel->error($this->user, _('No such user.'));
- return;
- }
+ $recipient = $this->getProfile($this->other);
$whois = sprintf(_("%1\$s (%2\$s)"), $recipient->nickname,
$recipient->profileurl);
@@ -332,9 +424,18 @@ class MessageCommand extends Command
$this->text = $text;
}
- function execute($channel)
+ function handle($channel)
{
- $other = User::staticGet('nickname', common_canonical_nickname($this->other));
+ try {
+ $other = $this->getUser($this->other);
+ } catch (CommandException $e) {
+ try {
+ $profile = $this->getProfile($this->other);
+ } catch (CommandException $f) {
+ throw $e;
+ }
+ throw new CommandException(sprintf(_('%s is a remote profile; you can only send direct messages to users on the same server.'), $this->other));
+ }
$len = mb_strlen($this->text);
@@ -380,33 +481,9 @@ class RepeatCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
- if(substr($this->other,0,1)=='#'){
- //repeating a specific notice_id
-
- $notice = Notice::staticGet(substr($this->other,1));
- if (!$notice) {
- $channel->error($this->user, _('Notice with that id does not exist'));
- return;
- }
- $recipient = $notice->getProfile();
- }else{
- //repeating a given user's last notice
-
- $recipient =
- common_relative_profile($this->user, common_canonical_nickname($this->other));
-
- if (!$recipient) {
- $channel->error($this->user, _('No such user.'));
- return;
- }
- $notice = $recipient->getCurrentNotice();
- if (!$notice) {
- $channel->error($this->user, _('User has no last notice'));
- return;
- }
- }
+ $notice = $this->getNotice($this->other);
if($this->user->id == $notice->profile_id)
{
@@ -414,7 +491,7 @@ class RepeatCommand extends Command
return;
}
- if ($recipient->hasRepeated($notice->id)) {
+ if ($this->user->getProfile()->hasRepeated($notice->id)) {
$channel->error($this->user, _('Already repeated that notice'));
return;
}
@@ -441,33 +518,10 @@ class ReplyCommand extends Command
$this->text = $text;
}
- function execute($channel)
+ function handle($channel)
{
- if(substr($this->other,0,1)=='#'){
- //replying to a specific notice_id
-
- $notice = Notice::staticGet(substr($this->other,1));
- if (!$notice) {
- $channel->error($this->user, _('Notice with that id does not exist'));
- return;
- }
- $recipient = $notice->getProfile();
- }else{
- //replying to a given user's last notice
-
- $recipient =
- common_relative_profile($this->user, common_canonical_nickname($this->other));
-
- if (!$recipient) {
- $channel->error($this->user, _('No such user.'));
- return;
- }
- $notice = $recipient->getCurrentNotice();
- if (!$notice) {
- $channel->error($this->user, _('User has no last notice'));
- return;
- }
- }
+ $notice = $this->getNotice($this->other);
+ $recipient = $notice->getProfile();
$len = mb_strlen($this->text);
@@ -507,17 +561,10 @@ class GetCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
- $target_nickname = common_canonical_nickname($this->other);
-
- $target =
- common_relative_profile($this->user, $target_nickname);
+ $target = $this->getProfile($this->other);
- if (!$target) {
- $channel->error($this->user, _('No such user.'));
- return;
- }
$notice = $target->getCurrentNotice();
if (!$notice) {
$channel->error($this->user, _('User has no last notice'));
@@ -525,7 +572,7 @@ class GetCommand extends Command
}
$notice_content = $notice->content;
- $channel->output($this->user, $target_nickname . ": " . $notice_content);
+ $channel->output($this->user, $target->nickname . ": " . $notice_content);
}
}
@@ -540,7 +587,7 @@ class SubCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
if (!$this->other) {
@@ -548,16 +595,16 @@ class SubCommand extends Command
return;
}
- $otherUser = User::staticGet('nickname', $this->other);
+ $target = $this->getProfile($this->other);
- if (empty($otherUser)) {
- $channel->error($this->user, _('No such user'));
- return;
+ $remote = Remote_profile::staticGet('id', $target->id);
+ if ($remote) {
+ throw new CommandException(_("Can't subscribe to OMB profiles by command."));
}
try {
Subscription::start($this->user->getProfile(),
- $otherUser->getProfile());
+ $target);
$channel->output($this->user, sprintf(_('Subscribed to %s'), $this->other));
} catch (Exception $e) {
$channel->error($this->user, $e->getMessage());
@@ -576,22 +623,18 @@ class UnsubCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
if(!$this->other) {
$channel->error($this->user, _('Specify the name of the user to unsubscribe from'));
return;
}
- $otherUser = User::staticGet('nickname', $this->other);
-
- if (empty($otherUser)) {
- $channel->error($this->user, _('No such user'));
- }
+ $target = $this->getProfile($this->other);
try {
Subscription::cancel($this->user->getProfile(),
- $otherUser->getProfile());
+ $target);
$channel->output($this->user, sprintf(_('Unsubscribed from %s'), $this->other));
} catch (Exception $e) {
$channel->error($this->user, $e->getMessage());
@@ -607,7 +650,7 @@ class OffCommand extends Command
parent::__construct($user);
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
if ($other) {
$channel->error($this->user, _("Command not yet implemented."));
@@ -630,7 +673,7 @@ class OnCommand extends Command
$this->other = $other;
}
- function execute($channel)
+ function handle($channel)
{
if ($other) {
$channel->error($this->user, _("Command not yet implemented."));
@@ -646,7 +689,7 @@ class OnCommand extends Command
class LoginCommand extends Command
{
- function execute($channel)
+ function handle($channel)
{
$disabled = common_config('logincommand','disabled');
$disabled = isset($disabled) && $disabled;
@@ -670,7 +713,7 @@ class LoginCommand extends Command
class SubscriptionsCommand extends Command
{
- function execute($channel)
+ function handle($channel)
{
$profile = $this->user->getSubscriptions(0);
$nicknames=array();
@@ -692,7 +735,7 @@ class SubscriptionsCommand extends Command
class SubscribersCommand extends Command
{
- function execute($channel)
+ function handle($channel)
{
$profile = $this->user->getSubscribers();
$nicknames=array();
@@ -714,7 +757,7 @@ class SubscribersCommand extends Command
class GroupsCommand extends Command
{
- function execute($channel)
+ function handle($channel)
{
$group = $this->user->getGroups();
$groups=array();
@@ -735,7 +778,7 @@ class GroupsCommand extends Command
class HelpCommand extends Command
{
- function execute($channel)
+ function handle($channel)
{
$channel->output($this->user,
_("Commands:\n".
diff --git a/lib/default.php b/lib/default.php
index bdd78d4d8..46d3d4774 100644
--- a/lib/default.php
+++ b/lib/default.php
@@ -293,4 +293,6 @@ $default =
array('crawldelay' => 0,
'disallow' => array('main', 'settings', 'admin', 'search', 'message')
),
+ 'api' =>
+ array('realm' => null),
);
diff --git a/lib/jabber.php b/lib/jabber.php
index e1bf06ba6..db4e2e9a7 100644
--- a/lib/jabber.php
+++ b/lib/jabber.php
@@ -88,22 +88,30 @@ class Sharing_XMPP extends XMPPHP_XMPP
/**
* Build an XMPP proxy connection that'll save outgoing messages
* to the 'xmppout' queue to be picked up by xmppdaemon later.
+ *
+ * If queueing is disabled, we'll grab a live connection.
+ *
+ * @return XMPPHP
*/
function jabber_proxy()
{
- $proxy = new Queued_XMPP(common_config('xmpp', 'host') ?
- common_config('xmpp', 'host') :
- common_config('xmpp', 'server'),
- common_config('xmpp', 'port'),
- common_config('xmpp', 'user'),
- common_config('xmpp', 'password'),
- common_config('xmpp', 'resource') . 'daemon',
- common_config('xmpp', 'server'),
- common_config('xmpp', 'debug') ?
- true : false,
- common_config('xmpp', 'debug') ?
- XMPPHP_Log::LEVEL_VERBOSE : null);
- return $proxy;
+ if (common_config('queue', 'enabled')) {
+ $proxy = new Queued_XMPP(common_config('xmpp', 'host') ?
+ common_config('xmpp', 'host') :
+ common_config('xmpp', 'server'),
+ common_config('xmpp', 'port'),
+ common_config('xmpp', 'user'),
+ common_config('xmpp', 'password'),
+ common_config('xmpp', 'resource') . 'daemon',
+ common_config('xmpp', 'server'),
+ common_config('xmpp', 'debug') ?
+ true : false,
+ common_config('xmpp', 'debug') ?
+ XMPPHP_Log::LEVEL_VERBOSE : null);
+ return $proxy;
+ } else {
+ return jabber_connect();
+ }
}
/**
diff --git a/lib/queued_xmpp.php b/lib/queued_xmpp.php
index fdd074db2..f6bccfd5b 100644
--- a/lib/queued_xmpp.php
+++ b/lib/queued_xmpp.php
@@ -49,10 +49,20 @@ class Queued_XMPP extends XMPPHP_XMPP
*/
public function __construct($host, $port, $user, $password, $resource, $server = null, $printlog = false, $loglevel = null)
{
- parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
- // Normally the fulljid isn't filled out until resource binding time;
- // we need to save it here since we're not talking to a real server.
- $this->fulljid = "{$this->basejid}/{$this->resource}";
+ parent::__construct($host, $port, $user, $password, $resource, $server, $printlog, $loglevel);
+
+ // We use $host to connect, but $server to build JIDs if specified.
+ // This seems to fix an upstream bug where $host was used to build
+ // $this->basejid, never seen since it isn't actually used in the base
+ // classes.
+ if (!$server) {
+ $server = $this->host;
+ }
+ $this->basejid = $this->user . '@' . $server;
+
+ // Normally the fulljid is filled out by the server at resource binding
+ // time, but we need to do it since we're not talking to a real server.
+ $this->fulljid = "{$this->basejid}/{$this->resource}";
}
/**
diff --git a/lib/util.php b/lib/util.php
index da2799d4f..44ccc0def 100644
--- a/lib/util.php
+++ b/lib/util.php
@@ -52,17 +52,43 @@ function common_init_language()
{
mb_internal_encoding('UTF-8');
- // gettext seems very picky... We first need to setlocale()
- // to a locale which _does_ exist on the system, and _then_
- // we can set in another locale that may not be set up
- // (say, ga_ES for Galego/Galician) it seems to take it.
- common_init_locale("en_US");
-
// Note that this setlocale() call may "fail" but this is harmless;
// gettext will still select the right language.
$language = common_language();
$locale_set = common_init_locale($language);
+ if (!$locale_set) {
+ // The requested locale doesn't exist on the system.
+ //
+ // gettext seems very picky... We first need to setlocale()
+ // to a locale which _does_ exist on the system, and _then_
+ // we can set in another locale that may not be set up
+ // (say, ga_ES for Galego/Galician) it seems to take it.
+ //
+ // For some reason C and POSIX which are guaranteed to work
+ // don't do the job. en_US.UTF-8 should be there most of the
+ // time, but not guaranteed.
+ $ok = common_init_locale("en_US");
+ if (!$ok) {
+ // Try to find a complete, working locale...
+ // @fixme shelling out feels awfully inefficient
+ // but I don't think there's a more standard way.
+ $all = `locale -a`;
+ foreach (explode("\n", $all) as $locale) {
+ if (preg_match('/\.utf[-_]?8$/i', $locale)) {
+ $ok = setlocale(LC_ALL, $locale);
+ if ($ok) {
+ break;
+ }
+ }
+ }
+ if (!$ok) {
+ common_log(LOG_ERR, "Unable to find a UTF-8 locale on this system; UI translations may not work.");
+ }
+ }
+ $locale_set = common_init_locale($language);
+ }
+
setlocale(LC_CTYPE, 'C');
// So we do not have to make people install the gettext locales
$path = common_config('site','locale_path');
@@ -133,6 +159,11 @@ function common_munge_password($password, $id)
function common_check_user($nickname, $password)
{
+ // empty nickname always unacceptable
+ if (empty($nickname)) {
+ return false;
+ }
+
$authenticatedUser = false;
if (Event::handle('StartCheckPassword', array($nickname, $password, &$authenticatedUser))) {
diff --git a/lib/xmppmanager.php b/lib/xmppmanager.php
index f37635855..cca54db08 100644
--- a/lib/xmppmanager.php
+++ b/lib/xmppmanager.php
@@ -36,6 +36,7 @@ class XmppManager extends IoManager
protected $site = null;
protected $pingid = 0;
protected $lastping = null;
+ protected $conn = null;
static protected $singletons = array();
diff --git a/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php
index bdcaae366..a97f3475b 100644
--- a/plugins/OStatus/OStatusPlugin.php
+++ b/plugins/OStatus/OStatusPlugin.php
@@ -322,6 +322,86 @@ class OStatusPlugin extends Plugin
}
/**
+ * Allow remote profile references to be used in commands:
+ * sub update@status.net
+ * whois evan@identi.ca
+ * reply http://identi.ca/evan hey what's up
+ *
+ * @param Command $command
+ * @param string $arg
+ * @param Profile &$profile
+ * @return hook return code
+ */
+ function onStartCommandGetProfile($command, $arg, &$profile)
+ {
+ $oprofile = $this->pullRemoteProfile($arg);
+ if ($oprofile && !$oprofile->isGroup()) {
+ $profile = $oprofile->localProfile();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ * Allow remote group references to be used in commands:
+ * join group+statusnet@identi.ca
+ * join http://identi.ca/group/statusnet
+ * drop identi.ca/group/statusnet
+ *
+ * @param Command $command
+ * @param string $arg
+ * @param User_group &$group
+ * @return hook return code
+ */
+ function onStartCommandGetGroup($command, $arg, &$group)
+ {
+ $oprofile = $this->pullRemoteProfile($arg);
+ if ($oprofile && $oprofile->isGroup()) {
+ $group = $oprofile->localGroup();
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ protected function pullRemoteProfile($arg)
+ {
+ $oprofile = null;
+ if (preg_match('!^((?:\w+\.)*\w+@(?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+)$!', $arg)) {
+ // webfinger lookup
+ try {
+ return Ostatus_profile::ensureWebfinger($arg);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, 'Webfinger lookup failed for ' .
+ $arg . ': ' . $e->getMessage());
+ }
+ }
+
+ // Look for profile URLs, with or without scheme:
+ $urls = array();
+ if (preg_match('!^https?://((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!', $arg)) {
+ $urls[] = $arg;
+ }
+ if (preg_match('!^((?:\w+\.)*\w+(?:\w+\-\w+)*\.\w+(?:/\w+)+)$!', $arg)) {
+ $schemes = array('http', 'https');
+ foreach ($schemes as $scheme) {
+ $urls[] = "$scheme://$arg";
+ }
+ }
+
+ foreach ($urls as $url) {
+ try {
+ return Ostatus_profile::ensureProfile($url);
+ } catch (Exception $e) {
+ common_log(LOG_ERR, 'Profile lookup failed for ' .
+ $arg . ': ' . $e->getMessage());
+ }
+ }
+ return null;
+ }
+
+ /**
* Make sure necessary tables are filled out.
*/
function onCheckSchema() {
diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php
index 3120a70f9..c420b3eef 100644
--- a/plugins/OStatus/classes/HubSub.php
+++ b/plugins/OStatus/classes/HubSub.php
@@ -192,7 +192,7 @@ class HubSub extends Memcached_DataObject
// Any existing query string parameters must be preserved
$url = $this->callback;
- if (strpos('?', $url) !== false) {
+ if (strpos($url, '?') !== false) {
$url .= '&';
} else {
$url .= '?';
diff --git a/plugins/OStatus/lib/feeddiscovery.php b/plugins/OStatus/lib/feeddiscovery.php
index 7afb71bdc..ff76b229e 100644
--- a/plugins/OStatus/lib/feeddiscovery.php
+++ b/plugins/OStatus/lib/feeddiscovery.php
@@ -129,7 +129,7 @@ class FeedDiscovery
function initFromResponse($response)
{
if (!$response->isOk()) {
- throw new FeedSubBadResponseException($response->getCode());
+ throw new FeedSubBadResponseException($response->getStatus());
}
$sourceurl = $response->getUrl();
diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php
index d454691c8..9e7b53680 100644
--- a/plugins/RSSCloud/RSSCloudNotifier.php
+++ b/plugins/RSSCloud/RSSCloudNotifier.php
@@ -152,7 +152,7 @@ class RSSCloudNotifier
function notify($profile)
{
$feed = common_path('api/statuses/user_timeline/') .
- $profile->nickname . '.rss';
+ $profile->id . '.rss';
$cloudSub = new RSSCloudSubscription();
diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php
index d76c08d37..030529534 100644
--- a/plugins/RSSCloud/RSSCloudRequestNotify.php
+++ b/plugins/RSSCloud/RSSCloudRequestNotify.php
@@ -270,13 +270,14 @@ class RSSCloudRequestNotifyAction extends Action
function userFromFeed($feed)
{
- // We only do profile feeds
+ // We only do canonical RSS2 profile feeds (specified by ID), e.g.:
+ // http://www.example.com/api/statuses/user_timeline/2.rss
$path = common_path('api/statuses/user_timeline/');
- $valid = '%^' . $path . '(?<nickname>.*)\.rss$%';
+ $valid = '%^' . $path . '(?<id>.*)\.rss$%';
if (preg_match($valid, $feed, $matches)) {
- $user = User::staticGet('nickname', $matches['nickname']);
+ $user = User::staticGet('id', $matches['id']);
if (!empty($user)) {
return $user;
}
diff --git a/plugins/RequireValidatedEmail/README b/plugins/RequireValidatedEmail/README
index ccd94d271..46ee24d5f 100644
--- a/plugins/RequireValidatedEmail/README
+++ b/plugins/RequireValidatedEmail/README
@@ -14,8 +14,6 @@ registered prior to that timestamp.
Todo:
-* make email field required on registration form
* add a more visible indicator that validation is still outstanding
-* localization for UI strings
* test with XMPP, API posting
diff --git a/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php b/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
index 3581f1de9..ccefa14f6 100644
--- a/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
+++ b/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
@@ -54,13 +54,34 @@ class RequireValidatedEmailPlugin extends Plugin
$user = User::staticGet('id', $notice->profile_id);
if (!empty($user)) { // it's a remote notice
if (!$this->validated($user)) {
- throw new ClientException(_("You must validate your email address before posting."));
+ throw new ClientException(_m("You must validate your email address before posting."));
}
}
return true;
}
/**
+ * Event handler for registration attempts; rejects the registration
+ * if email field is missing.
+ *
+ * @param RegisterAction $action
+ * @return bool hook result code
+ */
+ function onStartRegistrationTry($action)
+ {
+ $email = $action->trimmed('email');
+
+ if (empty($email)) {
+ $action->showForm(_m('You must provide an email address to register.'));
+ return false;
+ }
+
+ // Default form will run address format validation and reject if bad.
+
+ return true;
+ }
+
+ /**
* Check if a user has a validated email address or has been
* otherwise grandfathered in.
*
diff --git a/plugins/RequireValidatedEmail/locale/RequireValidatedEmail.po b/plugins/RequireValidatedEmail/locale/RequireValidatedEmail.po
new file mode 100644
index 000000000..49ac4f6f4
--- /dev/null
+++ b/plugins/RequireValidatedEmail/locale/RequireValidatedEmail.po
@@ -0,0 +1,31 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2010-03-10 10:05-0800\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
+"Language-Team: LANGUAGE <LL@li.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: RequireValidatedEmailPlugin.php:57
+msgid "You must validate your email address before posting."
+msgstr ""
+
+#: RequireValidatedEmailPlugin.php:75
+msgid "You must provide an email address to register."
+msgstr ""
+
+#: RequireValidatedEmailPlugin.php:128
+msgid ""
+"The Require Validated Email plugin disables posting for accounts that do not "
+"have a validated email address."
+msgstr ""
diff --git a/scripts/command.php b/scripts/command.php
new file mode 100755
index 000000000..6041b02eb
--- /dev/null
+++ b/scripts/command.php
@@ -0,0 +1,80 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a distributed open-source microblogging tool
+ * Copyright (C) 2008, 2009, 2010 StatusNet, Inc.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'i:n:';
+$longoptions = array('id=', 'nickname=');
+
+$helptext = <<<END_OF_USERROLE_HELP
+command.php [options] [command line]
+Perform commands on behalf of a user, such as sub, unsub, join, drop
+
+ -i --id ID of the user
+ -n --nickname nickname of the user
+
+END_OF_USERROLE_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+
+
+function interpretCommand($user, $body)
+{
+ $inter = new CommandInterpreter();
+ $chan = new CLIChannel();
+ $cmd = $inter->handle_command($user, $body);
+ if ($cmd) {
+ $cmd->execute($chan);
+ return true;
+ } else {
+ $chan->error($user, "Not a valid command. Try 'help'?");
+ return false;
+ }
+}
+
+
+
+if (have_option('i', 'id')) {
+ $id = get_option_value('i', 'id');
+ $user = User::staticGet('id', $id);
+ if (empty($user)) {
+ print "Can't find user with ID $id\n";
+ exit(1);
+ }
+} else if (have_option('n', 'nickname')) {
+ $nickname = get_option_value('n', 'nickname');
+ $user = User::staticGet('nickname', $nickname);
+ if (empty($user)) {
+ print "Can't find user with nickname '$nickname'\n";
+ exit(1);
+ }
+} else {
+ print "You must provide either an ID or a nickname.\n\n";
+ print $helptext;
+ exit(1);
+}
+
+// @todo refactor the interactive console in console.php and use
+// that to optionally make an interactive test console here too.
+// Would be good to help people test commands when XMPP or email
+// isn't available locally.
+interpretCommand($user, implode(' ', $args));
+
diff --git a/scripts/flushsite.php b/scripts/flushsite.php
new file mode 100644
index 000000000..b7f385ac4
--- /dev/null
+++ b/scripts/flushsite.php
@@ -0,0 +1,45 @@
+#!/usr/bin/env php
+<?php
+/*
+ * StatusNet - a 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 <http://www.gnu.org/licenses/>.
+ */
+
+define('INSTALLDIR', realpath(dirname(__FILE__) . '/..'));
+
+$shortoptions = 'd';
+$longoptions = array('delete');
+
+$helptext = <<<END_OF_FLUSHSITE_HELP
+flushsite.php -s<sitename>
+Flush the site with the given name from memcached.
+
+END_OF_FLUSHSITE_HELP;
+
+require_once INSTALLDIR.'/scripts/commandline.inc';
+
+$nickname = common_config('site', 'nickname');
+
+$sn = Status_network::memGet('nickname', $nickname);
+
+if (empty($sn)) {
+ print "No such site.\n";
+ exit(-1);
+}
+
+print "Flushing cache for {$nickname}...";
+$sn->decache();
+print "OK.\n"; \ No newline at end of file