From 5f7aa6f2e3c82b9598e3405885eb455bed9b0edc Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 8 Mar 2010 12:36:03 -0500 Subject: make API realm configurable --- lib/apiauth.php | 6 +++++- lib/default.php | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/apiauth.php b/lib/apiauth.php index 5090871cf..f63c84d8f 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -235,7 +235,11 @@ class ApiAuthAction extends ApiAction { $this->basicAuthProcessHeader(); - $realm = common_config('site', 'name') . ' API'; + $realm = common_config('api', 'realm'); + + if (empty($realm)) { + $realm = common_config('site', 'name') . ' API'; + } if (!isset($this->auth_user_nickname) && $required) { header('WWW-Authenticate: Basic realm="' . $realm . '"'); 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), ); -- cgit v1.2.3-54-g00ecf From a77efb2447abe75d3b9902410bced61b76377de3 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 10:32:40 -0800 Subject: XMPP cleanup: fix outgoing XMPP when queuing is disabled; fix notice for first access to undefined member variable --- lib/jabber.php | 34 +++++++++++++++++++++------------- lib/xmppmanager.php | 1 + 2 files changed, 22 insertions(+), 13 deletions(-) (limited to 'lib') 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/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(); -- cgit v1.2.3-54-g00ecf From 217ad420ac8085fe620235dfc47bea27e4ac75dc Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 12:19:06 -0800 Subject: Fix ticket #2208: regression in XMPP sending when server != host The upstream class sets $this->basejid with host unconditionally, which wasn't previously an issue as the fulljid would always be filled in by the server at connect time before sending messages. With the new queued messaging, we need to make sure we've filled out $this->fulljid correctly without making a connection. Now using $server if provided to build $this->basejid and $this->fulljid in the queued XMPP proxy class, so queued messages are sent correctly. --- lib/queued_xmpp.php | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'lib') 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}"; } /** -- cgit v1.2.3-54-g00ecf From 51a245f18c1e4a830c5eb94f3e60c6b4b3e560ee Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Thu, 4 Mar 2010 18:24:32 -0500 Subject: Added Memcached plugin (using pecl/memcached versus pecl/memcache) --- lib/statusnet.php | 6 +- plugins/MemcachedPlugin.php | 223 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 plugins/MemcachedPlugin.php (limited to 'lib') diff --git a/lib/statusnet.php b/lib/statusnet.php index eba9ab9b8..ef3adebf9 100644 --- a/lib/statusnet.php +++ b/lib/statusnet.php @@ -342,7 +342,11 @@ class StatusNet if (array_key_exists('memcached', $config)) { if ($config['memcached']['enabled']) { - addPlugin('Memcache', array('servers' => $config['memcached']['server'])); + if(class_exists('Memcached')) { + addPlugin('Memcached', array('servers' => $config['memcached']['server'])); + } else { + addPlugin('Memcache', array('servers' => $config['memcached']['server'])); + } } if (!empty($config['memcached']['base'])) { diff --git a/plugins/MemcachedPlugin.php b/plugins/MemcachedPlugin.php new file mode 100644 index 000000000..707e6db9a --- /dev/null +++ b/plugins/MemcachedPlugin.php @@ -0,0 +1,223 @@ +. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou , Craig Andrews + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +if (!defined('STATUSNET')) { + // This check helps protect against security problems; + // your code file can't be executed directly from the web. + exit(1); +} + +/** + * A plugin to use memcached for the cache interface + * + * This used to be encoded as config-variable options in the core code; + * it's now broken out to a separate plugin. The same interface can be + * implemented by other plugins. + * + * @category Cache + * @package StatusNet + * @author Evan Prodromou , Craig Andrews + * @copyright 2009 StatusNet, Inc. + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + */ + +class MemcachedPlugin extends Plugin +{ + static $cacheInitialized = false; + + private $_conn = null; + public $servers = array('127.0.0.1;11211'); + + public $defaultExpiry = 86400; // 24h + + /** + * Initialize the plugin + * + * Note that onStartCacheGet() may have been called before this! + * + * @return boolean flag value + */ + + function onInitializePlugin() + { + $this->_ensureConn(); + self::$cacheInitialized = true; + return true; + } + + /** + * Get a value associated with a key + * + * The value should have been set previously. + * + * @param string &$key in; Lookup key + * @param mixed &$value out; value associated with key + * + * @return boolean hook success + */ + + function onStartCacheGet(&$key, &$value) + { + $this->_ensureConn(); + $value = $this->_conn->get($key); + Event::handle('EndCacheGet', array($key, &$value)); + return false; + } + + /** + * Associate a value with a key + * + * @param string &$key in; Key to use for lookups + * @param mixed &$value in; Value to associate + * @param integer &$flag in; Flag empty or Cache::COMPRESSED + * @param integer &$expiry in; Expiry (passed through to Memcache) + * @param boolean &$success out; Whether the set was successful + * + * @return boolean hook success + */ + + function onStartCacheSet(&$key, &$value, &$flag, &$expiry, &$success) + { + $this->_ensureConn(); + if ($expiry === null) { + $expiry = $this->defaultExpiry; + } + $success = $this->_conn->set($key, $value, $expiry); + Event::handle('EndCacheSet', array($key, $value, $flag, + $expiry)); + return false; + } + + /** + * Atomically increment an existing numeric key value. + * Existing expiration time will not be changed. + * + * @param string &$key in; Key to use for lookups + * @param int &$step in; Amount to increment (default 1) + * @param mixed &$value out; Incremented value, or false if key not set. + * + * @return boolean hook success + */ + function onStartCacheIncrement(&$key, &$step, &$value) + { + $this->_ensureConn(); + $value = $this->_conn->increment($key, $step); + Event::handle('EndCacheIncrement', array($key, $step, $value)); + return false; + } + + /** + * Delete a value associated with a key + * + * @param string &$key in; Key to lookup + * @param boolean &$success out; whether it worked + * + * @return boolean hook success + */ + + function onStartCacheDelete(&$key, &$success) + { + $this->_ensureConn(); + $success = $this->_conn->delete($key); + Event::handle('EndCacheDelete', array($key)); + return false; + } + + function onStartCacheReconnect(&$success) + { + // nothing to do + return true; + } + + /** + * Ensure that a connection exists + * + * Checks the instance $_conn variable and connects + * if it is empty. + * + * @return void + */ + + private function _ensureConn() + { + if (empty($this->_conn)) { + $this->_conn = new Memcached(common_config('site', 'nickname')); + + if (!count($this->_conn->getServerList())) { + if (is_array($this->servers)) { + $servers = $this->servers; + } else { + $servers = array($this->servers); + } + foreach ($servers as $server) { + if (strpos($server, ';') !== false) { + list($host, $port) = explode(';', $server); + } else { + $host = $server; + $port = 11211; + } + + $this->_conn->addServer($host, $port); + } + + // Compress items stored in the cache. + + // Allows the cache to store objects larger than 1MB (if they + // compress to less than 1MB), and improves cache memory efficiency. + + $this->_conn->setOption(Memcached::OPT_COMPRESSION, true); + } + } + } + + /** + * Translate general flags to Memcached-specific flags + * @param int $flag + * @return int + */ + protected function flag($flag) + { + //no flags are presently supported + return $flag; + } + + function onPluginVersion(&$versions) + { + $versions[] = array('name' => 'Memcached', + 'version' => STATUSNET_VERSION, + 'author' => 'Evan Prodromou, Craig Andrews', + 'homepage' => 'http://status.net/wiki/Plugin:Memcached', + 'rawdescription' => + _m('Use Memcached to cache query results.')); + return true; + } +} + -- cgit v1.2.3-54-g00ecf From 689e2e112bbd84ab05549b83bf99be1d8c1a39e9 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Mon, 8 Mar 2010 21:42:17 -0500 Subject: make common_copy_args() work when the post/get request includes arrays (form elements with names ending in [] having multiple values) --- lib/util.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'lib') diff --git a/lib/util.php b/lib/util.php index da2799d4f..c5dacb699 100644 --- a/lib/util.php +++ b/lib/util.php @@ -1462,7 +1462,15 @@ function common_copy_args($from) $to = array(); $strip = get_magic_quotes_gpc(); foreach ($from as $k => $v) { - $to[$k] = ($strip) ? stripslashes($v) : $v; + if($strip) { + if(is_array($v)) { + $to[$k] = common_copy_args($v); + } else { + $to[$k] = stripslashes($v); + } + } else { + $to[$k] = $v; + } } return $to; } -- cgit v1.2.3-54-g00ecf From 80a17387bfb32ec627a8df55eb902483dba1eb97 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 14:01:43 -0800 Subject: Command input processing now has centralized places for looking up notice, user/profile, and group arguments. OStatus plugin overrides these to allow using webfinger (user@example.com), profile URL (http://example.com/user) and bare profile URL (example.com/user) as arguments. --- lib/command.php | 355 +++++++++++++++++++++----------------- plugins/OStatus/OStatusPlugin.php | 80 +++++++++ 2 files changed, 279 insertions(+), 156 deletions(-) (limited to 'lib') 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 @@ 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/plugins/OStatus/OStatusPlugin.php b/plugins/OStatus/OStatusPlugin.php index bdcaae366..a97f3475b 100644 --- a/plugins/OStatus/OStatusPlugin.php +++ b/plugins/OStatus/OStatusPlugin.php @@ -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. */ -- cgit v1.2.3-54-g00ecf From 971f1f64f1f42a51bced51665ae693a9d37750a0 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Mar 2010 13:41:05 -0800 Subject: Added scripts/command.php, can be used to run commands such as subscription on behalf of users. This includes whatever support for extended command parsing plugins may have added. Example: ./scripts/command.php -nbrionv sub update@status.net --- lib/channel.php | 19 +++++++++++++ scripts/command.php | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) create mode 100755 scripts/command.php (limited to 'lib') 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/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 +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i:n:'; +$longoptions = array('id=', 'nickname='); + +$helptext = <<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)); + -- cgit v1.2.3-54-g00ecf