From 2dd5a5f86d5242362499c381c8d9519f40a8925b Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Thu, 27 Aug 2009 12:06:45 -0400 Subject: [PATCH 01/32] Do not used named capturing groups I'm not sure all php 5.2's are compiled with a PCRE library that supported named captures. --- lib/util.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/util.php b/lib/util.php index 7c1e219138..1e78340579 100644 --- a/lib/util.php +++ b/lib/util.php @@ -413,7 +413,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { // Start off with a regex $regex = '#'. '(?:^|[\s\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'. - '(?P'. + '('. '(?:'. '(?:'. //Known protocols '(?:'. @@ -454,7 +454,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { } function callback_helper($matches, $callback, $notice_id) { - $url=$matches['url']; + $url=$matches[1]; $left = strpos($matches[0],$url); $right = $left+strlen($url); From 6ff00c9404cb2b2dddcd12d63c497c85d9d087d8 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Thu, 27 Aug 2009 22:55:32 -0400 Subject: [PATCH 02/32] Implement the is_member and membership group api's --- actions/api.php | 2 + actions/twitapigroups.php | 95 ++++++++++++++++++++++++++++++++++++++- lib/twitterapi.php | 46 +++++++++++++++++++ 3 files changed, 142 insertions(+), 1 deletion(-) diff --git a/actions/api.php b/actions/api.php index 93e33085f9..f425a8dcd7 100644 --- a/actions/api.php +++ b/actions/api.php @@ -133,6 +133,8 @@ class ApiAction extends Action 'groups/show', 'groups/timeline', 'groups/list_all', + 'groups/membership', + 'groups/is_member', 'groups/timeline'); static $bareauth = array('statuses/user_timeline', diff --git a/actions/twitapigroups.php b/actions/twitapigroups.php index 214fa8214f..1740a33b50 100644 --- a/actions/twitapigroups.php +++ b/actions/twitapigroups.php @@ -21,7 +21,7 @@ * * @category Twitter * @package StatusNet - * @author Craig Andrews + * @author Craig Andrews * @author Zach Copley * @copyright 2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 @@ -233,4 +233,97 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; } } + function membership($args, $apidata) + { + parent::handle($args); + + common_debug("in groups api action"); + + $this->auth_user = $apidata['user']; + $group = $this->get_group($apidata['api_arg'], $apidata); + + if (empty($group)) { + $this->clientError('Not Found', 404, $apidata['content-type']); + return; + } + + $sitename = common_config('site', 'name'); + $title = sprintf(_("Members of %s group"), $group->nickname); + $taguribase = common_config('integration', 'taguri'); + $id = "tag:$taguribase:GroupMembership:".$group->id; + $link = common_local_url('showgroup', + array('nickname' => $group->nickname)); + $subtitle = sprintf(_('Members of %1$s on %2$s'), + $group->nickname, $sitename); + + $page = (int)$this->arg('page', 1); + $count = (int)$this->arg('count', 20); + $max_id = (int)$this->arg('max_id', 0); + $since_id = (int)$this->arg('since_id', 0); + $since = $this->arg('since'); + + $member = $group->getMembers(($page-1)*$count, + $count, $since_id, $max_id, $since); + + switch($apidata['content-type']) { + case 'xml': + $this->show_twitter_xml_users($member); + break; + //TODO implement the RSS and ATOM content types + /*case 'rss': + $this->show_rss_users($member, $title, $link, $subtitle); + break;*/ + /*case 'atom': + if (isset($apidata['api_arg'])) { + $selfuri = common_root_url() . + 'api/statusnet/groups/membership/' . + $apidata['api_arg'] . '.atom'; + } else { + $selfuri = common_root_url() . + 'api/statusnet/groups/membership.atom'; + } + $this->show_atom_users($member, $title, $id, $link, + $subtitle, null, $selfuri); + break;*/ + case 'json': + $this->show_json_users($member); + break; + default: + $this->clientError(_('API method not found!'), $code = 404); + } + } + + function is_member($args, $apidata) + { + parent::handle($args); + + common_debug("in groups api action"); + + $this->auth_user = $apidata['user']; + $group = User_group::staticGet($args['group_id']); + if(! $group){ + $this->clientError(_('Group not found'), $code = 500); + } + $user = User::staticGet('id', $args['user_id']); + if(! $user){ + $this->clientError(_('User not found'), $code = 500); + } + + $is_member=$user->isMember($group); + + switch($apidata['content-type']) { + case 'xml': + $this->init_document('xml'); + $this->element('is_member', null, $is_member); + $this->end_document('xml'); + break; + case 'json': + $this->init_document('json'); + $this->show_json_objects(array('is_member'=>$is_member)); + $this->end_document('json'); + break; + default: + $this->clientError(_('API method not found!'), $code = 404); + } + } } diff --git a/lib/twitterapi.php b/lib/twitterapi.php index c8099978fa..638efba241 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -791,6 +791,52 @@ class TwitterapiAction extends Action $this->end_document('xml'); } + function show_twitter_xml_users($user) + { + + $this->init_document('xml'); + $this->elementStart('users', array('type' => 'array')); + + if (is_array($user)) { + foreach ($group as $g) { + $twitter_user = $this->twitter_user_array($g); + $this->show_twitter_xml_user($twitter_user,'user'); + } + } else { + while ($user->fetch()) { + $twitter_user = $this->twitter_user_array($user); + $this->show_twitter_xml_user($twitter_user); + } + } + + $this->elementEnd('users'); + $this->end_document('xml'); + } + + function show_json_users($user) + { + + $this->init_document('json'); + + $users = array(); + + if (is_array($user)) { + foreach ($user as $u) { + $twitter_user = $this->twitter_user_array($u); + array_push($users, $twitter_user); + } + } else { + while ($user->fetch()) { + $twitter_user = $this->twitter_user_array($user); + array_push($users, $twitter_user); + } + } + + $this->show_json_objects($users); + + $this->end_document('json'); + } + function show_single_json_group($group) { $this->init_document('json'); From 70fc32c5b93a6cd71cf33212ac792c38632bbdb3 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Thu, 27 Aug 2009 22:56:54 -0400 Subject: [PATCH 03/32] added my email address next to my name at the top of the files --- actions/twitapigroups.php | 2 +- actions/twitapitags.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actions/twitapigroups.php b/actions/twitapigroups.php index 1740a33b50..4deb1b764a 100644 --- a/actions/twitapigroups.php +++ b/actions/twitapigroups.php @@ -41,7 +41,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; * * @category Twitter * @package StatusNet - * @author Craig Andrews + * @author Craig Andrews * @author Zach Copley * @copyright 2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 diff --git a/actions/twitapitags.php b/actions/twitapitags.php index 2bb7ee01ab..0bcc55d378 100644 --- a/actions/twitapitags.php +++ b/actions/twitapitags.php @@ -21,7 +21,7 @@ * * @category Twitter * @package StatusNet - * @author Craig Andrews + * @author Craig Andrews * @author Zach Copley * @copyright 2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 @@ -41,7 +41,7 @@ require_once INSTALLDIR.'/lib/twitterapi.php'; * * @category Twitter * @package StatusNet - * @author Craig Andrews + * @author Craig Andrews * @author Zach Copley * @copyright 2009 StatusNet, Inc. * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 From 0056b635c661c4aaf6bce08795af925388e35f5c Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 27 Aug 2009 20:06:03 -0700 Subject: [PATCH 04/32] reformat curry() to make my editor happy --- lib/util.php | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/lib/util.php b/lib/util.php index cedb708739..79a219fd6d 100644 --- a/lib/util.php +++ b/lib/util.php @@ -457,7 +457,7 @@ function callback_helper($matches, $callback, $notice_id) { $url=$matches[1]; $left = strpos($matches[0],$url); $right = $left+strlen($url); - + $groupSymbolSets=array( array( 'left'=>'(', @@ -491,9 +491,7 @@ function callback_helper($matches, $callback, $notice_id) { $url=substr($url,0,-1); } }while($original_url!=$url); - - - + if(empty($notice_id)){ $result = call_user_func_array($callback,$url); }else{ @@ -508,16 +506,13 @@ function curry($fn) { array_shift($args); $id = uniqid('_partial'); $GLOBALS[$id] = array($fn, $args); - return create_function( - '', - ' - $args = func_get_args(); - return call_user_func_array( - $GLOBALS["'.$id.'"][0], - array_merge( - $args, - $GLOBALS["'.$id.'"][1])); - '); + return create_function('', + '$args = func_get_args(); '. + 'return call_user_func_array('. + '$GLOBALS["'.$id.'"][0],'. + 'array_merge('. + '$args,'. + '$GLOBALS["'.$id.'"][1]));'); } function common_linkify($url) { From 34ce75c71d37754fa941233c805c042a47910184 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 27 Aug 2009 20:09:07 -0700 Subject: [PATCH 05/32] remove duplicate save of Notice and streamline attachment detection --- classes/Notice.php | 13 +------------ lib/util.php | 39 +++++++++++---------------------------- 2 files changed, 12 insertions(+), 40 deletions(-) diff --git a/classes/Notice.php b/classes/Notice.php index e597128641..28d5b8ddf9 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -58,7 +58,7 @@ class Notice extends Memcached_DataObject /* the code above is auto generated do not remove the tag below */ ###END_AUTOCODE - /* Notice types */ + /* Notice types */ const LOCAL_PUBLIC = 1; const REMOTE_OMB = 0; const LOCAL_NONPUBLIC = -1; @@ -248,17 +248,6 @@ class Notice extends Memcached_DataObject $notice->saveUrls(); - // FIXME: why do we have to re-render the content? - // Remove this if it's not necessary. - - $orig2 = clone($notice); - - $notice->rendered = common_render_content($final, $notice); - if (!$notice->update($orig2)) { - common_log_db_error($notice, 'UPDATE', __FILE__); - return _('Problem saving notice.'); - } - $notice->query('COMMIT'); Event::handle('EndNoticeSave', array($notice)); diff --git a/lib/util.php b/lib/util.php index 79a219fd6d..070b4232c5 100644 --- a/lib/util.php +++ b/lib/util.php @@ -542,8 +542,7 @@ function common_linkify($url) { $attachment_id = null; $has_thumb = false; - // Check to see whether there's a filename associated with this URL. - // If there is, it's an upload and qualifies as an attachment + // Check to see whether this is a known "attachment" URL. $localfile = File::staticGet('url', $longurl); @@ -551,33 +550,17 @@ function common_linkify($url) { if (isset($localfile->filename)) { $is_attachment = true; $attachment_id = $localfile->id; - } - } + } else { // if it has OEmbed info, it's an attachment, too + $foe = File_oembed::staticGet('file_id', $localfile->id); + if (!empty($foe)) { + $is_attachment = true; + $attachment_id = $localfile->id; - // if this URL is an attachment, then we set class='attachment' and id='attahcment-ID' - // where ID is the id of the attachment for the given URL. - // - // we need a better test telling what can be shown as an attachment - // we're currently picking up oembeds only. - // I think the best option is another file_view table in the db - // and associated dbobject. - - $query = "select file_oembed.file_id as file_id from file join file_oembed on file.id = file_oembed.file_id where file.url='$longurl'"; - $file = new File; - $file->query($query); - $file->fetch(); - - if (!empty($file->file_id)) { - $is_attachment = true; - $attachment_id = $file->file_id; - - $query = "select file_thumbnail.file_id as file_id from file join file_thumbnail on file.id = file_thumbnail.file_id where file.url='$longurl'"; - $file2 = new File; - $file2->query($query); - $file2->fetch(); - - if (!empty($file2)) { - $has_thumb = true; + $thumb = File_thumbnail::staticGet('file_id', $localfile->id); + if (!empty($thumb)) { + $has_thumb = true; + } + } } } From c0d03fc2799c7b0c57d05166b404a3d13427f497 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Thu, 27 Aug 2009 20:23:31 -0700 Subject: [PATCH 06/32] make URL analyzer save new info on URLs --- classes/File.php | 8 +++++--- lib/util.php | 21 +++++++++++++-------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/classes/File.php b/classes/File.php index 0cebfc55e3..f4d0a3a488 100644 --- a/classes/File.php +++ b/classes/File.php @@ -85,7 +85,7 @@ class File extends Memcached_DataObject return $x; } - function processNew($given_url, $notice_id) { + function processNew($given_url, $notice_id=null) { if (empty($given_url)) return -1; // error, no url to process $given_url = File_redirection::_canonUrl($given_url); if (empty($given_url)) return -1; // error, no url to process @@ -96,7 +96,7 @@ class File extends Memcached_DataObject $redir_data = File_redirection::where($given_url); $redir_url = $redir_data['url']; // TODO: max field length - if ($redir_url === $given_url || strlen($redir_url) > 255) { + if ($redir_url === $given_url || strlen($redir_url) > 255) { $x = File::saveNew($redir_data, $given_url); $file_id = $x->id; } else { @@ -119,7 +119,9 @@ class File extends Memcached_DataObject } } - File_to_post::processNew($file_id, $notice_id); + if (!empty($notice_id)) { + File_to_post::processNew($file_id, $notice_id); + } return $x; } diff --git a/lib/util.php b/lib/util.php index 070b4232c5..8a56be55d5 100644 --- a/lib/util.php +++ b/lib/util.php @@ -520,7 +520,7 @@ function common_linkify($url) { // functions $url = htmlspecialchars_decode($url); - if(strpos($url, '@')!==false && strpos($url, ':')===false){ + if(strpos($url, '@') !== false && strpos($url, ':') === false) { //url is an email address without the mailto: protocol return XMLStringer::estring('a', array('href' => "mailto:$url", 'rel' => 'external'), $url); } @@ -544,19 +544,24 @@ function common_linkify($url) { // Check to see whether this is a known "attachment" URL. - $localfile = File::staticGet('url', $longurl); + $f = File::staticGet('url', $longurl); - if (!empty($localfile)) { - if (isset($localfile->filename)) { + if (empty($f)) { + // XXX: this writes to the database. :< + $f = File::processNew($longurl); + } + + if (!empty($f)) { + if (isset($f->filename)) { $is_attachment = true; - $attachment_id = $localfile->id; + $attachment_id = $f->id; } else { // if it has OEmbed info, it's an attachment, too - $foe = File_oembed::staticGet('file_id', $localfile->id); + $foe = File_oembed::staticGet('file_id', $f->id); if (!empty($foe)) { $is_attachment = true; - $attachment_id = $localfile->id; + $attachment_id = $f->id; - $thumb = File_thumbnail::staticGet('file_id', $localfile->id); + $thumb = File_thumbnail::staticGet('file_id', $f->id); if (!empty($thumb)) { $has_thumb = true; } From 51adf00bd80253322b473ab199e5b97dd4951d5c Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 28 Aug 2009 04:36:47 +0000 Subject: [PATCH 07/32] Renable basic auth posting to Twitter for users who already have a bridge setup --- lib/twitter.php | 200 ++++++++++++++++++++++++++----------- lib/twitteroauthclient.php | 9 ++ 2 files changed, 153 insertions(+), 56 deletions(-) diff --git a/lib/twitter.php b/lib/twitter.php index 7546ffa98a..d63384fc93 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -154,80 +154,168 @@ function broadcast_twitter($notice) TWITTER_SERVICE); if (is_twitter_bound($notice, $flink)) { - - $user = $flink->getUser(); - - // XXX: Hack to get around PHP cURL's use of @ being a a meta character - $statustxt = preg_replace('/^@/', ' @', $notice->content); - - $token = TwitterOAuthClient::unpackToken($flink->credentials); - - $client = new TwitterOAuthClient($token->key, $token->secret); - - $status = null; - - try { - $status = $client->statusesUpdate($statustxt); - } catch (OAuthClientCurlException $e) { - - if ($e->getMessage() == 'The requested URL returned error: 401') { - - $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . - 'Twitter OAuth access token.', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - // Bad auth token! We need to delete the foreign_link - // to Twitter and inform the user. - - remove_twitter_link($flink); - return true; - - } else { - - // Some other error happened, so we should probably - // try to send again later. - - $errmsg = sprintf('cURL error trying to send notice to Twitter ' . - 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: $4$s.', - $user->nickname, $user->id, - $e->getCode(), $e->getMessage()); - common_log(LOG_WARNING, $errmsg); - - return false; - } + if (TwitterOAuthClient::isPackedToken($flink->credentials)) { + return broadcast_oauth($notice, $flink); + } else { + return broadcast_basicauth($notice, $flink); } + } +} - if (empty($status)) { +function broadcast_oauth($notice, $flink) { - // This could represent a failure posting, - // or the Twitter API might just be behaving flakey. + $user = $flink->getUser(); + $statustxt = format_status($notice); + $token = TwitterOAuthClient::unpackToken($flink->credentials); + $client = new TwitterOAuthClient($token->key, $token->secret); + $status = null; - $errmsg = sprint('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); + try { + $status = $client->statusesUpdate($statustxt); + } catch (OAuthClientCurlException $e) { + + if ($e->getMessage() == 'The requested URL returned error: 401') { + + $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . + 'Twitter OAuth access token.', + $user->nickname, $user->id); + common_log(LOG_WARNING, $errmsg); + + // Bad auth token! We need to delete the foreign_link + // to Twitter and inform the user. + + remove_twitter_link($flink); + return true; + + } else { + + // Some other error happened, so we should probably + // try to send again later. + + $errmsg = sprintf('cURL error trying to send notice to Twitter ' . + 'for user %1$s (user id: %2$s) - ' . + 'code: %3$s message: $4$s.', + $user->nickname, $user->id, + $e->getCode(), $e->getMessage()); common_log(LOG_WARNING, $errmsg); return false; } - - // Notice crossed the great divide - - $msg = sprintf('Twitter bridge posted notice %s to Twitter.', - $notice->id); - common_log(LOG_INFO, $msg); } + if (empty($status)) { + + // This could represent a failure posting, + // or the Twitter API might just be behaving flakey. + + $errmsg = sprintf('No data returned by Twitter API when ' . + 'trying to send update for %1$s (user id %2$s).', + $user->nickname, $user->id); + common_log(LOG_WARNING, $errmsg); + + return false; + } + + // Notice crossed the great divide + + $msg = sprintf('Twitter bridge posted notice %s to Twitter.', + $notice->id); + common_log(LOG_INFO, $msg); + return true; } +function broadcast_basicauth($notice, $flink) +{ + $user = $flink->getUser(); + $fuser = $flink->getForeignUser(); + $twitter_user = $fuser->nickname; + $twitter_password = $flink->credentials; + $uri = 'http://www.twitter.com/statuses/update.json'; + $statustxt = format_status($notice); + + $options = array(CURLOPT_USERPWD => "$twitter_user:$twitter_password", + CURLOPT_POST => true, + CURLOPT_POSTFIELDS => + array( + 'status' => $statustxt, + 'source' => common_config('integration', 'source') + ), + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FAILONERROR => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_USERAGENT => "StatusNet", + CURLOPT_CONNECTTIMEOUT => 120, + CURLOPT_TIMEOUT => 120, + + # Twitter is strict about accepting invalid "Expect" headers + CURLOPT_HTTPHEADER => array('Expect:')); + + $ch = curl_init($uri); + curl_setopt_array($ch, $options); + $data = curl_exec($ch); + $errmsg = curl_error($ch); + + if ($errmsg == 'The requested URL returned error: 401') { + + $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . + 'Twitter basic auth username/password.', + $user->nickname, $user->id); + common_log(LOG_WARNING, $errmsg); + + // Bad credentials. We need to delete the foreign_link + // to Twitter and inform the user. + + remove_twitter_link($flink); + return true; + + } elseif (!empty($errmsg)) { + + $code = curl_errno($ch); + + $msg = "cURL error: $code, '$errmsg' - trying to send notice $notice->id " . + "to Twitter using basic auth."; + + common_log(LOG_WARNING, $msg); + + return false; + } + + curl_close($ch); + + $status = json_decode($data); + + if (empty($status)) { + + $errmsg = sprintf('No data returned by Twitter API when ' . + 'trying to send update for %1$s (user id %2$s) ' . + 'using basic auth.', + $user->nickname, $user->id); + common_log(LOG_WARNING, $errmsg); + + return false; + } + + $msg = sprintf('Twitter bridge posted notice %s to Twitter using basic auth.', + $notice->id); + common_log(LOG_INFO, $msg); + + return true; +} + +function format_status($notice) +{ + // XXX: Hack to get around PHP cURL's use of @ being a a meta character + return preg_replace('/^@/', ' @', $notice->content); +} + function remove_twitter_link($flink) { $user = $flink->getUser(); common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' . - "user $user->nickname (user id: $user->id)."); + "user $user->nickname (user id: $user->id)."); $result = $flink->delete(); diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php index 3da522fc51..9821a491e5 100644 --- a/lib/twitteroauthclient.php +++ b/lib/twitteroauthclient.php @@ -81,6 +81,15 @@ class TwitterOAuthClient extends OAuthClient return new OAuthToken($vals[0], $vals[1]); } + static function isPackedToken($str) + { + if (strpos($str, chr(0)) === false) { + return false; + } else { + return true; + } + } + /** * Builds a link to Twitter's endpoint for authorizing a request token * From 36b6ef8d05bf6e4290894f9677cc9618a9bfd486 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 28 Aug 2009 06:00:30 +0000 Subject: [PATCH 08/32] Abstract the Twitter basic auth stuff into its own client class --- lib/twitter.php | 77 ++++------- lib/twitterbasicauthclient.php | 236 +++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 51 deletions(-) create mode 100644 lib/twitterbasicauthclient.php diff --git a/lib/twitter.php b/lib/twitter.php index d63384fc93..b734d22d8e 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -218,7 +218,7 @@ function broadcast_oauth($notice, $flink) { // Notice crossed the great divide - $msg = sprintf('Twitter bridge posted notice %s to Twitter.', + $msg = sprintf('Twitter bridge posted notice %s to Twitter using OAuth.', $notice->id); common_log(LOG_INFO, $msg); @@ -228,70 +228,44 @@ function broadcast_oauth($notice, $flink) { function broadcast_basicauth($notice, $flink) { $user = $flink->getUser(); - $fuser = $flink->getForeignUser(); - $twitter_user = $fuser->nickname; - $twitter_password = $flink->credentials; - $uri = 'http://www.twitter.com/statuses/update.json'; + $statustxt = format_status($notice); - $options = array(CURLOPT_USERPWD => "$twitter_user:$twitter_password", - CURLOPT_POST => true, - CURLOPT_POSTFIELDS => - array( - 'status' => $statustxt, - 'source' => common_config('integration', 'source') - ), - CURLOPT_RETURNTRANSFER => true, - CURLOPT_FAILONERROR => true, - CURLOPT_HEADER => false, - CURLOPT_FOLLOWLOCATION => true, - CURLOPT_USERAGENT => "StatusNet", - CURLOPT_CONNECTTIMEOUT => 120, - CURLOPT_TIMEOUT => 120, + $client = new TwitterBasicAuthClient($flink); + $status = null; - # Twitter is strict about accepting invalid "Expect" headers - CURLOPT_HTTPHEADER => array('Expect:')); + try { + $status = $client->statusesUpdate($statustxt); + } catch (BasicAuthCurlException $e) { - $ch = curl_init($uri); - curl_setopt_array($ch, $options); - $data = curl_exec($ch); - $errmsg = curl_error($ch); + if ($e->getMessage() == 'The requested URL returned error: 401') { - if ($errmsg == 'The requested URL returned error: 401') { + $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . + 'Twitter screen_name/password combo.', + $user->nickname, $user->id); + common_log(LOG_WARNING, $errmsg); - $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . - 'Twitter basic auth username/password.', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); + remove_twitter_link($flink); + return true; - // Bad credentials. We need to delete the foreign_link - // to Twitter and inform the user. + } else { - remove_twitter_link($flink); - return true; + $errmsg = sprintf('cURL error trying to send notice to Twitter ' . + 'for user %1$s (user id: %2$s) - ' . + 'code: %3$s message: $4$s.', + $user->nickname, $user->id, + $e->getCode(), $e->getMessage()); + common_log(LOG_WARNING, $errmsg); - } elseif (!empty($errmsg)) { - - $code = curl_errno($ch); - - $msg = "cURL error: $code, '$errmsg' - trying to send notice $notice->id " . - "to Twitter using basic auth."; - - common_log(LOG_WARNING, $msg); - - return false; + return false; + } } - curl_close($ch); - - $status = json_decode($data); - if (empty($status)) { $errmsg = sprintf('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s) ' . - 'using basic auth.', - $user->nickname, $user->id); + 'trying to send update for %1$s (user id %2$s).', + $user->nickname, $user->id); common_log(LOG_WARNING, $errmsg); return false; @@ -302,6 +276,7 @@ function broadcast_basicauth($notice, $flink) common_log(LOG_INFO, $msg); return true; + } function format_status($notice) diff --git a/lib/twitterbasicauthclient.php b/lib/twitterbasicauthclient.php new file mode 100644 index 0000000000..82359d93d1 --- /dev/null +++ b/lib/twitterbasicauthclient.php @@ -0,0 +1,236 @@ +. + * + * @category Integration + * @package StatusNet + * @author Zach Copley + * @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') && !defined('LACONICA')) { + exit(1); +} + +/** + * Exception wrapper for cURL errors + * + * @category Integration + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + */ +class BasicAuthCurlException extends Exception +{ +} + +/** + * Class for talking to the Twitter API with HTTP Basic Auth. + * + * @category Integration + * @package StatusNet + * @author Zach Copley + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://status.net/ + * + */ +class TwitterBasicAuthClient +{ + var $screen_name = null; + var $password = null; + + /** + * constructor + * + * @param Foreign_link $flink a Foreign_link storing the + * Twitter user's password, etc. + */ + function __construct($flink) + { + $fuser = $flink->getForeignUser(); + $this->screen_name = $fuser->nickname; + $this->password = $flink->credentials; + } + + /** + * Calls Twitter's /stutuses/update API method + * + * @param string $status text of the status + * @param int $in_reply_to_status_id optional id of the status it's + * a reply to + * + * @return mixed the status + */ + function statusesUpdate($status, $in_reply_to_status_id = null) + { + $url = 'https://twitter.com/statuses/update.json'; + $params = array('status' => $status, + 'source' => common_config('integration', 'source'), + 'in_reply_to_status_id' => $in_reply_to_status_id); + $response = $this->httpRequest($url, $params, true); + $status = json_decode($response); + return $status; + } + + /** + * Calls Twitter's /stutuses/friends_timeline API method + * + * @param int $since_id show statuses after this id + * @param int $max_id show statuses before this id + * @param int $cnt number of statuses to show + * @param int $page page number + * + * @return mixed an array of statuses + */ + function statusesFriendsTimeline($since_id = null, $max_id = null, + $cnt = null, $page = null) + { + $url = 'https://twitter.com/statuses/friends_timeline.json'; + $params = array('since_id' => $since_id, + 'max_id' => $max_id, + 'count' => $cnt, + 'page' => $page); + $qry = http_build_query($params); + + if (!empty($qry)) { + $url .= "?$qry"; + } + + $response = $this->httpRequest($url, null, true); + $statuses = json_decode($response); + return $statuses; + } + + /** + * Calls Twitter's /stutuses/friends API method + * + * @param int $id id of the user whom you wish to see friends of + * @param int $user_id numerical user id + * @param int $screen_name screen name + * @param int $page page number + * + * @return mixed an array of twitter users and their latest status + */ + function statusesFriends($id = null, $user_id = null, $screen_name = null, + $page = null) + { + $url = "https://twitter.com/statuses/friends.json"; + + $params = array('id' => $id, + 'user_id' => $user_id, + 'screen_name' => $screen_name, + 'page' => $page); + $qry = http_build_query($params); + + if (!empty($qry)) { + $url .= "?$qry"; + } + + $response = $this->httpRequest($url); + $friends = json_decode($response); + return $friends; + } + + /** + * Calls Twitter's /stutuses/friends/ids API method + * + * @param int $id id of the user whom you wish to see friends of + * @param int $user_id numerical user id + * @param int $screen_name screen name + * @param int $page page number + * + * @return mixed a list of ids, 100 per page + */ + function friendsIds($id = null, $user_id = null, $screen_name = null, + $page = null) + { + $url = "https://twitter.com/friends/ids.json"; + + $params = array('id' => $id, + 'user_id' => $user_id, + 'screen_name' => $screen_name, + 'page' => $page); + $qry = http_build_query($params); + + if (!empty($qry)) { + $url .= "?$qry"; + } + + $response = $this->httpRequest($url); + $ids = json_decode($response); + return $ids; + } + + /** + * Make a HTTP request using cURL. + * + * @param string $url Where to make the request + * @param array $params post parameters + * + * @return mixed the request + */ + function httpRequest($url, $params = null, $auth = false) + { + $options = array( + CURLOPT_RETURNTRANSFER => true, + CURLOPT_FAILONERROR => true, + CURLOPT_HEADER => false, + CURLOPT_FOLLOWLOCATION => true, + CURLOPT_USERAGENT => 'StatusNet', + CURLOPT_CONNECTTIMEOUT => 120, + CURLOPT_TIMEOUT => 120, + CURLOPT_HTTPAUTH => CURLAUTH_ANY, + CURLOPT_SSL_VERIFYPEER => false, + + // Twitter is strict about accepting invalid "Expect" headers + + CURLOPT_HTTPHEADER => array('Expect:') + ); + + if (isset($params)) { + $options[CURLOPT_POST] = true; + $options[CURLOPT_POSTFIELDS] = $params; + } + + if ($auth) { + $options[CURLOPT_USERPWD] = $this->screen_name . + ':' . $this->password; + } + + $ch = curl_init($url); + curl_setopt_array($ch, $options); + $response = curl_exec($ch); + + if ($response === false) { + $msg = curl_error($ch); + $code = curl_errno($ch); + throw new BasicAuthCurlException($msg, $code); + } + + curl_close($ch); + + return $response; + } + +} From 36c104fb34d7128a0372dd3f77504c0b2f76ba80 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 28 Aug 2009 07:02:27 +0000 Subject: [PATCH 09/32] Make SyncTwitterFriends and TwitterStatusFetcher daemons use both HTTP Basic Auth as well as OAuth --- lib/oauthclient.php | 2 +- lib/twitterbasicauthclient.php | 6 +++--- lib/twitteroauthclient.php | 2 +- scripts/synctwitterfriends.php | 17 +++++++++++++---- scripts/twitterqueuehandler.php | 2 ++ scripts/twitterstatusfetcher.php | 21 +++++++++++++++------ 6 files changed, 35 insertions(+), 15 deletions(-) diff --git a/lib/oauthclient.php b/lib/oauthclient.php index cc10cea8f9..f1827726e7 100644 --- a/lib/oauthclient.php +++ b/lib/oauthclient.php @@ -22,7 +22,7 @@ * @category Action * @package StatusNet * @author Zach Copley - * @copyright 2008 StatusNet, Inc. + * @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/ */ diff --git a/lib/twitterbasicauthclient.php b/lib/twitterbasicauthclient.php index 82359d93d1..66bb01e539 100644 --- a/lib/twitterbasicauthclient.php +++ b/lib/twitterbasicauthclient.php @@ -88,7 +88,7 @@ class TwitterBasicAuthClient $params = array('status' => $status, 'source' => common_config('integration', 'source'), 'in_reply_to_status_id' => $in_reply_to_status_id); - $response = $this->httpRequest($url, $params, true); + $response = $this->httpRequest($url, $params); $status = json_decode($response); return $status; } @@ -117,7 +117,7 @@ class TwitterBasicAuthClient $url .= "?$qry"; } - $response = $this->httpRequest($url, null, true); + $response = $this->httpRequest($url); $statuses = json_decode($response); return $statuses; } @@ -190,7 +190,7 @@ class TwitterBasicAuthClient * * @return mixed the request */ - function httpRequest($url, $params = null, $auth = false) + function httpRequest($url, $params = null, $auth = true) { $options = array( CURLOPT_RETURNTRANSFER => true, diff --git a/lib/twitteroauthclient.php b/lib/twitteroauthclient.php index 9821a491e5..e37fa05f0a 100644 --- a/lib/twitteroauthclient.php +++ b/lib/twitteroauthclient.php @@ -22,7 +22,7 @@ * @category Integration * @package StatusNet * @author Zach Copley - * @copyright 2008 StatusNet, Inc. + * @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/ */ diff --git a/scripts/synctwitterfriends.php b/scripts/synctwitterfriends.php index 545cb23b3c..2cb7525eaf 100755 --- a/scripts/synctwitterfriends.php +++ b/scripts/synctwitterfriends.php @@ -19,6 +19,8 @@ */ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('STATUSNET', true); +define('LACONICA', true); // compatibility $shortoptions = 'di::'; $longoptions = array('id::', 'debug'); @@ -142,13 +144,20 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon { $friends = array(); - $token = TwitterOAuthClient::unpackToken($flink->credentials); + $client = null; - $client = new TwitterOAuthClient($token->key, $token->secret); + if (TwitterOAuthClient::isPackedToken($flink->credentials)) { + $token = TwitterOAuthClient::unpackToken($flink->credentials); + $client = new TwitterOAuthClient($token->key, $token->secret); + common_debug($this->name() . '- Grabbing friends IDs with OAuth.'); + } else { + $client = new TwitterBasicAuthClient($flink); + common_debug($this->name() . '- Grabbing friends IDs with basic auth.'); + } try { $friends_ids = $client->friendsIds(); - } catch (OAuthCurlException $e) { + } catch (Exception $e) { common_log(LOG_WARNING, $this->name() . ' - cURL error getting friend ids ' . $e->getCode() . ' - ' . $e->getMessage()); @@ -177,7 +186,7 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon try { $more_friends = $client->statusesFriends(null, null, null, $i); - } catch (OAuthCurlException $e) { + } catch (Exception $e) { common_log(LOG_WARNING, $this->name() . ' - cURL error getting Twitter statuses/friends ' . "page $i - " . $e->getCode() . ' - ' . diff --git a/scripts/twitterqueuehandler.php b/scripts/twitterqueuehandler.php index ce4d824d0d..992141f9de 100755 --- a/scripts/twitterqueuehandler.php +++ b/scripts/twitterqueuehandler.php @@ -19,6 +19,8 @@ */ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('STATUSNET', true); +define('LACONICA', true); // compatibility $shortoptions = 'i::'; $longoptions = array('id::'); diff --git a/scripts/twitterstatusfetcher.php b/scripts/twitterstatusfetcher.php index 68f7e9bf75..6dca6f75b8 100755 --- a/scripts/twitterstatusfetcher.php +++ b/scripts/twitterstatusfetcher.php @@ -19,6 +19,8 @@ */ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); +define('STATUSNET', true); +define('LACONICA', true); // compatibility // Tune number of processes and how often to poll Twitter // XXX: Should these things be in config.php? @@ -148,9 +150,9 @@ class TwitterStatusFetcher extends ParallelizingDaemon function getTimeline($flink) { - if (empty($flink)) { + if (empty($flink)) { common_log(LOG_WARNING, $this->name() . - " - Can't retrieve Foreign_link for foreign ID $fid"); + " - Can't retrieve Foreign_link for foreign ID $fid"); return; } @@ -161,17 +163,24 @@ class TwitterStatusFetcher extends ParallelizingDaemon // to start importing? How many statuses? Right now I'm going // with the default last 20. - $token = TwitterOAuthClient::unpackToken($flink->credentials); + $client = null; - $client = new TwitterOAuthClient($token->key, $token->secret); + if (TwitterOAuthClient::isPackedToken($flink->credentials)) { + $token = TwitterOAuthClient::unpackToken($flink->credentials); + $client = new TwitterOAuthClient($token->key, $token->secret); + common_debug($this->name() . ' - Grabbing friends timeline with OAuth.'); + } else { + $client = new TwitterBasicAuthClient($flink); + common_debug($this->name() . ' - Grabbing friends timeline with basic auth.'); + } $timeline = null; try { $timeline = $client->statusesFriendsTimeline(); - } catch (OAuthClientCurlException $e) { + } catch (Exception $e) { common_log(LOG_WARNING, $this->name() . - ' - OAuth client unable to get friends timeline for user ' . + ' - Twitter client unable to get friends timeline for user ' . $flink->user_id . ' - code: ' . $e->getCode() . 'msg: ' . $e->getMessage()); } From e277c856d65885a487b4d7f090a10cf817da836a Mon Sep 17 00:00:00 2001 From: Brenda Wallace Date: Fri, 28 Aug 2009 20:00:55 +1200 Subject: [PATCH 10/32] fix for SQL error: ERROR: syntax error at or near ")" at character 45 http://laconi.ca/trac/ticket/1735 --- classes/Notice.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/classes/Notice.php b/classes/Notice.php index 28d5b8ddf9..89afe9d1dc 100644 --- a/classes/Notice.php +++ b/classes/Notice.php @@ -732,6 +732,10 @@ class Notice extends Memcached_DataObject return new ArrayWrapper($notices); } else { $notice = new Notice(); + if (empty($ids)) { + //if no IDs requested, just return the notice object + return $notice; + } $notice->whereAdd('id in (' . implode(', ', $ids) . ')'); $notice->orderBy('id DESC'); From e04e009ddb51cf8327744a2d642b793a3d9716cd Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 28 Aug 2009 06:13:47 -0700 Subject: [PATCH 11/32] stutuses -> statuses --- lib/twitterbasicauthclient.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/twitterbasicauthclient.php b/lib/twitterbasicauthclient.php index 66bb01e539..fd331fbdc9 100644 --- a/lib/twitterbasicauthclient.php +++ b/lib/twitterbasicauthclient.php @@ -74,7 +74,7 @@ class TwitterBasicAuthClient } /** - * Calls Twitter's /stutuses/update API method + * Calls Twitter's /statuses/update API method * * @param string $status text of the status * @param int $in_reply_to_status_id optional id of the status it's @@ -94,7 +94,7 @@ class TwitterBasicAuthClient } /** - * Calls Twitter's /stutuses/friends_timeline API method + * Calls Twitter's /statuses/friends_timeline API method * * @param int $since_id show statuses after this id * @param int $max_id show statuses before this id @@ -123,7 +123,7 @@ class TwitterBasicAuthClient } /** - * Calls Twitter's /stutuses/friends API method + * Calls Twitter's /statuses/friends API method * * @param int $id id of the user whom you wish to see friends of * @param int $user_id numerical user id @@ -153,7 +153,7 @@ class TwitterBasicAuthClient } /** - * Calls Twitter's /stutuses/friends/ids API method + * Calls Twitter's /statuses/friends/ids API method * * @param int $id id of the user whom you wish to see friends of * @param int $user_id numerical user id @@ -163,7 +163,7 @@ class TwitterBasicAuthClient * @return mixed a list of ids, 100 per page */ function friendsIds($id = null, $user_id = null, $screen_name = null, - $page = null) + $page = null) { $url = "https://twitter.com/friends/ids.json"; From b4ca06edb286a4518b0a59e2a59e50e1c4b6ecb1 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 28 Aug 2009 08:43:28 -0700 Subject: [PATCH 12/32] fix 'callback_helper' --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index 8a56be55d5..87d5800f69 100644 --- a/lib/util.php +++ b/lib/util.php @@ -450,7 +450,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { '#ixu'; preg_match_all($regex,$text,$matches); //print_r($matches); - return preg_replace_callback($regex, curry(callback_helper,$callback,$notice_id) ,$text); + return preg_replace_callback($regex, curry('callback_helper',$callback,$notice_id) ,$text); } function callback_helper($matches, $callback, $notice_id) { From c628029ef124698fe39522a5038af3f3e1a11a26 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 28 Aug 2009 10:42:34 -0700 Subject: [PATCH 13/32] Status_network had wrong ini file --- classes/Status_network.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/Status_network.php b/classes/Status_network.php index d526cb4d61..fe4f0b0c58 100644 --- a/classes/Status_network.php +++ b/classes/Status_network.php @@ -54,7 +54,7 @@ class Status_network extends DB_DataObject global $config; $config['db']['database_'.$dbname] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname"; - $config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/statusnet.ini'; + $config['db']['ini_'.$dbname] = INSTALLDIR.'/classes/status_network.ini'; $config['db']['table_status_network'] = $dbname; self::$cache = new Memcache(); From a0a376bf0eca00cce974de12c2f275006c1281ee Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Fri, 28 Aug 2009 17:55:58 +0000 Subject: [PATCH 14/32] Take out unnecessary defines --- scripts/synctwitterfriends.php | 2 -- scripts/twitterqueuehandler.php | 2 -- scripts/twitterstatusfetcher.php | 2 -- 3 files changed, 6 deletions(-) diff --git a/scripts/synctwitterfriends.php b/scripts/synctwitterfriends.php index 2cb7525eaf..b30e700a1c 100755 --- a/scripts/synctwitterfriends.php +++ b/scripts/synctwitterfriends.php @@ -19,8 +19,6 @@ */ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); -define('STATUSNET', true); -define('LACONICA', true); // compatibility $shortoptions = 'di::'; $longoptions = array('id::', 'debug'); diff --git a/scripts/twitterqueuehandler.php b/scripts/twitterqueuehandler.php index 992141f9de..ce4d824d0d 100755 --- a/scripts/twitterqueuehandler.php +++ b/scripts/twitterqueuehandler.php @@ -19,8 +19,6 @@ */ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); -define('STATUSNET', true); -define('LACONICA', true); // compatibility $shortoptions = 'i::'; $longoptions = array('id::'); diff --git a/scripts/twitterstatusfetcher.php b/scripts/twitterstatusfetcher.php index 6dca6f75b8..3cdf1867a1 100755 --- a/scripts/twitterstatusfetcher.php +++ b/scripts/twitterstatusfetcher.php @@ -19,8 +19,6 @@ */ define('INSTALLDIR', realpath(dirname(__FILE__) . '/..')); -define('STATUSNET', true); -define('LACONICA', true); // compatibility // Tune number of processes and how often to poll Twitter // XXX: Should these things be in config.php? From 3368452ebf3c738933b05e902c277296684e280b Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Fri, 28 Aug 2009 16:18:05 -0400 Subject: [PATCH 15/32] Add % and ~ as valid characters in the path, querystring, and fragment parts of URLs --- lib/util.php | 10 +++++----- tests/URLDetectionTest.php | 4 ++++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/lib/util.php b/lib/util.php index 8a56be55d5..edc08d4c12 100644 --- a/lib/util.php +++ b/lib/util.php @@ -421,7 +421,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { '|'. '(?:(?:mailto|aim|tel|xmpp):)'. ')'. - '(?:[\pN\pL\-\_\+]+(?::[\pN\pL\-\_\+]+)?\@)?'. //user:pass@ + '(?:[\pN\pL\-\_\+\%\~]+(?::[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@ '(?:'. '(?:'. '\[[\pN\pL\-\_\:\.]+(?127.0.0.1:99'), array('127.0.0.1/test.php', '127.0.0.1/test.php'), + array('127.0.0.1/~test', + '127.0.0.1/~test'), + array('127.0.0.1/test%20stuff', + '127.0.0.1/test%20stuff'), array('http://[::1]:99/test.php', 'http://[::1]:99/test.php'), array('http://::1/test.php', From b562ff1f2c6bca7e200034dac78b6debdcc0b214 Mon Sep 17 00:00:00 2001 From: Evan Prodromou Date: Fri, 28 Aug 2009 13:35:28 -0700 Subject: [PATCH 16/32] change version to 0.8.2dev --- lib/common.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/common.php b/lib/common.php index 39d4ffc9b9..88d77732f4 100644 --- a/lib/common.php +++ b/lib/common.php @@ -19,7 +19,7 @@ if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } -define('STATUSNET_VERSION', '0.8.1'); +define('STATUSNET_VERSION', '0.8.2dev'); define('LACONICA_VERSION', STATUSNET_VERSION); // compatibility define('STATUSNET_CODENAME', 'Second Guessing'); From c02e8a46878ba3ba1ea24f746db8ef0039d19612 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Sat, 29 Aug 2009 06:20:19 +0000 Subject: [PATCH 17/32] Fix error in log msg format specifier --- lib/twitter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/twitter.php b/lib/twitter.php index b734d22d8e..6555251943 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -194,7 +194,7 @@ function broadcast_oauth($notice, $flink) { $errmsg = sprintf('cURL error trying to send notice to Twitter ' . 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: $4$s.', + 'code: %3$s message: %4$s.', $user->nickname, $user->id, $e->getCode(), $e->getMessage()); common_log(LOG_WARNING, $errmsg); @@ -252,7 +252,7 @@ function broadcast_basicauth($notice, $flink) $errmsg = sprintf('cURL error trying to send notice to Twitter ' . 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: $4$s.', + 'code: %3$s message: %4$s.', $user->nickname, $user->id, $e->getCode(), $e->getMessage()); common_log(LOG_WARNING, $errmsg); From 20423af689fbf4dd139e1254dc49ae3df1db0de2 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Mon, 31 Aug 2009 10:33:37 -0400 Subject: [PATCH 18/32] Allow :'s in the path, query string, and fragment parts of the url (Mediawiki URLs often do this) --- lib/util.php | 6 +++--- tests/URLDetectionTest.php | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/util.php b/lib/util.php index 7228b3fe3e..6e79ffda4f 100644 --- a/lib/util.php +++ b/lib/util.php @@ -442,9 +442,9 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) { ')'. '(?:'. '(?:\:\d+)?'. //:port - '(?:/[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\%\~]*)?'. // /path - '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\%\~\/]*)?'. // ?query string - '(?:\#[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\%\~\/\?\#]*)?'. // #fragment + '(?:/[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~]*)?'. // /path + '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\/]*)?'. // ?query string + '(?:\#[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\/\?\#]*)?'. // #fragment ')(?127.0.0.1'), array('127.0.0.1:99', '127.0.0.1:99'), - array('127.0.0.1/test.php', - '127.0.0.1/test.php'), + array('127.0.0.1/Name:test.php', + '127.0.0.1/Name:test.php'), array('127.0.0.1/~test', '127.0.0.1/~test'), array('127.0.0.1/test%20stuff', From 9f372da3da4bd445175eda9155fa7fdd13d3c85e Mon Sep 17 00:00:00 2001 From: Sarven Capadisli Date: Mon, 31 Aug 2009 17:52:45 +0000 Subject: [PATCH 19/32] Removed
structure from MailboxAction::showMessage. Same as committ e0b877b26c5e93809b2a53b6c46326d5e31fa0e8. --- lib/mailbox.php | 30 ++++++++++++------------------ 1 file changed, 12 insertions(+), 18 deletions(-) diff --git a/lib/mailbox.php b/lib/mailbox.php index a09bf1060a..e1d384a063 100644 --- a/lib/mailbox.php +++ b/lib/mailbox.php @@ -213,26 +213,20 @@ class MailboxAction extends CurrentUserDesignAction } $this->elementStart('div', 'entry-content'); - $this->elementStart('dl', 'timestamp'); - $this->element('dt', null, _('Published')); - $this->elementStart('dd', null); - $dt = common_date_iso8601($message->created); $this->elementStart('a', array('rel' => 'bookmark', + 'class' => 'timestamp', 'href' => $messageurl)); + $dt = common_date_iso8601($message->created); $this->element('abbr', array('class' => 'published', 'title' => $dt), common_date_string($message->created)); $this->elementEnd('a'); - $this->elementEnd('dd'); - $this->elementEnd('dl'); if ($message->source) { - $this->elementStart('dl', 'device'); - $this->elementStart('dt'); - $this->text(_('From')); - $this->elementEnd('dt'); - $this->showSource($message->source); - $this->elementEnd('dl'); + $this->elementStart('span', 'source'); + $this->text(_('from')); + $this->element('span', 'device', $this->showSource($message->source)); + $this->elementEnd('span'); } $this->elementEnd('div'); @@ -277,18 +271,18 @@ class MailboxAction extends CurrentUserDesignAction case 'mail': case 'omb': case 'api': - $this->element('dd', null, $source_name); + $this->element('span', 'device', $source_name); break; default: $ns = Notice_source::staticGet($source); if ($ns) { - $this->elementStart('dd', null); + $this->elementStart('span', 'device'); $this->element('a', array('href' => $ns->url, - 'rel' => 'external'), - $ns->name); - $this->elementEnd('dd'); + 'rel' => 'external'), + $ns->name); + $this->elementEnd('span'); } else { - $this->element('dd', null, $source_name); + $this->out->element('span', 'device', $source_name); } break; } From 951a787877f450fc7f225cba4331f0763b71dbc2 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Mon, 31 Aug 2009 15:33:23 -0400 Subject: [PATCH 20/32] Fix attachment saving --- lib/util.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/util.php b/lib/util.php index 6e79ffda4f..f4ba3a6c21 100644 --- a/lib/util.php +++ b/lib/util.php @@ -495,7 +495,7 @@ function callback_helper($matches, $callback, $notice_id) { if(empty($notice_id)){ $result = call_user_func_array($callback,$url); }else{ - $result = call_user_func_array($callback, array($url,$notice_id) ); + $result = call_user_func_array($callback, array(array($url,$notice_id)) ); } return substr($matches[0],0,$left) . $result . substr($matches[0],$right); } From 3fd0a9693dc666478b29c85a045be3b084bc37e2 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Mon, 31 Aug 2009 15:49:11 -0400 Subject: [PATCH 21/32] Fix typo in Stomp Thanks Marcel|HSD --- extlib/Stomp.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extlib/Stomp.php b/extlib/Stomp.php index 9e1c97b3b3..abd9cba62b 100644 --- a/extlib/Stomp.php +++ b/extlib/Stomp.php @@ -454,7 +454,7 @@ class Stomp */ public function disconnect () { - $header = array(); + $headers = array(); if ($this->clientId != null) { $headers["client-id"] = $this->clientId; From 00032e11122463a3b69babc464caa26783c7c644 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Mon, 31 Aug 2009 22:16:49 -0400 Subject: [PATCH 22/32] Allow the oEmbed tag to be split across lines --- extlib/Services/oEmbed.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extlib/Services/oEmbed.php b/extlib/Services/oEmbed.php index 7d507b6f62..b05e3a1d14 100644 --- a/extlib/Services/oEmbed.php +++ b/extlib/Services/oEmbed.php @@ -303,7 +303,7 @@ class Services_oEmbed // Find all tags that have a valid oembed type set. We then // extract the href attribute for each type. $regexp = '#]*)type="' . - '(application/json|text/xml)\+oembed"([^>]*)>#i'; + '(application/json|text/xml)\+oembed"([^>]*)>#im'; $m = $ret = array(); if (!preg_match_all($regexp, $body, $m)) { From e0e30552cf069e71453f3c3bcf8dce960cf6b553 Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Tue, 1 Sep 2009 19:00:18 +0000 Subject: [PATCH 23/32] Stop requeuing notices not bound for Twitter. --- lib/twitter.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/twitter.php b/lib/twitter.php index 6555251943..455f7e7ef0 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -160,6 +160,8 @@ function broadcast_twitter($notice) return broadcast_basicauth($notice, $flink); } } + + return true; } function broadcast_oauth($notice, $flink) { From aa7bda4677f1fe7a6168665d4d5e29e8fa2db68c Mon Sep 17 00:00:00 2001 From: Carlos Perilla Date: Tue, 1 Sep 2009 09:19:10 -0500 Subject: [PATCH 24/32] Fixes foaf notices, use Profile for information that's missing in Remote_profile --- actions/foaf.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/actions/foaf.php b/actions/foaf.php index 4dae9dfc19..356393304e 100644 --- a/actions/foaf.php +++ b/actions/foaf.php @@ -146,8 +146,10 @@ class FoafAction extends Action 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) { common_debug('Got a bad subscription: '.print_r($sub,true)); @@ -158,12 +160,15 @@ class FoafAction extends Action } else { $person[$other->uri] = array(LISTENER, $other->id, - $other->nickname, + $profile->nickname, (empty($sub->token)) ? 'User' : 'Remote_profile'); } $other->free(); $other = null; unset($other); + $profile->free(); + $profile = null; + unset($profile); } } @@ -254,8 +259,10 @@ class FoafAction extends Action 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)) { common_debug('Got a bad subscription: '.print_r($sub,true)); @@ -264,11 +271,14 @@ class FoafAction extends Action $this->element('sioc:follows', array('rdf:resource' => $other->uri.'#acct')); $person[$other->uri] = array(LISTENEE, $other->id, - $other->nickname, + $profile->nickname, (empty($sub->token)) ? 'User' : 'Remote_profile'); $other->free(); $other = null; unset($other); + $profile->free(); + $profile = null; + unset($profile); } } From 5668959399dc953bb59b28e46690e51066d6ab3d Mon Sep 17 00:00:00 2001 From: Carlos Perilla Date: Tue, 11 Aug 2009 19:09:51 -0500 Subject: [PATCH 25/32] Let users join and drop group membership from xmpp --- lib/command.php | 105 ++++++++++++++++++++++++++++++++++++- lib/commandinterpreter.php | 20 +++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/lib/command.php b/lib/command.php index 91a20b8109..01b14f83e7 100644 --- a/lib/command.php +++ b/lib/command.php @@ -114,7 +114,6 @@ class StatsCommand extends Command class FavCommand extends Command { - var $other = null; function __construct($user, $other) @@ -158,6 +157,108 @@ class FavCommand extends Command $channel->output($this->user, _('Notice marked as fave.')); } + +} +class JoinCommand extends Command +{ + var $other = null; + + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } + + function execute($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; + } + + if ($cur->isMember($group)) { + $channel->error($cur, _('You are already a member of that group')); + return; + } + if (Group_block::isBlocked($group, $cur->getProfile())) { + $channel->error($cur, _('You have been blocked from that group by the admin.')); + return; + } + + $member = new Group_member(); + + $member->group_id = $group->id; + $member->profile_id = $cur->id; + $member->created = common_sql_now(); + + $result = $member->insert(); + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + $channel->error($cur, sprintf(_('Could not join user %s to group %s'), + $cur->nickname, $group->nickname)); + return; + } + + $channel->output($cur, sprintf(_('%s joined group %s'), + $cur->nickname, + $group->nickname)); + } + +} +class DropCommand extends Command +{ + var $other = null; + + function __construct($user, $other) + { + parent::__construct($user); + $this->other = $other; + } + + function execute($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; + } + + if (!$cur->isMember($group)) { + $channel->error($cur, _('You are not a member of that group.')); + return; + } + + $member = new Group_member(); + + $member->group_id = $group->id; + $member->profile_id = $cur->id; + + if (!$member->find(true)) { + $channel->error($cur,_('Could not find membership record.')); + return; + } + $result = $member->delete(); + if (!$result) { + common_log_db_error($member, 'INSERT', __FILE__); + $channel->error($cur, sprintf(_('Could not remove user %s to group %s'), + $cur->nickname, $group->nickname)); + return; + } + + $channel->output($cur, sprintf(_('%s left group %s'), + $cur->nickname, + $group->nickname)); + } + } class WhoisCommand extends Command @@ -392,6 +493,8 @@ class HelpCommand extends Command "get - get last notice from user\n". "whois - get profile info on user\n". "fav - add user's last notice as a 'fave'\n". + "join - join group\n". + "drop - leave group\n". "stats - get your stats\n". "stop - same as 'off'\n". "quit - same as 'off'\n". diff --git a/lib/commandinterpreter.php b/lib/commandinterpreter.php index ac6bf73c8e..6e4340e5dc 100644 --- a/lib/commandinterpreter.php +++ b/lib/commandinterpreter.php @@ -70,6 +70,26 @@ class CommandInterpreter } else { return new OffCommand($user); } + case 'join': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new JoinCommand($user, $other); + } + case 'drop': + if (!$arg) { + return null; + } + list($other, $extra) = explode(' ', $arg, 2); + if ($extra) { + return null; + } else { + return new DropCommand($user, $other); + } case 'follow': case 'sub': if (!$arg) { From 8b040eba4062bcb464f49e3e04d7a172a1d55c1e Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 2 Sep 2009 00:24:30 +0000 Subject: [PATCH 26/32] Fixed bug in which you cannot turn off importing friends timelines flag --- actions/twittersettings.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actions/twittersettings.php b/actions/twittersettings.php index 563d867a49..89169941eb 100644 --- a/actions/twittersettings.php +++ b/actions/twittersettings.php @@ -165,7 +165,7 @@ class TwittersettingsAction extends ConnectSettingsAction ($flink->noticesync & FOREIGN_NOTICE_RECV) : false); $this->elementEnd('li'); - + } else { // preserve setting even if bidrection bridge toggled off if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) { From f86fed357c07f1c1d2c633e8c2c730f205187e1a Mon Sep 17 00:00:00 2001 From: Zach Copley Date: Wed, 2 Sep 2009 00:50:41 +0000 Subject: [PATCH 27/32] Better error handling --- lib/twitter.php | 100 ++++++++++++++++++++---------------------------- 1 file changed, 42 insertions(+), 58 deletions(-) diff --git a/lib/twitter.php b/lib/twitter.php index 455f7e7ef0..676c9b20a2 100644 --- a/lib/twitter.php +++ b/lib/twitter.php @@ -175,34 +175,7 @@ function broadcast_oauth($notice, $flink) { try { $status = $client->statusesUpdate($statustxt); } catch (OAuthClientCurlException $e) { - - if ($e->getMessage() == 'The requested URL returned error: 401') { - - $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . - 'Twitter OAuth access token.', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - // Bad auth token! We need to delete the foreign_link - // to Twitter and inform the user. - - remove_twitter_link($flink); - return true; - - } else { - - // Some other error happened, so we should probably - // try to send again later. - - $errmsg = sprintf('cURL error trying to send notice to Twitter ' . - 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: %4$s.', - $user->nickname, $user->id, - $e->getCode(), $e->getMessage()); - common_log(LOG_WARNING, $errmsg); - - return false; - } + return process_error($e, $flink); } if (empty($status)) { @@ -210,9 +183,9 @@ function broadcast_oauth($notice, $flink) { // This could represent a failure posting, // or the Twitter API might just be behaving flakey. - $errmsg = sprintf('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); + $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' . + 'trying to send update for %1$s (user id %2$s).', + $user->nickname, $user->id); common_log(LOG_WARNING, $errmsg); return false; @@ -220,7 +193,7 @@ function broadcast_oauth($notice, $flink) { // Notice crossed the great divide - $msg = sprintf('Twitter bridge posted notice %s to Twitter using OAuth.', + $msg = sprintf('Twitter bridge - posted notice %s to Twitter using OAuth.', $notice->id); common_log(LOG_INFO, $msg); @@ -239,46 +212,57 @@ function broadcast_basicauth($notice, $flink) try { $status = $client->statusesUpdate($statustxt); } catch (BasicAuthCurlException $e) { - - if ($e->getMessage() == 'The requested URL returned error: 401') { - - $errmsg = sprintf('User %1$s (user id: %2$s) has an invalid ' . - 'Twitter screen_name/password combo.', - $user->nickname, $user->id); - common_log(LOG_WARNING, $errmsg); - - remove_twitter_link($flink); - return true; - - } else { - - $errmsg = sprintf('cURL error trying to send notice to Twitter ' . - 'for user %1$s (user id: %2$s) - ' . - 'code: %3$s message: %4$s.', - $user->nickname, $user->id, - $e->getCode(), $e->getMessage()); - common_log(LOG_WARNING, $errmsg); - - return false; - } + return process_error($e, $flink); } if (empty($status)) { - $errmsg = sprintf('No data returned by Twitter API when ' . - 'trying to send update for %1$s (user id %2$s).', - $user->nickname, $user->id); + $errmsg = sprintf('Twitter bridge - No data returned by Twitter API when ' . + 'trying to send update for %1$s (user id %2$s).', + $user->nickname, $user->id); common_log(LOG_WARNING, $errmsg); return false; } - $msg = sprintf('Twitter bridge posted notice %s to Twitter using basic auth.', + $msg = sprintf('Twitter bridge - posted notice %s to Twitter using basic auth.', $notice->id); common_log(LOG_INFO, $msg); return true; +} +function process_error($e, $flink) +{ + $user = $flink->getUser(); + $errmsg = $e->getMessage(); + $delivered = false; + + switch($errmsg) { + case 'The requested URL returned error: 401': + $logmsg = sprintf('Twiter bridge - User %1$s (user id: %2$s) has an invalid ' . + 'Twitter screen_name/password combo or an invalid acesss token.', + $user->nickname, $user->id); + $delivered = true; + remove_twitter_link($flink); + break; + case 'The requested URL returned error: 403': + $logmsg = sprintf('Twitter bridge - User %1$s (user id: %2$s) has exceeded ' . + 'his/her Twitter request limit.', + $user->nickname, $user->id); + break; + default: + $logmsg = sprintf('Twitter bridge - cURL error trying to send notice to Twitter ' . + 'for user %1$s (user id: %2$s) - ' . + 'code: %3$s message: %4$s.', + $user->nickname, $user->id, + $e->getCode(), $e->getMessage()); + break; + } + + common_log(LOG_WARNING, $logmsg); + + return $delivered; } function format_status($notice) From efcfd209ef737f4dbe99401df282e7a341176ea7 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Tue, 1 Sep 2009 23:02:03 -0400 Subject: [PATCH 28/32] Check "Files" of type 'application/xhtml+xml' for oEmbed in addition to just text/html --- classes/File.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classes/File.php b/classes/File.php index f4d0a3a488..96a4de6e8e 100644 --- a/classes/File.php +++ b/classes/File.php @@ -78,7 +78,7 @@ class File extends Memcached_DataObject $file_id = $x->insert(); if (isset($redir_data['type']) - && ('text/html' === substr($redir_data['type'], 0, 9)) + && (('text/html' === substr($redir_data['type'], 0, 9) || 'application/xhtml+xml' === substr($redir_data['type'], 0, 21))) && ($oembed_data = File_oembed::_getOembed($given_url))) { File_oembed::saveNew($oembed_data, $file_id); } From 29d0dd740c329c2674de58bec172419148a1b495 Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Tue, 1 Sep 2009 23:17:45 -0400 Subject: [PATCH 29/32] Allow whitespace before and after the = and require space before the href in html --- extlib/Services/oEmbed.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/extlib/Services/oEmbed.php b/extlib/Services/oEmbed.php index b05e3a1d14..0dc8f01b2f 100644 --- a/extlib/Services/oEmbed.php +++ b/extlib/Services/oEmbed.php @@ -256,7 +256,7 @@ class Services_oEmbed $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (substr($code, 0, 1) != '2') { - throw new Services_oEmbed_Exception('Non-200 code returned'); + throw new Services_oEmbed_Exception('Non-200 code returned. Got code ' . $code); } curl_close($ch); @@ -302,7 +302,7 @@ class Services_oEmbed // Find all tags that have a valid oembed type set. We then // extract the href attribute for each type. - $regexp = '#]*)type="' . + $regexp = '#]*)type[\s\n]*=[\s\n]*"' . '(application/json|text/xml)\+oembed"([^>]*)>#im'; $m = $ret = array(); @@ -314,7 +314,7 @@ class Services_oEmbed foreach ($m[0] as $i => $link) { $h = array(); - if (preg_match('/href="([^"]+)"/i', $link, $h)) { + if (preg_match('/[\s\n]+href[\s\n]*=[\s\n]*"([^"]+)"/im', $link, $h)) { $ret[$m[2][$i]] = $h[1]; } } @@ -347,7 +347,7 @@ class Services_oEmbed $code = curl_getinfo($ch, CURLINFO_HTTP_CODE); if (substr($code, 0, 1) != '2') { - throw new Services_oEmbed_Exception('Non-200 code returned'); + throw new Services_oEmbed_Exception('Non-200 code returned. Got code ' . $code); } return $result; From 637182e7546e1721a1a5746088cc96b1e1611536 Mon Sep 17 00:00:00 2001 From: Christopher Vollick Date: Thu, 3 Sep 2009 17:57:08 -0400 Subject: [PATCH 30/32] Updated Location of Bug Tracker in Contact Page. --- doc-src/contact | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc-src/contact b/doc-src/contact index 31f3a4d2b3..c63fcd01ad 100644 --- a/doc-src/contact +++ b/doc-src/contact @@ -13,7 +13,7 @@ Bugs ---- If you think you've found a bug in the [StatusNet](http://status.net/) software, -or if there's a new feature you'd like to see, add it into the [StatusNet bug database](http://status.net/PITS/HomePage). Don't forget to check the list of +or if there's a new feature you'd like to see, add it into the [StatusNet bug database](http://status.net/bugs/). Don't forget to check the list of existing bugs to make sure it hasn't already been reported! Email From 0ae4c7a80ce4869faac102386ed33f97a401ca0f Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Wed, 2 Sep 2009 13:33:54 -0400 Subject: [PATCH 31/32] The 'tidy' extension is a requirement Fixes http://status.net/trac/ticket/1844 --- install.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install.php b/install.php index a59b9469de..c49043e5c6 100644 --- a/install.php +++ b/install.php @@ -230,7 +230,7 @@ function checkPrereqs() } $reqs = array('gd', 'curl', - 'xmlwriter', 'mbstring'); + 'xmlwriter', 'mbstring','tidy'); foreach ($reqs as $req) { if (!checkExtension($req)) { From 0c95734a6874608ea5ed44608cabeda7c3a1b4ea Mon Sep 17 00:00:00 2001 From: Craig Andrews Date: Wed, 2 Sep 2009 13:39:05 -0400 Subject: [PATCH 32/32] Add Tidy requirement to the README --- README | 1 + 1 file changed, 1 insertion(+) diff --git a/README b/README index 3dd365e1f8..7562199811 100644 --- a/README +++ b/README @@ -146,6 +146,7 @@ Your PHP installation must include the following PHP extensions: - GD. For scaling down avatar images. - mbstring. For handling Unicode (UTF-8) encoded strings. - gettext. For multiple languages. Default on many PHP installs. +- tidy. Used to clean up HTML/URLs for the URL shortener to consume. For some functionality, you will also need the following extensions: