From 6beddfdbb926aaa5af42e68ff459f11e0e5f6b5f Mon Sep 17 00:00:00 2001 From: zach Date: Tue, 30 Sep 2008 22:54:21 -0400 Subject: [PATCH] Twitter-compatible API - cleaned up sloppy control flow: exit() statements everywhere darcs-hash:20081001025421-462f3-3bf666327de3a3c5ea506b2c387741dc3d0e82bf.gz --- actions/api.php | 54 ++++++++++++++-------------- actions/twitapiaccount.php | 57 ++++++++++++------------------ actions/twitapiblocks.php | 6 ++-- actions/twitapidirect_messages.php | 15 ++++---- actions/twitapifavorites.php | 17 ++++----- actions/twitapifriendships.php | 21 ++++++----- actions/twitapihelp.php | 3 +- actions/twitapinotifications.php | 6 ++-- actions/twitapistatuses.php | 31 ++++++---------- actions/twitapiusers.php | 35 +++++++++--------- lib/twitterapi.php | 3 -- 11 files changed, 105 insertions(+), 143 deletions(-) diff --git a/actions/api.php b/actions/api.php index c4cfd569d6..47a69f1526 100644 --- a/actions/api.php +++ b/actions/api.php @@ -26,14 +26,14 @@ class ApiAction extends Action { var $api_arg; var $api_method; var $api_action; - + function handle($args) { parent::handle($args); $this->api_action = $this->arg('apiaction'); $method = $this->arg('method'); $argument = $this->arg('argument'); - + if (isset($argument)) { $cmdext = explode('.', $argument); $this->api_arg = $cmdext[0]; @@ -45,71 +45,70 @@ class ApiAction extends Action { $this->api_method = $cmdext[0]; $this->content_type = strtolower($cmdext[1]); } - + # XXX Maybe check to see if the command actually exists first? if($this->requires_auth()) { if (!isset($_SERVER['PHP_AUTH_USER'])) { - + # This header makes basic auth go header('WWW-Authenticate: Basic realm="Laconica API"'); - + # if the user hits cancel -- bam! - $this->show_basic_auth_error(); + $this->show_basic_auth_error(); } else { $nickname = $_SERVER['PHP_AUTH_USER']; $password = $_SERVER['PHP_AUTH_PW']; $user = common_check_user($nickname, $password); - + if ($user) { $this->user = $user; $this->process_command(); } else { # basic authentication failed - $this->show_basic_auth_error(); - } + $this->show_basic_auth_error(); + } } } else { $this->process_command(); - } + } } - - function process_command() { + + function process_command() { $action = "twitapi$this->api_action"; - $actionfile = INSTALLDIR."/actions/$action.php"; + $actionfile = INSTALLDIR."/actions/$action.php"; if (file_exists($actionfile)) { require_once($actionfile); $action_class = ucfirst($action)."Action"; $action_obj = new $action_class(); if (method_exists($action_obj, $this->api_method)) { - + $apidata = array( 'content-type' => $this->content_type, 'api_method' => $this->api_method, 'api_arg' => $this->api_arg, 'user' => $this->user); - - call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata); - # all API methods should exit() - } - } - common_user_error("API method not found!", $code=404); - } + call_user_func(array($action_obj, $this->api_method), $_REQUEST, $apidata); + } + } else { + common_user_error("API method not found!", $code=404); + } + } # Whitelist of API methods that don't need authentication function requires_auth() { static $noauth = array( 'statuses/public_timeline', 'statuses/show', 'users/show', - 'help/test', + 'help/test', 'help/downtime_schedule'); - + static $bareauth = array('statuses/user_timeline', - 'statuses/friends', + 'statuses/friends', 'statuses/followers'); $fullname = "$this->api_action/$this->api_method"; - + if (in_array($fullname, $bareauth)) { # bareauth: only needs auth if without an argument if ($this->api_arg) { @@ -125,12 +124,11 @@ class ApiAction extends Action { return true; } } - + function show_basic_auth_error() { header('HTTP/1.1 401 Unauthorized'); header('Content-type: text/plain'); print("Could not authenticate you."); # exactly what Twitter says - no \n - exit(); } - + } diff --git a/actions/twitapiaccount.php b/actions/twitapiaccount.php index 5baf0e3e39..4ce61f3536 100644 --- a/actions/twitapiaccount.php +++ b/actions/twitapiaccount.php @@ -24,73 +24,66 @@ require_once(INSTALLDIR.'/lib/twitterapi.php'); class TwitapiaccountAction extends TwitterapiAction { function is_readonly() { - + static $write_methods = array( 'update_location', 'update_delivery_device'); - - $cmdtext = explode('.', $this->arg('method')); - - if (in_array($cmdtext[0], $write_methods)) { + + $cmdtext = explode('.', $this->arg('method')); + + if (in_array($cmdtext[0], $write_methods)) { return false; } - + return true; } function verify_credentials($args, $apidata) { if ($apidata['content-type'] == 'xml') { - header('Content-Type: application/xml; charset=utf-8'); + header('Content-Type: application/xml; charset=utf-8'); print 'true'; } elseif ($apidata['content-type'] == 'json') { - header('Content-Type: application/json; charset=utf-8'); + header('Content-Type: application/json; charset=utf-8'); print '{"authorized":true}'; } else { common_user_error(_('API method not found!'), $code=404); } - - exit(); + } - + function end_session($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } - + function update_location($args, $apidata) { parent::handle($args); - if (!in_array($apidata['content-type'], array('xml', 'json'))) { - common_user_error(_('API method not found!'), $code = 404); - exit; - } - if ($_SERVER['REQUEST_METHOD'] != 'POST') { $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - exit(); + return; } $location = trim($this->arg('location')); if (!is_null($location) && strlen($location) > 255) { - - // XXX: But Twitter just truncates and runs with it. -- Zach + + // XXX: But Twitter just truncates and runs with it. -- Zach $this->client_error(_('That\'s too long. Max notice size is 255 chars.'), 406, $apidate['content-type']); - exit(); + return; } - + $user = $apidata['user']; $profile = $user->getProfile(); - + if (!$profile) { common_server_error(_('User has no profile.')); - exit(); + return; } - + $orig_profile = clone($profile); $profile->location = $location; - + common_debug('Old profile: ' . common_log_objstring($orig_profile), __FILE__); common_debug('New profile: ' . common_log_objstring($profile), __FILE__); @@ -99,29 +92,25 @@ class TwitapiaccountAction extends TwitterapiAction { if (!$result) { common_log_db_error($profile, 'UPDATE', __FILE__); common_server_error(_('Couldn\'t save profile.')); - exit(); + return; } common_broadcast_profile($profile); $type = $apidata['content-type']; - + $this->init_document($type); $this->show_profile($profile, $type); $this->end_document($type); - - exit(); } function update_delivery_device($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } - + function rate_limit_status($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } } \ No newline at end of file diff --git a/actions/twitapiblocks.php b/actions/twitapiblocks.php index 0fdbba199f..21d620dd3a 100644 --- a/actions/twitapiblocks.php +++ b/actions/twitapiblocks.php @@ -26,13 +26,11 @@ class TwitapiblocksAction extends TwitterapiAction { function create($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } - + function destroy($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } - + } \ No newline at end of file diff --git a/actions/twitapidirect_messages.php b/actions/twitapidirect_messages.php index 1ef543f814..466e748238 100644 --- a/actions/twitapidirect_messages.php +++ b/actions/twitapidirect_messages.php @@ -104,7 +104,6 @@ class Twitapidirect_messagesAction extends TwitterapiAction { common_user_error(_('API method not found!'), $code = 404); } - exit(); } // had to change this from "new" to "create" to avoid PHP reserved word @@ -113,7 +112,7 @@ class Twitapidirect_messagesAction extends TwitterapiAction { if ($_SERVER['REQUEST_METHOD'] != 'POST') { $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - exit(); + return; } $user = $apidata['user']; @@ -130,30 +129,30 @@ class Twitapidirect_messagesAction extends TwitterapiAction { } else if (mb_strlen($status) > 140) { $this->client_error(_('That\'s too long. Max message size is 140 chars.'), $code = 406, $apidata['content-type']); - exit(); + return; } $other = $this->get_user($this->trimmed('user')); if (!$other) { $this->client_error(_('Recipient user not found.'), $code = 403, $apidata['content-type']); - exit(); + return; } else if (!$user->mutuallySubscribed($other)) { $this->client_error(_('Can\'t send direct messages to users who aren\'t your friend.'), $code = 403, $apidata['content-type']); - exit(); + return; } else if ($user->id == $other->id) { // Sending msgs to yourself is allowed by Twitter $this->client_error(_('Don\'t send a message to yourself; just say it to yourself quietly instead.'), $code = 403, $apidata['content-type']); - exit(); + return; } $message = Message::saveNew($user->id, $other->id, $content, $source); if (is_string($message)) { $this->server_error($message); - exit(); + return; } $this->notify($user, $other, $message); @@ -164,13 +163,11 @@ class Twitapidirect_messagesAction extends TwitterapiAction { $this->show_single_json_dmsg($message); } - exit(); } function destroy($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } function show_xml_dmsgs($message) { diff --git a/actions/twitapifavorites.php b/actions/twitapifavorites.php index d7d77907dd..a327cddb44 100644 --- a/actions/twitapifavorites.php +++ b/actions/twitapifavorites.php @@ -66,7 +66,7 @@ class TwitapifavoritesAction extends TwitterapiAction { if (!$profile) { common_server_error(_('User has no profile.')); - exit(); + return; } $page = $this->arg('page'); @@ -83,7 +83,7 @@ class TwitapifavoritesAction extends TwitterapiAction { if (!$notice) { common_server_error(_('Could not retrieve favorite notices.')); - exit(); + return; } $sitename = common_config('site', 'name'); @@ -111,7 +111,6 @@ class TwitapifavoritesAction extends TwitterapiAction { common_user_error(_('API method not found!'), $code = 404); } - exit(); } function create($args, $apidata) { @@ -119,14 +118,14 @@ class TwitapifavoritesAction extends TwitterapiAction { if (!in_array($apidata['content-type'], array('xml', 'json'))) { common_user_error(_('API method not found!'), $code = 404); - exit; + return; } // Check for RESTfulness if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { // XXX: Twitter just prints the err msg, no XML / JSON. $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); - exit(); + return; } $user = $apidata['user']; @@ -135,13 +134,13 @@ class TwitapifavoritesAction extends TwitterapiAction { if (!$notice) { $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']); - exit(); + return; } // XXX: Twitter lets you fave things repeatedly via api. if ($user->hasFave($notice)) { $this->client_error(_('This notice is already a favorite!'), 403, $apidata['content-type']); - exit(); + return; } common_debug("notice: " . $apidata['api_arg']); @@ -150,7 +149,7 @@ class TwitapifavoritesAction extends TwitterapiAction { if (!$fave) { common_server_error(_('Could not create favorite.')); - exit(); + return; } $this->notify($fave, $notice, $user); @@ -162,13 +161,11 @@ class TwitapifavoritesAction extends TwitterapiAction { $this->show_single_json_status($notice); } - exit(); } function destroy($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } // XXX: these two funcs swiped from faves. Maybe put in util.php, or some common base class? diff --git a/actions/twitapifriendships.php b/actions/twitapifriendships.php index f9ff251d67..01aa5fa364 100644 --- a/actions/twitapifriendships.php +++ b/actions/twitapifriendships.php @@ -42,7 +42,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { if ($_SERVER['REQUEST_METHOD'] != 'POST') { $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - exit(); + return; } $id = $apidata['api_arg']; @@ -51,7 +51,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { if (!$other) { $this->client_error(_('Could not follow user: User not found.'), 403, $apidata['content-type']); - exit(); + return; } $user = $apidata['user']; @@ -59,7 +59,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { if ($user->isSubscribed($other)) { $errmsg = sprintf(_('Could not follow user: %s is already on your list.'), $other->nickname); $this->client_error($errmsg, 403, $apidata['content-type']); - exit(); + return; } $sub = new Subscription(); @@ -75,7 +75,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { if (!$result) { $errmsg = sprintf(_('Could not follow user: %s is already on your list.'), $other->nickname); $this->client_error($errmsg, 400, $apidata['content-type']); - exit(); + return; } $sub->query('COMMIT'); @@ -86,7 +86,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { $this->init_document($type); $this->show_profile($other, $type); $this->end_document($type); - exit(); + } //destroy @@ -106,7 +106,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); - exit(); + return; } $id = $apidata['api_arg']; @@ -126,14 +126,14 @@ class TwitapifriendshipsAction extends TwitterapiAction { $sub->query('COMMIT'); } else { $this->client_error(_('You are not friends with the specified user.'), 403, $apidata['content-type']); - exit(); + return; } $type = $apidata['content-type']; $this->init_document($type); $this->show_profile($other, $type); $this->end_document($type); - exit(); + } // Tests if a friendship exists between two users. @@ -154,7 +154,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { if (!in_array($apidata['content-type'], array('xml', 'json'))) { common_user_error(_('API method not found!'), $code = 404); - exit; + return; } $user_a_id = $this->trimmed('user_a'); @@ -165,7 +165,7 @@ class TwitapifriendshipsAction extends TwitterapiAction { if (!$user_a || !$user_b) { $this->client_error(_('Two user ids or screen_names must be supplied.'), 400, $apidata['content-type']); - exit(); + return; } if ($user_a->isSubscribed($user_b)) { @@ -189,7 +189,6 @@ class TwitapifriendshipsAction extends TwitterapiAction { break; } - exit(); } } \ No newline at end of file diff --git a/actions/twitapihelp.php b/actions/twitapihelp.php index d2439484c1..6e9b41f8ae 100644 --- a/actions/twitapihelp.php +++ b/actions/twitapihelp.php @@ -45,13 +45,12 @@ class TwitapihelpAction extends TwitterapiAction { } else { common_user_error(_('API method not found!'), $code=404); } - exit(); + } function downtime_schedule($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } } \ No newline at end of file diff --git a/actions/twitapinotifications.php b/actions/twitapinotifications.php index 9e3cc15c42..8d93309a25 100644 --- a/actions/twitapinotifications.php +++ b/actions/twitapinotifications.php @@ -27,13 +27,11 @@ class TwitapinotificationsAction extends TwitterapiAction { function follow($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } - + function leave($args, $apidata) { parent::handle($args); common_server_error(_('API method under construction.'), $code=501); - exit(); } - + } \ No newline at end of file diff --git a/actions/twitapistatuses.php b/actions/twitapistatuses.php index ed6c224fa5..16ed7484cf 100644 --- a/actions/twitapistatuses.php +++ b/actions/twitapistatuses.php @@ -98,7 +98,6 @@ class TwitapistatusesAction extends TwitterapiAction { common_server_error(_('Couldn\'t find any statuses.'), $code = 503); } - exit(); } /* @@ -177,7 +176,6 @@ class TwitapistatusesAction extends TwitterapiAction { common_user_error(_('API method not found!'), $code = 404); } - exit(); } /* @@ -235,7 +233,7 @@ class TwitapistatusesAction extends TwitterapiAction { if (!$profile) { common_server_error(_('User has no profile.')); - exit(); + return; } $count = $this->arg('count'); @@ -290,7 +288,6 @@ class TwitapistatusesAction extends TwitterapiAction { common_user_error(_('API method not found!'), $code = 404); } - exit(); } function update($args, $apidata) { @@ -299,12 +296,12 @@ class TwitapistatusesAction extends TwitterapiAction { if (!in_array($apidata['content-type'], array('xml', 'json'))) { common_user_error(_('API method not found!'), $code = 404); - exit; + return; } if ($_SERVER['REQUEST_METHOD'] != 'POST') { $this->client_error(_('This method requires a POST.'), 400, $apidata['content-type']); - exit(); + return; } $user = $apidata['user']; @@ -322,7 +319,7 @@ class TwitapistatusesAction extends TwitterapiAction { // No error is given, but the status is not posted to the // user's timeline. Seems bad. Shouldn't we throw an // errror? -- Zach - exit(); + return; } else if (mb_strlen($status) > 140) { @@ -331,7 +328,7 @@ class TwitapistatusesAction extends TwitterapiAction { // that assume Twitter will truncate for them. Should we just // truncate too? -- Zach $this->client_error(_('That\'s too long. Max notice size is 140 chars.'), $code = 406, $apidata['content-type']); - exit(); + return; } $reply_to = NULL; @@ -345,7 +342,7 @@ class TwitapistatusesAction extends TwitterapiAction { $reply_to = $in_reply_to_status_id; } else { $this->client_error(_('Not found'), $code = 404, $apidata['content-type']); - exit(); + return; } } @@ -353,7 +350,7 @@ class TwitapistatusesAction extends TwitterapiAction { if (is_string($notice)) { $this->server_error($notice); - exit(); + return; } common_broadcast_notice($notice); @@ -367,7 +364,6 @@ class TwitapistatusesAction extends TwitterapiAction { $apidata['api_arg'] = $notice->id; $this->show($args, $apidata); - exit(); } /* @@ -447,7 +443,6 @@ class TwitapistatusesAction extends TwitterapiAction { common_user_error(_('API method not found!'), $code = 404); } - exit(); } function show($args, $apidata) { @@ -455,7 +450,7 @@ class TwitapistatusesAction extends TwitterapiAction { if (!in_array($apidata['content-type'], array('xml', 'json'))) { common_user_error(_('API method not found!'), $code = 404); - exit; + return; } $notice_id = $apidata['api_arg']; @@ -472,7 +467,6 @@ class TwitapistatusesAction extends TwitterapiAction { $this->client_error(_('No status with that ID found.'), 404, $apidata['content-type']); } - exit(); } @@ -497,14 +491,14 @@ class TwitapistatusesAction extends TwitterapiAction { if (!in_array($apidata['content-type'], array('xml', 'json'))) { common_user_error(_('API method not found!'), $code = 404); - exit; + return; } // Check for RESTfulness if (!in_array($_SERVER['REQUEST_METHOD'], array('POST', 'DELETE'))) { // XXX: Twitter just prints the err msg, no XML / JSON. $this->client_error(_('This method requires a POST or DELETE.'), 400, $apidata['content-type']); - exit(); + return; } $user = $apidata['user']; @@ -513,7 +507,7 @@ class TwitapistatusesAction extends TwitterapiAction { if (!$notice) { $this->client_error(_('No status found with that ID.'), 404, $apidata['content-type']); - exit(); + return; } if ($user->id == $notice->profile_id) { @@ -532,7 +526,6 @@ class TwitapistatusesAction extends TwitterapiAction { $this->client_error(_('You may not delete another user\'s status.'), 403, $apidata['content-type']); } - exit(); } # User Methods @@ -626,7 +619,6 @@ class TwitapistatusesAction extends TwitterapiAction { $this->init_document($type); $this->show_profiles($others, $type); $this->end_document($type); - exit(); } function get_subs_user($apidata) { @@ -673,7 +665,6 @@ class TwitapistatusesAction extends TwitterapiAction { break; default: $this->client_error(_('unsupported file type')); - exit(); } } diff --git a/actions/twitapiusers.php b/actions/twitapiusers.php index b43a641520..625a695e37 100644 --- a/actions/twitapiusers.php +++ b/actions/twitapiusers.php @@ -23,11 +23,11 @@ require_once(INSTALLDIR.'/lib/twitterapi.php'); class TwitapiusersAction extends TwitterapiAction { - function is_readonly() { + function is_readonly() { return true; } -/* +/* Returns extended information of a given user, specified by ID or screen name as per the required id parameter below. This information includes design settings, so third party developers can theme their @@ -47,45 +47,45 @@ class TwitapiusersAction extends TwitterapiAction { * email. Optional. The email address of a user. Ex: http://twitter.com/users/show.xml?email=test@example.com -*/ +*/ function show($args, $apidata) { parent::handle($args); - + if (!in_array($apidata['content-type'], array('xml', 'json'))) { common_user_error(_('API method not found!'), $code = 404); - exit; + return; } - + $user = null; $email = $this->arg('email'); - + if (isset($apidata['api_arg'])) { if (is_numeric($apidata['api_arg'])) { // by user id - $user = User::staticGet($apidata['api_arg']); + $user = User::staticGet($apidata['api_arg']); } else { // by nickname $nickname = common_canonical_nickname($apidata['api_arg']); $user = User::staticGet('nickname', $nickname); - } + } } elseif ($email) { // or, find user by email address // XXX: The Twitter API spec say an id is *required*, but you can actually // pull up a user with just an email address. -- Zach - $user = User::staticGet('email', $email); - } + $user = User::staticGet('email', $email); + } if (!$user) { // XXX: Twitter returns a random(?) user instead of throwing and err! -- Zach $this->client_error(_('User not found.'), 404, $apidata['content-type']); - exit(); + return; } - + $profile = $user->getProfile(); if (!$profile) { common_server_error(_('User has no profile.')); - exit(); + return; } $twitter_user = $this->twitter_user_array($profile, true); @@ -115,7 +115,7 @@ class TwitapiusersAction extends TwitterapiAction { $twitter_user['following'] = ''; $twitter_user['notifications'] = ''; - if ($apidata['content-type'] == 'xml') { + if ($apidata['content-type'] == 'xml') { $this->init_document('xml'); $this->show_twitter_xml_user($twitter_user); $this->end_document('xml'); @@ -123,8 +123,7 @@ class TwitapiusersAction extends TwitterapiAction { $this->init_document('json'); $this->show_json_objects($twitter_user); $this->end_document('json'); - } - - exit(); + } + } } diff --git a/lib/twitterapi.php b/lib/twitterapi.php index d6ec1e9a09..0570a68671 100644 --- a/lib/twitterapi.php +++ b/lib/twitterapi.php @@ -194,7 +194,6 @@ class TwitterapiAction extends Action { $twitter_status = $this->twitter_status_array($notice); $this->show_twitter_xml_status($twitter_status); $this->end_document('xml'); - exit(); } function show_single_json_status($notice) { @@ -202,7 +201,6 @@ class TwitterapiAction extends Action { $status = $this->twitter_status_array($notice); $this->show_json_objects($status); $this->end_document('json'); - exit(); } function show_single_xml_dmsg($message) { @@ -469,7 +467,6 @@ class TwitterapiAction extends Action { $this->end_document('json'); } - exit(); } function init_twitter_rss() {