#
# 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 .
define('NO_GUI', 1); // blank page, please
global $db;
global $_USER;
//So we load the $start variable, for the pages.
if (!$_GET['page']) $start = getStart(1); else $start = getStart($_GET['page']);
$params = PARAMS;
$module = MODULE;
$extra = EXTRA;
/*
Structure: http://myserver.com/$module[0].$module[1]/$params[0].$params[1]/$extra[0].$extra[1]
$XXXXX[0] = the name , $XXXXX[1] = the extension
*/
$params = explode('.', $params);
$module = explode('.', $module);
$extra = explode('.', $extra);
// If we find a cookie, then we set $_USER
checkUserAPI();
// Because not all the applications can handle HTTP Errors, we allow them to overpass this limit
if (isset($_GET['suppress_response_codes'])) $no401 = true; else $no401 = false;
// If an application wants more than 25 notes, we allow it to load more than 25 notes.
if (isset($_GET['count']) && (!empty($_GET['count']) && (is_numeric($_GET['count'])))) $count = $_GET['count'];
else $count = NOTES_PER_PAGE;
// Array containing the actions that an non-authentificated user can do
$public_actions = array('/statuses/public_timeline', '/statuses/show', '/help/test', '/users/show');
if (!empty($params[0])) {
if (!empty($extra[0])) {
$action = '/'.$module[0].'/'.$params[0].'/'.$extra[0].'.'.$extra[1];
if (empty($extra[1])) $extension = 'basic';
else $extension = $extra[1];
}
else {
$action = '/'.$module[0].'/'.$params[0].'.'.$params[1];
if (empty($params[1])) $extension = 'basic';
else $extension = $params[1];
}
}
else {
$action = '/'.$module[0].'.'.$module[1];
if (empty($module[1])) $extension = 'basic';
else $extension = $module[1];
}
//We split $action in 2 strings, the action and the extension
$explode = explode('.', $action);
if (!in_array($explode[0], $public_actions)) {
if ($_USER) {
//Because the user is banned, it shouldn't have access to the API
if ($_USER['status'] == 'banned') die(apiError($extension, $action, 'User not allowed.', $no401));
else extract($_USER);
}
else {
if ($_SERVER['PHP_AUTH_USER'] && $_SERVER['PHP_AUTH_PW']) {
if (filter_var($_SERVER['PHP_AUTH_USER'], FILTER_VALIDATE_EMAIL)) $_USER = $db->getUserInfoAPI(false, $_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']);
else $_USER = $db->getUserInfoAPI($_SERVER['PHP_AUTH_USER'], false, $_SERVER['PHP_AUTH_PW']);
if (!empty($_USER)) {
$SID = md5($_USER['password'].$_USER['salt'].$_USER['api'].time());
$db->newSession($_USER['ID'], $SID, 'api');
setcookie('JSESSIONID', $SID, time()+(86400*60));
} else header('WWW-Authenticate: Basic realm="'.NAME.'"');
} else header('WWW-Authenticate: Basic realm="'.NAME.'"');
}
//The user is not logged, so we return a 401 Error.
if (!$_USER) {
die(apiError($extension, $action, 'This method requires authentication.', $no401));
}
else {
$db->updateLastSeen($_USER['ID'], time());
}
}
// OK, party starts here!
if ($module[0] == 'statuses') {
if ($params[0] == 'public_timeline') {
$result = $db->getNotes('public', $start, $count, false, false, $_USER['ignored']);
switch ($params[1]) {
case 'xml':
createXML('notes', $result);
break;
case 'json':
createJSON('notes', $result);
break;
case 'rss':
createRSS($result, __('Public notes from all the users'), __('Public notes from all the users'), 'statuses/public_timeline.rss');
break;
}
} elseif ($params[0] == 'friends_timeline') {
$result = $db->getNotes('friends', $start, $count, $_USER['ID'], false, $_USER['ignored']);
switch ($params[1]) {
case 'xml':
createXML('notes', $result);
break;
case 'json':
createJSON('notes', $result);
break;
}
} elseif ($params[0] == 'user_timeline') {
$result = $db->getNotes('archive', $start, $count, $_USER['ID'], false);
switch ($params[1]) {
case 'xml':
createXML('notes', $result);
break;
case 'json':
createJSON('notes', $result);
break;
case 'rss':
createRSS($result, __('Notes of ').$_USER['username'], __('Notes of ').$_USER['username'], 'statuses/user_timeline.rss');
break;
}
} elseif ($params[0] == 'replies') {
$result = $db->getNotes('replies', $start, $count, $_USER['ID'], $_USER['username'], $_USER['ignored']);
switch ($params[1]) {
case 'xml':
createXML('notes', $result);
break;
case 'json':
createJSON('notes', $result);
break;
case 'rss':
createRSS($result, __('Replies for ').$_USER['username'], __('Related replies to ').$_USER['username'], 'statuses/replies.rss');
break;
}
} elseif ($params[0] == 'followers') {
if (!$extra[0]) $result = $db->getFollowers($_USER['ID'], $start, $count);
else {
if (!is_numeric($extra[0])) $extra[0] = $db->getIdByUser($extra[0]);
$result = $db->getFollowers($extra[0], $start, $count);
}
switch ($extension) {
case 'xml':
createXML('users', $result);
break;
case 'json':
createJSON('users', $result);
break;
}
} elseif ($params[0] == 'friends') {
if (!$extra[0]) $result = $db->getFollowing($_USER['ID'], $start, $count);
else {
if (!is_numeric($extra[0])) $extra[0] = $db->getIdByUser($extra[0]);
$result = $db->getFollowing($extra[0], $start, $count);
}
switch ($extension) {
case 'xml':
createXML('users', $result);
break;
case 'json':
createJSON('users', $result);
break;
}
} elseif ($params[0] == 'show') {
if (!$extra[0]) die(apiError($extension, $action, 'No status found with that ID.', $no401));
else {
$result = $db->getNoteCombined($extra[0]);
if (!$result) die(apiError($extension, $action, 'No status found with that ID.', $no401));
else {
if ($type == 'private') {
if (!$_USER['ID']) die(apiError($extension, $action, 'This method requires authentication.', $no401));
else {
if ($db->checkFollowing($result['user_id'], $_USER['ID'])) {
switch ($extra[1]) {
case 'xml':
createXML('notess', array('id'=>$extra[0]));
break;
case 'json':
createJSON('notess', array('id'=>$extra[0]));
break;
}
}
else die(apiError($extension, $action, 'This method requires authentication.', $no401));
}
}
else {
switch ($extra[1]) {
case 'xml':
createXML('notess', array('id'=>$extra[0]));
break;
case 'json':
createJSON('notess', array('id'=>$extra[0]));
break;
}
}
}
}
} elseif ($params[0] == 'update') {
if (!$_POST) die(apiError($extension, $action, 'This method requires a POST.', $no401));
else {
if (!$_POST['status']) die(apiError($extension, $action, "Client must provide a 'status' parameter with a value.", $no401));
else {
if ((time() - $_USER['last_note']) < WAIT_UNTIL_REPOST) die(apiError($extension, $action, 'Relax...', $no401));
global $skipauth;
$skipauth = 1;
if (empty($_POST['source'])) $source = 'api'; else $source = $_POST['source'];
if ($_USER['twitter_username'] && ($_USER['twitter_username'] && ($_USER['post_tweets']))) $twitter = true;
else $twitter = false;
$result = postNote($_POST['status'], $_USER, $source, false, false, false, $twitter);
switch ($params[1]) {
case 'xml':
createXML('notess', array('id'=>$result));
break;
case 'json':
createJSON('notess', array('id'=>$result));
break;
}
}
}
} elseif ($params[0] == 'destroy') {
if (!$_POST || (!$_DELETE)) die(apiError($extension, $action, 'This method requires a POST or DELETE.'));
else {
if (!$extra[0]) die(apiError($extension, $action, 'No status found with that ID.'));
else {
$result = getNoteCombined($extra[0]);
if (!$result) die(apiError($extension, $action, 'No status found with that ID.'));
else {
if ($result['username'] != $_SERVER['PHP_AUTH_USER']) die(apiError($extension, $action, "You may not delete another user's status.", $no401));
switch ($extra[1]) {
case 'xml':
createXML('notess', $extra[0]);
break;
case 'json':
createJSON('notess', $extra[0]);
break;
}
$result = $db->deleteNote($extra[0], false, $_SERVER['PHP_AUTH_USER']);
}
}
}
}
} elseif ($module[0] == 'direct_messages') {
if (!$params[0]) {
$result = $db->getNotes('private', $start, $count, $_USER['ID'], $_USER['username']);
switch ($module[1]) {
case 'xml':
createXML('direct_messages', $result);
break;
case 'json':
createJSON('direct_messages', $result);
break;
case 'rss':
createRSS($result, __('Private notes to ').$_USER['username'], __('Private notes to ').$_USER['username'], 'direct_messages.rss');
break;
}
} elseif ($params[0] == 'sent') {
$result = $db->getNotes('private_sent', $start, $count, $_USER['ID'], $_USER['username']);
switch ($params[1]) {
case 'xml':
createXML('direct_messages', $result);
break;
case 'json':
createJSON('direct_messages', $result);
break;
case 'rss':
createRSS($result, __('Private notes sent by ').$_USER['username'], __('Private notes sent by ').$_USER['username'], 'direct_messages/sent.rss');
break;
}
}
} elseif ($module[0] == 'users') {
if ($params[0] == 'show') {
if (!$extra[0]) die(apiError($extension, $action, 'Not found', $no401));
else {
if (!is_numeric($extra[0])) $userInfo = $db->getUserInfo(false, $extra[0]);
else $userInfo = $db->getUserInfo($extra[0]);
if (!$userInfo) die(apiError($extension, $action, 'Not found', $no401));
else {
if ($userInfo['private'] == 1) {
if (!$_USER['ID']) die(apiError($extension, $action, 'This method requires authentication.', $mo401));
else {
if ($db->checkFollowing($userInfo['user_id'], $_USER['ID'])) {
switch ($extra[1]) {
case 'xml':
createXML('users', $userInfo['ID']);
break;
case 'json':
createJSON('users', array('id'=>$userInfo['ID']));
break;
}
}
else die(apiError($extension, $action, 'This method requires authentication.', $no401));
}
}
else {
switch ($extra[1]) {
case 'xml':
createXML('users', array('id'=>$userInfo['ID']));
break;
case 'json':
createJSON('users', array('id'=>$userInfo['ID']));
break;
}
}
}
}
}
} elseif ($module[0] == 'favorites') {
if ($params[0] == 'create') {
if (!$_POST) die(apiError($extension, $action, 'This method requires a POST.', $no401));
else {
if ($extra[0]) {
$result = $db->getNoteCombined($extra[0]);
if (!$result) die(apiError($extension, $action, 'Not found', $no401));
else {
$favorited = $db->checkFavorite($_USER['ID'], $extra[0]);
if (!$favorited) {
$db->newFavorite($_USER['ID'], $extra[0]);
switch ($extra[1]) {
case 'xml':
createXML('notess', array('id'=>$extra[0]));
break;
case 'json':
createJSON('notess', array('id'=>$extra[0]));
break;
}
}
else die(apiError($extension, $action, 'You have already favorited this status.', $no401));
}
}
}
} elseif ($params[0] == 'destroy') {
if ($_POST) die(apiError($extension, $action, 'This method requires a POST.', $no401));
else {
if ($extra[0]) {
$result = $db->getNoteCombined($extra[0]);
if (!$result) die(apiError($extension, $action, 'Not found', $no401));
else {
$favorited = $db->checkFavorite($_USER['ID'], $extra[0]);
if ($favorited) {
$db->deleteFavorite($_USER['ID'], $extra[0]);
switch ($extra[1]) {
case 'xml':
createXML('notess', array('id'=>$extra[0]));
break;
case 'json':
createJSON('notess', array('id'=>$extra[0]));
break;
}
}
else die(apiError($extension, $action, 'Not found.', $no401));
}
}
}
} else {
if (!$params[0]) $result = $db->getNotes('favorites', $start, $count, $_USER['ID'], false);
else {
if (!is_numeric($params[0])) $params[0] = $db->getIdByUser($params[0]);
$result = $db->getNotes('favorites', $start, $count, $params[0], false);
}
switch ($params[1]) {
case 'xml':
createXML('notes', $result);
break;
case 'json':
createJSON('notes', $result);
break;
case 'rss':
if (!$params[0]) $title = __('Favorite notes of ').$_USER['username'];
else $title = __('Favorite notes of ').$params[0];
createRSS($result, $title, $title, substr($action, 1));
break;
}
}
} elseif ($module[0] == 'followers') {
if ($params[0] == 'ids') {
$result = $db->getFollowers($_USER['ID'], $start, $count);
switch ($params[1]) {
case 'xml':
createXML('ids', $result);
break;
case 'json':
createJSON('ids', $result);
break;
}
}
} elseif ($module[0] == 'friends') {
if ($params[0] == 'ids') {
$result = $db->getFollowing($_USER['ID'], $start, $count);
switch ($params[1]) {
case 'xml':
createXML('ids', $result);
break;
case 'json':
createJSON('ids', $result);
break;
}
}
} elseif ($module[0] == 'account') {
if ($params[0] == 'verify_credentials') {
switch ($params[1]) {
case 'xml':
createXML('users', array('id'=>$_USER['ID']));
break;
case 'json':
createJSON('acc_verif', array('id'=>$_USER['ID']));
break;
default:
echo 'Authorized';
break;
}
} elseif ($params[0] == 'rate_limit_status') {
if ($params[1] == 'xml') {
header('HTTP/1.0 200 OK');
header('Content-Type: text/xml charset=UTF-8');
$XMLWriter = new XMLWriter();
$XMLWriter->openURI('php://output');
$XMLWriter->startDocument('1.0', 'UTF-8');
$XMLWriter->startElement('hash');
$XMLWriter->startElement('reset-time');
$XMLWriter->writeAttribute('type','datetime');
$XMLWriter->text('2020-01-01T00:00:00+00:00');
$XMLWriter->endElement();
$XMLWriter->startElement('remaining-hits');
$XMLWriter->writeAttribute('type','integer');
$XMLWriter->text('10000');
$XMLWriter->endElement();
$XMLWriter->startElement('hourly-limit');
$XMLWriter->writeAttribute('type','integer');
$XMLWriter->text('10000');
$XMLWriter->endElement();
$XMLWriter->startElement('reset-time-in-seconds');
$XMLWriter->writeAttribute('type','integer');
$XMLWriter->text('1577836800');
$XMLWriter->endElement();
$XMLWriter->endElement();
$XMLWriter->flush();
} elseif ($params[1] == 'json') {
header('Content-Type: text/javascript; charset=utf-8');
echo json_encode(array(
'hourly_limit' => 10000,
'reset_time_in_seconds' => 1577836800,
'reset_time' => 'Mon Jan 1 00:00:00 +0000 2012',
'remaining_hits' => 10000
));
}
} elseif ($params[0] == 'end_session') {
setcookie(NAME, '', time()-3600);
}
} elseif ($module[0] == 'help') {
if ($params[0] == 'test') {
switch ($params[1]) {
case 'xml':
echo 'true';
break;
case 'json':
echo '"ok"';
break;
default:
echo 'ok';
break;
}
}
}
?>