aboutsummaryrefslogtreecommitdiff
path: root/p/api/greader.php
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2018-02-24 12:02:16 +0100
committerGravatar GitHub <noreply@github.com> 2018-02-24 12:02:16 +0100
commit5ebeb9e3e5d46195a83211140c1d28d58be19b2a (patch)
tree6b93ae52a1206b6045087f893dde67a04b4e1bda /p/api/greader.php
parent889888f20eb6f3dd476b78c0f59672f7c7962354 (diff)
parent294f9336ad0f315574c74d6b527b1bb8a280f3c6 (diff)
Merge pull request #1786 from FreshRSS/dev1.10.0
FreshRSS 1.10.0
Diffstat (limited to 'p/api/greader.php')
-rw-r--r--p/api/greader.php190
1 files changed, 127 insertions, 63 deletions
diff --git a/p/api/greader.php b/p/api/greader.php
index 99304f4ec..b1546192e 100644
--- a/p/api/greader.php
+++ b/p/api/greader.php
@@ -216,9 +216,13 @@ function token($conf) {
function checkToken($conf, $token) {
//http://code.google.com/p/google-reader-api/wiki/ActionToken
$user = Minz_Session::param('currentUser', '_');
+ if ($user !== '_' && $token == '') {
+ return true; //FeedMe //TODO: Check security consequences
+ }
if ($token === str_pad(sha1(FreshRSS_Context::$system_conf->salt . $user . $conf->apiPasswordHash), 57, 'Z')) {
return true;
}
+ Minz_Log::warning('Invalid POST token: ' . $token, API_LOG);
unauthorized();
}
@@ -261,11 +265,13 @@ function subscriptionList() {
$pdo = new MyPDO();
$stm = $pdo->prepare('SELECT f.id, f.name, f.url, f.website, c.id as c_id, c.name as c_name FROM `%_feed` f
- INNER JOIN `%_category` c ON c.id = f.category');
- $stm->execute();
+ INNER JOIN `%_category` c ON c.id = f.category AND f.priority >= :priority_normal');
+ $stm->execute(array(':priority_normal' => FreshRSS_Feed::PRIORITY_NORMAL));
$res = $stm->fetchAll(PDO::FETCH_ASSOC);
$salt = FreshRSS_Context::$system_conf->salt;
+ $faviconsUrl = Minz_Url::display('/f.php?', '', true);
+ $faviconsUrl = str_replace('/api/greader.php/reader/api/0/subscription', '', $faviconsUrl); //Security if base_url is not set properly
$subscriptions = array();
foreach ($res as $line) {
@@ -282,7 +288,7 @@ function subscriptionList() {
//'firstitemmsec' => 0,
'url' => $line['url'],
'htmlUrl' => $line['website'],
- 'iconUrl' => Minz_Url::display('/f.php?' . hash('crc32b', $salt . $line['url']), '', true),
+ 'iconUrl' => $faviconsUrl . hash('crc32b', $salt . $line['url']),
);
}
@@ -324,6 +330,9 @@ function subscriptionEdit($streamNames, $titles, $action, $add = '', $remove = '
$addCatId = 1; //Default category
}
$feedDAO = FreshRSS_Factory::createFeedDao();
+ if (!is_array($streamNames) || count($streamNames) < 1) {
+ badRequest();
+ }
for ($i = count($streamNames) - 1; $i >= 0; $i--) {
$streamName = $streamNames[$i]; //feed/http://example.net/sample.xml ; feed/338
if (strpos($streamName, 'feed/') === 0) {
@@ -435,6 +444,51 @@ function unreadCount() { //http://blog.martindoms.com/2009/10/16/using-the-googl
exit();
}
+function entriesToArray($entries) {
+ $items = array();
+ foreach ($entries as $entry) {
+ $f_id = $entry->feed();
+ if (isset($arrayFeedCategoryNames[$f_id])) {
+ $c_name = $arrayFeedCategoryNames[$f_id]['c_name'];
+ $f_name = $arrayFeedCategoryNames[$f_id]['name'];
+ } else {
+ $c_name = '_';
+ $f_name = '_';
+ }
+ $item = array(
+ 'id' => /*'tag:google.com,2005:reader/item/' .*/ dec2hex($entry->id()), //64-bit hexa http://code.google.com/p/google-reader-api/wiki/ItemId
+ 'crawlTimeMsec' => substr($entry->id(), 0, -3),
+ 'timestampUsec' => '' . $entry->id(), //EasyRSS
+ 'published' => $entry->date(true),
+ 'title' => $entry->title(),
+ 'summary' => array('content' => $entry->content()),
+ 'alternate' => array(
+ array('href' => htmlspecialchars_decode($entry->link(), ENT_QUOTES)),
+ ),
+ 'categories' => array(
+ 'user/-/state/com.google/reading-list',
+ 'user/-/label/' . $c_name,
+ ),
+ 'origin' => array(
+ 'streamId' => 'feed/' . $f_id,
+ 'title' => $f_name, //EasyRSS
+ //'htmlUrl' => $line['f_website'],
+ ),
+ );
+ if ($entry->author() != '') {
+ $item['author'] = $entry->author();
+ }
+ if ($entry->isRead()) {
+ $item['categories'][] = 'user/-/state/com.google/read';
+ }
+ if ($entry->isFavorite()) {
+ $item['categories'][] = 'user/-/state/com.google/starred';
+ }
+ $items[] = $item;
+ }
+ return $items;
+}
+
function streamContents($path, $include_target, $start_time, $count, $order, $exclude_target, $continuation) {
//http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
@@ -476,57 +530,18 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex
break;
}
- if (!empty($continuation)) {
+ if ($continuation != '') {
$count++; //Shift by one element
}
$entryDAO = FreshRSS_Factory::createEntryDao();
$entries = $entryDAO->listWhere($type, $include_target, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, new FreshRSS_Search(''), $start_time);
- $items = array();
- foreach ($entries as $entry) {
- $f_id = $entry->feed();
- if (isset($arrayFeedCategoryNames[$f_id])) {
- $c_name = $arrayFeedCategoryNames[$f_id]['c_name'];
- $f_name = $arrayFeedCategoryNames[$f_id]['name'];
- } else {
- $c_name = '_';
- $f_name = '_';
- }
- $item = array(
- 'id' => /*'tag:google.com,2005:reader/item/' .*/ dec2hex($entry->id()), //64-bit hexa http://code.google.com/p/google-reader-api/wiki/ItemId
- 'crawlTimeMsec' => substr($entry->id(), 0, -3),
- 'timestampUsec' => '' . $entry->id(), //EasyRSS
- 'published' => $entry->date(true),
- 'title' => $entry->title(),
- 'summary' => array('content' => $entry->content()),
- 'alternate' => array(
- array('href' => htmlspecialchars_decode($entry->link(), ENT_QUOTES)),
- ),
- 'categories' => array(
- 'user/-/state/com.google/reading-list',
- 'user/-/label/' . $c_name,
- ),
- 'origin' => array(
- 'streamId' => 'feed/' . $f_id,
- 'title' => $f_name, //EasyRSS
- //'htmlUrl' => $line['f_website'],
- ),
- );
- if ($entry->author() != '') {
- $item['author'] = $entry->author();
- }
- if ($entry->isRead()) {
- $item['categories'][] = 'user/-/state/com.google/read';
- }
- if ($entry->isFavorite()) {
- $item['categories'][] = 'user/-/state/com.google/starred';
- }
- $items[] = $item;
- }
+ $items = entriesToArray($entries);
- if (!empty($continuation)) {
+ if ($continuation != '') {
array_shift($items); //Discard first element that was already sent in the previous response
+ $count--;
}
$response = array(
@@ -534,15 +549,18 @@ function streamContents($path, $include_target, $start_time, $count, $order, $ex
'updated' => time(),
'items' => $items,
);
- if ((count($entries) >= $count) && (!empty($entry))) {
- $response['continuation'] = $entry->id();
+ if (count($entries) >= $count) {
+ $entry = end($entries);
+ if ($entry != false) {
+ $response['continuation'] = $entry->id();
+ }
}
echo json_encode($response), "\n";
exit();
}
-function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target) {
+function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target, $continuation) {
//http://code.google.com/p/google-reader-api/wiki/ApiStreamItemsIds
//http://code.google.com/p/pyrfeed/wiki/GoogleReaderAPI
//http://blog.martindoms.com/2009/10/16/using-the-google-reader-api-part-2/#feed
@@ -572,8 +590,17 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
break;
}
+ if ($continuation != '') {
+ $count++; //Shift by one element
+ }
+
$entryDAO = FreshRSS_Factory::createEntryDao();
- $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, '', new FreshRSS_Search(''), $start_time);
+ $ids = $entryDAO->listIdsWhere($type, $id, $state, $order === 'o' ? 'ASC' : 'DESC', $count, $continuation, new FreshRSS_Search(''), $start_time);
+
+ if ($continuation != '') {
+ array_shift($ids); //Discard first element that was already sent in the previous response
+ $count--;
+ }
if (empty($ids)) { //For News+ bug https://github.com/noinnion/newsplus/issues/84#issuecomment-57834632
$ids[] = 0;
@@ -585,9 +612,39 @@ function streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude
);
}
- echo json_encode(array(
+ $response = array(
'itemRefs' => $itemRefs,
- )), "\n";
+ );
+ if (count($ids) >= $count) {
+ $id = end($ids);
+ if ($id != false) {
+ $response['continuation'] = $id;
+ }
+ }
+
+ echo json_encode($response), "\n";
+ exit();
+}
+
+function streamContentsItems($e_ids, $order) {
+ header('Content-Type: application/json; charset=UTF-8');
+
+ foreach ($e_ids as $i => $e_id) {
+ $e_ids[$i] = hex2dec(basename($e_id)); //Strip prefix 'tag:google.com,2005:reader/item/'
+ }
+
+ $entryDAO = FreshRSS_Factory::createEntryDao();
+ $entries = $entryDAO->listByIds($e_ids, $order === 'o' ? 'ASC' : 'DESC');
+
+ $items = entriesToArray($entries);
+
+ $response = array(
+ 'id' => 'user/-/state/com.google/reading-list',
+ 'updated' => time(),
+ 'items' => $items,
+ );
+
+ echo json_encode($response), "\n";
exit();
}
@@ -673,9 +730,6 @@ function markAllAsRead($streamId, $olderThanId) {
exit('OK');
}
-//Minz_Log::debug('----------------------------------------------------------------', API_LOG);
-//Minz_Log::debug(debugInfo(), API_LOG);
-
$pathInfo = empty($_SERVER['PATH_INFO']) ? '/Error' : urldecode($_SERVER['PATH_INFO']);
$pathInfos = explode('/', $pathInfo);
@@ -683,6 +737,10 @@ Minz_Configuration::register('system',
DATA_PATH . '/config.php',
FRESHRSS_PATH . '/config.default.php');
FreshRSS_Context::$system_conf = Minz_Configuration::get('system');
+
+//Minz_Log::debug('----------------------------------------------------------------', API_LOG);
+//Minz_Log::debug(debugInfo(), API_LOG);
+
if (!FreshRSS_Context::$system_conf->api_enabled) {
serviceUnavailable();
}
@@ -726,7 +784,10 @@ if (count($pathInfos) < 3) {
* all items in a timestamp range, it will have a continuation attribute.
* The same request can be re-issued with the value of that attribute put
* in this parameter to get more items */
- $continuation = isset($_GET['c']) ? $_GET['c'] : '';
+ $continuation = isset($_GET['c']) ? trim($_GET['c']) : '';
+ if (!ctype_digit($continuation)) {
+ $continuation = '';
+ }
if (isset($pathInfos[5]) && $pathInfos[5] === 'contents' && isset($pathInfos[6])) {
if (isset($pathInfos[7])) {
if ($pathInfos[6] === 'feed') {
@@ -755,7 +816,10 @@ if (count($pathInfos) < 3) {
* be repeated to fetch the item IDs from multiple streams at once
* (more efficient from a backend perspective than multiple requests). */
$streamId = $_GET['s'];
- streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target);
+ streamContentsItemsIds($streamId, $start_time, $count, $order, $exclude_target, $continuation);
+ } else if ($pathInfos[6] === 'contents' && isset($_POST['i'])) { //FeedMe
+ $e_ids = multiplePosts('i'); //item IDs
+ streamContentsItems($e_ids, $order);
}
}
break;
@@ -775,16 +839,16 @@ if (count($pathInfos) < 3) {
subscriptionList($_GET['output']);
break;
case 'edit':
- if (isset($_POST['s']) && isset($_POST['ac'])) {
+ if (isset($_REQUEST['s']) && isset($_REQUEST['ac'])) {
//StreamId to operate on. The parameter may be repeated to edit multiple subscriptions at once
- $streamNames = multiplePosts('s');
+ $streamNames = empty($_POST['s']) && isset($_GET['s']) ? array($_GET['s']) : multiplePosts('s');
/* Title to use for the subscription. For the `subscribe` action,
* if not specified then the feed's current title will be used. Can
* be used with the `edit` action to rename a subscription */
- $titles = multiplePosts('t');
- $action = $_POST['ac']; //Action to perform on the given StreamId. Possible values are `subscribe`, `unsubscribe` and `edit`
- $add = isset($_POST['a']) ? $_POST['a'] : ''; //StreamId to add the subscription to (generally a user label)
- $remove = isset($_POST['r']) ? $_POST['r'] : ''; //StreamId to remove the subscription from (generally a user label)
+ $titles = empty($_POST['t']) && isset($_GET['t']) ? array($_GET['t']) : multiplePosts('t');
+ $action = $_REQUEST['ac']; //Action to perform on the given StreamId. Possible values are `subscribe`, `unsubscribe` and `edit`
+ $add = isset($_REQUEST['a']) ? $_REQUEST['a'] : ''; //StreamId to add the subscription to (generally a user label)
+ $remove = isset($_REQUEST['r']) ? $_REQUEST['r'] : ''; //StreamId to remove the subscription from (generally a user label)
subscriptionEdit($streamNames, $titles, $action, $add, $remove);
}
break;