Merge branch '0.9.x' of git://gitorious.org/statusnet/mainline into 0.9.x

This commit is contained in:
Brion Vibber 2009-09-20 21:13:00 -07:00
commit f7f8ac87a7
19 changed files with 739 additions and 389 deletions

View File

@ -1,5 +1,5 @@
<?php <?php
/* /**
* StatusNet - the distributed open-source microblogging tool * StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc. * Copyright (C) 2008, 2009, StatusNet, Inc.
* *
@ -15,9 +15,25 @@
* *
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Actions
* @package Actions
* @author Evan Prodromou <evan@status.net>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <millette@controlyourself.ca>
* @author Adrian Lang <mail@adrianlang.de>
* @author Meitar Moscovitz <meitarm@gmail.com>
* @author Sarven Capadisli <csarven@status.net>
* @author Craig Andrews <candrews@integralblue.com>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Zach Copley <zach@controlyourself.ca>
* @license GNU Affero General Public License http://www.gnu.org/licenses/
* @link http://status.net
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
require_once INSTALLDIR.'/lib/personalgroupnav.php'; require_once INSTALLDIR.'/lib/personalgroupnav.php';
require_once INSTALLDIR.'/lib/noticelist.php'; require_once INSTALLDIR.'/lib/noticelist.php';
@ -43,8 +59,8 @@ class AllAction extends ProfileAction
$this->notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1); $this->notice = $this->user->noticesWithFriends(($this->page-1)*NOTICES_PER_PAGE, NOTICES_PER_PAGE + 1);
} }
if($this->page > 1 && $this->notice->N == 0){ if ($this->page > 1 && $this->notice->N == 0) {
$this->serverError(_('No such page'),$code=404); $this->serverError(_('No such page'), $code = 404);
} }
return true; return true;
@ -73,20 +89,33 @@ class AllAction extends ProfileAction
function getFeeds() function getFeeds()
{ {
return array(new Feed(Feed::RSS1, return array(
common_local_url('allrss', array('nickname' => new Feed(Feed::RSS1,
$this->user->nickname)), common_local_url(
sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)), 'allrss', array(
new Feed(Feed::RSS2, 'nickname' =>
common_local_url('api', array('apiaction' => 'statuses', $this->user->nickname)
'method' => 'friends_timeline', ),
'argument' => $this->user->nickname.'.rss')), sprintf(_('Feed for friends of %s (RSS 1.0)'), $this->user->nickname)),
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)), new Feed(Feed::RSS2,
new Feed(Feed::ATOM, common_local_url(
common_local_url('api', array('apiaction' => 'statuses', 'api', array(
'method' => 'friends_timeline', 'apiaction' => 'statuses',
'argument' => $this->user->nickname.'.atom')), 'method' => 'friends_timeline',
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))); 'argument' => $this->user->nickname.'.rss'
)
),
sprintf(_('Feed for friends of %s (RSS 2.0)'), $this->user->nickname)),
new Feed(Feed::ATOM,
common_local_url(
'api', array(
'apiaction' => 'statuses',
'method' => 'friends_timeline',
'argument' => $this->user->nickname.'.atom'
)
),
sprintf(_('Feed for friends of %s (Atom)'), $this->user->nickname))
);
} }
function showLocalNav() function showLocalNav()
@ -106,8 +135,7 @@ class AllAction extends ProfileAction
} else { } else {
$message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname); $message .= sprintf(_('You can try to [nudge %s](../%s) from his profile or [post something to his or her attention](%%%%action.newnotice%%%%?status_textarea=%s).'), $this->user->nickname, $this->user->nickname, '@' . $this->user->nickname);
} }
} } else {
else {
$message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname); $message .= sprintf(_('Why not [register an account](%%%%action.register%%%%) and then nudge %s or post a notice to his or her attention.'), $this->user->nickname);
} }
@ -126,17 +154,19 @@ class AllAction extends ProfileAction
$this->showEmptyListMessage(); $this->showEmptyListMessage();
} }
$this->pagination($this->page > 1, $cnt > NOTICES_PER_PAGE, $this->pagination(
$this->page, 'all', array('nickname' => $this->user->nickname)); $this->page > 1, $cnt > NOTICES_PER_PAGE,
$this->page, 'all', array('nickname' => $this->user->nickname)
);
} }
function showPageTitle() function showPageTitle()
{ {
$user =& common_current_user(); $user =& common_current_user();
if ($user && ($user->id == $this->user->id)) { if ($user && ($user->id == $this->user->id)) {
$this->element('h1', NULL, _("You and friends")); $this->element('h1', null, _("You and friends"));
} else { } else {
$this->element('h1', NULL, sprintf(_('%s and friends'), $this->user->nickname)); $this->element('h1', null, sprintf(_('%s and friends'), $this->user->nickname));
} }
} }

View File

@ -1,5 +1,5 @@
<?php <?php
/* /**
* StatusNet - the distributed open-source microblogging tool * StatusNet - the distributed open-source microblogging tool
* Copyright (C) 2008, 2009, StatusNet, Inc. * Copyright (C) 2008, 2009, StatusNet, Inc.
* *
@ -15,9 +15,27 @@
* *
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Actions
* @package Actions
* @author Evan Prodromou <evan@status.net>
* @author Brenda Wallace <shiny@cpan.org>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Robin Millette <millette@controlyourself.ca>
* @author Tom Adams <tom@holizz.com>
* @author Christopher Vollick <psycotica0@gmail.com>
* @author CiaranG <ciaran@ciarang.com>
* @author Craig Andrews <candrews@integralblue.com>
* @author Gina Haeussge <osd@foosel.net>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Sarven Capadisli <csarven@status.net>
* @license GNU Affero General Public License http://www.gnu.org/licenses/
* @link http://status.net
*/ */
if (!defined('STATUSNET') && !defined('LACONICA')) { exit(1); } if (!defined('STATUSNET') && !defined('LACONICA')) {
exit(1);
}
class ApiAction extends Action class ApiAction extends Action
{ {
@ -37,7 +55,7 @@ class ApiAction extends Action
$this->api_action = $this->arg('apiaction'); $this->api_action = $this->arg('apiaction');
$method = $this->arg('method'); $method = $this->arg('method');
$argument = $this->arg('argument'); $argument = $this->arg('argument');
$this->basic_auth_process_header(); $this->basic_auth_process_header();
if (isset($argument)) { if (isset($argument)) {
$cmdext = explode('.', $argument); $cmdext = explode('.', $argument);
@ -46,7 +64,7 @@ class ApiAction extends Action
$this->content_type = strtolower($cmdext[1]); $this->content_type = strtolower($cmdext[1]);
} else { } else {
# Requested format / content-type will be an extension on the method //Requested format / content-type will be an extension on the method
$cmdext = explode('.', $method); $cmdext = explode('.', $method);
$this->api_method = $cmdext[0]; $this->api_method = $cmdext[0];
$this->content_type = strtolower($cmdext[1]); $this->content_type = strtolower($cmdext[1]);
@ -55,10 +73,10 @@ class ApiAction extends Action
if ($this->requires_auth()) { if ($this->requires_auth()) {
if (!isset($this->auth_user)) { if (!isset($this->auth_user)) {
# This header makes basic auth go //This header makes basic auth go
header('WWW-Authenticate: Basic realm="StatusNet API"'); header('WWW-Authenticate: Basic realm="StatusNet API"');
# If the user hits cancel -- bam! //If the user hits cancel -- bam!
$this->show_basic_auth_error(); $this->show_basic_auth_error();
} else { } else {
$nickname = $this->auth_user; $nickname = $this->auth_user;
@ -69,7 +87,7 @@ class ApiAction extends Action
$this->user = $user; $this->user = $user;
$this->process_command(); $this->process_command();
} else { } else {
# basic authentication failed //basic authentication failed
list($proxy, $ip) = common_client_ip(); list($proxy, $ip) = common_client_ip();
common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip."); common_log(LOG_WARNING, "Failed API auth attempt, nickname = $nickname, proxy = $proxy, ip = $ip.");
@ -84,7 +102,7 @@ class ApiAction extends Action
if ($user) { if ($user) {
$this->user = $user; $this->user = $user;
} }
# Twitter doesn't throw an error if the user isn't found //Twitter doesn't throw an error if the user isn't found
} }
$this->process_command(); $this->process_command();
@ -97,7 +115,7 @@ class ApiAction extends Action
$actionfile = INSTALLDIR."/actions/$action.php"; $actionfile = INSTALLDIR."/actions/$action.php";
if (file_exists($actionfile)) { if (file_exists($actionfile)) {
require_once($actionfile); include_once $actionfile;
$action_class = ucfirst($action)."Action"; $action_class = ucfirst($action)."Action";
$action_obj = new $action_class(); $action_obj = new $action_class();
@ -113,10 +131,10 @@ class ApiAction extends Action
call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata); call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata);
} else { } else {
$this->clientError("API method not found!", $code=404); $this->clientError("API method not found!", $code = 404);
} }
} else { } else {
$this->clientError("API method not found!", $code=404); $this->clientError("API method not found!", $code = 404);
} }
} }
@ -184,10 +202,11 @@ class ApiAction extends Action
$user_id = $this->arg('user_id'); $user_id = $this->arg('user_id');
$screen_name = $this->arg('screen_name'); $screen_name = $this->arg('screen_name');
if (empty($this->api_arg) && if (empty($this->api_arg)
empty($id) && && empty($id)
empty($user_id) && && empty($user_id)
empty($screen_name)) { && empty($screen_name)
) {
return true; return true;
} else { } else {
return false; return false;
@ -208,35 +227,29 @@ class ApiAction extends Action
function basic_auth_process_header() function basic_auth_process_header()
{ {
if(isset($_SERVER['AUTHORIZATION']) || isset($_SERVER['HTTP_AUTHORIZATION'])) if (isset($_SERVER['AUTHORIZATION']) || isset($_SERVER['HTTP_AUTHORIZATION'])) {
{ $authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])? $_SERVER['HTTP_AUTHORIZATION'] : $_SERVER['AUTHORIZATION'];
$authorization_header = isset($_SERVER['HTTP_AUTHORIZATION'])?$_SERVER['HTTP_AUTHORIZATION']:$_SERVER['AUTHORIZATION']; }
}
if(isset($_SERVER['PHP_AUTH_USER'])) if (isset($_SERVER['PHP_AUTH_USER'])) {
{ $this->auth_user = $_SERVER['PHP_AUTH_USER'];
$this->auth_user = $_SERVER['PHP_AUTH_USER']; $this->auth_pw = $_SERVER['PHP_AUTH_PW'];
$this->auth_pw = $_SERVER['PHP_AUTH_PW']; } elseif (isset($authorization_header) && strstr(substr($authorization_header, 0, 5), 'Basic')) {
} // decode the HTTP_AUTHORIZATION header on php-cgi server self
elseif ( isset($authorization_header) && strstr(substr($authorization_header, 0,5),'Basic') ) // on fcgid server the header name is AUTHORIZATION
{
// decode the HTTP_AUTHORIZATION header on php-cgi server self
// on fcgid server the header name is AUTHORIZATION
$auth_hash = base64_decode( substr($authorization_header, 6) ); $auth_hash = base64_decode(substr($authorization_header, 6));
list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash); list($this->auth_user, $this->auth_pw) = explode(':', $auth_hash);
// set all to NULL on a empty basic auth request // set all to null on a empty basic auth request
if($this->auth_user == "") { if ($this->auth_user == "") {
$this->auth_user = NULL; $this->auth_user = null;
$this->auth_pw = NULL; $this->auth_pw = null;
} }
} } else {
else $this->auth_user = null;
{ $this->auth_pw = null;
$this->auth_user = NULL; }
$this->auth_pw = NULL;
}
} }
function show_basic_auth_error() function show_basic_auth_error()
@ -252,7 +265,7 @@ class ApiAction extends Action
$this->element('request', null, $_SERVER['REQUEST_URI']); $this->element('request', null, $_SERVER['REQUEST_URI']);
$this->elementEnd('hash'); $this->elementEnd('hash');
$this->endXML(); $this->endXML();
} else if ($this->content_type == 'json') { } else if ($this->content_type == 'json') {
header('Content-Type: application/json; charset=utf-8'); header('Content-Type: application/json; charset=utf-8');
$error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']); $error_array = array('error' => $msg, 'request' => $_SERVER['REQUEST_URI']);
print(json_encode($error_array)); print(json_encode($error_array));

View File

@ -41,7 +41,7 @@ require_once INSTALLDIR.'/lib/omb.php';
* *
* @category Action * @category Action
* @package Laconica * @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca> * @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@controlyourself.ca> * @author Robin Millette <millette@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://laconi.ca/ * @link http://laconi.ca/
@ -143,67 +143,4 @@ class FinishremotesubscribeAction extends Action
$user->nickname)), $user->nickname)),
303); 303);
} }
function add_avatar($profile, $url)
{
$temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
copy($url, $temp_filename);
$imagefile = new ImageFile($profile->id, $temp_filename);
$filename = Avatar::filename($profile->id,
image_type_to_extension($imagefile->type),
null,
common_timestamp());
rename($temp_filename, Avatar::path($filename));
return $profile->setOriginal($filename);
}
function access_token($omb)
{
common_debug('starting request for access token', __FILE__);
$con = omb_oauth_consumer();
$tok = new OAuthToken($omb['token'], $omb['secret']);
common_debug('using request token "'.$tok.'"', __FILE__);
$url = $omb['access_token_url'];
common_debug('using access token url "'.$url.'"', __FILE__);
# XXX: Is this the right thing to do? Strip off GET params and make them
# POST params? Seems wrong to me.
$parsed = parse_url($url);
$params = array();
parse_str($parsed['query'], $params);
$req = OAuthRequest::from_consumer_and_token($con, $tok, "POST", $url, $params);
$req->set_parameter('omb_version', OMB_VERSION_01);
# XXX: test to see if endpoint accepts this signature method
$req->sign_request(omb_hmac_sha1(), $con, $tok);
# We re-use this tool's fetcher, since it's pretty good
common_debug('posting to access token url "'.$req->get_normalized_http_url().'"', __FILE__);
common_debug('posting request data "'.$req->to_postdata().'"', __FILE__);
$fetcher = Auth_Yadis_Yadis::getHTTPFetcher();
$result = $fetcher->post($req->get_normalized_http_url(),
$req->to_postdata(),
array('User-Agent: StatusNet/' . STATUSNET_VERSION));
common_debug('got result: "'.print_r($result,true).'"', __FILE__);
if ($result->status != 200) {
return null;
}
parse_str($result->body, $return);
return array($return['oauth_token'], $return['oauth_token_secret']);
}
} }

View File

@ -99,6 +99,12 @@ class TwitapifriendshipsAction extends TwitterapiAction
$other = $this->get_profile($id); $other = $this->get_profile($id);
$user = $apidata['user']; // Alwyas the auth user $user = $apidata['user']; // Alwyas the auth user
if ($user->id == $other->id) {
$this->clientError(_("You cannot unfollow yourself!"),
403, $apidata['content-type']);
return;
}
$sub = new Subscription(); $sub = new Subscription();
$sub->subscriber = $user->id; $sub->subscriber = $user->id;
$sub->subscribed = $other->id; $sub->subscribed = $other->id;

View File

@ -136,6 +136,11 @@ class TwitapistatusesAction extends TwitterapiAction
} }
function home_timeline($args, $apidata)
{
call_user_func(array($this, 'friends_timeline'), $args, $apidata);
}
function user_timeline($args, $apidata) function user_timeline($args, $apidata)
{ {
parent::handle($args); parent::handle($args);

View File

@ -38,7 +38,7 @@ require_once INSTALLDIR.'/extlib/libomb/service_provider.php';
* *
* @category Action * @category Action
* @package Laconica * @package Laconica
* @author Evan Prodromou <evan@controlyourself.ca> * @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@controlyourself.ca> * @author Robin Millette <millette@controlyourself.ca>
* @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3 * @license http://www.fsf.org/licensing/licenses/agpl.html AGPLv3
* @link http://laconi.ca/ * @link http://laconi.ca/
@ -55,46 +55,13 @@ class UpdateprofileAction extends Action
*/ */
function prepare($argarray) function prepare($argarray)
{ {
$version = $req->get_parameter('omb_version'); parent::prepare($argarray);
if ($version != OMB_VERSION_01) { $license = $_POST['omb_listenee_license'];
$this->clientError(_('Unsupported OMB version'), 400); $site_license = common_config('license', 'url');
return false; if (!common_compatible_license($license, $site_license)) {
} $this->clientError(sprintf(_('Listenee stream license %s is not '.
# First, check to see if listenee exists 'compatible with site license %s.'),
$listenee = $req->get_parameter('omb_listenee'); $license, $site_license));
$remote = Remote_profile::staticGet('uri', $listenee);
if (!$remote) {
$this->clientError(_('Profile unknown'), 404);
return false;
}
# Second, check to see if they should be able to post updates!
# We see if there are any subscriptions to that remote user with
# the given token.
$sub = new Subscription();
$sub->subscribed = $remote->id;
$sub->token = $token->key;
if (!$sub->find(true)) {
$this->clientError(_('You did not send us that profile'), 403);
return false;
}
$profile = Profile::staticGet('id', $remote->id);
if (!$profile) {
# This one is our fault
$this->serverError(_('Remote profile with no matching profile'), 500);
return false;
}
$nickname = $req->get_parameter('omb_listenee_nickname');
if ($nickname && !Validate::string($nickname, array('min_length' => 1,
'max_length' => 64,
'format' => NICKNAME_FMT))) {
$this->clientError(_('Nickname must have only lowercase letters and numbers and no spaces.'));
return false;
}
$license = $req->get_parameter('omb_listenee_license');
if ($license && !common_valid_http_url($license)) {
$this->clientError(sprintf(_("Invalid license URL '%s'"), $license));
return false; return false;
} }
return true; return true;
@ -113,4 +80,4 @@ class UpdateprofileAction extends Action
return; return;
} }
} }
} }

4
classes/Config.php Executable file → Normal file
View File

@ -17,7 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('LACONICA')) { exit(1); } if (!defined('STATUSNET')) {
exit(1);
}
/** /**
* Table Definition for config * Table Definition for config

4
classes/Deleted_notice.php Executable file → Normal file
View File

@ -17,7 +17,9 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
if (!defined('LACONICA')) { exit(1); } if (!defined('STATUSNET')) {
exit(1);
}
/** /**
* Table Definition for notice * Table Definition for notice

0
classes/statusnet.ini Executable file → Normal file
View File

View File

@ -21,6 +21,7 @@ VALUES
('identichat','identichat','http://identichat.prosody.im/', now()), ('identichat','identichat','http://identichat.prosody.im/', now()),
('IdentiFox','IdentiFox','http://www.bitbucket.org/uncryptic/identifox/', now()), ('IdentiFox','IdentiFox','http://www.bitbucket.org/uncryptic/identifox/', now()),
('identitwitch','IdentiTwitch','http://richfish.org/identitwitch/', now()), ('identitwitch','IdentiTwitch','http://richfish.org/identitwitch/', now()),
('Jiminy','Jiminy','http://code.google.com/p/jiminy/', now()),
('LaTwit','LaTwit','http://latwit.mac65.com/', now()), ('LaTwit','LaTwit','http://latwit.mac65.com/', now()),
('LiveTweeter', 'LiveTweeter', 'http://addons.songbirdnest.com/addon/1204', now()), ('LiveTweeter', 'LiveTweeter', 'http://addons.songbirdnest.com/addon/1204', now()),
('livetweeter', 'livetweeter', 'http://addons.songbirdnest.com/addon/1204', now()), ('livetweeter', 'livetweeter', 'http://addons.songbirdnest.com/addon/1204', now()),

View File

@ -15,6 +15,22 @@
* *
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category StatusNet
* @package StatusNet
* @author Brenda Wallace <shiny@cpan.org>
* @author Christopher Vollick <psycotica0@gmail.com>
* @author CiaranG <ciaran@ciarang.com>
* @author Craig Andrews <candrews@integralblue.com>
* @author Evan Prodromou <evan@controlezvous.ca>
* @author Gina Haeussge <osd@foosel.net>
* @author Jeffery To <jeffery.to@gmail.com>
* @author Mike Cochrane <mikec@mikenz.geek.nz>
* @author Robin Millette <millette@controlyourself.ca>
* @author Sarven Capadisli <csarven@controlyourself.ca>
* @author Tom Adams <tom@holizz.com>
*
* @license GNU Affero General Public License http://www.gnu.org/licenses/
*/ */
define('INSTALLDIR', dirname(__FILE__)); define('INSTALLDIR', dirname(__FILE__));
@ -29,7 +45,8 @@ $action = null;
function getPath($req) function getPath($req)
{ {
if ((common_config('site', 'fancy') || !array_key_exists('PATH_INFO', $_SERVER)) if ((common_config('site', 'fancy') || !array_key_exists('PATH_INFO', $_SERVER))
&& array_key_exists('p', $req)) { && array_key_exists('p', $req)
) {
return $req['p']; return $req['p'];
} else if (array_key_exists('PATH_INFO', $_SERVER)) { } else if (array_key_exists('PATH_INFO', $_SERVER)) {
return $_SERVER['PATH_INFO']; return $_SERVER['PATH_INFO'];
@ -45,28 +62,35 @@ function handleError($error)
} }
$logmsg = "PEAR error: " . $error->getMessage(); $logmsg = "PEAR error: " . $error->getMessage();
if(common_config('site', 'logdebug')) { if (common_config('site', 'logdebug')) {
$logmsg .= " : ". $error->getDebugInfo(); $logmsg .= " : ". $error->getDebugInfo();
} }
common_log(LOG_ERR, $logmsg); common_log(LOG_ERR, $logmsg);
if(common_config('site', 'logdebug')) { if (common_config('site', 'logdebug')) {
$bt = $error->getBacktrace(); $bt = $error->getBacktrace();
foreach ($bt as $line) { foreach ($bt as $line) {
common_log(LOG_ERR, $line); common_log(LOG_ERR, $line);
} }
} }
if ($error instanceof DB_DataObject_Error || if ($error instanceof DB_DataObject_Error
$error instanceof DB_Error) { || $error instanceof DB_Error
$msg = sprintf(_('The database for %s isn\'t responding correctly, '. ) {
'so the site won\'t work properly. '. $msg = sprintf(
'The site admins probably know about the problem, '. _(
'but you can contact them at %s to make sure. '. 'The database for %s isn\'t responding correctly, '.
'Otherwise, wait a few minutes and try again.'), 'so the site won\'t work properly. '.
common_config('site', 'name'), 'The site admins probably know about the problem, '.
common_config('site', 'email')); 'but you can contact them at %s to make sure. '.
'Otherwise, wait a few minutes and try again.'
),
common_config('site', 'name'),
common_config('site', 'email')
);
} else { } else {
$msg = _('An important error occured, probably related to email setup. '. $msg = _(
'Check logfiles for more info..'); 'An important error occured, probably related to email setup. '.
'Check logfiles for more info..'
);
} }
$dac = new DBErrorAction($msg, 500); $dac = new DBErrorAction($msg, 500);
@ -127,10 +151,11 @@ function main()
$_lighty_url = @parse_url($_lighty_url); $_lighty_url = @parse_url($_lighty_url);
if ($_lighty_url['path'] != '/index.php' && $_lighty_url['path'] != '/') { if ($_lighty_url['path'] != '/index.php' && $_lighty_url['path'] != '/') {
$_lighty_path = preg_replace('/^'.preg_quote(common_config('site','path')).'\//', '', substr($_lighty_url['path'], 1)); $_lighty_path = preg_replace('/^'.preg_quote(common_config('site', 'path')).'\//', '', substr($_lighty_url['path'], 1));
$_SERVER['QUERY_STRING'] = 'p='.$_lighty_path; $_SERVER['QUERY_STRING'] = 'p='.$_lighty_path;
if ($_lighty_url['query']) if ($_lighty_url['query']) {
$_SERVER['QUERY_STRING'] .= '&'.$_lighty_url['query']; $_SERVER['QUERY_STRING'] .= '&'.$_lighty_url['query'];
}
parse_str($_lighty_url['query'], $_lighty_query); parse_str($_lighty_url['query'], $_lighty_query);
foreach ($_lighty_query as $key => $val) { foreach ($_lighty_query as $key => $val) {
$_GET[$key] = $_REQUEST[$key] = $val; $_GET[$key] = $_REQUEST[$key] = $val;
@ -141,7 +166,7 @@ function main()
$_SERVER['REDIRECT_URL'] = preg_replace("/\?.+$/", "", $_SERVER['REQUEST_URI']); $_SERVER['REDIRECT_URL'] = preg_replace("/\?.+$/", "", $_SERVER['REQUEST_URI']);
// quick check for fancy URL auto-detection support in installer. // quick check for fancy URL auto-detection support in installer.
if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/","",(dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) { if (isset($_SERVER['REDIRECT_URL']) && (preg_replace("/^\/$/", "", (dirname($_SERVER['REQUEST_URI']))) . '/check-fancy') === $_SERVER['REDIRECT_URL']) {
die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs."); die("Fancy URL support detection succeeded. We suggest you enable this to get fancy (pretty) URLs.");
} }
global $user, $action; global $user, $action;
@ -149,8 +174,12 @@ function main()
Snapshot::check(); Snapshot::check();
if (!_have_config()) { if (!_have_config()) {
$msg = sprintf(_("No configuration file found. Try running ". $msg = sprintf(
"the installation program first.")); _(
"No configuration file found. Try running ".
"the installation program first."
)
);
$sac = new ServerErrorAction($msg); $sac = new ServerErrorAction($msg);
$sac->showPage(); $sac->showPage();
return; return;
@ -196,9 +225,10 @@ function main()
// If the site is private, and they're not on one of the "public" // If the site is private, and they're not on one of the "public"
// parts of the site, redirect to login // parts of the site, redirect to login
if (!$user && common_config('site', 'private') && if (!$user && common_config('site', 'private')
!isLoginAction($action) && && !isLoginAction($action)
!preg_match('/rss$/', $action)) { && !preg_match('/rss$/', $action)
) {
common_redirect(common_local_url('login')); common_redirect(common_local_url('login'));
return; return;
} }

View File

@ -15,6 +15,24 @@
* *
* You should have received a copy of the GNU Affero General Public License * 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/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* @category Installation
* @package Installation
*
* @author Adrian Lang <mail@adrianlang.de>
* @author Brenda Wallace <shiny@cpan.org>
* @author Brett Taylor <brett@webfroot.co.nz>
* @author Brion Vibber <brion@pobox.com>
* @author CiaranG <ciaran@ciarang.com>
* @author Craig Andrews <candrews@integralblue.com>
* @author Eric Helgeson <helfire@Erics-MBP.local>
* @author Evan Prodromou <evan@status.net>
* @author Robin Millette <millette@controlyourself.ca>
* @author Sarven Capadisli <csarven@status.net>
* @author Tom Adams <tom@holizz.com>
* @license GNU Affero General Public License http://www.gnu.org/licenses/
* @version 0.9
* @link http://status.net
*/ */
define('INSTALLDIR', dirname(__FILE__)); define('INSTALLDIR', dirname(__FILE__));
@ -181,17 +199,34 @@ $external_libraries=array(
'check_class'=>'Validate' 'check_class'=>'Validate'
) )
); );
$dbModules = array(
'mysql' => array(
'name' => 'MySQL',
'check_module' => 'mysql', // mysqli?
'installer' => 'mysql_db_installer',
),
'pgsql' => array(
'name' => 'PostgreSQL',
'check_module' => 'pgsql',
'installer' => 'pgsql_db_installer',
),
);
/**
* the actual installation.
* If call libraries are present, then install
*
* @return void
*/
function main() function main()
{ {
if (!checkPrereqs()) if (!checkPrereqs()) {
{
return; return;
} }
if( $_GET['checklibs'] ){ if (!empty($_GET['checklibs'])) {
showLibs(); showLibs();
}else{ } else {
if ($_SERVER['REQUEST_METHOD'] == 'POST') { if ($_SERVER['REQUEST_METHOD'] == 'POST') {
handlePost(); handlePost();
} else { } else {
@ -200,33 +235,44 @@ function main()
} }
} }
/**
* checks if an external libary is present
*
* @param string $external_library Name of library
*
* @return boolean indicates if library present
*/
function haveExternalLibrary($external_library) function haveExternalLibrary($external_library)
{ {
if(isset($external_library['include']) && ! include_once($external_library['include'])){ if (isset($external_library['include']) && ! @include_once $external_library['include'] ) {
return false; return false;
} }
if(isset($external_library['check_function']) && ! function_exists($external_library['check_function'])){ if (isset($external_library['check_function']) && ! function_exists($external_library['check_function'])) {
return false; return false;
} }
if(isset($external_library['check_class']) && ! class_exists($external_library['check_class'])){ if (isset($external_library['check_class']) && ! class_exists($external_library['check_class'])) {
return false; return false;
} }
return true; return true;
} }
/**
* Check if all is ready for installation
*
* @return void
*/
function checkPrereqs() function checkPrereqs()
{ {
$pass = true; $pass = true;
if (file_exists(INSTALLDIR.'/config.php')) { if (file_exists(INSTALLDIR.'/config.php')) {
?><p class="error">Config file &quot;config.php&quot; already exists.</p> printf('<p class="error">Config file &quot;config.php&quot; already exists.</p>');
<?php
$pass = false; $pass = false;
} }
if (version_compare(PHP_VERSION, '5.2.3', '<')) { if (version_compare(PHP_VERSION, '5.2.3', '<')) {
?><p class="error">Require PHP version 5.2.3 or greater.</p><?php printf('<p class="error">Require PHP version 5.2.3 or greater.</p>');
$pass = false; $pass = false;
} }
$reqs = array('gd', 'curl', $reqs = array('gd', 'curl',
@ -234,37 +280,52 @@ function checkPrereqs()
foreach ($reqs as $req) { foreach ($reqs as $req) {
if (!checkExtension($req)) { if (!checkExtension($req)) {
?><p class="error">Cannot load required extension: <code><?php echo $req; ?></code></p><?php printf('<p class="error">Cannot load required extension: <code>%s</code></p>', $req);
$pass = false; $pass = false;
} }
} }
if (!checkExtension('pgsql') && !checkExtension('mysql')) { // Make sure we have at least one database module available
?><p class="error">Cannot find mysql or pgsql extension. You need one or the other: <code><?php echo $req; ?></code></p><?php global $dbModules;
$pass = false; $missingExtensions = array();
foreach ($dbModules as $type => $info) {
if (!checkExtension($info['check_module'])) {
$missingExtensions[] = $info['check_module'];
}
} }
if (!is_writable(INSTALLDIR)) { if (count($missingExtensions) == count($dbModules)) {
?><p class="error">Cannot write config file to: <code><?php echo INSTALLDIR; ?></code></p> $req = implode(', ', $missingExtensions);
<p>On your server, try this command: <code>chmod a+w <?php echo INSTALLDIR; ?></code> printf('<p class="error">Cannot find mysql or pgsql extension. You need one or the other.');
<?php $pass = false;
$pass = false; }
}
// Check the subdirs used for file uploads if (!is_writable(INSTALLDIR)) {
$fileSubdirs = array('avatar', 'background', 'file'); printf('<p class="error">Cannot write config file to: <code>%s</code></p>', INSTALLDIR);
foreach ($fileSubdirs as $fileSubdir) { printf('<p>On your server, try this command: <code>chmod a+w %s</code>', INSTALLDIR);
$fileFullPath = INSTALLDIR."/$fileSubdir/"; $pass = false;
if (!is_writable($fileFullPath)) { }
?><p class="error">Cannot write <?php echo $fileSubdir; ?> directory: <code><?php echo $fileFullPath; ?></code></p>
<p>On your server, try this command: <code>chmod a+w <?php echo $fileFullPath; ?></code></p>
<?php
$pass = false;
}
}
return $pass; // Check the subdirs used for file uploads
$fileSubdirs = array('avatar', 'background', 'file');
foreach ($fileSubdirs as $fileSubdir) {
$fileFullPath = INSTALLDIR."/$fileSubdir/";
if (!is_writable($fileFullPath)) {
printf('<p class="error">Cannot write to %s directory: <code>%s</code></p>', $fileSubdir, $fileFullPath);
printf('<p>On your server, try this command: <code>chmod a+w %s</code></p>', $fileFullPath);
$pass = false;
}
}
return $pass;
} }
/**
* Checks if a php extension is both installed and loaded
*
* @param string $name of extension to check
*
* @return boolean whether extension is installed and loaded
*/
function checkExtension($name) function checkExtension($name)
{ {
if (!extension_loaded($name)) { if (!extension_loaded($name)) {
@ -275,15 +336,20 @@ function checkExtension($name)
return true; return true;
} }
/**
* Show list of libraries
*
* @return void
*/
function showLibs() function showLibs()
{ {
global $external_libraries; global $external_libraries;
$present_libraries=array(); $present_libraries=array();
$absent_libraries=array(); $absent_libraries=array();
foreach($external_libraries as $external_library){ foreach ($external_libraries as $external_library) {
if(haveExternalLibrary($external_library)){ if (haveExternalLibrary($external_library)) {
$present_libraries[]=$external_library; $present_libraries[]=$external_library;
}else{ } else {
$absent_libraries[]=$external_library; $absent_libraries[]=$external_library;
} }
} }
@ -298,22 +364,21 @@ function showLibs()
<h2>Absent Libraries</h2> <h2>Absent Libraries</h2>
<ul id="absent_libraries"> <ul id="absent_libraries">
E_O_T; E_O_T;
foreach($absent_libraries as $library) foreach ($absent_libraries as $library) {
{
echo '<li>'; echo '<li>';
if($library['url']){ if (isset($library['url'])) {
echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>'; echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
}else{ } else {
echo htmlentities($library['name']); echo htmlentities($library['name']);
} }
echo '<ul>'; echo '<ul>';
if($library['deb']){ if (isset($library['deb'])) {
echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>'; echo '<li class="deb package">deb: <a href="apt:' . urlencode($library['deb']) . '">' . htmlentities($library['deb']) . '</a></li>';
} }
if($library['rpm']){ if (isset($library['rpm'])) {
echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>'; echo '<li class="rpm package">rpm: ' . htmlentities($library['rpm']) . '</li>';
} }
if($library['pear']){ if (isset($library['pear'])) {
echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>'; echo '<li class="pear package">pear: ' . htmlentities($library['pear']) . '</li>';
} }
echo '</ul>'; echo '</ul>';
@ -323,12 +388,11 @@ E_O_T;
<h2>Installed Libraries</h2> <h2>Installed Libraries</h2>
<ul id="present_libraries"> <ul id="present_libraries">
E_O_T; E_O_T;
foreach($present_libraries as $library) foreach ($present_libraries as $library) {
{
echo '<li>'; echo '<li>';
if($library['url']){ if ($library['url']) {
echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>'; echo '<a href=">'.$library['url'].'">'.htmlentities($library['name']).'</a>';
}else{ } else {
echo htmlentities($library['name']); echo htmlentities($library['name']);
} }
echo '</li>'; echo '</li>';
@ -340,6 +404,15 @@ E_O_T;
function showForm() function showForm()
{ {
global $dbModules;
$dbRadios = '';
$checked = 'checked="checked" '; // Check the first one which exists
foreach ($dbModules as $type => $info) {
if (checkExtension($info['check_module'])) {
$dbRadios .= "<input type=\"radio\" name=\"dbtype\" id=\"dbtype-$type\" value=\"$type\" $checked/> $info[name]<br />\n";
$checked = '';
}
}
echo<<<E_O_T echo<<<E_O_T
</ul> </ul>
</dd> </dd>
@ -376,8 +449,7 @@ function showForm()
<li> <li>
<label for="dbtype">Type</label> <label for="dbtype">Type</label>
<input type="radio" name="dbtype" id="fancy-mysql" value="mysql" checked='checked' /> MySQL<br /> $dbRadios
<input type="radio" name="dbtype" id="dbtype-pgsql" value="pgsql" /> PostgreSQL<br />
<p class="form_guide">Database type</p> <p class="form_guide">Database type</p>
</li> </li>
@ -406,17 +478,11 @@ E_O_T;
function updateStatus($status, $error=false) function updateStatus($status, $error=false)
{ {
?> echo '<li' . ($error ? ' class="error"': '' ) . ">$status</li>";
<li <?php echo ($error) ? 'class="error"': ''; ?>><?php echo $status;?></li>
<?php
} }
function handlePost() function handlePost()
{ {
?>
<?php
$host = $_POST['host']; $host = $_POST['host'];
$dbtype = $_POST['dbtype']; $dbtype = $_POST['dbtype'];
$database = $_POST['database']; $database = $_POST['database'];
@ -427,55 +493,41 @@ function handlePost()
$server = $_SERVER['HTTP_HOST']; $server = $_SERVER['HTTP_HOST'];
$path = substr(dirname($_SERVER['PHP_SELF']), 1); $path = substr(dirname($_SERVER['PHP_SELF']), 1);
?> echo <<<STR
<dl class="system_notice"> <dl class="system_notice">
<dt>Page notice</dt> <dt>Page notice</dt>
<dd> <dd>
<ul> <ul>
<?php STR;
$fail = false; $fail = false;
if (empty($host)) { if (empty($host)) {
updateStatus("No hostname specified.", true); updateStatus("No hostname specified.", true);
$fail = true; $fail = true;
} }
if (empty($database)) { if (empty($database)) {
updateStatus("No database specified.", true); updateStatus("No database specified.", true);
$fail = true; $fail = true;
} }
if (empty($username)) { if (empty($username)) {
updateStatus("No username specified.", true); updateStatus("No username specified.", true);
$fail = true; $fail = true;
} }
// if (empty($password)) {
// updateStatus("No password specified.", true);
// $fail = true;
// }
if (empty($sitename)) { if (empty($sitename)) {
updateStatus("No sitename specified.", true); updateStatus("No sitename specified.", true);
$fail = true; $fail = true;
} }
if($fail){ if ($fail) {
showForm(); showForm();
return; return;
} }
// FIXME: use PEAR::DB or PDO instead of our own switch global $dbModules;
$db = call_user_func($dbModules[$dbtype]['installer'], $host, $database, $username, $password);
switch($dbtype) {
case 'mysql':
$db = mysql_db_installer($host, $database, $username, $password);
break;
case 'pgsql':
$db = pgsql_db_installer($host, $database, $username, $password);
break;
default:
}
if (!$db) { if (!$db) {
// database connection failed, do not move on to create config file. // database connection failed, do not move on to create config file.
@ -498,112 +550,110 @@ function handlePost()
updateStatus("StatusNet has been installed at $link"); updateStatus("StatusNet has been installed at $link");
updateStatus("You can visit your <a href='$link'>new StatusNet site</a>."); updateStatus("You can visit your <a href='$link'>new StatusNet site</a>.");
?>
<?php
} }
function pgsql_db_installer($host, $database, $username, $password) { function Pgsql_Db_installer($host, $database, $username, $password)
$connstring = "dbname=$database host=$host user=$username"; {
$connstring = "dbname=$database host=$host user=$username";
//No password would mean trust authentication used. //No password would mean trust authentication used.
if (!empty($password)) { if (!empty($password)) {
$connstring .= " password=$password"; $connstring .= " password=$password";
} }
updateStatus("Starting installation..."); updateStatus("Starting installation...");
updateStatus("Checking database..."); updateStatus("Checking database...");
$conn = pg_connect($connstring); $conn = pg_connect($connstring);
if ($conn ===false) { if ($conn ===false) {
updateStatus("Failed to connect to database: $connstring"); updateStatus("Failed to connect to database: $connstring");
showForm(); showForm();
return false; return false;
} }
//ensure database encoding is UTF8 //ensure database encoding is UTF8
$record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding')); $record = pg_fetch_object(pg_query($conn, 'SHOW server_encoding'));
if ($record->server_encoding != 'UTF8') { if ($record->server_encoding != 'UTF8') {
updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding)); updateStatus("StatusNet requires UTF8 character encoding. Your database is ". htmlentities($record->server_encoding));
showForm(); showForm();
return false; return false;
} }
updateStatus("Running database script..."); updateStatus("Running database script...");
//wrap in transaction; //wrap in transaction;
pg_query($conn, 'BEGIN'); pg_query($conn, 'BEGIN');
$res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql'); $res = runDbScript(INSTALLDIR.'/db/statusnet_pg.sql', $conn, 'pgsql');
if ($res === false) { if ($res === false) {
updateStatus("Can't run database script.", true); updateStatus("Can't run database script.", true);
showForm(); showForm();
return false; return false;
} }
foreach (array('sms_carrier' => 'SMS carrier', foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source', 'notice_source' => 'notice source',
'foreign_services' => 'foreign service') 'foreign_services' => 'foreign service')
as $scr => $name) { as $scr => $name) {
updateStatus(sprintf("Adding %s data to database...", $name)); updateStatus(sprintf("Adding %s data to database...", $name));
$res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql'); $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn, 'pgsql');
if ($res === false) { if ($res === false) {
updateStatus(sprintf("Can't run %d script.", $name), true); updateStatus(sprintf("Can't run %d script.", $name), true);
showForm(); showForm();
return false; return false;
} }
} }
pg_query($conn, 'COMMIT'); pg_query($conn, 'COMMIT');
if (empty($password)) { if (empty($password)) {
$sqlUrl = "pgsql://$username@$host/$database"; $sqlUrl = "pgsql://$username@$host/$database";
} } else {
else { $sqlUrl = "pgsql://$username:$password@$host/$database";
$sqlUrl = "pgsql://$username:$password@$host/$database"; }
}
$db = array('type' => 'pgsql', 'database' => $sqlUrl); $db = array('type' => 'pgsql', 'database' => $sqlUrl);
return $db; return $db;
} }
function mysql_db_installer($host, $database, $username, $password) { function Mysql_Db_installer($host, $database, $username, $password)
updateStatus("Starting installation..."); {
updateStatus("Checking database..."); updateStatus("Starting installation...");
updateStatus("Checking database...");
$conn = mysql_connect($host, $username, $password); $conn = mysql_connect($host, $username, $password);
if (!$conn) { if (!$conn) {
updateStatus("Can't connect to server '$host' as '$username'.", true); updateStatus("Can't connect to server '$host' as '$username'.", true);
showForm(); showForm();
return false; return false;
} }
updateStatus("Changing to database..."); updateStatus("Changing to database...");
$res = mysql_select_db($database, $conn); $res = mysql_select_db($database, $conn);
if (!$res) { if (!$res) {
updateStatus("Can't change to database.", true); updateStatus("Can't change to database.", true);
showForm(); showForm();
return false; return false;
} }
updateStatus("Running database script..."); updateStatus("Running database script...");
$res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn); $res = runDbScript(INSTALLDIR.'/db/statusnet.sql', $conn);
if ($res === false) { if ($res === false) {
updateStatus("Can't run database script.", true); updateStatus("Can't run database script.", true);
showForm(); showForm();
return false; return false;
} }
foreach (array('sms_carrier' => 'SMS carrier', foreach (array('sms_carrier' => 'SMS carrier',
'notice_source' => 'notice source', 'notice_source' => 'notice source',
'foreign_services' => 'foreign service') 'foreign_services' => 'foreign service')
as $scr => $name) { as $scr => $name) {
updateStatus(sprintf("Adding %s data to database...", $name)); updateStatus(sprintf("Adding %s data to database...", $name));
$res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn); $res = runDbScript(INSTALLDIR.'/db/'.$scr.'.sql', $conn);
if ($res === false) { if ($res === false) {
updateStatus(sprintf("Can't run %d script.", $name), true); updateStatus(sprintf("Can't run %d script.", $name), true);
showForm(); showForm();
return false; return false;
} }
} }
$sqlUrl = "mysqli://$username:$password@$host/$database"; $sqlUrl = "mysqli://$username:$password@$host/$database";
$db = array('type' => 'mysql', 'database' => $sqlUrl); $db = array('type' => 'mysql', 'database' => $sqlUrl);
return $db; return $db;
} }
function writeConf($sitename, $server, $path, $fancy, $db) function writeConf($sitename, $server, $path, $fancy, $db)
@ -634,7 +684,16 @@ function writeConf($sitename, $server, $path, $fancy, $db)
return $res; return $res;
} }
function runDbScript($filename, $conn, $type = 'mysql') /**
* Install schema into the database
*
* @param string $filename location of database schema file
* @param dbconn $conn connection to database
* @param string $type type of database, currently mysql or pgsql
*
* @return boolean - indicating success or failure
*/
function runDbScript($filename, $conn, $type = 'mysqli')
{ {
$sql = trim(file_get_contents($filename)); $sql = trim(file_get_contents($filename));
$stmts = explode(';', $sql); $stmts = explode(';', $sql);
@ -645,7 +704,7 @@ function runDbScript($filename, $conn, $type = 'mysql')
} }
// FIXME: use PEAR::DB or PDO instead of our own switch // FIXME: use PEAR::DB or PDO instead of our own switch
switch ($type) { switch ($type) {
case 'mysql': case 'mysqli':
$res = mysql_query($stmt, $conn); $res = mysql_query($stmt, $conn);
if ($res === false) { if ($res === false) {
$error = mysql_error(); $error = mysql_error();

View File

@ -284,6 +284,8 @@ $config =
array('contentlimit' => null), array('contentlimit' => null),
'message' => 'message' =>
array('contentlimit' => null), array('contentlimit' => null),
'http' =>
array('client' => 'curl'), // XXX: should this be the default?
); );
$config['db'] = &PEAR::getStaticProperty('DB_DataObject','options'); $config['db'] = &PEAR::getStaticProperty('DB_DataObject','options');

179
lib/curlclient.php Normal file
View File

@ -0,0 +1,179 @@
n<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Utility class for wrapping Curl
*
* 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 HTTP
* @package StatusNet
* @author Evan Prodromou <evan@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')) {
exit(1);
}
define(CURLCLIENT_VERSION, "0.1");
/**
* Wrapper for Curl
*
* Makes Curl HTTP client calls within our HTTPClient framework
*
* @category HTTP
* @package StatusNet
* @author Evan Prodromou <evan@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 CurlClient extends HTTPClient
{
function __construct()
{
}
function head($url, $headers=null)
{
$ch = curl_init($url);
$this->setup($ch);
curl_setopt_array($ch,
array(CURLOPT_NOBODY => true));
if (!is_null($headers)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
$result = curl_exec($ch);
curl_close($ch);
return $this->parseResults($result);
}
function get($url, $headers=null)
{
$ch = curl_init($url);
$this->setup($ch);
if (!is_null($headers)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
$result = curl_exec($ch);
curl_close($ch);
return $this->parseResults($result);
}
function post($url, $headers=null, $body=null)
{
$ch = curl_init($url);
$this->setup($ch);
curl_setopt($ch, CURLOPT_POST, true);
if (!is_null($body)) {
curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
}
if (!is_null($headers)) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}
$result = curl_exec($ch);
curl_close($ch);
return $this->parseResults($result);
}
function setup($ch)
{
curl_setopt_array($ch,
array(CURLOPT_USERAGENT => $this->userAgent(),
CURLOPT_HEADER => true,
CURLOPT_RETURNTRANSFER => true));
}
function userAgent()
{
$version = curl_version();
return parent::userAgent() . " CurlClient/".CURLCLIENT_VERSION . " cURL/" . $version['version'];
}
function parseResults($results)
{
$resp = new HTTPResponse();
$lines = explode("\r\n", $results);
if (preg_match("#^HTTP/1.[01] (\d\d\d) .+$#", $lines[0], $match)) {
$resp->code = $match[1];
} else {
throw Exception("Bad format: initial line is not HTTP status line");
}
$lastk = null;
for ($i = 1; $i < count($lines); $i++) {
$l =& $lines[$i];
if (mb_strlen($l) == 0) {
$resp->body = implode("\r\n", array_slice($lines, $i + 1));
break;
}
if (preg_match("#^(\S+):\s+(.*)$#", $l, $match)) {
$k = $match[1];
$v = $match[2];
if (array_key_exists($k, $resp->headers)) {
if (is_array($resp->headers[$k])) {
$resp->headers[$k][] = $v;
} else {
$resp->headers[$k] = array($resp->headers[$k], $v);
}
} else {
$resp->headers[$k] = $v;
}
$lastk = $k;
} else if (preg_match("#^\s+(.*)$#", $l, $match)) {
// continuation line
if (is_null($lastk)) {
throw Exception("Bad format: initial whitespace in headers");
}
$h =& $resp->headers[$lastk];
if (is_array($h)) {
$n = count($h);
$h[$n-1] .= $match[1];
} else {
$h .= $match[1];
}
}
}
return $resp;
}
}

122
lib/httpclient.php Normal file
View File

@ -0,0 +1,122 @@
<?php
/**
* StatusNet, the distributed open-source microblogging tool
*
* Utility for doing HTTP-related things
*
* 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 Action
* @package StatusNet
* @author Evan Prodromou <evan@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')) {
exit(1);
}
/**
* Useful structure for HTTP responses
*
* We make HTTP calls in several places, and we have several different
* ways of doing them. This class hides the specifics of what underlying
* library (curl or PHP-HTTP or whatever) that's used.
*
* @category HTTP
* @package StatusNet
* @author Evan Prodromou <evan@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 HTTPResponse
{
public $code = null;
public $headers = null;
public $body = null;
}
/**
* Utility class for doing HTTP client stuff
*
* We make HTTP calls in several places, and we have several different
* ways of doing them. This class hides the specifics of what underlying
* library (curl or PHP-HTTP or whatever) that's used.
*
* @category HTTP
* @package StatusNet
* @author Evan Prodromou <evan@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 HTTPClient
{
static $_client = null;
static function start()
{
if (!is_null(self::$_client)) {
return self::$_client;
}
$type = common_config('http', 'client');
switch ($type) {
case 'curl':
self::$_client = new CurlClient();
break;
default:
throw new Exception("Unknown HTTP client type '$type'");
break;
}
return self::$_client;
}
function head($url, $headers)
{
throw new Exception("HEAD method unimplemented");
}
function get($url, $headers)
{
throw new Exception("GET method unimplemented");
}
function post($url, $headers, $body)
{
throw new Exception("POST method unimplemented");
}
function put($url, $headers, $body)
{
throw new Exception("PUT method unimplemented");
}
function delete($url, $headers)
{
throw new Exception("DELETE method unimplemented");
}
function userAgent()
{
return "StatusNet/".STATUSNET_VERSION." (".STATUSNET_CODENAME.")";
}
}

View File

@ -80,14 +80,9 @@ function omb_broadcast_notice($notice)
$posted = array(); $posted = array();
while ($rp->fetch()) { while ($rp->fetch()) {
if (!array_key_exists($rp->postnoticeurl, $posted)) { if (isset($posted[$rp->postnoticeurl])) {
common_log(LOG_DEBUG, 'Posting to ' . $rp->postnoticeurl); /* We already posted to this url. */
if (omb_post_notice_keys($notice, $rp->postnoticeurl, $rp->token, $rp->secret)) { continue;
common_log(LOG_DEBUG, 'Finished to ' . $rp->postnoticeurl);
$posted[$rp->postnoticeurl] = true;
} else {
common_log(LOG_DEBUG, 'Failed posting to ' . $rp->postnoticeurl);
}
} }
common_debug('Posting to ' . $rp->postnoticeurl, __FILE__); common_debug('Posting to ' . $rp->postnoticeurl, __FILE__);

View File

@ -265,12 +265,12 @@ class Router
$m->connect('api/statuses/:method', $m->connect('api/statuses/:method',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'statuses'), 'apiaction' => 'statuses'),
array('method' => '(public_timeline|friends_timeline|user_timeline|update|replies|mentions|show|friends|followers|featured)(\.(atom|rss|xml|json))?')); array('method' => '(public_timeline|home_timeline|friends_timeline|user_timeline|update|replies|mentions|show|friends|followers|featured)(\.(atom|rss|xml|json))?'));
$m->connect('api/statuses/:method/:argument', $m->connect('api/statuses/:method/:argument',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'statuses'), 'apiaction' => 'statuses'),
array('method' => '(|user_timeline|friends_timeline|replies|mentions|show|destroy|friends|followers)')); array('method' => '(user_timeline|home_timeline|friends_timeline|replies|mentions|show|destroy|friends|followers)'));
// users // users
@ -429,7 +429,7 @@ class Router
$m->connect('api/statuses/:method/:argument', $m->connect('api/statuses/:method/:argument',
array('action' => 'api', array('action' => 'api',
'apiaction' => 'statuses'), 'apiaction' => 'statuses'),
array('method' => '(|user_timeline|friends_timeline|replies|mentions|show|destroy|friends|followers)')); array('method' => '(user_timeline|home_timeline|friends_timeline|replies|mentions|show|destroy|friends|followers)'));
$m->connect('api/statusnet/groups/:method/:argument', $m->connect('api/statusnet/groups/:method/:argument',
array('action' => 'api', array('action' => 'api',

View File

@ -1,6 +1,6 @@
<?php <?php
/** /**
* Laconica, the distributed open-source microblogging tool * StatusNet, the distributed open-source microblogging tool
* *
* PHP version 5 * PHP version 5
* *
@ -18,14 +18,14 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
* @category Plugin * @category Plugin
* @package Laconica * @package StatusNet
* @author Evan Prodromou <evan@controlyourself.ca> * @author Evan Prodromou <evan@status.net>
* @copyright 2009 Control Yourself, 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://laconi.ca/ * @link http://status.net/
*/ */
if (!defined('LACONICA')) { if (!defined('STATUSNET')) {
exit(1); exit(1);
} }
@ -36,10 +36,10 @@ if (!defined('LACONICA')) {
* and identity system. * and identity system.
* *
* @category Plugin * @category Plugin
* @package Laconica * @package StatusNet
* @author Evan Prodromou <evan@controlyourself.ca> * @author Evan Prodromou <evan@status.net>
* @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://laconi.ca/ * @link http://status.net/
* @link http://openid.net/ * @link http://openid.net/
*/ */

View File

@ -1,7 +1,7 @@
/** Howto: create a statusnet theme /** Howto: create a statusnet theme
* *
* @package StatusNet * @package StatusNet
* @author Sarven Capadisli <csarven@controlyourself.ca> * @author Sarven Capadisli <csarven@status.net>
* @copyright 2009 Control Yourself, Inc. * @copyright 2009 Control Yourself, 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://laconi.ca/ * @link http://laconi.ca/