From 5697e4edb0fc4bb1d4c9365100501e795b2553de Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Mon, 22 Mar 2010 10:35:54 -0700 Subject: [PATCH 01/15] Replace the "give up and dump object" attachment view fallback with a client-side redirect to the target URL, which will at least be useful. --- lib/attachmentlist.php | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/attachmentlist.php b/lib/attachmentlist.php index 51ceca8576..fe38281af9 100644 --- a/lib/attachmentlist.php +++ b/lib/attachmentlist.php @@ -306,7 +306,7 @@ class Attachment extends AttachmentListItem function showRepresentation() { if (empty($this->oembed->type)) { if (empty($this->attachment->mimetype)) { - $this->out->element('pre', null, 'oh well... not sure how to handle the following: ' . print_r($this->attachment, true)); + $this->showFallback(); } else { switch ($this->attachment->mimetype) { case 'image/gif': @@ -332,6 +332,8 @@ class Attachment extends AttachmentListItem $this->out->element('param', array('name' => 'autoStart', 'value' => 1)); $this->out->elementEnd('object'); break; + default: + $this->showFallback(); } } } else { @@ -354,9 +356,23 @@ class Attachment extends AttachmentListItem break; default: - $this->out->element('pre', null, 'oh well... not sure how to handle the following oembed: ' . print_r($this->oembed, true)); + $this->showFallback(); } } } + + function showFallback() + { + // If we don't know how to display an attachment inline, we probably + // shouldn't have gotten to this point. + // + // But, here we are... displaying details on a file or remote URL + // either on the main view or in an ajax-loaded lightbox. As a lesser + // of several evils, we'll try redirecting to the actual target via + // client-side JS. + + common_log(LOG_ERR, "Empty or unknown type for file id {$this->attachment->id}; falling back to client-side redirect."); + $this->out->raw(''); + } } From 80b16c8499d0cfdb4deb442ba18345befed4e29d Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 09:50:01 -0700 Subject: [PATCH 02/15] Don't add PHPSESSID parameter onto notice and conversation URIs if we save a notice during a session override. This was being triggered by welcomebot messages created at account creation time, then propagated through replies. --- classes/Conversation.php | 3 ++- lib/util.php | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/classes/Conversation.php b/classes/Conversation.php index ea8bd87b56..f540004ef3 100755 --- a/classes/Conversation.php +++ b/classes/Conversation.php @@ -63,7 +63,8 @@ class Conversation extends Memcached_DataObject } $orig = clone($conv); - $orig->uri = common_local_url('conversation', array('id' => $id)); + $orig->uri = common_local_url('conversation', array('id' => $id), + null, null, false); $result = $orig->update($conv); if (empty($result)) { diff --git a/lib/util.php b/lib/util.php index 44ccc0deff..3d4ed087f9 100644 --- a/lib/util.php +++ b/lib/util.php @@ -1521,7 +1521,8 @@ function common_user_uri(&$user) function common_notice_uri(&$notice) { return common_local_url('shownotice', - array('notice' => $notice->id)); + array('notice' => $notice->id), + null, null, false); } // 36 alphanums - lookalikes (0, O, 1, I) = 32 chars = 5 bits From 44caa3a93f452777c795006edb52ef4c5c2c4997 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 11:06:37 -0700 Subject: [PATCH 03/15] Consistently send Profiles into Fave::addNew() --- actions/apifavoritecreate.php | 2 +- classes/Fave.php | 10 +++++++++- lib/command.php | 2 +- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/actions/apifavoritecreate.php b/actions/apifavoritecreate.php index 3618f94018..00b6349b0a 100644 --- a/actions/apifavoritecreate.php +++ b/actions/apifavoritecreate.php @@ -123,7 +123,7 @@ class ApiFavoriteCreateAction extends ApiAuthAction return; } - $fave = Fave::addNew($this->user, $this->notice); + $fave = Fave::addNew($this->user->getProfile(), $this->notice); if (empty($fave)) { $this->clientError( diff --git a/classes/Fave.php b/classes/Fave.php index a04f15e9c4..7ca9ade7f0 100644 --- a/classes/Fave.php +++ b/classes/Fave.php @@ -21,7 +21,15 @@ class Fave extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - static function addNew($profile, $notice) { + /** + * Save a favorite record. + * @fixme post-author notification should be moved here + * + * @param Profile $profile the local or remote user who likes + * @param Notice $notice the notice that is liked + * @return mixed false on failure, or Fave record on success + */ + static function addNew(Profile $profile, Notice $notice) { $fave = null; diff --git a/lib/command.php b/lib/command.php index 9d550550f7..8080fb8bc0 100644 --- a/lib/command.php +++ b/lib/command.php @@ -273,7 +273,7 @@ class FavCommand extends Command function handle($channel) { $notice = $this->getNotice($this->other); - $fave = Fave::addNew($this->user, $notice); + $fave = Fave::addNew($this->user->getProfile(), $notice); if (!$fave) { $channel->error($this->user, _('Could not create favorite.')); From 7dc24b4ca7dffda85338d35da3618ec50ce0dbf7 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 13:10:23 -0700 Subject: [PATCH 04/15] FOAF was missing OStatus remote subscriptions, now fixed. --- actions/foaf.php | 68 ++++++++++++++++-------------------------------- 1 file changed, 23 insertions(+), 45 deletions(-) diff --git a/actions/foaf.php b/actions/foaf.php index fc2ec9b12f..fc56e19b4f 100644 --- a/actions/foaf.php +++ b/actions/foaf.php @@ -162,40 +162,29 @@ class FoafAction extends Action if ($sub->find()) { while ($sub->fetch()) { - if ($sub->token) { - $other = Remote_profile::staticGet('id', $sub->subscriber); - $profile = Profile::staticGet('id', $sub->subscriber); - } else { - $other = User::staticGet('id', $sub->subscriber); - $profile = Profile::staticGet('id', $sub->subscriber); - } - if (!$other) { + $profile = Profile::staticGet('id', $sub->subscriber); + if (empty($profile)) { common_debug('Got a bad subscription: '.print_r($sub,true)); continue; } - if (array_key_exists($other->uri, $person)) { - $person[$other->uri][0] = BOTH; + $user = $profile->getUser(); + $other_uri = $profile->getUri(); + if (array_key_exists($other_uri, $person)) { + $person[$other_uri][0] = BOTH; } else { - $person[$other->uri] = array(LISTENER, - $other->id, - $profile->nickname, - (empty($sub->token)) ? 'User' : 'Remote_profile'); + $person[$other_uri] = array(LISTENER, + $profile->id, + $profile->nickname, + $user ? 'local' : 'remote'); } - $other->free(); - $other = null; - unset($other); - $profile->free(); - $profile = null; unset($profile); } } - $sub->free(); - $sub = null; unset($sub); foreach ($person as $uri => $p) { - list($type, $id, $nickname, $cls) = $p; + list($type, $id, $nickname, $local) = $p; if ($type == BOTH) { $this->element('knows', array('rdf:resource' => $uri)); } @@ -206,8 +195,8 @@ class FoafAction extends Action foreach ($person as $uri => $p) { $foaf_url = null; - list($type, $id, $nickname, $cls) = $p; - if ($cls == 'User') { + list($type, $id, $nickname, $local) = $p; + if ($local == 'local') { $foaf_url = common_local_url('foaf', array('nickname' => $nickname)); } $profile = Profile::staticGet($id); @@ -216,7 +205,7 @@ class FoafAction extends Action $this->element('knows', array('rdf:resource' => $this->user->uri)); } $this->showMicrobloggingAccount($profile, - ($cls == 'User') ? common_root_url() : null, + ($local == 'local') ? common_root_url() : null, $uri, true); if ($foaf_url) { @@ -275,33 +264,22 @@ class FoafAction extends Action if ($sub->find()) { while ($sub->fetch()) { - if (!empty($sub->token)) { - $other = Remote_profile::staticGet('id', $sub->subscribed); - $profile = Profile::staticGet('id', $sub->subscribed); - } else { - $other = User::staticGet('id', $sub->subscribed); - $profile = Profile::staticGet('id', $sub->subscribed); - } - if (empty($other)) { + $profile = Profile::staticGet('id', $sub->subscribed); + if (empty($profile)) { common_debug('Got a bad subscription: '.print_r($sub,true)); continue; } - $this->element('sioc:follows', array('rdf:resource' => $other->uri.'#acct')); - $person[$other->uri] = array(LISTENEE, - $other->id, - $profile->nickname, - (empty($sub->token)) ? 'User' : 'Remote_profile'); - $other->free(); - $other = null; - unset($other); - $profile->free(); - $profile = null; + $user = $profile->getUser(); + $other_uri = $profile->getUri(); + $this->element('sioc:follows', array('rdf:resource' => $other_uri.'#acct')); + $person[$other_uri] = array(LISTENEE, + $profile->id, + $profile->nickname, + $user ? 'local' : 'remote'); unset($profile); } } - $sub->free(); - $sub = null; unset($sub); } From 5f32cf32cd7d4a5df7ba64d4f1e7d9edee8d418c Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 14:18:45 -0700 Subject: [PATCH 05/15] Don't spew XML parse warnings to output when checking a remote XRD page --- plugins/OStatus/lib/xrd.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/plugins/OStatus/lib/xrd.php b/plugins/OStatus/lib/xrd.php index aa13ef0242..34b28790b7 100644 --- a/plugins/OStatus/lib/xrd.php +++ b/plugins/OStatus/lib/xrd.php @@ -53,7 +53,14 @@ class XRD $xrd = new XRD(); $dom = new DOMDocument(); - if (!$dom->loadXML($xml)) { + + // Don't spew XML warnings to output + $old = error_reporting(); + error_reporting($old & ~E_WARNING); + $ok = $dom->loadXML($xml); + error_reporting($old); + + if (!$ok) { throw new Exception("Invalid XML"); } $xrd_element = $dom->getElementsByTagName('XRD')->item(0); From df8c9090c0deabe20b804e0fd0766d6b86b7968f Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 14:19:12 -0700 Subject: [PATCH 06/15] Add basic subscribe/unsubscribe test to OStatus test cases --- plugins/OStatus/tests/remote-tests.php | 176 ++++++++++++++++++++++--- 1 file changed, 157 insertions(+), 19 deletions(-) diff --git a/plugins/OStatus/tests/remote-tests.php b/plugins/OStatus/tests/remote-tests.php index 103ca066c0..b064114911 100644 --- a/plugins/OStatus/tests/remote-tests.php +++ b/plugins/OStatus/tests/remote-tests.php @@ -40,6 +40,20 @@ class TestBase } return true; } + + function assertTrue($a) + { + if (!$a) { + throw new Exception("Failed to assert true: got false"); + } + } + + function assertFalse($a) + { + if ($a) { + throw new Exception("Failed to assert false: got true"); + } + } } class OStatusTester extends TestBase @@ -60,8 +74,12 @@ class OStatusTester extends TestBase function run() { $this->setup(); + $this->testLocalPost(); $this->testMentionUrl(); + $this->testSubscribe(); + $this->testUnsubscribe(); + $this->log("DONE!"); } @@ -98,6 +116,25 @@ class OStatusTester extends TestBase $post = $this->pub->post("@$base/$name should have this in home and replies"); $this->sub->assertReceived($post); } + + function testSubscribe() + { + $this->assertFalse($this->sub->hasSubscription($this->pub->getProfileUri())); + $this->assertFalse($this->pub->hasSubscriber($this->sub->getProfileUri())); + $this->sub->subscribe($this->pub->getProfileLink()); + $this->assertTrue($this->sub->hasSubscription($this->pub->getProfileUri())); + $this->assertTrue($this->pub->hasSubscriber($this->sub->getProfileUri())); + } + + function testUnsubscribe() + { + $this->assertTrue($this->sub->hasSubscription($this->pub->getProfileUri())); + $this->assertTrue($this->pub->hasSubscriber($this->sub->getProfileUri())); + $this->sub->unsubscribe($this->pub->getProfileLink()); + $this->assertFalse($this->sub->hasSubscription($this->pub->getProfileUri())); + $this->assertFalse($this->pub->hasSubscriber($this->sub->getProfileUri())); + } + } class SNTestClient extends TestBase @@ -202,6 +239,43 @@ class SNTestClient extends TestBase return $dom; } + protected function parseXml($path, $body) + { + $dom = new DOMDocument(); + if ($dom->loadXML($body)) { + return $dom; + } else { + throw new Exception("Bogus XML data from $path:\n$body"); + } + } + + /** + * Make a hit to a REST-y XML page on the site, without authentication. + * @param string $path URL fragment for something relative to base + * @param array $params POST parameters to send + * @return DOMDocument + * @throws Exception on low-level error conditions + */ + protected function xml($path, $params=array()) + { + $response = $this->hit($path, $params, true); + $body = $response->getBody(); + return $this->parseXml($path, $body); + } + + protected function parseJson($path, $body) + { + $data = json_decode($body, true); + if ($data !== null) { + if (!empty($data['error'])) { + throw new Exception("JSON API returned error: " . $data['error']); + } + return $data; + } else { + throw new Exception("Bogus JSON data from $path:\n$body"); + } + } + /** * Make an API hit to this site, with authentication. * @param string $path URL fragment for something under 'api' folder @@ -215,22 +289,9 @@ class SNTestClient extends TestBase $response = $this->hit("api/$path.$style", $params, true); $body = $response->getBody(); if ($style == 'json') { - $data = json_decode($body, true); - if ($data !== null) { - if (!empty($data['error'])) { - throw new Exception("JSON API returned error: " . $data['error']); - } - return $data; - } else { - throw new Exception("Bogus JSON data from $path:\n$body"); - } + return $this->parseJson($path, $body); } else if ($style == 'xml' || $style == 'atom') { - $dom = new DOMDocument(); - if ($dom->loadXML($body)) { - return $dom; - } else { - throw new Exception("Bogus XML data from $path:\n$body"); - } + return $this->parseXml($path, $body); } else { throw new Exception("API needs to be JSON, XML, or Atom"); } @@ -257,6 +318,24 @@ class SNTestClient extends TestBase 'submit' => 'Register')); } + /** + * @return string canonical URI/URL to profile page + */ + function getProfileUri() + { + $data = $this->api('account/verify_credentials', 'json'); + $id = $data['id']; + return $this->basepath . '/user/' . $id; + } + + /** + * @return string human-friendly URL to profile page + */ + function getProfileLink() + { + return $this->basepath . '/' . $this->username; + } + /** * Check that the account has been registered and can be used. * On failure, throws a test failure exception. @@ -349,22 +428,81 @@ class SNTestClient extends TestBase return false; } + /** + * @param string $profile user page link or webfinger + */ + function subscribe($profile) + { + // This uses the command interface, since there's not currently + // a friendly Twit-API way to do a fresh remote subscription and + // the web form's a pain to use. + $this->post('follow ' . $profile); + } + + /** + * @param string $profile user page link or webfinger + */ + function unsubscribe($profile) + { + // This uses the command interface, since there's not currently + // a friendly Twit-API way to do a fresh remote subscription and + // the web form's a pain to use. + $this->post('leave ' . $profile); + } + /** * Check that this account is subscribed to the given profile. * @param string $profile_uri URI for the profile to check for + * @return boolean */ - function assertHasSubscription($profile_uri) + function hasSubscription($profile_uri) { - throw new Exception('tbi'); + $this->log("Checking if $this->username has a subscription to $profile_uri"); + + $me = $this->getProfileUri(); + return $this->checkSubscription($me, $profile_uri); } /** * Check that this account is subscribed to by the given profile. * @param string $profile_uri URI for the profile to check for + * @return boolean */ - function assertHasSubscriber($profile_uri) + function hasSubscriber($profile_uri) { - throw new Exception('tbi'); + $this->log("Checking if $this->username is subscribed to by $profile_uri"); + + $me = $this->getProfileUri(); + return $this->checkSubscription($profile_uri, $me); + } + + protected function checkSubscription($subscriber, $subscribed) + { + // Using FOAF as the API methods for checking the social graph + // currently are unfriendly to remote profiles + $ns_foaf = 'http://xmlns.com/foaf/0.1/'; + $ns_sioc = 'http://rdfs.org/sioc/ns#'; + $ns_rdf = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#'; + + $dom = $this->xml($this->username . '/foaf'); + $agents = $dom->getElementsByTagNameNS($ns_foaf, 'Agent'); + foreach ($agents as $agent) { + $agent_uri = $agent->getAttributeNS($ns_rdf, 'about'); + if ($agent_uri == $subscriber) { + $follows = $agent->getElementsByTagNameNS($ns_sioc, 'follows'); + foreach ($follows as $follow) { + $target = $follow->getAttributeNS($ns_rdf, 'resource'); + if ($target == ($subscribed . '#acct')) { + $this->log("Confirmed $subscriber subscribed to $subscribed"); + return true; + } + } + $this->log("We found $subscriber but they don't follow $subscribed"); + return false; + } + } + $this->log("Can't find $subscriber in {$this->username}'s social graph."); + return false; } } From 13d59e0c76b887a2bfd2e5cfcc2e0fedf728bc07 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 17:24:01 -0700 Subject: [PATCH 07/15] fixup_deletions.php script to look for notices posted by now-deleted profiles and remove them. --- classes/Notice.php | 4 +- scripts/fixup_deletions.php | 166 ++++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+), 1 deletion(-) create mode 100755 scripts/fixup_deletions.php diff --git a/classes/Notice.php b/classes/Notice.php index f7194e3394..be3e9ca2a6 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -421,7 +421,9 @@ class Notice extends Memcached_DataObject } $profile = Profile::staticGet($this->profile_id); - $profile->blowNoticeCount(); + if (!empty($profile)) { + $profile->blowNoticeCount(); + } } /** diff --git a/scripts/fixup_deletions.php b/scripts/fixup_deletions.php new file mode 100755 index 0000000000..07ada7f9d9 --- /dev/null +++ b/scripts/fixup_deletions.php @@ -0,0 +1,166 @@ +#!/usr/bin/env php +. + */ + +define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); + +$longoptions = array('dry-run', 'start=', 'end='); + +$helptext = <<query($query); + + if ($profile->fetch()) { + return intval($profile->id); + } else { + die("Something went awry; could not look up max used profile_id."); + } +} + +/** + * Check for profiles in the given id range that are missing, presumed deleted. + * + * @param int $start beginning profile.id, inclusive + * @param int $end final profile.id, inclusive + * @return array of integer profile.ids + * @access private + */ +function get_missing_profiles($start, $end) +{ + $query = sprintf("SELECT id FROM profile WHERE id BETWEEN %d AND %d", + $start, $end); + + $profile = new Profile(); + $profile->query($query); + + $all = range($start, $end); + $known = array(); + while ($row = $profile->fetch()) { + $known[] = intval($profile->id); + } + unset($profile); + + $missing = array_diff($all, $known); + return $missing; +} + +/** + * Look for stray notices from this profile and, if present, kill them. + * + * @param int $profile_id + * @param bool $dry if true, we won't delete anything + */ +function cleanup_missing_profile($profile_id, $dry) +{ + $notice = new Notice(); + $notice->profile_id = $profile_id; + $notice->find(); + if ($notice->N == 0) { + return; + } + + $s = ($notice->N == 1) ? '' : 's'; + print "Deleted profile $profile_id has $notice->N stray notice$s:\n"; + + while ($notice->fetch()) { + print " notice $notice->id"; + if ($dry) { + print " (skipped; dry run)\n"; + } else { + $victim = clone($notice); + try { + $victim->delete(); + print " (deleted)\n"; + } catch (Exception $e) { + print " FAILED: "; + print $e->getMessage(); + print "\n"; + } + } + } +} + +$dry = have_option('dry-run'); + +$max_profile_id = get_max_profile_id(); +$chunk = 1000; + +if (have_option('start')) { + $begin = intval(get_option_value('start')); +} else { + $begin = 1; +} +if (have_option('end')) { + $final = min($max_profile_id, intval(get_option_value('end'))); +} else { + $final = $max_profile_id; +} + +if ($begin < 1) { + die("Silly human, you can't begin before profile number 1!\n"); +} +if ($final < $begin) { + die("Silly human, you can't end at $final if it's before $begin!\n"); +} + +// Identify missing profiles... +for ($start = $begin; $start <= $final; $start += $chunk) { + $end = min($start + $chunk - 1, $final); + + print "Checking for missing profiles between id $start and $end"; + if ($dry) { + print " (dry run)"; + } + print "...\n"; + $missing = get_missing_profiles($start, $end); + + foreach ($missing as $profile_id) { + cleanup_missing_profile($profile_id, $dry); + } +} + +echo "done.\n"; + From 9380eed794e1bd419a4af4dcbbcd176f164112fc Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 18:44:54 -0700 Subject: [PATCH 08/15] add a general PuSHed post and an @-reply back to a subscribee by name to OStatus remote test cases --- plugins/OStatus/tests/remote-tests.php | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/plugins/OStatus/tests/remote-tests.php b/plugins/OStatus/tests/remote-tests.php index b064114911..a27ecb854f 100644 --- a/plugins/OStatus/tests/remote-tests.php +++ b/plugins/OStatus/tests/remote-tests.php @@ -78,6 +78,8 @@ class OStatusTester extends TestBase $this->testLocalPost(); $this->testMentionUrl(); $this->testSubscribe(); + $this->testPush(); + $this->testMentionSubscribee(); $this->testUnsubscribe(); $this->log("DONE!"); @@ -126,6 +128,26 @@ class OStatusTester extends TestBase $this->assertTrue($this->pub->hasSubscriber($this->sub->getProfileUri())); } + function testPush() + { + $this->assertTrue($this->sub->hasSubscription($this->pub->getProfileUri())); + $this->assertTrue($this->pub->hasSubscriber($this->sub->getProfileUri())); + + $name = $this->sub->username; + $post = $this->pub->post("Regular post, which $name should get via PuSH"); + $this->sub->assertReceived($post); + } + + function testMentionSubscribee() + { + $this->assertTrue($this->sub->hasSubscription($this->pub->getProfileUri())); + $this->assertFalse($this->pub->hasSubscription($this->sub->getProfileUri())); + + $name = $this->pub->username; + $post = $this->sub->post("Just a quick note back to my remote subscribee @$name"); + $this->pub->assertReceived($post); + } + function testUnsubscribe() { $this->assertTrue($this->sub->hasSubscription($this->pub->getProfileUri())); From fcf86b4fdf200b1f2955f4f93c5b85054c7254b7 Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Tue, 23 Mar 2010 18:56:40 -0700 Subject: [PATCH 09/15] Improve legibility of OStatus remote tests output --- plugins/OStatus/tests/remote-tests.php | 29 ++++++++++++++------------ 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/plugins/OStatus/tests/remote-tests.php b/plugins/OStatus/tests/remote-tests.php index a27ecb854f..24b4b1660a 100644 --- a/plugins/OStatus/tests/remote-tests.php +++ b/plugins/OStatus/tests/remote-tests.php @@ -75,13 +75,16 @@ class OStatusTester extends TestBase { $this->setup(); - $this->testLocalPost(); - $this->testMentionUrl(); - $this->testSubscribe(); - $this->testPush(); - $this->testMentionSubscribee(); - $this->testUnsubscribe(); + $methods = get_class_methods($this); + foreach ($methods as $method) { + if (strtolower(substr($method, 0, 4)) == 'test') { + print "\n"; + print "== $method ==\n"; + call_user_func(array($this, $method)); + } + } + print "\n"; $this->log("DONE!"); } @@ -372,6 +375,7 @@ class SNTestClient extends TestBase $this->assertEqual($this->fullname, $data['name']); $this->assertEqual($this->homepage, $data['url']); $this->assertEqual($this->bio, $data['description']); + $this->log(" looks good!"); } /** @@ -408,11 +412,11 @@ class SNTestClient extends TestBase } $tries--; if ($tries) { - $this->log("Didn't see it yet, waiting $timeout seconds"); + $this->log(" didn't see it yet, waiting $timeout seconds"); sleep($timeout); } } - throw new Exception("Message $notice_uri not received by $this->username"); + throw new Exception(" message $notice_uri not received by $this->username"); } /** @@ -442,10 +446,9 @@ class SNTestClient extends TestBase } foreach ($entries as $entry) { if ($entry->id == $notice_uri) { - $this->log("found it $notice_uri"); + $this->log(" found it $notice_uri"); return true; } - //$this->log("nope... " . $entry->id); } return false; } @@ -515,15 +518,15 @@ class SNTestClient extends TestBase foreach ($follows as $follow) { $target = $follow->getAttributeNS($ns_rdf, 'resource'); if ($target == ($subscribed . '#acct')) { - $this->log("Confirmed $subscriber subscribed to $subscribed"); + $this->log(" confirmed $subscriber subscribed to $subscribed"); return true; } } - $this->log("We found $subscriber but they don't follow $subscribed"); + $this->log(" we found $subscriber but they don't follow $subscribed"); return false; } } - $this->log("Can't find $subscriber in {$this->username}'s social graph."); + $this->log(" can't find $subscriber in {$this->username}'s social graph."); return false; } From 7b1b6045e61973b8835e7253d6b532a752535297 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 24 Mar 2010 00:00:55 -0700 Subject: [PATCH 10/15] Look for the first object in the Activity --- plugins/OStatus/actions/usersalmon.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugins/OStatus/actions/usersalmon.php b/plugins/OStatus/actions/usersalmon.php index ecdcfa1939..6c360c49f9 100644 --- a/plugins/OStatus/actions/usersalmon.php +++ b/plugins/OStatus/actions/usersalmon.php @@ -92,7 +92,7 @@ class UsersalmonAction extends SalmonAction throw new ClientException("Not to anyone in reply to anything!"); } - $existing = Notice::staticGet('uri', $this->act->object->id); + $existing = Notice::staticGet('uri', $this->act->objects[0]->id); if (!empty($existing)) { common_log(LOG_ERR, "Not saving notice '{$existing->uri}'; already exists."); @@ -143,7 +143,7 @@ class UsersalmonAction extends SalmonAction function handleFavorite() { - $notice = $this->getNotice($this->act->object); + $notice = $this->getNotice($this->act->objects[0]); $profile = $this->ensureProfile()->localProfile(); $old = Fave::pkeyGet(array('user_id' => $profile->id, @@ -164,7 +164,7 @@ class UsersalmonAction extends SalmonAction */ function handleUnfavorite() { - $notice = $this->getNotice($this->act->object); + $notice = $this->getNotice($this->act->objects[0]); $profile = $this->ensureProfile()->localProfile(); $fave = Fave::pkeyGet(array('user_id' => $profile->id, From 10410907a0a6f1af9fb18cb3341db792baa49cf3 Mon Sep 17 00:00:00 2001 From: James Walker Date: Wed, 24 Mar 2010 14:27:35 -0400 Subject: [PATCH 11/15] A bit safer checking in the keypair parsing --- plugins/OStatus/lib/magicenvelope.php | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/plugins/OStatus/lib/magicenvelope.php b/plugins/OStatus/lib/magicenvelope.php index 9266cab5cf..799b5e3079 100644 --- a/plugins/OStatus/lib/magicenvelope.php +++ b/plugins/OStatus/lib/magicenvelope.php @@ -59,12 +59,21 @@ class MagicEnvelope } if ($xrd->links) { if ($link = Discovery::getService($xrd->links, Magicsig::PUBLICKEYREL)) { - list($type, $keypair) = explode(',', $link['href']); - if (empty($keypair)) { + $keypair = false; + $parts = explode(',', $link['href']); + if (count($parts) == 2) { + $keypair = $parts[1]; + } else { // Backwards compatibility check for separator bug in 0.9.0 - list($type, $keypair) = explode(';', $link['href']); + $parts = explode(';', $link['href']); + if (count($parts) == 2) { + $keypair = $parts[1]; + } + } + + if ($keypair) { + return $keypair; } - return $keypair; } } throw new Exception('Unable to locate signer public key'); From c4273f0ef32f65267ddf43dc5dc6977659a0697e Mon Sep 17 00:00:00 2001 From: James Walker Date: Wed, 24 Mar 2010 15:15:20 -0400 Subject: [PATCH 12/15] Check for 0.9.0 bad keys from old Crypt_RSA library --- plugins/OStatus/classes/Magicsig.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index 87c684c93d..1a95414958 100644 --- a/plugins/OStatus/classes/Magicsig.php +++ b/plugins/OStatus/classes/Magicsig.php @@ -50,7 +50,15 @@ class Magicsig extends Memcached_DataObject { $obj = parent::staticGet(__CLASS__, $k, $v); if (!empty($obj)) { - return Magicsig::fromString($obj->keypair); + $obj = Magicsig::fromString($obj->keypair); + + // Double check keys: Crypt_RSA did not + // consistently generate good keypairs. + // We've also moved to 1024 bit keys. + if (strlen($obj->publicKey->modulus->toBits()) != 1024) { + $obj->delete(); + return false; + } } return $obj; From cfca789b34eeac6c531c4c7aac622ed2e2510390 Mon Sep 17 00:00:00 2001 From: James Walker Date: Wed, 24 Mar 2010 15:18:41 -0400 Subject: [PATCH 13/15] Updated Math_Biginteger from upstream - removing safe* workarounds --- plugins/OStatus/classes/Magicsig.php | 8 ++++---- plugins/OStatus/extlib/Math/BigInteger.php | 8 ++++---- plugins/OStatus/lib/safecrypt_rsa.php | 18 ------------------ plugins/OStatus/lib/safemath_biginteger.php | 20 -------------------- 4 files changed, 8 insertions(+), 46 deletions(-) delete mode 100644 plugins/OStatus/lib/safecrypt_rsa.php delete mode 100644 plugins/OStatus/lib/safemath_biginteger.php diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index 1a95414958..c7dd17c268 100644 --- a/plugins/OStatus/classes/Magicsig.php +++ b/plugins/OStatus/classes/Magicsig.php @@ -108,16 +108,16 @@ class Magicsig extends Memcached_DataObject public function generate($user_id) { - $rsa = new SafeCrypt_RSA(); + $rsa = new Crypt_RSA(); $keypair = $rsa->createKey(); $rsa->loadKey($keypair['privatekey']); - $this->privateKey = new SafeCrypt_RSA(); + $this->privateKey = new Crypt_RSA(); $this->privateKey->loadKey($keypair['privatekey']); - $this->publicKey = new SafeCrypt_RSA(); + $this->publicKey = new Crypt_RSA(); $this->publicKey->loadKey($keypair['publickey']); $this->user_id = $user_id; @@ -169,7 +169,7 @@ class Magicsig extends Memcached_DataObject { common_log(LOG_DEBUG, "Adding ".$type." key: (".$mod .', '. $exp .")"); - $rsa = new SafeCrypt_RSA(); + $rsa = new Crypt_RSA(); $rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1; $rsa->setHash('sha256'); $rsa->modulus = new Math_BigInteger(base64_url_decode($mod), 256); diff --git a/plugins/OStatus/extlib/Math/BigInteger.php b/plugins/OStatus/extlib/Math/BigInteger.php index 9733351d42..4373805f99 100644 --- a/plugins/OStatus/extlib/Math/BigInteger.php +++ b/plugins/OStatus/extlib/Math/BigInteger.php @@ -67,7 +67,7 @@ * @author Jim Wigginton * @copyright MMVI Jim Wigginton * @license http://www.gnu.org/licenses/lgpl.txt - * @version $Id: BigInteger.php,v 1.31 2010/03/01 17:28:19 terrafrost Exp $ + * @version $Id: BigInteger.php,v 1.33 2010/03/22 22:32:03 terrafrost Exp $ * @link http://pear.php.net/package/Math_BigInteger */ @@ -294,7 +294,7 @@ class Math_BigInteger { $this->value = array(); } - if ($x === 0) { + if (empty($x)) { return; } @@ -718,7 +718,7 @@ class Math_BigInteger { * * Will be called, automatically, when serialize() is called on a Math_BigInteger object. * - * @see __wakeup + * @see __wakeup() * @access public */ function __sleep() @@ -740,7 +740,7 @@ class Math_BigInteger { * * Will be called, automatically, when unserialize() is called on a Math_BigInteger object. * - * @see __sleep + * @see __sleep() * @access public */ function __wakeup() diff --git a/plugins/OStatus/lib/safecrypt_rsa.php b/plugins/OStatus/lib/safecrypt_rsa.php deleted file mode 100644 index f3aa2c9285..0000000000 --- a/plugins/OStatus/lib/safecrypt_rsa.php +++ /dev/null @@ -1,18 +0,0 @@ -zero = new SafeMath_BigInteger(); - } -} - diff --git a/plugins/OStatus/lib/safemath_biginteger.php b/plugins/OStatus/lib/safemath_biginteger.php deleted file mode 100644 index c05e24d1ec..0000000000 --- a/plugins/OStatus/lib/safemath_biginteger.php +++ /dev/null @@ -1,20 +0,0 @@ -hex == '') { - $this->hex = '0'; - } - parent::__wakeup(); - } -} - From 9e0b9857f435bf45d353bc88eb2462d483bcc46b Mon Sep 17 00:00:00 2001 From: James Walker Date: Wed, 24 Mar 2010 15:26:03 -0400 Subject: [PATCH 14/15] Make sure we're requiring the library --- plugins/OStatus/classes/Magicsig.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/OStatus/classes/Magicsig.php b/plugins/OStatus/classes/Magicsig.php index c7dd17c268..864fef6285 100644 --- a/plugins/OStatus/classes/Magicsig.php +++ b/plugins/OStatus/classes/Magicsig.php @@ -27,6 +27,8 @@ * @link http://status.net/ */ +require_once 'Crypt/RSA.php'; + class Magicsig extends Memcached_DataObject { From a954fd65ba00328cd1a76e620113d2f639340aaf Mon Sep 17 00:00:00 2001 From: Brion Vibber Date: Wed, 24 Mar 2010 13:36:57 -0700 Subject: [PATCH 15/15] Fix for API group methods, caused failure or output corruption when pulling up local groups by name in api/statusnet/groups/is_member.json/xml --- lib/apiaction.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apiaction.php b/lib/apiaction.php index e6aaf93161..9fc1a07799 100644 --- a/lib/apiaction.php +++ b/lib/apiaction.php @@ -1273,7 +1273,7 @@ class ApiAction extends Action if (empty($local)) { return null; } else { - return User_group::staticGet('id', $local->id); + return User_group::staticGet('id', $local->group_id); } }