From d59284d42d3735e393e5e99d027136d96778600d Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 5 Mar 2010 16:52:15 -0800 Subject: [PATCH 01/26] No need to pass in $this->limit and $this-tag --- actions/userrss.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actions/userrss.php b/actions/userrss.php index 77bd316b2d..e03eb93566 100644 --- a/actions/userrss.php +++ b/actions/userrss.php @@ -41,9 +41,9 @@ class UserrssAction extends Rss10Action return false; } else { if (!empty($this->tag)) { - $this->notices = $this->getTaggedNotices($this->tag, $this->limit); + $this->notices = $this->getTaggedNotices(); } else { - $this->notices = $this->getNotices($this->limit); + $this->notices = $this->getNotices(); } return true; } From 7faf6ec75592764b829b1954352dfaf0329cdbf0 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Sun, 7 Mar 2010 23:41:55 -0500 Subject: [PATCH 02/26] add a script to import Twitter atom feed as notices --- scripts/importtwitteratom.php | 192 ++++++++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 scripts/importtwitteratom.php diff --git a/scripts/importtwitteratom.php b/scripts/importtwitteratom.php new file mode 100644 index 0000000000..7316f21080 --- /dev/null +++ b/scripts/importtwitteratom.php @@ -0,0 +1,192 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'i:n:f:'; +$longoptions = array('id=', 'nickname=', 'file='); + +$helptext = <<documentElement->namespaceURI != Activity::ATOM || + $dom->documentElement->localName != 'feed') { + throw new Exception("'$filename' is not an Atom feed."); + } + + return $dom; +} + +function importActivityStream($user, $doc) +{ + $feed = $doc->documentElement; + + $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry'); + + for ($i = $entries->length - 1; $i >= 0; $i--) { + $entry = $entries->item($i); + $activity = new Activity($entry, $feed); + $object = $activity->object; + if (!have_option('q', 'quiet')) { + print $activity->content . "\n"; + } + $html = getTweetHtml($object->link); + + $config = array('safe' => 1, + 'deny_attribute' => 'class,rel,id,style,on*'); + + $html = htmLawed($html, $config); + + $content = html_entity_decode(strip_tags($html)); + + $notice = Notice::saveNew($user->id, + $content, + 'importtwitter', + array('uri' => $object->id, + 'url' => $object->link, + 'rendered' => $html, + 'created' => common_sql_date($activity->time), + 'replies' => array(), + 'groups' => array())); + } +} + +function getTweetHtml($url) +{ + try { + $client = new HTTPClient(); + $response = $client->get($url); + } catch (HTTP_Request2_Exception $e) { + print "ERROR: HTTP response " . $e->getMessage() . "\n"; + return false; + } + + if (!$response->isOk()) { + print "ERROR: HTTP response " . $response->getCode() . "\n"; + return false; + } + + $body = $response->getBody(); + + return tweetHtmlFromBody($body); +} + +function tweetHtmlFromBody($body) +{ + $doc = DOMDocument::loadHTML($body); + $xpath = new DOMXPath($doc); + + $spans = $xpath->query('//span[@class="entry-content"]'); + + if ($spans->length == 0) { + print "ERROR: No content in tweet page.\n"; + return ''; + } + + $span = $spans->item(0); + + $children = $span->childNodes; + + $text = ''; + + for ($i = 0; $i < $children->length; $i++) { + $child = $children->item($i); + if ($child instanceof DOMElement && + $child->tagName == 'a' && + !preg_match('#^https?://#', $child->getAttribute('href'))) { + $child->setAttribute('href', 'http://twitter.com' . $child->getAttribute('href')); + } + $text .= $doc->saveXML($child); + } + + return $text; +} + +try { + + $doc = getAtomFeedDocument(); + $user = getUser(); + + importActivityStream($user, $doc); + +} catch (Exception $e) { + print $e->getMessage()."\n"; + exit(1); +} + From 5f7aa6f2e3c82b9598e3405885eb455bed9b0edc Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 8 Mar 2010 12:36:03 -0500 Subject: [PATCH 03/26] make API realm configurable --- lib/apiauth.php | 6 +++++- lib/default.php | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/apiauth.php b/lib/apiauth.php index 5090871cfe..f63c84d8f3 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 bdd78d4d86..46d3d4774f 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), ); From a77efb2447abe75d3b9902410bced61b76377de3 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 10:32:40 -0800 Subject: [PATCH 04/26] 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(-) diff --git a/lib/jabber.php b/lib/jabber.php index e1bf06ba66..db4e2e9a70 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 f376358555..cca54db08d 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(); From 217ad420ac8085fe620235dfc47bea27e4ac75dc Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 12:19:06 -0800 Subject: [PATCH 05/26] 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(-) diff --git a/lib/queued_xmpp.php b/lib/queued_xmpp.php index fdd074db29..f6bccfd5ba 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}"; } /** From 7e7d88831cf8b3e8876499b86890da2e63b08c97 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 13:32:18 -0800 Subject: [PATCH 06/26] CentOS 5.4 still bogus on a stock install. --- extlib/Auth/OpenID/Consumer.php | 3 +++ install.php | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/extlib/Auth/OpenID/Consumer.php b/extlib/Auth/OpenID/Consumer.php index 500890b656..130fe07130 100644 --- a/extlib/Auth/OpenID/Consumer.php +++ b/extlib/Auth/OpenID/Consumer.php @@ -966,6 +966,9 @@ class Auth_OpenID_GenericConsumer { // framework will not want to block on this call to // _checkAuth. if (!$this->_checkAuth($message, $server_url)) { + var_dump($message); + var_dump($server_url); + var_dump($this); return new Auth_OpenID_FailureResponse(null, "Server denied check_authentication"); } diff --git a/install.php b/install.php index 7fece8999f..929277e5e8 100644 --- a/install.php +++ b/install.php @@ -308,7 +308,7 @@ function checkPrereqs() printf('

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 our documentation page on fixing this.

'); $pass = false; From 3b0bdc0ae2730f9dc8ad4772fa5a65dae351b976 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 13:38:26 -0800 Subject: [PATCH 07/26] Revert "CentOS 5.4 still bogus on a stock install." - bad debug lines crept in This reverts commit 7e7d88831cf8b3e8876499b86890da2e63b08c97. --- extlib/Auth/OpenID/Consumer.php | 3 --- install.php | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/extlib/Auth/OpenID/Consumer.php b/extlib/Auth/OpenID/Consumer.php index 130fe07130..500890b656 100644 --- a/extlib/Auth/OpenID/Consumer.php +++ b/extlib/Auth/OpenID/Consumer.php @@ -966,9 +966,6 @@ class Auth_OpenID_GenericConsumer { // framework will not want to block on this call to // _checkAuth. if (!$this->_checkAuth($message, $server_url)) { - var_dump($message); - var_dump($server_url); - var_dump($this); return new Auth_OpenID_FailureResponse(null, "Server denied check_authentication"); } diff --git a/install.php b/install.php index 929277e5e8..7fece8999f 100644 --- a/install.php +++ b/install.php @@ -308,7 +308,7 @@ function checkPrereqs() printf('

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.4 or earlier, see our documentation page on fixing this.

'); $pass = false; From 927a368d0e4c924ec8132ff054be311e17ded45e Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 13:38:59 -0800 Subject: [PATCH 08/26] CentOS 5.4 still has bad PCRE in stock (though all bets off for PHP packages, since you'd need a version update anyway...) --- install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.php b/install.php index 7fece8999f..929277e5e8 100644 --- a/install.php +++ b/install.php @@ -308,7 +308,7 @@ function checkPrereqs() printf('

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 our documentation page on fixing this.

'); $pass = false; From 49722f65502b9730f1c1434851e21d02b8b2fd41 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 8 Mar 2010 22:53:43 +0000 Subject: [PATCH 09/26] Only allow RSSCloud subs to canonical RSS2 profile feeds --- plugins/RSSCloud/RSSCloudRequestNotify.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index d76c08d379..0305295348 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 . '(?.*)\.rss$%'; + $valid = '%^' . $path . '(?.*)\.rss$%'; if (preg_match($valid, $feed, $matches)) { - $user = User::staticGet('nickname', $matches['nickname']); + $user = User::staticGet('id', $matches['id']); if (!empty($user)) { return $user; } From 0d66dc543d368092de08b49857c67248210d8d84 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Mon, 8 Mar 2010 18:06:21 -0500 Subject: [PATCH 10/26] an otp is a real login --- actions/otp.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actions/otp.php b/actions/otp.php index acf84aee81..1e06603d43 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); } From 691c88bce8fa37d1d371988857645b6cdd9994d9 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Mon, 8 Mar 2010 22:53:43 +0000 Subject: [PATCH 11/26] Only allow RSSCloud subs to canonical RSS2 profile feeds --- plugins/RSSCloud/RSSCloudRequestNotify.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/plugins/RSSCloud/RSSCloudRequestNotify.php b/plugins/RSSCloud/RSSCloudRequestNotify.php index d76c08d379..0305295348 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 . '(?.*)\.rss$%'; + $valid = '%^' . $path . '(?.*)\.rss$%'; if (preg_match($valid, $feed, $matches)) { - $user = User::staticGet('nickname', $matches['nickname']); + $user = User::staticGet('id', $matches['id']); if (!empty($user)) { return $user; } From 7214db14fe37798623acab2e6f5755d05bacbc8f Mon Sep 17 00:00:00 2001 From: James Walker Date: Tue, 9 Mar 2010 01:24:21 -0500 Subject: [PATCH 12/26] wrong param order to strpos() --- plugins/OStatus/classes/HubSub.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/OStatus/classes/HubSub.php b/plugins/OStatus/classes/HubSub.php index 3120a70f9f..c420b3eef8 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 .= '?'; From 72e4c733735ac590599bef392611314370135b37 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 9 Mar 2010 11:06:08 +0000 Subject: [PATCH 13/26] Use canonical URL for notification in RSSCloud plugin --- plugins/RSSCloud/RSSCloudNotifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index d454691c80..9e7b536803 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(); From 7afad469c200d9d57af75fc667f05041bec137b6 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 9 Mar 2010 11:06:08 +0000 Subject: [PATCH 14/26] Use canonical URL for notification in RSSCloud plugin --- plugins/RSSCloud/RSSCloudNotifier.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/RSSCloud/RSSCloudNotifier.php b/plugins/RSSCloud/RSSCloudNotifier.php index d454691c80..9e7b536803 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(); From 61434ebaa291ebd11cd1423ec36bb66073d55a74 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Tue, 9 Mar 2010 10:55:48 -0500 Subject: [PATCH 15/26] a script to flush site --- scripts/flushsite.php | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 scripts/flushsite.php diff --git a/scripts/flushsite.php b/scripts/flushsite.php new file mode 100644 index 0000000000..b7f385ac45 --- /dev/null +++ b/scripts/flushsite.php @@ -0,0 +1,45 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$shortoptions = 'd'; +$longoptions = array('delete'); + +$helptext = << +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 From 80a17387bfb32ec627a8df55eb902483dba1eb97 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 8 Mar 2010 14:01:43 -0800 Subject: [PATCH 16/26] 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(-) diff --git a/lib/command.php b/lib/command.php index ea7b60372d..9d550550f7 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 = $this->getProfile($this->other); - $target = - common_relative_profile($this->user, $target_nickname); - - 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 bdcaae366b..a97f3475b2 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. */ From 971f1f64f1f42a51bced51665ae693a9d37750a0 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Mar 2010 13:41:05 -0800 Subject: [PATCH 17/26] 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 diff --git a/lib/channel.php b/lib/channel.php index 3cd168786c..689bca0be9 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 0000000000..6041b02eb1 --- /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)); + From 2c6eb770457b5e763a2ca960dcde11201c08952f Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Tue, 9 Mar 2010 11:08:21 -0500 Subject: [PATCH 18/26] Added a checkbox for subscribing the admin of a StatusNet instance to update@status.net. Checked by default. Subscription optional. --- install.php | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/install.php b/install.php index 929277e5e8..fbedbaf017 100644 --- a/install.php +++ b/install.php @@ -483,6 +483,7 @@ function showForm() $dbRadios .= " $info[name]
\n"; } } + echo<< @@ -559,6 +560,11 @@ function showForm()

Optional email address for the initial StatusNet user (administrator)

+
  • + + +

    Release and security feed from update@status.net (recommended)

    +
  • @@ -587,6 +593,7 @@ function handlePost() $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); @@ -657,7 +664,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 +861,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 +889,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()); From 60e6172bc9e52f1e6b4941811e2d6fd6050c1c6b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Mar 2010 14:15:55 -0800 Subject: [PATCH 19/26] Check for invalid and reserved usernames for the admin user at install time. --- install.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/install.php b/install.php index fbedbaf017..9a7e27fa2c 100644 --- a/install.php +++ b/install.php @@ -589,7 +589,7 @@ 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']; @@ -630,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); From b98f956c6b1a4b2754e13dac7f2a72be7412dd24 Mon Sep 17 00:00:00 2001 From: Michele Date: Sun, 17 Jan 2010 18:33:56 +0100 Subject: [PATCH 20/26] API config return textlimit value --- actions/apistatusnetconfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/apistatusnetconfig.php b/actions/apistatusnetconfig.php index 296376d195..51400dfc98 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'), From 60e0f0426133544eaaea7ff84da5f02ca86bd8cc Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Mar 2010 17:38:16 +0100 Subject: [PATCH 21/26] Ticket #2210: adjust locale setup fallback to try more locales on the system if en_US isn't available. We just need *something* other than C or POSIX to let gettext initialize itself, apparently... Gets Spanish, French, Russian etc UI localization working on Debian Lenny fresh installation set up in Spanish (so es_ES.UTF-8 is available but en_US.UTF-8 isn't). --- lib/util.php | 38 ++++++++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/lib/util.php b/lib/util.php index da2799d4f9..76639e2d40 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'); From 58192ad68758437a37d8af19d6676d35699ed070 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 9 Mar 2010 10:56:33 -0800 Subject: [PATCH 22/26] OStatus: fix exception thrown on HTTP error during feed discovery --- plugins/OStatus/lib/feeddiscovery.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/OStatus/lib/feeddiscovery.php b/plugins/OStatus/lib/feeddiscovery.php index 7afb71bdc1..ff76b229e7 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(); From 1d1bf69f9a0520f1948f15107808cb1846850076 Mon Sep 17 00:00:00 2001 From: Michele Date: Sun, 17 Jan 2010 18:33:56 +0100 Subject: [PATCH 23/26] API config return textlimit value --- actions/apistatusnetconfig.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/apistatusnetconfig.php b/actions/apistatusnetconfig.php index 296376d195..51400dfc98 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'), From 7f2253759ccdc5ab8698c447b29762314883db1a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 10 Mar 2010 03:39:05 +0000 Subject: [PATCH 24/26] A blank username should never be allowed. --- lib/apiauth.php | 2 +- lib/util.php | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/apiauth.php b/lib/apiauth.php index f63c84d8f3..32502399f9 100644 --- a/lib/apiauth.php +++ b/lib/apiauth.php @@ -241,7 +241,7 @@ class ApiAuthAction extends ApiAction $realm = common_config('site', 'name') . ' API'; } - if (!isset($this->auth_user_nickname) && $required) { + if (empty($this->auth_user_nickname) && $required) { header('WWW-Authenticate: Basic realm="' . $realm . '"'); // show error if the user clicks 'cancel' diff --git a/lib/util.php b/lib/util.php index 76639e2d40..44ccc0deff 100644 --- a/lib/util.php +++ b/lib/util.php @@ -159,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))) { From 8ee8b89dd8b3483724aa52a812eef89b8bfae38b Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 10 Mar 2010 09:36:00 -0800 Subject: [PATCH 25/26] Ticket #2221: fix for missing whitespace between messages in en-gb. The final whitespace should be dropped from the source messages after we've stabilized; trailing space is pretty unreliable to keep through translation tools and should be avoided. Use separator strings outside the messages! --- lib/action.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/action.php b/lib/action.php index 816086d202..9884f529c9 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); From 69b2f19b6fef793aa607c6d8f4590b93e2565626 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 10 Mar 2010 10:06:46 -0800 Subject: [PATCH 26/26] RequireValidatedEmailPlugin fixes: require email on registration form, tidy up i18n infrastructure. --- plugins/RequireValidatedEmail/README | 2 -- .../RequireValidatedEmailPlugin.php | 23 +++++++++++++- .../locale/RequireValidatedEmail.po | 31 +++++++++++++++++++ 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 plugins/RequireValidatedEmail/locale/RequireValidatedEmail.po diff --git a/plugins/RequireValidatedEmail/README b/plugins/RequireValidatedEmail/README index ccd94d271d..46ee24d5fe 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 3581f1de92..ccefa14f62 100644 --- a/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php +++ b/plugins/RequireValidatedEmail/RequireValidatedEmailPlugin.php @@ -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 index 0000000000..49ac4f6f4d --- /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 , 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 \n" +"Language-Team: LANGUAGE \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 ""