Merge remote branch 'laconica/0.8.x' into 0.9.x

Conflicts:
	lib/common.php
	lib/twitter.php
This commit is contained in:
Craig Andrews 2009-09-02 16:42:15 -04:00
commit a535ccdc4e
25 changed files with 736 additions and 179 deletions

1
README
View File

@ -146,6 +146,7 @@ Your PHP installation must include the following PHP extensions:
- GD. For scaling down avatar images. - GD. For scaling down avatar images.
- mbstring. For handling Unicode (UTF-8) encoded strings. - mbstring. For handling Unicode (UTF-8) encoded strings.
- gettext. For multiple languages. Default on many PHP installs. - 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: For some functionality, you will also need the following extensions:

View File

@ -133,6 +133,8 @@ class ApiAction extends Action
'groups/show', 'groups/show',
'groups/timeline', 'groups/timeline',
'groups/list_all', 'groups/list_all',
'groups/membership',
'groups/is_member',
'groups/timeline'); 'groups/timeline');
static $bareauth = array('statuses/user_timeline', static $bareauth = array('statuses/user_timeline',

View File

@ -146,8 +146,10 @@ class FoafAction extends Action
while ($sub->fetch()) { while ($sub->fetch()) {
if ($sub->token) { if ($sub->token) {
$other = Remote_profile::staticGet('id', $sub->subscriber); $other = Remote_profile::staticGet('id', $sub->subscriber);
$profile = Profile::staticGet('id', $sub->subscriber);
} else { } else {
$other = User::staticGet('id', $sub->subscriber); $other = User::staticGet('id', $sub->subscriber);
$profile = Profile::staticGet('id', $sub->subscriber);
} }
if (!$other) { if (!$other) {
common_debug('Got a bad subscription: '.print_r($sub,true)); common_debug('Got a bad subscription: '.print_r($sub,true));
@ -158,12 +160,15 @@ class FoafAction extends Action
} else { } else {
$person[$other->uri] = array(LISTENER, $person[$other->uri] = array(LISTENER,
$other->id, $other->id,
$other->nickname, $profile->nickname,
(empty($sub->token)) ? 'User' : 'Remote_profile'); (empty($sub->token)) ? 'User' : 'Remote_profile');
} }
$other->free(); $other->free();
$other = null; $other = null;
unset($other); unset($other);
$profile->free();
$profile = null;
unset($profile);
} }
} }
@ -254,8 +259,10 @@ class FoafAction extends Action
while ($sub->fetch()) { while ($sub->fetch()) {
if (!empty($sub->token)) { if (!empty($sub->token)) {
$other = Remote_profile::staticGet('id', $sub->subscribed); $other = Remote_profile::staticGet('id', $sub->subscribed);
$profile = Profile::staticGet('id', $sub->subscribed);
} else { } else {
$other = User::staticGet('id', $sub->subscribed); $other = User::staticGet('id', $sub->subscribed);
$profile = Profile::staticGet('id', $sub->subscribed);
} }
if (empty($other)) { if (empty($other)) {
common_debug('Got a bad subscription: '.print_r($sub,true)); 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')); $this->element('sioc:follows', array('rdf:resource' => $other->uri.'#acct'));
$person[$other->uri] = array(LISTENEE, $person[$other->uri] = array(LISTENEE,
$other->id, $other->id,
$other->nickname, $profile->nickname,
(empty($sub->token)) ? 'User' : 'Remote_profile'); (empty($sub->token)) ? 'User' : 'Remote_profile');
$other->free(); $other->free();
$other = null; $other = null;
unset($other); unset($other);
$profile->free();
$profile = null;
unset($profile);
} }
} }

View File

@ -21,7 +21,7 @@
* *
* @category Twitter * @category Twitter
* @package StatusNet * @package StatusNet
* @author Craig Andrews * @author Craig Andrews <candrews@integralblue.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 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 * @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 * @category Twitter
* @package StatusNet * @package StatusNet
* @author Craig Andrews * @author Craig Andrews <candrews@integralblue.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 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 * @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);
}
}
} }

View File

@ -21,7 +21,7 @@
* *
* @category Twitter * @category Twitter
* @package StatusNet * @package StatusNet
* @author Craig Andrews * @author Craig Andrews <candrews@integralblue.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 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 * @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 * @category Twitter
* @package StatusNet * @package StatusNet
* @author Craig Andrews * @author Craig Andrews <candrews@integralblue.com>
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @copyright 2009 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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0

View File

@ -165,7 +165,7 @@ class TwittersettingsAction extends ConnectSettingsAction
($flink->noticesync & FOREIGN_NOTICE_RECV) : ($flink->noticesync & FOREIGN_NOTICE_RECV) :
false); false);
$this->elementEnd('li'); $this->elementEnd('li');
} else {
// preserve setting even if bidrection bridge toggled off // preserve setting even if bidrection bridge toggled off
if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) { if ($flink && ($flink->noticesync & FOREIGN_NOTICE_RECV)) {

View File

@ -78,14 +78,14 @@ class File extends Memcached_DataObject
$file_id = $x->insert(); $file_id = $x->insert();
if (isset($redir_data['type']) 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))) { && ($oembed_data = File_oembed::_getOembed($given_url))) {
File_oembed::saveNew($oembed_data, $file_id); File_oembed::saveNew($oembed_data, $file_id);
} }
return $x; 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 if (empty($given_url)) return -1; // error, no url to process
$given_url = File_redirection::_canonUrl($given_url); $given_url = File_redirection::_canonUrl($given_url);
if (empty($given_url)) return -1; // error, no url to process 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_data = File_redirection::where($given_url);
$redir_url = $redir_data['url']; $redir_url = $redir_data['url'];
// TODO: max field length // 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); $x = File::saveNew($redir_data, $given_url);
$file_id = $x->id; $file_id = $x->id;
} else { } 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; return $x;
} }

View File

@ -56,7 +56,7 @@ class Notice extends Memcached_DataObject
/* the code above is auto generated do not remove the tag below */ /* the code above is auto generated do not remove the tag below */
###END_AUTOCODE ###END_AUTOCODE
/* Notice types */ /* Notice types */
const LOCAL_PUBLIC = 1; const LOCAL_PUBLIC = 1;
const REMOTE_OMB = 0; const REMOTE_OMB = 0;
const LOCAL_NONPUBLIC = -1; const LOCAL_NONPUBLIC = -1;
@ -260,17 +260,6 @@ class Notice extends Memcached_DataObject
$notice->saveUrls(); $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'); $notice->query('COMMIT');
Event::handle('EndNoticeSave', array($notice)); Event::handle('EndNoticeSave', array($notice));

View File

@ -54,7 +54,7 @@ class Status_network extends DB_DataObject
global $config; global $config;
$config['db']['database_'.$dbname] = "mysqli://$dbuser:$dbpass@$dbhost/$dbname"; $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; $config['db']['table_status_network'] = $dbname;
self::$cache = new Memcache(); self::$cache = new Memcache();

View File

@ -13,7 +13,7 @@ Bugs
---- ----
If you think you've found a bug in the [StatusNet](http://status.net/) software, 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! existing bugs to make sure it hasn't already been reported!
Email Email

View File

@ -256,7 +256,7 @@ class Services_oEmbed
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (substr($code, 0, 1) != '2') { 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); curl_close($ch);
@ -302,8 +302,8 @@ class Services_oEmbed
// Find all <link /> tags that have a valid oembed type set. We then // Find all <link /> tags that have a valid oembed type set. We then
// extract the href attribute for each type. // extract the href attribute for each type.
$regexp = '#<link([^>]*)type="' . $regexp = '#<link([^>]*)type[\s\n]*=[\s\n]*"' .
'(application/json|text/xml)\+oembed"([^>]*)>#i'; '(application/json|text/xml)\+oembed"([^>]*)>#im';
$m = $ret = array(); $m = $ret = array();
if (!preg_match_all($regexp, $body, $m)) { if (!preg_match_all($regexp, $body, $m)) {
@ -314,7 +314,7 @@ class Services_oEmbed
foreach ($m[0] as $i => $link) { foreach ($m[0] as $i => $link) {
$h = array(); $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]; $ret[$m[2][$i]] = $h[1];
} }
} }
@ -347,7 +347,7 @@ class Services_oEmbed
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if (substr($code, 0, 1) != '2') { 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; return $result;

View File

@ -454,7 +454,7 @@ class Stomp
*/ */
public function disconnect () public function disconnect ()
{ {
$header = array(); $headers = array();
if ($this->clientId != null) { if ($this->clientId != null) {
$headers["client-id"] = $this->clientId; $headers["client-id"] = $this->clientId;

View File

@ -230,7 +230,7 @@ function checkPrereqs()
} }
$reqs = array('gd', 'curl', $reqs = array('gd', 'curl',
'xmlwriter', 'mbstring'); 'xmlwriter', 'mbstring','tidy');
foreach ($reqs as $req) { foreach ($reqs as $req) {
if (!checkExtension($req)) { if (!checkExtension($req)) {

View File

@ -114,7 +114,6 @@ class StatsCommand extends Command
class FavCommand extends Command class FavCommand extends Command
{ {
var $other = null; var $other = null;
function __construct($user, $other) function __construct($user, $other)
@ -158,6 +157,108 @@ class FavCommand extends Command
$channel->output($this->user, _('Notice marked as fave.')); $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 class WhoisCommand extends Command
@ -396,6 +497,8 @@ class HelpCommand extends Command
"get <nickname> - get last notice from user\n". "get <nickname> - get last notice from user\n".
"whois <nickname> - get profile info on user\n". "whois <nickname> - get profile info on user\n".
"fav <nickname> - add user's last notice as a 'fave'\n". "fav <nickname> - add user's last notice as a 'fave'\n".
"join <group> - join group\n".
"drop <group> - leave group\n".
"stats - get your stats\n". "stats - get your stats\n".
"stop - same as 'off'\n". "stop - same as 'off'\n".
"quit - same as 'off'\n". "quit - same as 'off'\n".

View File

@ -70,6 +70,26 @@ class CommandInterpreter
} else { } else {
return new OffCommand($user); 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 'follow':
case 'sub': case 'sub':
if (!$arg) { if (!$arg) {

View File

@ -213,26 +213,20 @@ class MailboxAction extends CurrentUserDesignAction
} }
$this->elementStart('div', 'entry-content'); $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', $this->elementStart('a', array('rel' => 'bookmark',
'class' => 'timestamp',
'href' => $messageurl)); 'href' => $messageurl));
$dt = common_date_iso8601($message->created);
$this->element('abbr', array('class' => 'published', $this->element('abbr', array('class' => 'published',
'title' => $dt), 'title' => $dt),
common_date_string($message->created)); common_date_string($message->created));
$this->elementEnd('a'); $this->elementEnd('a');
$this->elementEnd('dd');
$this->elementEnd('dl');
if ($message->source) { if ($message->source) {
$this->elementStart('dl', 'device'); $this->elementStart('span', 'source');
$this->elementStart('dt'); $this->text(_('from'));
$this->text(_('From')); $this->element('span', 'device', $this->showSource($message->source));
$this->elementEnd('dt'); $this->elementEnd('span');
$this->showSource($message->source);
$this->elementEnd('dl');
} }
$this->elementEnd('div'); $this->elementEnd('div');
@ -277,18 +271,18 @@ class MailboxAction extends CurrentUserDesignAction
case 'mail': case 'mail':
case 'omb': case 'omb':
case 'api': case 'api':
$this->element('dd', null, $source_name); $this->element('span', 'device', $source_name);
break; break;
default: default:
$ns = Notice_source::staticGet($source); $ns = Notice_source::staticGet($source);
if ($ns) { if ($ns) {
$this->elementStart('dd', null); $this->elementStart('span', 'device');
$this->element('a', array('href' => $ns->url, $this->element('a', array('href' => $ns->url,
'rel' => 'external'), 'rel' => 'external'),
$ns->name); $ns->name);
$this->elementEnd('dd'); $this->elementEnd('span');
} else { } else {
$this->element('dd', null, $source_name); $this->out->element('span', 'device', $source_name);
} }
break; break;
} }

View File

@ -22,7 +22,7 @@
* @category Action * @category Action
* @package StatusNet * @package StatusNet
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */

View File

@ -154,83 +154,134 @@ function broadcast_twitter($notice)
TWITTER_SERVICE); TWITTER_SERVICE);
if (is_twitter_bound($notice, $flink)) { if (is_twitter_bound($notice, $flink)) {
if (TwitterOAuthClient::isPackedToken($flink->credentials)) {
$user = $flink->getUser(); return broadcast_oauth($notice, $flink);
} else {
// XXX: Hack to get around PHP cURL's use of @ being a a meta character return broadcast_basicauth($notice, $flink);
$statustxt = preg_replace('/^@/', ' @', $notice->content);
// Convert !groups to #hashes
$statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);
$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 (empty($status)) { return true;
}
// This could represent a failure posting, function broadcast_oauth($notice, $flink) {
// or the Twitter API might just be behaving flakey. $user = $flink->getUser();
$statustxt = format_status($notice);
// Convert !groups to #hashes
$statustxt = preg_replace('/(^|\s)!([A-Za-z0-9]{1,64})/', "\\1#\\2", $statustxt);
$token = TwitterOAuthClient::unpackToken($flink->credentials);
$client = new TwitterOAuthClient($token->key, $token->secret);
$status = null;
try {
$status = $client->statusesUpdate($statustxt);
} catch (OAuthClientCurlException $e) {
return process_error($e, $flink);
}
if (empty($status)) {
// This could represent a failure posting,
// or the Twitter API might just be behaving flakey.
$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;
}
// Notice crossed the great divide
$msg = sprintf('Twitter bridge - posted notice %s to Twitter using OAuth.',
$notice->id);
common_log(LOG_INFO, $msg);
return true;
}
function broadcast_basicauth($notice, $flink)
{
$user = $flink->getUser();
$statustxt = format_status($notice);
$client = new TwitterBasicAuthClient($flink);
$status = null;
try {
$status = $client->statusesUpdate($statustxt);
} catch (BasicAuthCurlException $e) {
return process_error($e, $flink);
}
if (empty($status)) {
$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);
$errmsg = sprintf('No data returned by Twitter API when ' . $errmsg = sprintf('No data returned by Twitter API when ' .
'trying to send update for %1$s (user id %2$s).', 'trying to send update for %1$s (user id %2$s).',
$user->nickname, $user->id); $user->nickname, $user->id);
common_log(LOG_WARNING, $errmsg); common_log(LOG_WARNING, $errmsg);
return false;
return false;
}
// Notice crossed the great divide
$msg = sprintf('Twitter bridge posted notice %s to Twitter.',
$notice->id);
common_log(LOG_INFO, $msg);
} }
$msg = sprintf('Twitter bridge - posted notice %s to Twitter using basic auth.',
$notice->id);
common_log(LOG_INFO, $msg);
return true; 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)
{
// 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) function remove_twitter_link($flink)
{ {
$user = $flink->getUser(); $user = $flink->getUser();
common_log(LOG_INFO, 'Removing Twitter bridge Foreign link for ' . 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(); $result = $flink->delete();

View File

@ -788,6 +788,52 @@ class TwitterapiAction extends Action
$this->end_document('xml'); $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) function show_single_json_group($group)
{ {
$this->init_document('json'); $this->init_document('json');

View File

@ -0,0 +1,236 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Class for doing OAuth calls against Twitter
*
* PHP version 5
*
* LICENCE: This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Integration
* @package StatusNet
* @author Zach Copley <zach@status.net>
* @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 <zach@status.net>
* @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 <zach@status.net>
* @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 /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
* 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);
$status = json_decode($response);
return $status;
}
/**
* 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
* @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);
$statuses = json_decode($response);
return $statuses;
}
/**
* 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
* @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 /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
* @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 = true)
{
$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;
}
}

View File

@ -22,7 +22,7 @@
* @category Integration * @category Integration
* @package StatusNet * @package StatusNet
* @author Zach Copley <zach@status.net> * @author Zach Copley <zach@status.net>
* @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 * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0
* @link http://status.net/ * @link http://status.net/
*/ */
@ -81,6 +81,15 @@ class TwitterOAuthClient extends OAuthClient
return new OAuthToken($vals[0], $vals[1]); 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 * Builds a link to Twitter's endpoint for authorizing a request token
* *

View File

@ -413,7 +413,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
// Start off with a regex // Start off with a regex
$regex = '#'. $regex = '#'.
'(?:^|[\s\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'. '(?:^|[\s\(\)\[\]\{\}\\\'\\\";]+)(?![\@\!\#])'.
'(?P<url>'. '('.
'(?:'. '(?:'.
'(?:'. //Known protocols '(?:'. //Known protocols
'(?:'. '(?:'.
@ -421,7 +421,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
'|'. '|'.
'(?:(?:mailto|aim|tel|xmpp):)'. '(?:(?:mailto|aim|tel|xmpp):)'.
')'. ')'.
'(?:[\pN\pL\-\_\+]+(?::[\pN\pL\-\_\+]+)?\@)?'. //user:pass@ '(?:[\pN\pL\-\_\+\%\~]+(?::[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
'(?:'. '(?:'.
'(?:'. '(?:'.
'\[[\pN\pL\-\_\:\.]+(?<![\.\:])\]'. //[dns] '\[[\pN\pL\-\_\:\.]+(?<![\.\:])\]'. //[dns]
@ -434,7 +434,7 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
'|(?:'. //IPv6 '|(?:'. //IPv6
'\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?'. '\[?(?:(?:(?:[0-9A-Fa-f]{1,4}:){7}(?:(?:[0-9A-Fa-f]{1,4})|:))|(?:(?:[0-9A-Fa-f]{1,4}:){6}(?::|(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})|(?::[0-9A-Fa-f]{1,4})))|(?:(?:[0-9A-Fa-f]{1,4}:){5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){4}(?::[0-9A-Fa-f]{1,4}){0,1}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){3}(?::[0-9A-Fa-f]{1,4}){0,2}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:){2}(?::[0-9A-Fa-f]{1,4}){0,3}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:[0-9A-Fa-f]{1,4}:)(?::[0-9A-Fa-f]{1,4}){0,4}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?::(?::[0-9A-Fa-f]{1,4}){0,5}(?:(?::(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})?)|(?:(?::[0-9A-Fa-f]{1,4}){1,2})))|(?:(?:(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})(?:\.(?:25[0-5]|2[0-4]\d|[01]?\d{1,2})){3})))\]?'.
')|(?:'. //DNS ')|(?:'. //DNS
'(?:[\pN\pL\-\_\+]+(?:\:[\pN\pL\-\_\+]+)?\@)?'. //user:pass@ '(?:[\pN\pL\-\_\+\%\~]+(?:\:[\pN\pL\-\_\+\%\~]+)?\@)?'. //user:pass@
'[\pN\pL\-\_]+(?:\.[\pN\pL\-\_]+)*\.'. '[\pN\pL\-\_]+(?:\.[\pN\pL\-\_]+)*\.'.
//tld list from http://data.iana.org/TLD/tlds-alpha-by-domain.txt, also added local, loc, and onion //tld list from http://data.iana.org/TLD/tlds-alpha-by-domain.txt, also added local, loc, and onion
'(?:AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN--0ZWM56D|测试|XN--11B5BS3A9AJ6G|परीक्षा|XN--80AKHBYKNJ4F|испытание|XN--9T4B11YI5A|테스트|XN--DEBA0AD|טעסט|XN--G6W251D|測試|XN--HGBK6AJ7F53BBA|آزمایشی|XN--HLCJ6AYA9ESC7A|பரிட்சை|XN--JXALPDLP|δοκιμή|XN--KGBECHTV|إختبار|XN--ZCKZAH|テスト|YE|YT|YU|ZA|ZM|ZW|local|loc|onion)'. '(?:AC|AD|AE|AERO|AF|AG|AI|AL|AM|AN|AO|AQ|AR|ARPA|AS|ASIA|AT|AU|AW|AX|AZ|BA|BB|BD|BE|BF|BG|BH|BI|BIZ|BJ|BM|BN|BO|BR|BS|BT|BV|BW|BY|BZ|CA|CAT|CC|CD|CF|CG|CH|CI|CK|CL|CM|CN|CO|COM|COOP|CR|CU|CV|CX|CY|CZ|DE|DJ|DK|DM|DO|DZ|EC|EDU|EE|EG|ER|ES|ET|EU|FI|FJ|FK|FM|FO|FR|GA|GB|GD|GE|GF|GG|GH|GI|GL|GM|GN|GOV|GP|GQ|GR|GS|GT|GU|GW|GY|HK|HM|HN|HR|HT|HU|ID|IE|IL|IM|IN|INFO|INT|IO|IQ|IR|IS|IT|JE|JM|JO|JOBS|JP|KE|KG|KH|KI|KM|KN|KP|KR|KW|KY|KZ|LA|LB|LC|LI|LK|LR|LS|LT|LU|LV|LY|MA|MC|MD|ME|MG|MH|MIL|MK|ML|MM|MN|MO|MOBI|MP|MQ|MR|MS|MT|MU|MUSEUM|MV|MW|MX|MY|MZ|NA|NAME|NC|NE|NET|NF|NG|NI|NL|NO|NP|NR|NU|NZ|OM|ORG|PA|PE|PF|PG|PH|PK|PL|PM|PN|PR|PRO|PS|PT|PW|PY|QA|RE|RO|RS|RU|RW|SA|SB|SC|SD|SE|SG|SH|SI|SJ|SK|SL|SM|SN|SO|SR|ST|SU|SV|SY|SZ|TC|TD|TEL|TF|TG|TH|TJ|TK|TL|TM|TN|TO|TP|TR|TRAVEL|TT|TV|TW|TZ|UA|UG|UK|US|UY|UZ|VA|VC|VE|VG|VI|VN|VU|WF|WS|XN--0ZWM56D|测试|XN--11B5BS3A9AJ6G|परीक्षा|XN--80AKHBYKNJ4F|испытание|XN--9T4B11YI5A|테스트|XN--DEBA0AD|טעסט|XN--G6W251D|測試|XN--HGBK6AJ7F53BBA|آزمایشی|XN--HLCJ6AYA9ESC7A|பரிட்சை|XN--JXALPDLP|δοκιμή|XN--KGBECHTV|إختبار|XN--ZCKZAH|テスト|YE|YT|YU|ZA|ZM|ZW|local|loc|onion)'.
@ -442,22 +442,22 @@ function common_replace_urls_callback($text, $callback, $notice_id = null) {
')'. ')'.
'(?:'. '(?:'.
'(?:\:\d+)?'. //:port '(?:\:\d+)?'. //:port
'(?:/[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;]*)?'. // /path '(?:/[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~]*)?'. // /path
'(?:\?[\pN\pL\$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\/]*)?'. // ?query string '(?:\?[\pN\pL\$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\/]*)?'. // ?query string
'(?:\#[\pN\pL$\[\]\,\!\(\)\.\-\_\+\/\=\&\;\/\?\#]*)?'. // #fragment '(?:\#[\pN\pL$\[\]\,\!\(\)\.\:\-\_\+\/\=\&\;\%\~\/\?\#]*)?'. // #fragment
')(?<![\?\.\,\#\,])'. ')(?<![\?\.\,\#\,])'.
')'. ')'.
'#ixu'; '#ixu';
preg_match_all($regex,$text,$matches); preg_match_all($regex,$text,$matches);
//print_r($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) { function callback_helper($matches, $callback, $notice_id) {
$url=$matches['url']; $url=$matches[1];
$left = strpos($matches[0],$url); $left = strpos($matches[0],$url);
$right = $left+strlen($url); $right = $left+strlen($url);
$groupSymbolSets=array( $groupSymbolSets=array(
array( array(
'left'=>'(', 'left'=>'(',
@ -491,13 +491,11 @@ function callback_helper($matches, $callback, $notice_id) {
$url=substr($url,0,-1); $url=substr($url,0,-1);
} }
}while($original_url!=$url); }while($original_url!=$url);
if(empty($notice_id)){ if(empty($notice_id)){
$result = call_user_func_array($callback,$url); $result = call_user_func_array($callback,$url);
}else{ }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); return substr($matches[0],0,$left) . $result . substr($matches[0],$right);
} }
@ -508,16 +506,13 @@ function curry($fn) {
array_shift($args); array_shift($args);
$id = uniqid('_partial'); $id = uniqid('_partial');
$GLOBALS[$id] = array($fn, $args); $GLOBALS[$id] = array($fn, $args);
return create_function( return create_function('',
'', '$args = func_get_args(); '.
' 'return call_user_func_array('.
$args = func_get_args(); '$GLOBALS["'.$id.'"][0],'.
return call_user_func_array( 'array_merge('.
$GLOBALS["'.$id.'"][0], '$args,'.
array_merge( '$GLOBALS["'.$id.'"][1]));');
$args,
$GLOBALS["'.$id.'"][1]));
');
} }
function common_linkify($url) { function common_linkify($url) {
@ -525,7 +520,7 @@ function common_linkify($url) {
// functions // functions
$url = htmlspecialchars_decode($url); $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 //url is an email address without the mailto: protocol
return XMLStringer::estring('a', array('href' => "mailto:$url", 'rel' => 'external'), $url); return XMLStringer::estring('a', array('href' => "mailto:$url", 'rel' => 'external'), $url);
} }
@ -547,42 +542,30 @@ function common_linkify($url) {
$attachment_id = null; $attachment_id = null;
$has_thumb = false; $has_thumb = false;
// Check to see whether there's a filename associated with this URL. // Check to see whether this is a known "attachment" URL.
// If there is, it's an upload and qualifies as an attachment
$localfile = File::staticGet('url', $longurl); $f = File::staticGet('url', $longurl);
if (!empty($localfile)) { if (empty($f)) {
if (isset($localfile->filename)) { // XXX: this writes to the database. :<
$is_attachment = true; $f = File::processNew($longurl);
$attachment_id = $localfile->id;
}
} }
// if this URL is an attachment, then we set class='attachment' and id='attahcment-ID' if (!empty($f)) {
// where ID is the id of the attachment for the given URL. if (isset($f->filename)) {
// $is_attachment = true;
// we need a better test telling what can be shown as an attachment $attachment_id = $f->id;
// we're currently picking up oembeds only. } else { // if it has OEmbed info, it's an attachment, too
// I think the best option is another file_view table in the db $foe = File_oembed::staticGet('file_id', $f->id);
// and associated dbobject. if (!empty($foe)) {
$is_attachment = true;
$attachment_id = $f->id;
$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'"; $thumb = File_thumbnail::staticGet('file_id', $f->id);
$file = new File; if (!empty($thumb)) {
$file->query($query); $has_thumb = true;
$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;
} }
} }

View File

@ -142,13 +142,20 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
{ {
$friends = array(); $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 { try {
$friends_ids = $client->friendsIds(); $friends_ids = $client->friendsIds();
} catch (OAuthCurlException $e) { } catch (Exception $e) {
common_log(LOG_WARNING, $this->name() . common_log(LOG_WARNING, $this->name() .
' - cURL error getting friend ids ' . ' - cURL error getting friend ids ' .
$e->getCode() . ' - ' . $e->getMessage()); $e->getCode() . ' - ' . $e->getMessage());
@ -177,7 +184,7 @@ class SyncTwitterFriendsDaemon extends ParallelizingDaemon
try { try {
$more_friends = $client->statusesFriends(null, null, null, $i); $more_friends = $client->statusesFriends(null, null, null, $i);
} catch (OAuthCurlException $e) { } catch (Exception $e) {
common_log(LOG_WARNING, $this->name() . common_log(LOG_WARNING, $this->name() .
' - cURL error getting Twitter statuses/friends ' . ' - cURL error getting Twitter statuses/friends ' .
"page $i - " . $e->getCode() . ' - ' . "page $i - " . $e->getCode() . ' - ' .

View File

@ -148,9 +148,9 @@ class TwitterStatusFetcher extends ParallelizingDaemon
function getTimeline($flink) function getTimeline($flink)
{ {
if (empty($flink)) { if (empty($flink)) {
common_log(LOG_WARNING, $this->name() . 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; return;
} }
@ -161,17 +161,24 @@ class TwitterStatusFetcher extends ParallelizingDaemon
// to start importing? How many statuses? Right now I'm going // to start importing? How many statuses? Right now I'm going
// with the default last 20. // 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; $timeline = null;
try { try {
$timeline = $client->statusesFriendsTimeline(); $timeline = $client->statusesFriendsTimeline();
} catch (OAuthClientCurlException $e) { } catch (Exception $e) {
common_log(LOG_WARNING, $this->name() . 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: ' . $flink->user_id . ' - code: ' .
$e->getCode() . 'msg: ' . $e->getMessage()); $e->getCode() . 'msg: ' . $e->getMessage());
} }

View File

@ -31,8 +31,12 @@ class URLDetectionTest extends PHPUnit_Framework_TestCase
'<a href="http://127.0.0.1/" rel="external">127.0.0.1</a>'), '<a href="http://127.0.0.1/" rel="external">127.0.0.1</a>'),
array('127.0.0.1:99', array('127.0.0.1:99',
'<a href="http://127.0.0.1:99/" rel="external">127.0.0.1:99</a>'), '<a href="http://127.0.0.1:99/" rel="external">127.0.0.1:99</a>'),
array('127.0.0.1/test.php', array('127.0.0.1/Name:test.php',
'<a href="http://127.0.0.1/test.php" rel="external">127.0.0.1/test.php</a>'), '<a href="http://127.0.0.1/Name:test.php" rel="external">127.0.0.1/Name:test.php</a>'),
array('127.0.0.1/~test',
'<a href="http://127.0.0.1/~test" rel="external">127.0.0.1/~test</a>'),
array('127.0.0.1/test%20stuff',
'<a href="http://127.0.0.1/test%20stuff" rel="external">127.0.0.1/test%20stuff</a>'),
array('http://[::1]:99/test.php', array('http://[::1]:99/test.php',
'<a href="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'), '<a href="http://[::1]:99/test.php" rel="external">http://[::1]:99/test.php</a>'),
array('http://::1/test.php', array('http://::1/test.php',