2009-08-01 17:20:44 +09:00
< ? php
/**
2009-08-26 07:12:20 +09:00
* StatusNet , the distributed open - source microblogging tool
2009-08-01 17:20:44 +09:00
*
* Class for doing OAuth authentication 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 />.
*
2009-08-01 17:20:44 +09:00
* @ category TwitterauthorizationAction
2009-08-26 07:12:20 +09:00
* @ package StatusNet
2009-12-09 06:16:03 +09:00
* @ author Zach Copley < zach @ status . net >
2009-08-26 07:12:20 +09:00
* @ copyright 2009 StatusNet , Inc .
2009-08-01 17:20:44 +09:00
* @ license http :// www . fsf . org / licensing / licenses / agpl - 3.0 . html GNU Affero General Public License version 3.0
2009-08-26 07:16:46 +09:00
* @ link http :// status . net /
2009-08-01 17:20:44 +09:00
*/
2009-08-26 23:41:36 +09:00
if ( ! defined ( 'STATUSNET' ) && ! defined ( 'LACONICA' )) {
2009-08-01 17:20:44 +09:00
exit ( 1 );
}
2009-08-26 09:59:06 +09:00
require_once INSTALLDIR . '/plugins/TwitterBridge/twitter.php' ;
2009-08-09 03:13:19 +09:00
/**
* Class for doing OAuth authentication against Twitter
*
2009-09-09 08:02:57 +09:00
* Peforms the OAuth " dance " between StatusNet and Twitter -- requests a token ,
2009-08-09 03:13:19 +09:00
* authorizes it , and exchanges it for an access token . It also creates a link
2009-09-09 08:02:57 +09:00
* ( Foreign_link ) between the StatusNet user and Twitter user and stores the
2009-08-09 03:13:19 +09:00
* access token and secret in the link .
*
* @ category Twitter
2009-09-09 08:02:57 +09:00
* @ package StatusNet
* @ author Zach Copley < zach @ status . net >
2009-08-09 03:13:19 +09:00
* @ license http :// www . fsf . org / licensing / licenses / agpl - 3.0 . html GNU Affero General Public License version 3.0
* @ link http :// laconi . ca /
*
*/
2009-08-01 17:20:44 +09:00
class TwitterauthorizationAction extends Action
{
2009-12-09 06:16:03 +09:00
var $twuid = null ;
var $tw_fields = null ;
var $access_token = null ;
2009-08-09 03:13:19 +09:00
/**
* Initialize class members . Looks for 'oauth_token' parameter .
*
* @ param array $args misc . arguments
*
* @ return boolean true
*/
2009-08-01 17:20:44 +09:00
function prepare ( $args )
{
parent :: prepare ( $args );
$this -> oauth_token = $this -> arg ( 'oauth_token' );
return true ;
}
2009-08-09 03:13:19 +09:00
/**
* Handler method
*
* @ param array $args is ignored since it ' s now passed in in prepare ()
*
* @ return nothing
*/
2009-08-01 17:20:44 +09:00
function handle ( $args )
{
parent :: handle ( $args );
2009-12-09 06:16:03 +09:00
if ( common_logged_in ()) {
$user = common_current_user ();
$flink = Foreign_link :: getByUserID ( $user -> id , TWITTER_SERVICE );
// If there's already a foreign link record, it means we already
// have an access token, and this is unecessary. So go back.
if ( isset ( $flink )) {
common_redirect ( common_local_url ( 'twittersettings' ));
}
2009-08-01 17:20:44 +09:00
}
2009-12-09 06:16:03 +09:00
if ( $_SERVER [ 'REQUEST_METHOD' ] == 'POST' ) {
// User was not logged in to StatusNet before
$this -> twuid = $this -> trimmed ( 'twuid' );
$this -> tw_fields = array ( " name " => $this -> trimmed ( 'name' ), " fullname " => $this -> trimmed ( 'fullname' ));
$this -> access_token = new OAuthToken ( $this -> trimmed ( 'access_token_key' ), $this -> trimmed ( 'access_token_secret' ));
$token = $this -> trimmed ( 'token' );
if ( ! $token || $token != common_session_token ()) {
$this -> showForm ( _ ( 'There was a problem with your session token. Try again, please.' ));
return ;
}
if ( $this -> arg ( 'create' )) {
if ( ! $this -> boolean ( 'license' )) {
$this -> showForm ( _ ( 'You can\'t register if you don\'t agree to the license.' ),
$this -> trimmed ( 'newname' ));
return ;
}
$this -> createNewUser ();
} else if ( $this -> arg ( 'connect' )) {
$this -> connectNewUser ();
} else {
common_debug ( 'Twitter Connect Plugin - ' .
print_r ( $this -> args , true ));
$this -> showForm ( _ ( 'Something weird happened.' ),
$this -> trimmed ( 'newname' ));
}
2009-08-09 03:13:19 +09:00
} else {
2009-12-09 06:16:03 +09:00
// $this->oauth_token is only populated once Twitter authorizes our
// request token. If it's empty we're at the beginning of the auth
// process
if ( empty ( $this -> oauth_token )) {
$this -> authorizeRequestToken ();
} else {
$this -> saveAccessToken ();
}
2009-08-09 03:13:19 +09:00
}
}
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
/**
* Asks Twitter for a request token , and then redirects to Twitter
* to authorize it .
*
* @ return nothing
*/
function authorizeRequestToken ()
{
try {
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
// Get a new request token and authorize it
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
$client = new TwitterOAuthClient ();
2009-08-10 16:00:59 +09:00
$req_tok =
2009-08-10 15:05:43 +09:00
$client -> getRequestToken ( TwitterOAuthClient :: $requestTokenURL );
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
// Sock the request token away in the session temporarily
2009-08-04 07:46:01 +09:00
2009-08-09 03:13:19 +09:00
$_SESSION [ 'twitter_request_token' ] = $req_tok -> key ;
2009-08-10 15:05:43 +09:00
$_SESSION [ 'twitter_request_token_secret' ] = $req_tok -> secret ;
2009-08-04 07:46:01 +09:00
2009-08-09 03:13:19 +09:00
$auth_link = $client -> getAuthorizeLink ( $req_tok );
2009-08-01 17:20:44 +09:00
2009-10-29 04:29:20 +09:00
} catch ( OAuthClientException $e ) {
2009-08-09 03:13:19 +09:00
$msg = sprintf ( 'OAuth client cURL error - code: %1s, msg: %2s' ,
$e -> getCode (), $e -> getMessage ());
2009-12-09 05:17:11 +09:00
$this -> serverError ( _m ( 'Couldn\'t link your Twitter account.' ));
2009-08-09 03:13:19 +09:00
}
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
common_redirect ( $auth_link );
}
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
/**
* Called when Twitter returns an authorized request token . Exchanges
* it for an access token and stores it .
*
* @ return nothing
*/
function saveAccessToken ()
{
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
// Check to make sure Twitter returned the same request
// token we sent them
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
if ( $_SESSION [ 'twitter_request_token' ] != $this -> oauth_token ) {
2009-12-09 05:17:11 +09:00
$this -> serverError ( _m ( 'Couldn\'t link your Twitter account.' ));
2009-08-09 03:13:19 +09:00
}
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
try {
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
$client = new TwitterOAuthClient ( $_SESSION [ 'twitter_request_token' ],
$_SESSION [ 'twitter_request_token_secret' ]);
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
// Exchange the request token for an access token
2009-08-01 17:20:44 +09:00
2009-08-10 15:05:43 +09:00
$atok = $client -> getAccessToken ( TwitterOAuthClient :: $accessTokenURL );
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
// Test the access token and get the user's Twitter info
2009-08-04 07:46:01 +09:00
2009-08-09 03:13:19 +09:00
$client = new TwitterOAuthClient ( $atok -> key , $atok -> secret );
2009-08-10 15:05:43 +09:00
$twitter_user = $client -> verifyCredentials ();
2009-08-04 07:46:01 +09:00
2009-08-09 03:13:19 +09:00
} catch ( OAuthClientException $e ) {
$msg = sprintf ( 'OAuth client cURL error - code: %1$s, msg: %2$s' ,
2009-08-04 07:46:01 +09:00
$e -> getCode (), $e -> getMessage ());
2009-12-09 05:17:11 +09:00
$this -> serverError ( _m ( 'Couldn\'t link your Twitter account.' ));
2009-08-09 03:13:19 +09:00
}
2009-08-01 17:20:44 +09:00
2009-12-09 06:16:03 +09:00
if ( common_logged_in ()) {
// Save the access token and Twitter user info
$this -> saveForeignLink ( $atok , $twitter_user );
}
else {
$this -> twuid = $twitter_user -> id ;
$this -> tw_fields = array ( " name " => $twitter_user -> screen_name , " fullname " => $twitter_user -> name );
$this -> access_token = $atok ;
$this -> tryLogin ();
}
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
// Clean up the the mess we made in the session
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
unset ( $_SESSION [ 'twitter_request_token' ]);
unset ( $_SESSION [ 'twitter_request_token_secret' ]);
2009-12-09 06:16:03 +09:00
if ( common_logged_in ()) {
common_redirect ( common_local_url ( 'twittersettings' ));
}
2009-08-09 03:13:19 +09:00
}
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
/**
* Saves a Foreign_link between Twitter user and local user ,
* which includes the access token and secret .
*
* @ param OAuthToken $access_token the access token to save
* @ param mixed $twitter_user twitter API user object
*
* @ return nothing
*/
function saveForeignLink ( $access_token , $twitter_user )
{
$user = common_current_user ();
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
$flink = new Foreign_link ();
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
$flink -> user_id = $user -> id ;
$flink -> foreign_id = $twitter_user -> id ;
$flink -> service = TWITTER_SERVICE ;
2009-08-10 16:00:59 +09:00
$creds = TwitterOAuthClient :: packToken ( $access_token );
$flink -> credentials = $creds ;
2009-08-09 03:13:19 +09:00
$flink -> created = common_sql_now ();
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
// Defaults: noticesync on, everything else off
2009-08-01 17:20:44 +09:00
2009-08-09 03:13:19 +09:00
$flink -> set_flags ( true , false , false , false );
$flink_id = $flink -> insert ();
if ( empty ( $flink_id )) {
common_log_db_error ( $flink , 'INSERT' , __FILE__ );
2009-12-09 05:17:11 +09:00
$this -> serverError ( _m ( 'Couldn\'t link your Twitter account.' ));
2009-08-01 17:20:44 +09:00
}
2009-08-09 03:13:19 +09:00
save_twitter_user ( $twitter_user -> id , $twitter_user -> screen_name );
2009-08-01 17:20:44 +09:00
}
2009-12-09 06:16:03 +09:00
function showPageNotice ()
{
if ( $this -> error ) {
$this -> element ( 'div' , array ( 'class' => 'error' ), $this -> error );
} else {
$this -> element ( 'div' , 'instructions' ,
sprintf ( _ ( 'This is the first time you\'ve logged into %s so we must connect your Twitter account to a local account. You can either create a new account, or connect with your existing account, if you have one.' ), common_config ( 'site' , 'name' )));
}
}
function title ()
{
return _ ( 'Twitter Account Setup' );
}
function showForm ( $error = null , $username = null )
{
$this -> error = $error ;
$this -> username = $username ;
$this -> showPage ();
}
function showPage ()
{
parent :: showPage ();
}
function showContent ()
{
if ( ! empty ( $this -> message_text )) {
$this -> element ( 'p' , null , $this -> message );
return ;
}
$this -> elementStart ( 'form' , array ( 'method' => 'post' ,
'id' => 'form_settings_twitter_connect' ,
'class' => 'form_settings' ,
'action' => common_local_url ( 'twitterauthorization' )));
$this -> elementStart ( 'fieldset' , array ( 'id' => 'settings_twitter_connect_options' ));
$this -> element ( 'legend' , null , _ ( 'Connection options' ));
$this -> elementStart ( 'ul' , 'form_data' );
$this -> elementStart ( 'li' );
$this -> element ( 'input' , array ( 'type' => 'checkbox' ,
'id' => 'license' ,
'class' => 'checkbox' ,
'name' => 'license' ,
'value' => 'true' ));
$this -> elementStart ( 'label' , array ( 'class' => 'checkbox' , 'for' => 'license' ));
$this -> text ( _ ( 'My text and files are available under ' ));
$this -> element ( 'a' , array ( 'href' => common_config ( 'license' , 'url' )),
common_config ( 'license' , 'title' ));
$this -> text ( _ ( ' except this private data: password, email address, IM address, phone number.' ));
$this -> elementEnd ( 'label' );
$this -> elementEnd ( 'li' );
$this -> elementEnd ( 'ul' );
$this -> hidden ( 'access_token_key' , $this -> access_token -> key );
$this -> hidden ( 'access_token_secret' , $this -> access_token -> secret );
$this -> hidden ( 'twuid' , $this -> twuid );
$this -> hidden ( 'tw_fields_name' , $this -> tw_fields [ 'name' ]);
$this -> hidden ( 'tw_fields_fullname' , $this -> tw_fields [ 'fullname' ]);
$this -> elementStart ( 'fieldset' );
$this -> hidden ( 'token' , common_session_token ());
$this -> element ( 'legend' , null ,
_ ( 'Create new account' ));
$this -> element ( 'p' , null ,
_ ( 'Create a new user with this nickname.' ));
$this -> elementStart ( 'ul' , 'form_data' );
$this -> elementStart ( 'li' );
$this -> input ( 'newname' , _ ( 'New nickname' ),
( $this -> username ) ? $this -> username : '' ,
_ ( '1-64 lowercase letters or numbers, no punctuation or spaces' ));
$this -> elementEnd ( 'li' );
$this -> elementEnd ( 'ul' );
$this -> submit ( 'create' , _ ( 'Create' ));
$this -> elementEnd ( 'fieldset' );
$this -> elementStart ( 'fieldset' );
$this -> element ( 'legend' , null ,
_ ( 'Connect existing account' ));
$this -> element ( 'p' , null ,
_ ( 'If you already have an account, login with your username and password to connect it to your Twitter account.' ));
$this -> elementStart ( 'ul' , 'form_data' );
$this -> elementStart ( 'li' );
$this -> input ( 'nickname' , _ ( 'Existing nickname' ));
$this -> elementEnd ( 'li' );
$this -> elementStart ( 'li' );
$this -> password ( 'password' , _ ( 'Password' ));
$this -> elementEnd ( 'li' );
$this -> elementEnd ( 'ul' );
$this -> submit ( 'connect' , _ ( 'Connect' ));
$this -> elementEnd ( 'fieldset' );
$this -> elementEnd ( 'fieldset' );
$this -> elementEnd ( 'form' );
}
function message ( $msg )
{
$this -> message_text = $msg ;
$this -> showPage ();
}
function createNewUser ()
{
if ( common_config ( 'site' , 'closed' )) {
$this -> clientError ( _ ( 'Registration not allowed.' ));
return ;
}
$invite = null ;
if ( common_config ( 'site' , 'inviteonly' )) {
$code = $_SESSION [ 'invitecode' ];
if ( empty ( $code )) {
$this -> clientError ( _ ( 'Registration not allowed.' ));
return ;
}
$invite = Invitation :: staticGet ( $code );
if ( empty ( $invite )) {
$this -> clientError ( _ ( 'Not a valid invitation code.' ));
return ;
}
}
$nickname = $this -> trimmed ( 'newname' );
if ( ! Validate :: string ( $nickname , array ( 'min_length' => 1 ,
'max_length' => 64 ,
'format' => NICKNAME_FMT ))) {
$this -> showForm ( _ ( 'Nickname must have only lowercase letters and numbers and no spaces.' ));
return ;
}
if ( ! User :: allowed_nickname ( $nickname )) {
$this -> showForm ( _ ( 'Nickname not allowed.' ));
return ;
}
if ( User :: staticGet ( 'nickname' , $nickname )) {
$this -> showForm ( _ ( 'Nickname already in use. Try another one.' ));
return ;
}
$fullname = trim ( $this -> tw_fields [ 'fullname' ]);
$args = array ( 'nickname' => $nickname , 'fullname' => $fullname );
if ( ! empty ( $invite )) {
$args [ 'code' ] = $invite -> code ;
}
$user = User :: register ( $args );
$result = $this -> flinkUser ( $user -> id , $this -> twuid );
if ( ! $result ) {
$this -> serverError ( _ ( 'Error connecting user to Twitter.' ));
return ;
}
common_set_user ( $user );
common_real_login ( true );
common_debug ( 'Twitter Connect Plugin - ' .
" Registered new user $user->id from Twitter user $this->fbuid " );
common_redirect ( common_local_url ( 'showstream' , array ( 'nickname' => $user -> nickname )),
303 );
}
function connectNewUser ()
{
$nickname = $this -> trimmed ( 'nickname' );
$password = $this -> trimmed ( 'password' );
if ( ! common_check_user ( $nickname , $password )) {
$this -> showForm ( _ ( 'Invalid username or password.' ));
return ;
}
$user = User :: staticGet ( 'nickname' , $nickname );
if ( ! empty ( $user )) {
common_debug ( 'Twitter Connect Plugin - ' .
" Legit user to connect to Twitter: $nickname " );
}
$result = $this -> flinkUser ( $user -> id , $this -> twuid );
if ( ! $result ) {
$this -> serverError ( _ ( 'Error connecting user to Twitter.' ));
return ;
}
common_debug ( 'Twitter Connnect Plugin - ' .
" Connected Twitter user $this->fbuid to local user $user->id " );
common_set_user ( $user );
common_real_login ( true );
$this -> goHome ( $user -> nickname );
}
function connectUser ()
{
$user = common_current_user ();
$result = $this -> flinkUser ( $user -> id , $this -> twuid );
if ( empty ( $result )) {
$this -> serverError ( _ ( 'Error connecting user to Twitter.' ));
return ;
}
common_debug ( 'Twitter Connect Plugin - ' .
" Connected Twitter user $this->fbuid to local user $user->id " );
// Return to Twitter connection settings tab
common_redirect ( common_local_url ( 'twittersettings' ), 303 );
}
function tryLogin ()
{
common_debug ( 'Twitter Connect Plugin - ' .
" Trying login for Twitter user $this->fbuid . " );
$flink = Foreign_link :: getByForeignID ( $this -> twuid , TWITTER_SERVICE );
if ( ! empty ( $flink )) {
$user = $flink -> getUser ();
if ( ! empty ( $user )) {
common_debug ( 'Twitter Connect Plugin - ' .
" Logged in Twitter user $flink->foreign_id as user $user->id ( $user->nickname ) " );
common_set_user ( $user );
common_real_login ( true );
$this -> goHome ( $user -> nickname );
}
} else {
common_debug ( 'Twitter Connect Plugin - ' .
" No flink found for twuid: $this->twuid - new user " );
$this -> showForm ( null , $this -> bestNewNickname ());
}
}
function goHome ( $nickname )
{
$url = common_get_returnto ();
if ( $url ) {
// We don't have to return to it again
common_set_returnto ( null );
} else {
$url = common_local_url ( 'all' ,
array ( 'nickname' =>
$nickname ));
}
common_redirect ( $url , 303 );
}
function flinkUser ( $user_id , $twuid )
{
$flink = new Foreign_link ();
$flink -> user_id = $user_id ;
$flink -> foreign_id = $twuid ;
$flink -> service = TWITTER_SERVICE ;
$creds = TwitterOAuthClient :: packToken ( $this -> access_token );
$flink -> credentials = $creds ;
$flink -> created = common_sql_now ();
// Defaults: noticesync on, everything else off
$flink -> set_flags ( true , false , false , false );
$flink_id = $flink -> insert ();
if ( empty ( $flink_id )) {
common_log_db_error ( $flink , 'INSERT' , __FILE__ );
$this -> serverError ( _ ( 'Couldn\'t link your Twitter account.' ));
}
save_twitter_user ( $twuid , $this -> tw_fields [ 'name' ]);
return $flink_id ;
}
function bestNewNickname ()
{
if ( ! empty ( $this -> tw_fields [ 'name' ])) {
$nickname = $this -> nicknamize ( $this -> tw_fields [ 'name' ]);
if ( $this -> isNewNickname ( $nickname )) {
return $nickname ;
}
}
return null ;
}
// Given a string, try to make it work as a nickname
function nicknamize ( $str )
{
$str = preg_replace ( '/\W/' , '' , $str );
$str = str_replace ( array ( '-' , '_' ), '' , $str );
return strtolower ( $str );
}
function isNewNickname ( $str )
{
if ( ! Validate :: string ( $str , array ( 'min_length' => 1 ,
'max_length' => 64 ,
'format' => NICKNAME_FMT ))) {
return false ;
}
if ( ! User :: allowed_nickname ( $str )) {
return false ;
}
if ( User :: staticGet ( 'nickname' , $str )) {
return false ;
}
return true ;
}
2009-08-01 17:20:44 +09:00
}