]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
Merge branch 'testing' of git@gitorious.org:statusnet/mainline into testing
authorEvan Prodromou <evan@status.net>
Wed, 10 Mar 2010 19:13:02 +0000 (14:13 -0500)
committerEvan Prodromou <evan@status.net>
Wed, 10 Mar 2010 19:13:02 +0000 (14:13 -0500)
22 files changed:
actions/apistatusnetconfig.php
actions/otp.php
install.php
lib/action.php
lib/apiauth.php
lib/channel.php
lib/command.php
lib/default.php
lib/jabber.php
lib/queued_xmpp.php
lib/util.php
lib/xmppmanager.php
plugins/OStatus/OStatusPlugin.php
plugins/OStatus/classes/HubSub.php
plugins/OStatus/lib/feeddiscovery.php
plugins/RSSCloud/RSSCloudNotifier.php
plugins/RSSCloud/RSSCloudRequestNotify.php
plugins/RequireValidatedEmail/README
plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php
plugins/RequireValidatedEmail/locale/RequireValidatedEmail.po [new file with mode: 0644]
scripts/command.php [new file with mode: 0755]
scripts/flushsite.php [new file with mode: 0644]

index 296376d1955095b7f232aa018d9d5144aaef12d8..51400dfc98c7a81be06a2d47eb16dc0e6942c74b 100644 (file)
@@ -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'),
index acf84aee814216071c5f8087137997275cf88387..1e06603d43f6cfeb764d528c748925f2181bdbd4 100644 (file)
@@ -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);
         }
index 7fece8999fe7bcf3ee644086c5543e114d807f69..9a7e27fa2c9c50b310c7e5ae0abf2c936cccf976 100644 (file)
@@ -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());
index 816086d202f00889f686d31a228ac1e5049b5d49..9884f529c9e1b970d3d708d437a2c9ed179aa4e4 100644 (file)
@@ -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);
index 5090871cfe18a931600558e06c202b68e437d08b..32502399f9f836bbd2d0607440a72200e14f4cd1 100644 (file)
@@ -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'
index 3cd168786c9d69e89343170e19cdda7bf27a308c..689bca0be98a8028966812aa578569dbc3efdb7d 100644 (file)
@@ -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
 {
 
index ea7b60372d96542dd134069d7a94b67b4d40756c..9d550550f75562849873ce0177966dc4e937e444 100644 (file)
@@ -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".
index bdd78d4d8618cdc01edc7ab28d8692372c6db4c3..46d3d4774ff6dcb2453baa23c146d877f3e76c58 100644 (file)
@@ -293,4 +293,6 @@ $default =
         array('crawldelay' => 0,
               'disallow' => array('main', 'settings', 'admin', 'search', 'message')
               ),
+        'api' =>
+        array('realm' => null),
         );
index e1bf06ba661bfc8a8e29bdc30637c5f3d8b22032..db4e2e9a706a62f8bf905e4b34167f37416492f3 100644 (file)
@@ -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();
+    }
 }
 
 /**
index fdd074db2996e422e934d1efa473d1df514b1526..f6bccfd5ba40ddbac9cc0edcb80b478dbdf34dc6 100644 (file)
@@ -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}";
     }
 
     /**
index da2799d4f92d23766c464b1e15b4c66ab6704217..44ccc0deff546d7c15d107010911fbe706789e37 100644 (file)
@@ -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))) {
index f376358555ab1aa4f442da174925fa01e6add3e7..cca54db08da4b4a016354ffa1b685564c9900fe8 100644 (file)
@@ -36,6 +36,7 @@ class XmppManager extends IoManager
     protected $site = null;
     protected $pingid = 0;
     protected $lastping = null;
+    protected $conn = null;
 
     static protected $singletons = array();
     
index bdcaae366ba3576103ef4da63974db4945eae9fc..a97f3475b2b409572bb34b11e2ea325a6ebb59e4 100644 (file)
@@ -321,6 +321,86 @@ class OStatusPlugin extends Plugin
         return true;
     }
 
+    /**
+     * 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.
      */
index 3120a70f9fb39dd978c14482c59b79ed90abbbc4..c420b3eef8414826101910e08ec9f8e07bb1a642 100644 (file)
@@ -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 .= '?';
index 7afb71bdc16044ad8ec763e64f2b2d442df5a0ae..ff76b229e76923030f0d56242bb4467024b93dbf 100644 (file)
@@ -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();
index d454691c80ec5ee028d814505b212aebe776d1b7..9e7b53680381c43930ebb781a6643ff3ea400bba 100644 (file)
@@ -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();
 
index d76c08d379aedef609c28d851de3a897b5e72a7e..03052953486e9da683c030855da8182ce00b2508 100644 (file)
@@ -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;
             }
index ccd94d271d2c6374cb91d17863ca6f3f9b567153..46ee24d5fe1e496baef4ebe96b9dd7155e4431ee 100644 (file)
@@ -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
 
index 3581f1de9240b5d283f56cc968a82c9dda4ee3de..ccefa14f62f7abffa33ececd6faace9859e94370 100644 (file)
@@ -54,12 +54,33 @@ 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 (file)
index 0000000..49ac4f6
--- /dev/null
@@ -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 (executable)
index 0000000..6041b02
--- /dev/null
@@ -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 (file)
index 0000000..b7f385a
--- /dev/null
@@ -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