aboutsummaryrefslogtreecommitdiff
path: root/p/api
diff options
context:
space:
mode:
authorGravatar Alexandre Alapetite <alexandre@alapetite.fr> 2025-06-03 00:16:17 +0200
committerGravatar GitHub <noreply@github.com> 2025-06-03 00:16:17 +0200
commitcc35094bb261cb3185def89d745317fa756560ee (patch)
tree0d94bbd1dfe1013101dd96b8dbfa975d275ddf7b /p/api
parent430d4e898e60ec1892d7191cf6e039aaf693822f (diff)
Add API endpoint for extensions (#7576)
* Add API endpoint for extensions Useful for https://github.com/FreshRSS/FreshRSS/issues/7572 * Support PATH_INFO Now also support being invoked like `/api/misc.php/Extension%20Name/` * More documentation
Diffstat (limited to 'p/api')
-rw-r--r--p/api/index.php14
-rw-r--r--p/api/misc.php68
2 files changed, 76 insertions, 6 deletions
diff --git a/p/api/index.php b/p/api/index.php
index 66c9e465c..fd9828080 100644
--- a/p/api/index.php
+++ b/p/api/index.php
@@ -28,9 +28,7 @@ echo json_encode([
<h2>Google Reader compatible API</h2>
<dl>
<dt>Your API address:</dt>
-<dd><?php
-echo Minz_Url::display('/api/greader.php', 'html', true);
-?></dd>
+<dd><?= Minz_Url::display('/api/greader.php', 'html', true) ?></dd>
<dt>Google Reader API configuration test:</dt>
<dd id="greaderOutput">?</dd>
</dl>
@@ -38,12 +36,16 @@ echo Minz_Url::display('/api/greader.php', 'html', true);
<h2>Fever compatible API</h2>
<dl>
<dt>Your API address:</dt>
-<dd><?php
-echo Minz_Url::display('/api/fever.php', 'html', true);
-?></dd>
+<dd><?= Minz_Url::display('/api/fever.php', 'html', true) ?></dd>
<dt>Fever API configuration test:</dt>
<dd id="feverOutput">?</dd>
</dl>
+<h2>API for extensions</h2>
+<dl>
+<dt>Your API address:</dt>
+<dd><?= Minz_Url::display('/api/misc.php/Extension%20name/', 'html', true) ?></dd>
+</dl>
+
</body>
</html>
diff --git a/p/api/misc.php b/p/api/misc.php
new file mode 100644
index 000000000..7724caa7a
--- /dev/null
+++ b/p/api/misc.php
@@ -0,0 +1,68 @@
+<?php
+declare(strict_types=1);
+/**
+ * API entry point for FreshRSS extensions on
+ * `/api/misc.php/Extension%20name/` or `/api/misc.php?ext=Extension%20name`
+ */
+
+require(__DIR__ . '/../../constants.php');
+require(LIB_PATH . '/lib_rss.php'); //Includes class autoloader
+
+function badRequest(): never {
+ header('HTTP/1.1 400 Bad Request');
+ header('Content-Type: text/plain; charset=UTF-8');
+ die('Bad Request!');
+}
+
+function serviceUnavailable(): never {
+ header('HTTP/1.1 503 Service Unavailable');
+ header('Content-Type: text/plain; charset=UTF-8');
+ die('Service Unavailable!');
+}
+
+$extensionName = is_string($_GET['ext'] ?? null) ? $_GET['ext'] : '';
+
+if ($extensionName === '') {
+ $pathInfo = '';
+ if (empty($_SERVER['PATH_INFO']) || !is_string($_SERVER['PATH_INFO'] ?? null)) {
+ if (!empty($_SERVER['ORIG_PATH_INFO']) && is_string($_SERVER['ORIG_PATH_INFO'])) {
+ // Compatibility https://php.net/reserved.variables.server
+ $pathInfo = $_SERVER['ORIG_PATH_INFO'];
+ }
+ } else {
+ $pathInfo = $_SERVER['PATH_INFO'];
+ }
+ $pathInfo = rawurldecode($pathInfo);
+ $pathInfo = preg_replace('%^(/api)?(/misc\.php)?%', '', $pathInfo); //Discard common errors
+ if ($pathInfo !== '' && is_string($pathInfo)) {
+ $pathInfos = explode('/', $pathInfo, limit: 3);
+ if (count($pathInfos) > 1) {
+ $extensionName = $pathInfos[1];
+ }
+ }
+}
+
+if ($extensionName === '') {
+ badRequest();
+}
+
+Minz_Session::init('FreshRSS', volatile: true);
+
+FreshRSS_Context::initSystem();
+if (
+ !FreshRSS_Context::hasSystemConf() ||
+ !FreshRSS_Context::systemConf()->api_enabled ||
+ empty(FreshRSS_Context::systemConf()->extensions_enabled[$extensionName])
+) {
+ serviceUnavailable();
+}
+
+// Only enable the extension that is being called
+FreshRSS_Context::systemConf()->extensions_enabled = [$extensionName => true];
+Minz_ExtensionManager::init();
+
+Minz_Translate::init();
+
+if (!Minz_ExtensionManager::callHookUnique('api_misc')) {
+ serviceUnavailable();
+}