aboutsummaryrefslogtreecommitdiffstats
path: root/lib/public/AppFramework/Http
diff options
context:
space:
mode:
Diffstat (limited to 'lib/public/AppFramework/Http')
-rw-r--r--lib/public/AppFramework/Http/Attribute/ARateLimit.php43
-rw-r--r--lib/public/AppFramework/Http/Attribute/AnonRateLimit.php22
-rw-r--r--lib/public/AppFramework/Http/Attribute/ApiRoute.php47
-rw-r--r--lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php40
-rw-r--r--lib/public/AppFramework/Http/Attribute/BruteForceProtection.php36
-rw-r--r--lib/public/AppFramework/Http/Attribute/CORS.php23
-rw-r--r--lib/public/AppFramework/Http/Attribute/ExAppRequired.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/FrontpageRoute.php47
-rw-r--r--lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php22
-rw-r--r--lib/public/AppFramework/Http/Attribute/NoAdminRequired.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/OpenAPI.php91
-rw-r--r--lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php38
-rw-r--r--lib/public/AppFramework/Http/Attribute/PublicPage.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/RequestHeader.php34
-rw-r--r--lib/public/AppFramework/Http/Attribute/Route.php145
-rw-r--r--lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/SubAdminRequired.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/UseSession.php21
-rw-r--r--lib/public/AppFramework/Http/Attribute/UserRateLimit.php22
-rw-r--r--lib/public/AppFramework/Http/ContentSecurityPolicy.php90
-rw-r--r--lib/public/AppFramework/Http/DataDisplayResponse.php72
-rw-r--r--lib/public/AppFramework/Http/DataDownloadResponse.php56
-rw-r--r--lib/public/AppFramework/Http/DataResponse.php65
-rw-r--r--lib/public/AppFramework/Http/DownloadResponse.php37
-rw-r--r--lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php549
-rw-r--r--lib/public/AppFramework/Http/EmptyFeaturePolicy.php164
-rw-r--r--lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php35
-rw-r--r--lib/public/AppFramework/Http/Events/BeforeTemplateRenderedEvent.php49
-rw-r--r--lib/public/AppFramework/Http/FeaturePolicy.php41
-rw-r--r--lib/public/AppFramework/Http/FileDisplayResponse.php55
-rw-r--r--lib/public/AppFramework/Http/ICallbackResponse.php23
-rw-r--r--lib/public/AppFramework/Http/IOutput.php59
-rw-r--r--lib/public/AppFramework/Http/JSONResponse.php91
-rw-r--r--lib/public/AppFramework/Http/NotFoundResponse.php30
-rw-r--r--lib/public/AppFramework/Http/ParameterOutOfRangeException.php62
-rw-r--r--lib/public/AppFramework/Http/RedirectResponse.php44
-rw-r--r--lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php36
-rw-r--r--lib/public/AppFramework/Http/Response.php408
-rw-r--r--lib/public/AppFramework/Http/StandaloneTemplateResponse.php24
-rw-r--r--lib/public/AppFramework/Http/StreamResponse.php53
-rw-r--r--lib/public/AppFramework/Http/StrictContentSecurityPolicy.php70
-rw-r--r--lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php33
-rw-r--r--lib/public/AppFramework/Http/StrictInlineContentSecurityPolicy.php33
-rw-r--r--lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php29
-rw-r--r--lib/public/AppFramework/Http/Template/IMenuAction.php51
-rw-r--r--lib/public/AppFramework/Http/Template/LinkMenuAction.php26
-rw-r--r--lib/public/AppFramework/Http/Template/PublicTemplateResponse.php176
-rw-r--r--lib/public/AppFramework/Http/Template/SimpleMenuAction.php120
-rw-r--r--lib/public/AppFramework/Http/TemplateResponse.php197
-rw-r--r--lib/public/AppFramework/Http/TextPlainResponse.php47
-rw-r--r--lib/public/AppFramework/Http/TooManyRequestsResponse.php41
-rw-r--r--lib/public/AppFramework/Http/ZipResponse.php77
54 files changed, 3721 insertions, 0 deletions
diff --git a/lib/public/AppFramework/Http/Attribute/ARateLimit.php b/lib/public/AppFramework/Http/Attribute/ARateLimit.php
new file mode 100644
index 00000000000..c06b1180ae3
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/ARateLimit.php
@@ -0,0 +1,43 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+/**
+ * Attribute for controller methods that want to limit the times a logged-in
+ * user can call the endpoint in a given time period.
+ *
+ * @since 27.0.0
+ */
+abstract class ARateLimit {
+ /**
+ * @param int $limit The maximum number of requests that can be made in the given period in seconds.
+ * @param int $period The time period in seconds.
+ * @since 27.0.0
+ */
+ public function __construct(
+ protected int $limit,
+ protected int $period,
+ ) {
+ }
+
+ /**
+ * @since 27.0.0
+ */
+ public function getLimit(): int {
+ return $this->limit;
+ }
+
+ /**
+ * @since 27.0.0
+ */
+ public function getPeriod(): int {
+ return $this->period;
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/AnonRateLimit.php b/lib/public/AppFramework/Http/Attribute/AnonRateLimit.php
new file mode 100644
index 00000000000..f02f2b695c5
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/AnonRateLimit.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that want to limit the times a not logged-in
+ * guest can call the endpoint in a given time period.
+ *
+ * @since 27.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD)]
+class AnonRateLimit extends ARateLimit {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/ApiRoute.php b/lib/public/AppFramework/Http/Attribute/ApiRoute.php
new file mode 100644
index 00000000000..1d61cfe7704
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/ApiRoute.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * This attribute can be used to define API routes on controller methods.
+ *
+ * It works in addition to the traditional routes.php method and has the same parameters
+ * (except for the `name` parameter which is not needed).
+ *
+ * @since 29.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
+class ApiRoute extends Route {
+ /**
+ * @inheritDoc
+ *
+ * @since 29.0.0
+ */
+ public function __construct(
+ protected string $verb,
+ protected string $url,
+ protected ?array $requirements = null,
+ protected ?array $defaults = null,
+ protected ?string $root = null,
+ protected ?string $postfix = null,
+ ) {
+ parent::__construct(
+ Route::TYPE_API,
+ $verb,
+ $url,
+ $requirements,
+ $defaults,
+ $root,
+ $postfix,
+ );
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php b/lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php
new file mode 100644
index 00000000000..6b78fee41af
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/AppApiAdminAccessWithoutUser.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for (sub)administrator controller methods that allow access for ExApps when the User is not set.
+ *
+ * @since 30.0.0
+ */
+#[Attribute]
+class AppApiAdminAccessWithoutUser {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php b/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php
new file mode 100644
index 00000000000..83101143fc9
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/AuthorizedAdminSetting.php
@@ -0,0 +1,40 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+use OCP\Settings\IDelegatedSettings;
+
+/**
+ * Attribute for controller methods that should be only accessible with
+ * full admin or partial admin permissions.
+ *
+ * @since 27.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
+class AuthorizedAdminSetting {
+ /**
+ * @param class-string<IDelegatedSettings> $settings A settings section the user needs to be able to access
+ * @since 27.0.0
+ */
+ public function __construct(
+ protected string $settings,
+ ) {
+ }
+
+ /**
+ *
+ * @return class-string<IDelegatedSettings>
+ * @since 27.0.0
+ */
+ public function getSettings(): string {
+ return $this->settings;
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php b/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php
new file mode 100644
index 00000000000..0fc1a3b9b6d
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/BruteForceProtection.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that want to protect passwords, keys, tokens
+ * or other data against brute force
+ *
+ * @since 27.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
+class BruteForceProtection {
+ /**
+ * @since 27.0.0
+ */
+ public function __construct(
+ protected string $action,
+ ) {
+ }
+
+ /**
+ * @since 27.0.0
+ */
+ public function getAction(): string {
+ return $this->action;
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/CORS.php b/lib/public/AppFramework/Http/Attribute/CORS.php
new file mode 100644
index 00000000000..ff639635635
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/CORS.php
@@ -0,0 +1,23 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that can also be accessed by other websites.
+ * See https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS for an explanation of the functionality and the security implications.
+ * See https://docs.nextcloud.com/server/latest/developer_manual/digging_deeper/rest_apis.html on how to implement it in your controller.
+ *
+ * @since 27.0.0
+ */
+#[Attribute]
+class CORS {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/ExAppRequired.php b/lib/public/AppFramework/Http/Attribute/ExAppRequired.php
new file mode 100644
index 00000000000..eb18da8027c
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/ExAppRequired.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that can only be accessed by ExApps
+ *
+ * @since 30.0.0
+ */
+#[Attribute]
+class ExAppRequired {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php b/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php
new file mode 100644
index 00000000000..398116d786f
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/FrontpageRoute.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * This attribute can be used to define Frontpage routes on controller methods.
+ *
+ * It works in addition to the traditional routes.php method and has the same parameters
+ * (except for the `name` parameter which is not needed).
+ *
+ * @since 29.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
+class FrontpageRoute extends Route {
+ /**
+ * @inheritDoc
+ *
+ * @since 29.0.0
+ */
+ public function __construct(
+ protected string $verb,
+ protected string $url,
+ protected ?array $requirements = null,
+ protected ?array $defaults = null,
+ protected ?string $root = null,
+ protected ?string $postfix = null,
+ ) {
+ parent::__construct(
+ Route::TYPE_FRONTPAGE,
+ $verb,
+ $url,
+ $requirements,
+ $defaults,
+ $root,
+ $postfix,
+ );
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php b/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php
new file mode 100644
index 00000000000..114637935db
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/IgnoreOpenAPI.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that should be ignored when generating OpenAPI documentation
+ *
+ * @since 28.0.0
+ * @deprecated 28.0.0 Use {@see OpenAPI} with {@see OpenAPI::SCOPE_IGNORE} instead: `#[OpenAPI(scope: OpenAPI::SCOPE_IGNORE)]`
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
+class IgnoreOpenAPI {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php b/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php
new file mode 100644
index 00000000000..59c6cf86800
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/NoAdminRequired.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that can be accessed by any logged-in user
+ *
+ * @since 27.0.0
+ */
+#[Attribute]
+class NoAdminRequired {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php b/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php
new file mode 100644
index 00000000000..ad7e569a3b9
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/NoCSRFRequired.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that are not CSRF protected
+ *
+ * @since 27.0.0
+ */
+#[Attribute]
+class NoCSRFRequired {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/OpenAPI.php b/lib/public/AppFramework/Http/Attribute/OpenAPI.php
new file mode 100644
index 00000000000..1b44b2a57fe
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/OpenAPI.php
@@ -0,0 +1,91 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * With this attribute a controller or a method can be moved into a different
+ * scope or tag. Scopes should be seen as API consumers, tags can be used to group
+ * different routes inside the same scope.
+ *
+ * @since 28.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS | Attribute::IS_REPEATABLE)]
+class OpenAPI {
+ /**
+ * APIs used for normal user facing interaction with your app,
+ * e.g. when you would implement a mobile client or standalone frontend.
+ *
+ * @since 28.0.0
+ */
+ public const SCOPE_DEFAULT = 'default';
+
+ /**
+ * APIs used to administrate your app's configuration on an administrative level.
+ * Will be set automatically when admin permissions are required to access the route.
+ *
+ * @since 28.0.0
+ */
+ public const SCOPE_ADMINISTRATION = 'administration';
+
+ /**
+ * APIs used by servers to federate with each other.
+ *
+ * @since 28.0.0
+ */
+ public const SCOPE_FEDERATION = 'federation';
+
+ /**
+ * Ignore this controller or method in all generated OpenAPI specifications.
+ *
+ * @since 28.0.0
+ */
+ public const SCOPE_IGNORE = 'ignore';
+
+ /**
+ * APIs used by ExApps.
+ * Will be set automatically when an ExApp is required to access the route.
+ *
+ * @since 30.0.0
+ */
+ public const SCOPE_EX_APP = 'ex_app';
+
+ /**
+ * @param self::SCOPE_*|string $scope Scopes are used to define different clients.
+ * It is recommended to go with the scopes available as self::SCOPE_* constants,
+ * but in exotic cases other APIs might need documentation as well,
+ * then a free string can be provided (but it should be `a-z` only).
+ * @param ?list<string> $tags Tags can be used to group routes inside a scope
+ * for easier implementation and reviewing of the API specification.
+ * It defaults to the controller name in snake_case (should be `a-z` and underscore only).
+ * @since 28.0.0
+ */
+ public function __construct(
+ protected string $scope = self::SCOPE_DEFAULT,
+ protected ?array $tags = null,
+ ) {
+ }
+
+ /**
+ * @since 28.0.0
+ */
+ public function getScope(): string {
+ return $this->scope;
+ }
+
+ /**
+ * @return ?list<string>
+ * @since 28.0.0
+ */
+ public function getTags(): ?array {
+ return $this->tags;
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php b/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php
new file mode 100644
index 00000000000..c41e5aa2445
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/PasswordConfirmationRequired.php
@@ -0,0 +1,38 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that require the password to be confirmed with in the last 30 minutes
+ *
+ * @since 27.0.0
+ */
+#[Attribute]
+class PasswordConfirmationRequired {
+ /**
+ * @param bool $strict - Whether password confirmation needs to happen in the request.
+ *
+ * @since 31.0.0
+ */
+ public function __construct(
+ protected bool $strict = false,
+ ) {
+ }
+
+ /**
+ * @since 31.0.0
+ */
+ public function getStrict(): bool {
+ return $this->strict;
+ }
+
+}
diff --git a/lib/public/AppFramework/Http/Attribute/PublicPage.php b/lib/public/AppFramework/Http/Attribute/PublicPage.php
new file mode 100644
index 00000000000..85c1ed06f80
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/PublicPage.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that can also be accessed by not logged-in user
+ *
+ * @since 27.0.0
+ */
+#[Attribute]
+class PublicPage {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/RequestHeader.php b/lib/public/AppFramework/Http/Attribute/RequestHeader.php
new file mode 100644
index 00000000000..1d0fbbfa0c3
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/RequestHeader.php
@@ -0,0 +1,34 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * This attribute allows documenting request headers and is primarily intended for OpenAPI documentation.
+ * It should be added whenever you use a request header in a controller method, in order to properly describe the header and its functionality.
+ * There are no checks that ensure the header is set, so you will still need to do this yourself in the controller method.
+ *
+ * @since 32.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
+class RequestHeader {
+ /**
+ * @param lowercase-string $name The name of the request header
+ * @param non-empty-string $description The description of the request header
+ * @param bool $indirect Allow indirect usage of the header for example in a middleware. Enabling this turns off the check which ensures that the header must be referenced in the controller method.
+ */
+ public function __construct(
+ protected string $name,
+ protected string $description,
+ protected bool $indirect = false,
+ ) {
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/Route.php b/lib/public/AppFramework/Http/Attribute/Route.php
new file mode 100644
index 00000000000..45e977d64f8
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/Route.php
@@ -0,0 +1,145 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * This attribute can be used to define routes on controller methods.
+ *
+ * It works in addition to the traditional routes.php method and has the same parameters
+ * (except for the `name` parameter which is not needed).
+ *
+ * @since 29.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD | Attribute::IS_REPEATABLE)]
+class Route {
+
+ /**
+ * Corresponds to the `ocs` key in routes.php
+ *
+ * @see ApiRoute
+ * @since 29.0.0
+ */
+ public const TYPE_API = 'ocs';
+
+ /**
+ * Corresponds to the `routes` key in routes.php
+ *
+ * @see FrontpageRoute
+ * @since 29.0.0
+ */
+ public const TYPE_FRONTPAGE = 'routes';
+
+ /**
+ * @param string $type Either Route::TYPE_API or Route::TYPE_FRONTPAGE.
+ * @psalm-param Route::TYPE_* $type
+ * @param string $verb HTTP method of the route.
+ * @psalm-param 'GET'|'HEAD'|'POST'|'PUT'|'DELETE'|'OPTIONS'|'PATCH' $verb
+ * @param string $url The path of the route.
+ * @param ?array<string, string> $requirements Array of regexes mapped to the path parameters.
+ * @param ?array<string, mixed> $defaults Array of default values mapped to the path parameters.
+ * @param ?string $root Custom root. For OCS all apps are allowed, but for index.php only some can use it.
+ * @param ?string $postfix Postfix for the route name.
+ * @since 29.0.0
+ */
+ public function __construct(
+ protected string $type,
+ protected string $verb,
+ protected string $url,
+ protected ?array $requirements = null,
+ protected ?array $defaults = null,
+ protected ?string $root = null,
+ protected ?string $postfix = null,
+ ) {
+ }
+
+ /**
+ * @return array{
+ * verb: string,
+ * url: string,
+ * requirements?: array<string, string>,
+ * defaults?: array<string, mixed>,
+ * root?: string,
+ * postfix?: string,
+ * }
+ * @since 29.0.0
+ */
+ public function toArray() {
+ $route = [
+ 'verb' => $this->verb,
+ 'url' => $this->url,
+ ];
+
+ if ($this->requirements !== null) {
+ $route['requirements'] = $this->requirements;
+ }
+ if ($this->defaults !== null) {
+ $route['defaults'] = $this->defaults;
+ }
+ if ($this->root !== null) {
+ $route['root'] = $this->root;
+ }
+ if ($this->postfix !== null) {
+ $route['postfix'] = $this->postfix;
+ }
+
+ return $route;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getType(): string {
+ return $this->type;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getVerb(): string {
+ return $this->verb;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getUrl(): string {
+ return $this->url;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getRequirements(): ?array {
+ return $this->requirements;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getDefaults(): ?array {
+ return $this->defaults;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getRoot(): ?string {
+ return $this->root;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getPostfix(): ?string {
+ return $this->postfix;
+ }
+}
diff --git a/lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php b/lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php
new file mode 100644
index 00000000000..a2697847ca6
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/StrictCookiesRequired.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that require strict cookies
+ *
+ * @since 27.0.0
+ */
+#[Attribute]
+class StrictCookiesRequired {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php b/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php
new file mode 100644
index 00000000000..38c4dd35f3c
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/SubAdminRequired.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that can be accessed by sub-admins
+ *
+ * @since 27.0.0
+ */
+#[Attribute]
+class SubAdminRequired {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/UseSession.php b/lib/public/AppFramework/Http/Attribute/UseSession.php
new file mode 100644
index 00000000000..f64b050144f
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/UseSession.php
@@ -0,0 +1,21 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that need to read/write PHP session data
+ *
+ * @since 26.0.0
+ */
+#[Attribute]
+class UseSession {
+}
diff --git a/lib/public/AppFramework/Http/Attribute/UserRateLimit.php b/lib/public/AppFramework/Http/Attribute/UserRateLimit.php
new file mode 100644
index 00000000000..6fcf7127e89
--- /dev/null
+++ b/lib/public/AppFramework/Http/Attribute/UserRateLimit.php
@@ -0,0 +1,22 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http\Attribute;
+
+use Attribute;
+
+/**
+ * Attribute for controller methods that want to limit the times a logged-in
+ * user can call the endpoint in a given time period.
+ *
+ * @since 27.0.0
+ */
+#[Attribute(Attribute::TARGET_METHOD)]
+class UserRateLimit extends ARateLimit {
+}
diff --git a/lib/public/AppFramework/Http/ContentSecurityPolicy.php b/lib/public/AppFramework/Http/ContentSecurityPolicy.php
new file mode 100644
index 00000000000..11ec79bbdb7
--- /dev/null
+++ b/lib/public/AppFramework/Http/ContentSecurityPolicy.php
@@ -0,0 +1,90 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Class ContentSecurityPolicy is a simple helper which allows applications to
+ * modify the Content-Security-Policy sent by Nextcloud. Per default only JavaScript,
+ * stylesheets, images, fonts, media and connections from the same domain
+ * ('self') are allowed.
+ *
+ * Even if a value gets modified above defaults will still get appended. Please
+ * notice that Nextcloud ships already with sensible defaults and those policies
+ * should require no modification at all for most use-cases.
+ *
+ * This class allows unsafe-inline of CSS.
+ *
+ * @since 8.1.0
+ */
+class ContentSecurityPolicy extends EmptyContentSecurityPolicy {
+ /** @var bool Whether inline JS snippets are allowed */
+ protected $inlineScriptAllowed = false;
+ /** @var bool Whether eval in JS scripts is allowed */
+ protected $evalScriptAllowed = false;
+ /** @var bool Whether WebAssembly compilation is allowed */
+ protected ?bool $evalWasmAllowed = false;
+ /** @var bool Whether strict-dynamic should be set */
+ protected $strictDynamicAllowed = false;
+ /** @var bool Whether strict-dynamic should be set for 'script-src-elem' */
+ protected $strictDynamicAllowedOnScripts = true;
+ /** @var array Domains from which scripts can get loaded */
+ protected $allowedScriptDomains = [
+ '\'self\'',
+ ];
+ /**
+ * @var bool Whether inline CSS is allowed
+ * TODO: Disallow per default
+ * @link https://github.com/owncloud/core/issues/13458
+ */
+ protected $inlineStyleAllowed = true;
+ /** @var array Domains from which CSS can get loaded */
+ protected $allowedStyleDomains = [
+ '\'self\'',
+ ];
+ /** @var array Domains from which images can get loaded */
+ protected $allowedImageDomains = [
+ '\'self\'',
+ 'data:',
+ 'blob:',
+ ];
+ /** @var array Domains to which connections can be done */
+ protected $allowedConnectDomains = [
+ '\'self\'',
+ ];
+ /** @var array Domains from which media elements can be loaded */
+ protected $allowedMediaDomains = [
+ '\'self\'',
+ ];
+ /** @var array Domains from which object elements can be loaded */
+ protected $allowedObjectDomains = [];
+ /** @var array Domains from which iframes can be loaded */
+ protected $allowedFrameDomains = [];
+ /** @var array Domains from which fonts can be loaded */
+ protected $allowedFontDomains = [
+ '\'self\'',
+ 'data:',
+ ];
+ /** @var array Domains from which web-workers and nested browsing content can load elements */
+ protected $allowedChildSrcDomains = [];
+
+ /** @var array Domains which can embed this Nextcloud instance */
+ protected $allowedFrameAncestors = [
+ '\'self\'',
+ ];
+
+ /** @var array Domains from which web-workers can be loaded */
+ protected $allowedWorkerSrcDomains = [];
+
+ /** @var array Domains which can be used as target for forms */
+ protected $allowedFormActionDomains = [
+ '\'self\'',
+ ];
+
+ /** @var array Locations to report violations to */
+ protected $reportTo = [];
+}
diff --git a/lib/public/AppFramework/Http/DataDisplayResponse.php b/lib/public/AppFramework/Http/DataDisplayResponse.php
new file mode 100644
index 00000000000..e1ded910328
--- /dev/null
+++ b/lib/public/AppFramework/Http/DataDisplayResponse.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * Class DataDisplayResponse
+ *
+ * @since 8.1.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class DataDisplayResponse extends Response {
+ /**
+ * response data
+ * @var string
+ */
+ protected $data;
+
+
+ /**
+ * @param string $data the data to display
+ * @param S $statusCode the Http status code, defaults to 200
+ * @param H $headers additional key value based headers
+ * @since 8.1.0
+ */
+ public function __construct(string $data = '', int $statusCode = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($statusCode, $headers);
+
+ $this->data = $data;
+ $this->addHeader('Content-Disposition', 'inline; filename=""');
+ }
+
+ /**
+ * Outputs data. No processing is done.
+ * @return string
+ * @since 8.1.0
+ */
+ public function render() {
+ return $this->data;
+ }
+
+
+ /**
+ * Sets values in the data
+ * @param string $data the data to display
+ * @return DataDisplayResponse Reference to this object
+ * @since 8.1.0
+ */
+ public function setData($data) {
+ $this->data = $data;
+
+ return $this;
+ }
+
+
+ /**
+ * Used to get the set parameters
+ * @return string the data
+ * @since 8.1.0
+ */
+ public function getData() {
+ return $this->data;
+ }
+}
diff --git a/lib/public/AppFramework/Http/DataDownloadResponse.php b/lib/public/AppFramework/Http/DataDownloadResponse.php
new file mode 100644
index 00000000000..ee6bcf0d0c5
--- /dev/null
+++ b/lib/public/AppFramework/Http/DataDownloadResponse.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * Class DataDownloadResponse
+ *
+ * @since 8.0.0
+ * @template S of Http::STATUS_*
+ * @template C of string
+ * @template H of array<string, mixed>
+ * @template-extends DownloadResponse<Http::STATUS_*, string, array<string, mixed>>
+ */
+class DataDownloadResponse extends DownloadResponse {
+ /**
+ * @var string
+ */
+ private $data;
+
+ /**
+ * Creates a response that prompts the user to download the text
+ * @param string $data text to be downloaded
+ * @param string $filename the name that the downloaded file should have
+ * @param C $contentType the mimetype that the downloaded file should have
+ * @param S $status
+ * @param H $headers
+ * @since 8.0.0
+ */
+ public function __construct(string $data, string $filename, string $contentType, int $status = Http::STATUS_OK, array $headers = []) {
+ $this->data = $data;
+ parent::__construct($filename, $contentType, $status, $headers);
+ }
+
+ /**
+ * @param string $data
+ * @since 8.0.0
+ */
+ public function setData($data) {
+ $this->data = $data;
+ }
+
+ /**
+ * @return string
+ * @since 8.0.0
+ */
+ public function render() {
+ return $this->data;
+ }
+}
diff --git a/lib/public/AppFramework/Http/DataResponse.php b/lib/public/AppFramework/Http/DataResponse.php
new file mode 100644
index 00000000000..2b54ce848ef
--- /dev/null
+++ b/lib/public/AppFramework/Http/DataResponse.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * A generic DataResponse class that is used to return generic data responses
+ * for responders to transform
+ * @since 8.0.0
+ * @psalm-type DataResponseType = array|int|float|string|bool|object|null|\stdClass|\JsonSerializable
+ * @template S of Http::STATUS_*
+ * @template-covariant T of DataResponseType
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class DataResponse extends Response {
+ /**
+ * response data
+ * @var T
+ */
+ protected $data;
+
+
+ /**
+ * @param T $data the object or array that should be transformed
+ * @param S $statusCode the Http status code, defaults to 200
+ * @param H $headers additional key value based headers
+ * @since 8.0.0
+ */
+ public function __construct(mixed $data = [], int $statusCode = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($statusCode, $headers);
+
+ $this->data = $data;
+ }
+
+
+ /**
+ * Sets values in the data json array
+ * @psalm-suppress InvalidTemplateParam
+ * @param T $data an array or object which will be transformed
+ * @return DataResponse Reference to this object
+ * @since 8.0.0
+ */
+ public function setData($data) {
+ $this->data = $data;
+
+ return $this;
+ }
+
+
+ /**
+ * Used to get the set parameters
+ * @return T the data
+ * @since 8.0.0
+ */
+ public function getData() {
+ return $this->data;
+ }
+}
diff --git a/lib/public/AppFramework/Http/DownloadResponse.php b/lib/public/AppFramework/Http/DownloadResponse.php
new file mode 100644
index 00000000000..190de022d36
--- /dev/null
+++ b/lib/public/AppFramework/Http/DownloadResponse.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * Prompts the user to download the a file
+ * @since 7.0.0
+ * @template S of Http::STATUS_*
+ * @template C of string
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class DownloadResponse extends Response {
+ /**
+ * Creates a response that prompts the user to download the file
+ * @param string $filename the name that the downloaded file should have
+ * @param C $contentType the mimetype that the downloaded file should have
+ * @param S $status
+ * @param H $headers
+ * @since 7.0.0
+ */
+ public function __construct(string $filename, string $contentType, int $status = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($status, $headers);
+
+ $filename = strtr($filename, ['"' => '\\"', '\\' => '\\\\']);
+
+ $this->addHeader('Content-Disposition', 'attachment; filename="' . $filename . '"');
+ $this->addHeader('Content-Type', $contentType);
+ }
+}
diff --git a/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php
new file mode 100644
index 00000000000..b8bbfdb7d67
--- /dev/null
+++ b/lib/public/AppFramework/Http/EmptyContentSecurityPolicy.php
@@ -0,0 +1,549 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Class EmptyContentSecurityPolicy is a simple helper which allows applications
+ * to modify the Content-Security-Policy sent by Nexcloud. Per default the policy
+ * is forbidding everything.
+ *
+ * As alternative with sane exemptions look at ContentSecurityPolicy
+ *
+ * @see \OCP\AppFramework\Http\ContentSecurityPolicy
+ * @since 9.0.0
+ */
+class EmptyContentSecurityPolicy {
+ /** @var ?string JS nonce to be used */
+ protected ?string $jsNonce = null;
+ /** @var bool Whether strict-dynamic should be used */
+ protected $strictDynamicAllowed = null;
+ /** @var bool Whether strict-dynamic should be used on script-src-elem */
+ protected $strictDynamicAllowedOnScripts = null;
+ /**
+ * @var bool Whether eval in JS scripts is allowed
+ * TODO: Disallow per default
+ * @link https://github.com/owncloud/core/issues/11925
+ */
+ protected $evalScriptAllowed = null;
+ /** @var bool Whether WebAssembly compilation is allowed */
+ protected ?bool $evalWasmAllowed = null;
+ /** @var array Domains from which scripts can get loaded */
+ protected $allowedScriptDomains = null;
+ /**
+ * @var bool Whether inline CSS is allowed
+ * TODO: Disallow per default
+ * @link https://github.com/owncloud/core/issues/13458
+ */
+ protected $inlineStyleAllowed = null;
+ /** @var array Domains from which CSS can get loaded */
+ protected $allowedStyleDomains = null;
+ /** @var array Domains from which images can get loaded */
+ protected $allowedImageDomains = null;
+ /** @var array Domains to which connections can be done */
+ protected $allowedConnectDomains = null;
+ /** @var array Domains from which media elements can be loaded */
+ protected $allowedMediaDomains = null;
+ /** @var array Domains from which object elements can be loaded */
+ protected $allowedObjectDomains = null;
+ /** @var array Domains from which iframes can be loaded */
+ protected $allowedFrameDomains = null;
+ /** @var array Domains from which fonts can be loaded */
+ protected $allowedFontDomains = null;
+ /** @var array Domains from which web-workers and nested browsing content can load elements */
+ protected $allowedChildSrcDomains = null;
+ /** @var array Domains which can embed this Nextcloud instance */
+ protected $allowedFrameAncestors = null;
+ /** @var array Domains from which web-workers can be loaded */
+ protected $allowedWorkerSrcDomains = null;
+ /** @var array Domains which can be used as target for forms */
+ protected $allowedFormActionDomains = null;
+
+ /** @var array Locations to report violations to */
+ protected $reportTo = null;
+
+ /**
+ * @param bool $state
+ * @return EmptyContentSecurityPolicy
+ * @since 24.0.0
+ */
+ public function useStrictDynamic(bool $state = false): self {
+ $this->strictDynamicAllowed = $state;
+ return $this;
+ }
+
+ /**
+ * In contrast to `useStrictDynamic` this only sets strict-dynamic on script-src-elem
+ * Meaning only grants trust to all imports of scripts that were loaded in `<script>` tags, and thus weakens less the CSP.
+ * @param bool $state
+ * @return EmptyContentSecurityPolicy
+ * @since 28.0.0
+ */
+ public function useStrictDynamicOnScripts(bool $state = false): self {
+ $this->strictDynamicAllowedOnScripts = $state;
+ return $this;
+ }
+
+ /**
+ * The base64 encoded nonce to be used for script source.
+ * This method is only for CSPMiddleware, custom values are ignored in mergePolicies of ContentSecurityPolicyManager
+ *
+ * @param string $nonce
+ * @return $this
+ * @since 11.0.0
+ */
+ public function useJsNonce($nonce) {
+ $this->jsNonce = $nonce;
+ return $this;
+ }
+
+ /**
+ * Whether eval in JavaScript is allowed or forbidden
+ * @param bool $state
+ * @return $this
+ * @since 8.1.0
+ * @deprecated 17.0.0 Eval should not be used anymore. Please update your scripts. This function will stop functioning in a future version of Nextcloud.
+ */
+ public function allowEvalScript($state = true) {
+ $this->evalScriptAllowed = $state;
+ return $this;
+ }
+
+ /**
+ * Whether WebAssembly compilation is allowed or forbidden
+ * @param bool $state
+ * @return $this
+ * @since 28.0.0
+ */
+ public function allowEvalWasm(bool $state = true) {
+ $this->evalWasmAllowed = $state;
+ return $this;
+ }
+
+ /**
+ * Allows to execute JavaScript files from a specific domain. Use * to
+ * allow JavaScript from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedScriptDomain($domain) {
+ $this->allowedScriptDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed script domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowScriptDomain($domain) {
+ $this->allowedScriptDomains = array_diff($this->allowedScriptDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Whether inline CSS snippets are allowed or forbidden
+ * @param bool $state
+ * @return $this
+ * @since 8.1.0
+ */
+ public function allowInlineStyle($state = true) {
+ $this->inlineStyleAllowed = $state;
+ return $this;
+ }
+
+ /**
+ * Allows to execute CSS files from a specific domain. Use * to allow
+ * CSS from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedStyleDomain($domain) {
+ $this->allowedStyleDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed style domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowStyleDomain($domain) {
+ $this->allowedStyleDomains = array_diff($this->allowedStyleDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Allows using fonts from a specific domain. Use * to allow
+ * fonts from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedFontDomain($domain) {
+ $this->allowedFontDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed font domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowFontDomain($domain) {
+ $this->allowedFontDomains = array_diff($this->allowedFontDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Allows embedding images from a specific domain. Use * to allow
+ * images from all domains.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedImageDomain($domain) {
+ $this->allowedImageDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed image domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowImageDomain($domain) {
+ $this->allowedImageDomains = array_diff($this->allowedImageDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * To which remote domains the JS connect to.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedConnectDomain($domain) {
+ $this->allowedConnectDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed connect domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowConnectDomain($domain) {
+ $this->allowedConnectDomains = array_diff($this->allowedConnectDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * From which domains media elements can be embedded.
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedMediaDomain($domain) {
+ $this->allowedMediaDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed media domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowMediaDomain($domain) {
+ $this->allowedMediaDomains = array_diff($this->allowedMediaDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * From which domains objects such as <object>, <embed> or <applet> are executed
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedObjectDomain($domain) {
+ $this->allowedObjectDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed object domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowObjectDomain($domain) {
+ $this->allowedObjectDomains = array_diff($this->allowedObjectDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Which domains can be embedded in an iframe
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ */
+ public function addAllowedFrameDomain($domain) {
+ $this->allowedFrameDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed frame domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ */
+ public function disallowFrameDomain($domain) {
+ $this->allowedFrameDomains = array_diff($this->allowedFrameDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Domains from which web-workers and nested browsing content can load elements
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 8.1.0
+ * @deprecated 15.0.0 use addAllowedWorkerSrcDomains or addAllowedFrameDomain
+ */
+ public function addAllowedChildSrcDomain($domain) {
+ $this->allowedChildSrcDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove the specified allowed child src domain from the allowed domains.
+ *
+ * @param string $domain
+ * @return $this
+ * @since 8.1.0
+ * @deprecated 15.0.0 use the WorkerSrcDomains or FrameDomain
+ */
+ public function disallowChildSrcDomain($domain) {
+ $this->allowedChildSrcDomains = array_diff($this->allowedChildSrcDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Domains which can embed an iFrame of the Nextcloud instance
+ *
+ * @param string $domain
+ * @return $this
+ * @since 13.0.0
+ */
+ public function addAllowedFrameAncestorDomain($domain) {
+ $this->allowedFrameAncestors[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Domains which can embed an iFrame of the Nextcloud instance
+ *
+ * @param string $domain
+ * @return $this
+ * @since 13.0.0
+ */
+ public function disallowFrameAncestorDomain($domain) {
+ $this->allowedFrameAncestors = array_diff($this->allowedFrameAncestors, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Domain from which workers can be loaded
+ *
+ * @param string $domain
+ * @return $this
+ * @since 15.0.0
+ */
+ public function addAllowedWorkerSrcDomain(string $domain) {
+ $this->allowedWorkerSrcDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove domain from which workers can be loaded
+ *
+ * @param string $domain
+ * @return $this
+ * @since 15.0.0
+ */
+ public function disallowWorkerSrcDomain(string $domain) {
+ $this->allowedWorkerSrcDomains = array_diff($this->allowedWorkerSrcDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Domain to where forms can submit
+ *
+ * @since 17.0.0
+ *
+ * @return $this
+ */
+ public function addAllowedFormActionDomain(string $domain) {
+ $this->allowedFormActionDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Remove domain to where forms can submit
+ *
+ * @return $this
+ * @since 17.0.0
+ */
+ public function disallowFormActionDomain(string $domain) {
+ $this->allowedFormActionDomains = array_diff($this->allowedFormActionDomains, [$domain]);
+ return $this;
+ }
+
+ /**
+ * Add location to report CSP violations to
+ *
+ * @param string $location
+ * @return $this
+ * @since 15.0.0
+ */
+ public function addReportTo(string $location) {
+ $this->reportTo[] = $location;
+ return $this;
+ }
+
+ /**
+ * Get the generated Content-Security-Policy as a string
+ * @return string
+ * @since 8.1.0
+ */
+ public function buildPolicy() {
+ $policy = "default-src 'none';";
+ $policy .= "base-uri 'none';";
+ $policy .= "manifest-src 'self';";
+
+ if (!empty($this->allowedScriptDomains) || $this->evalScriptAllowed || $this->evalWasmAllowed || is_string($this->jsNonce)) {
+ $policy .= 'script-src ';
+ $scriptSrc = '';
+ if (is_string($this->jsNonce)) {
+ if ($this->strictDynamicAllowed) {
+ $scriptSrc .= '\'strict-dynamic\' ';
+ }
+ $scriptSrc .= '\'nonce-' . $this->jsNonce . '\'';
+ $allowedScriptDomains = array_flip($this->allowedScriptDomains);
+ unset($allowedScriptDomains['\'self\'']);
+ $this->allowedScriptDomains = array_flip($allowedScriptDomains);
+ if (count($allowedScriptDomains) !== 0) {
+ $scriptSrc .= ' ';
+ }
+ }
+ if (is_array($this->allowedScriptDomains)) {
+ $scriptSrc .= implode(' ', $this->allowedScriptDomains);
+ }
+ if ($this->evalScriptAllowed) {
+ $scriptSrc .= ' \'unsafe-eval\'';
+ }
+ if ($this->evalWasmAllowed) {
+ $scriptSrc .= ' \'wasm-unsafe-eval\'';
+ }
+ $policy .= $scriptSrc . ';';
+ }
+
+ // We only need to set this if 'strictDynamicAllowed' is not set because otherwise we can simply fall back to script-src
+ if ($this->strictDynamicAllowedOnScripts && is_string($this->jsNonce) && !$this->strictDynamicAllowed) {
+ $policy .= 'script-src-elem \'strict-dynamic\' ';
+ $policy .= $scriptSrc ?? '';
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedStyleDomains) || $this->inlineStyleAllowed) {
+ $policy .= 'style-src ';
+ if (is_array($this->allowedStyleDomains)) {
+ $policy .= implode(' ', $this->allowedStyleDomains);
+ }
+ if ($this->inlineStyleAllowed) {
+ $policy .= ' \'unsafe-inline\'';
+ }
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedImageDomains)) {
+ $policy .= 'img-src ' . implode(' ', $this->allowedImageDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedFontDomains)) {
+ $policy .= 'font-src ' . implode(' ', $this->allowedFontDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedConnectDomains)) {
+ $policy .= 'connect-src ' . implode(' ', $this->allowedConnectDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedMediaDomains)) {
+ $policy .= 'media-src ' . implode(' ', $this->allowedMediaDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedObjectDomains)) {
+ $policy .= 'object-src ' . implode(' ', $this->allowedObjectDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedFrameDomains)) {
+ $policy .= 'frame-src ';
+ $policy .= implode(' ', $this->allowedFrameDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedChildSrcDomains)) {
+ $policy .= 'child-src ' . implode(' ', $this->allowedChildSrcDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedFrameAncestors)) {
+ $policy .= 'frame-ancestors ' . implode(' ', $this->allowedFrameAncestors);
+ $policy .= ';';
+ } else {
+ $policy .= 'frame-ancestors \'none\';';
+ }
+
+ if (!empty($this->allowedWorkerSrcDomains)) {
+ $policy .= 'worker-src ' . implode(' ', $this->allowedWorkerSrcDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->allowedFormActionDomains)) {
+ $policy .= 'form-action ' . implode(' ', $this->allowedFormActionDomains);
+ $policy .= ';';
+ }
+
+ if (!empty($this->reportTo)) {
+ $policy .= 'report-uri ' . implode(' ', $this->reportTo);
+ $policy .= ';';
+ }
+
+ return rtrim($policy, ';');
+ }
+}
diff --git a/lib/public/AppFramework/Http/EmptyFeaturePolicy.php b/lib/public/AppFramework/Http/EmptyFeaturePolicy.php
new file mode 100644
index 00000000000..a1d19a9f34b
--- /dev/null
+++ b/lib/public/AppFramework/Http/EmptyFeaturePolicy.php
@@ -0,0 +1,164 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Class EmptyFeaturePolicy is a simple helper which allows applications
+ * to modify the FeaturePolicy sent by Nextcloud. Per default the policy
+ * is forbidding everything.
+ *
+ * As alternative with sane exemptions look at FeaturePolicy
+ *
+ * @see \OCP\AppFramework\Http\FeaturePolicy
+ * @since 17.0.0
+ */
+class EmptyFeaturePolicy {
+ /** @var string[] of allowed domains to autoplay media */
+ protected $autoplayDomains = null;
+
+ /** @var string[] of allowed domains that can access the camera */
+ protected $cameraDomains = null;
+
+ /** @var string[] of allowed domains that can use fullscreen */
+ protected $fullscreenDomains = null;
+
+ /** @var string[] of allowed domains that can use the geolocation of the device */
+ protected $geolocationDomains = null;
+
+ /** @var string[] of allowed domains that can use the microphone */
+ protected $microphoneDomains = null;
+
+ /** @var string[] of allowed domains that can use the payment API */
+ protected $paymentDomains = null;
+
+ /**
+ * Allows to use autoplay from a specific domain. Use * to allow from all domains.
+ *
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 17.0.0
+ */
+ public function addAllowedAutoplayDomain(string $domain): self {
+ $this->autoplayDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Allows to use the camera on a specific domain. Use * to allow from all domains
+ *
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 17.0.0
+ */
+ public function addAllowedCameraDomain(string $domain): self {
+ $this->cameraDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Allows the full screen functionality to be used on a specific domain. Use * to allow from all domains
+ *
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 17.0.0
+ */
+ public function addAllowedFullScreenDomain(string $domain): self {
+ $this->fullscreenDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Allows to use the geolocation on a specific domain. Use * to allow from all domains
+ *
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 17.0.0
+ */
+ public function addAllowedGeoLocationDomain(string $domain): self {
+ $this->geolocationDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Allows to use the microphone on a specific domain. Use * to allow from all domains
+ *
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 17.0.0
+ */
+ public function addAllowedMicrophoneDomain(string $domain): self {
+ $this->microphoneDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Allows to use the payment API on a specific domain. Use * to allow from all domains
+ *
+ * @param string $domain Domain to whitelist. Any passed value needs to be properly sanitized.
+ * @return $this
+ * @since 17.0.0
+ */
+ public function addAllowedPaymentDomain(string $domain): self {
+ $this->paymentDomains[] = $domain;
+ return $this;
+ }
+
+ /**
+ * Get the generated Feature-Policy as a string
+ *
+ * @return string
+ * @since 17.0.0
+ */
+ public function buildPolicy(): string {
+ $policy = '';
+
+ if (empty($this->autoplayDomains)) {
+ $policy .= "autoplay 'none';";
+ } else {
+ $policy .= 'autoplay ' . implode(' ', $this->autoplayDomains);
+ $policy .= ';';
+ }
+
+ if (empty($this->cameraDomains)) {
+ $policy .= "camera 'none';";
+ } else {
+ $policy .= 'camera ' . implode(' ', $this->cameraDomains);
+ $policy .= ';';
+ }
+
+ if (empty($this->fullscreenDomains)) {
+ $policy .= "fullscreen 'none';";
+ } else {
+ $policy .= 'fullscreen ' . implode(' ', $this->fullscreenDomains);
+ $policy .= ';';
+ }
+
+ if (empty($this->geolocationDomains)) {
+ $policy .= "geolocation 'none';";
+ } else {
+ $policy .= 'geolocation ' . implode(' ', $this->geolocationDomains);
+ $policy .= ';';
+ }
+
+ if (empty($this->microphoneDomains)) {
+ $policy .= "microphone 'none';";
+ } else {
+ $policy .= 'microphone ' . implode(' ', $this->microphoneDomains);
+ $policy .= ';';
+ }
+
+ if (empty($this->paymentDomains)) {
+ $policy .= "payment 'none';";
+ } else {
+ $policy .= 'payment ' . implode(' ', $this->paymentDomains);
+ $policy .= ';';
+ }
+
+ return rtrim($policy, ';');
+ }
+}
diff --git a/lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php b/lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php
new file mode 100644
index 00000000000..b724b3a72ad
--- /dev/null
+++ b/lib/public/AppFramework/Http/Events/BeforeLoginTemplateRenderedEvent.php
@@ -0,0 +1,35 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http\Events;
+
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\EventDispatcher\Event;
+
+/**
+ * Emitted before the rendering step of the login TemplateResponse.
+ *
+ * @since 28.0.0
+ */
+class BeforeLoginTemplateRenderedEvent extends Event {
+ /**
+ * @since 28.0.0
+ */
+ public function __construct(
+ private TemplateResponse $response,
+ ) {
+ parent::__construct();
+ }
+
+ /**
+ * @since 28.0.0
+ */
+ public function getResponse(): TemplateResponse {
+ return $this->response;
+ }
+}
diff --git a/lib/public/AppFramework/Http/Events/BeforeTemplateRenderedEvent.php b/lib/public/AppFramework/Http/Events/BeforeTemplateRenderedEvent.php
new file mode 100644
index 00000000000..7219ca5bfb6
--- /dev/null
+++ b/lib/public/AppFramework/Http/Events/BeforeTemplateRenderedEvent.php
@@ -0,0 +1,49 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http\Events;
+
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\EventDispatcher\Event;
+
+/**
+ * Emitted before the rendering step of each TemplateResponse. The event holds a
+ * flag that specifies if an user is logged in.
+ *
+ * @since 20.0.0
+ */
+class BeforeTemplateRenderedEvent extends Event {
+ /** @var bool */
+ private $loggedIn;
+ /** @var TemplateResponse */
+ private $response;
+
+ /**
+ * @since 20.0.0
+ */
+ public function __construct(bool $loggedIn, TemplateResponse $response) {
+ parent::__construct();
+
+ $this->loggedIn = $loggedIn;
+ $this->response = $response;
+ }
+
+ /**
+ * @since 20.0.0
+ */
+ public function isLoggedIn(): bool {
+ return $this->loggedIn;
+ }
+
+ /**
+ * @since 20.0.0
+ */
+ public function getResponse(): TemplateResponse {
+ return $this->response;
+ }
+}
diff --git a/lib/public/AppFramework/Http/FeaturePolicy.php b/lib/public/AppFramework/Http/FeaturePolicy.php
new file mode 100644
index 00000000000..2291a78055c
--- /dev/null
+++ b/lib/public/AppFramework/Http/FeaturePolicy.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Class FeaturePolicy is a simple helper which allows applications to
+ * modify the Feature-Policy sent by Nextcloud. Per default only autoplay is allowed
+ * from the same domain and full screen as well from the same domain.
+ *
+ * Even if a value gets modified above defaults will still get appended. Please
+ * notice that Nextcloud ships already with sensible defaults and those policies
+ * should require no modification at all for most use-cases.
+ *
+ * @since 17.0.0
+ */
+class FeaturePolicy extends EmptyFeaturePolicy {
+ protected $autoplayDomains = [
+ '\'self\'',
+ ];
+
+ /** @var string[] of allowed domains that can access the camera */
+ protected $cameraDomains = [];
+
+ protected $fullscreenDomains = [
+ '\'self\'',
+ ];
+
+ /** @var string[] of allowed domains that can use the geolocation of the device */
+ protected $geolocationDomains = [];
+
+ /** @var string[] of allowed domains that can use the microphone */
+ protected $microphoneDomains = [];
+
+ /** @var string[] of allowed domains that can use the payment API */
+ protected $paymentDomains = [];
+}
diff --git a/lib/public/AppFramework/Http/FileDisplayResponse.php b/lib/public/AppFramework/Http/FileDisplayResponse.php
new file mode 100644
index 00000000000..c18404b7d91
--- /dev/null
+++ b/lib/public/AppFramework/Http/FileDisplayResponse.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+use OCP\Files\File;
+use OCP\Files\SimpleFS\ISimpleFile;
+
+/**
+ * Class FileDisplayResponse
+ *
+ * @since 11.0.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class FileDisplayResponse extends Response implements ICallbackResponse {
+ /** @var File|ISimpleFile */
+ private $file;
+
+ /**
+ * FileDisplayResponse constructor.
+ *
+ * @param File|ISimpleFile $file
+ * @param S $statusCode
+ * @param H $headers
+ * @since 11.0.0
+ */
+ public function __construct(File|ISimpleFile $file, int $statusCode = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($statusCode, $headers);
+
+ $this->file = $file;
+ $this->addHeader('Content-Disposition', 'inline; filename="' . rawurldecode($file->getName()) . '"');
+
+ $this->setETag($file->getEtag());
+ $lastModified = new \DateTime();
+ $lastModified->setTimestamp($file->getMTime());
+ $this->setLastModified($lastModified);
+ }
+
+ /**
+ * @param IOutput $output
+ * @since 11.0.0
+ */
+ public function callback(IOutput $output) {
+ if ($output->getHttpResponseCode() !== Http::STATUS_NOT_MODIFIED) {
+ $output->setHeader('Content-Length: ' . $this->file->getSize());
+ $output->setOutput($this->file->getContent());
+ }
+ }
+}
diff --git a/lib/public/AppFramework/Http/ICallbackResponse.php b/lib/public/AppFramework/Http/ICallbackResponse.php
new file mode 100644
index 00000000000..a51f72612fb
--- /dev/null
+++ b/lib/public/AppFramework/Http/ICallbackResponse.php
@@ -0,0 +1,23 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Interface ICallbackResponse
+ *
+ * @since 8.1.0
+ */
+interface ICallbackResponse {
+ /**
+ * Outputs the content that should be printed
+ *
+ * @param IOutput $output a small wrapper that handles output
+ * @since 8.1.0
+ */
+ public function callback(IOutput $output);
+}
diff --git a/lib/public/AppFramework/Http/IOutput.php b/lib/public/AppFramework/Http/IOutput.php
new file mode 100644
index 00000000000..105eaa0edb9
--- /dev/null
+++ b/lib/public/AppFramework/Http/IOutput.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Very thin wrapper class to make output testable
+ * @since 8.1.0
+ */
+interface IOutput {
+ /**
+ * @param string $out
+ * @since 8.1.0
+ */
+ public function setOutput($out);
+
+ /**
+ * @param string|resource $path or file handle
+ *
+ * @return bool false if an error occurred
+ * @since 8.1.0
+ */
+ public function setReadfile($path);
+
+ /**
+ * @param string $header
+ * @since 8.1.0
+ */
+ public function setHeader($header);
+
+ /**
+ * @return int returns the current http response code
+ * @since 8.1.0
+ */
+ public function getHttpResponseCode();
+
+ /**
+ * @param int $code sets the http status code
+ * @since 8.1.0
+ */
+ public function setHttpResponseCode($code);
+
+ /**
+ * @param string $name
+ * @param string $value
+ * @param int $expire
+ * @param string $path
+ * @param string $domain
+ * @param bool $secure
+ * @param bool $httpOnly
+ * @param string $sameSite (added in 20)
+ * @since 8.1.0
+ */
+ public function setCookie($name, $value, $expire, $path, $domain, $secure, $httpOnly, $sameSite = 'Lax');
+}
diff --git a/lib/public/AppFramework/Http/JSONResponse.php b/lib/public/AppFramework/Http/JSONResponse.php
new file mode 100644
index 00000000000..a226e29a1b5
--- /dev/null
+++ b/lib/public/AppFramework/Http/JSONResponse.php
@@ -0,0 +1,91 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * A renderer for JSON calls
+ * @since 6.0.0
+ * @template S of Http::STATUS_*
+ * @template-covariant T of null|string|int|float|bool|array|\stdClass|\JsonSerializable
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class JSONResponse extends Response {
+ /**
+ * response data
+ * @var T
+ */
+ protected $data;
+ /**
+ * Additional `json_encode` flags
+ * @var int
+ */
+ protected $encodeFlags;
+
+
+ /**
+ * constructor of JSONResponse
+ * @param T $data the object or array that should be transformed
+ * @param S $statusCode the Http status code, defaults to 200
+ * @param H $headers
+ * @param int $encodeFlags Additional `json_encode` flags
+ * @since 6.0.0
+ * @since 30.0.0 Added `$encodeFlags` param
+ */
+ public function __construct(
+ mixed $data = [],
+ int $statusCode = Http::STATUS_OK,
+ array $headers = [],
+ int $encodeFlags = 0,
+ ) {
+ parent::__construct($statusCode, $headers);
+
+ $this->data = $data;
+ $this->encodeFlags = $encodeFlags;
+ $this->addHeader('Content-Type', 'application/json; charset=utf-8');
+ }
+
+
+ /**
+ * Returns the rendered json
+ * @return string the rendered json
+ * @since 6.0.0
+ * @throws \Exception If data could not get encoded
+ *
+ * @psalm-taint-escape has_quotes
+ * @psalm-taint-escape html
+ */
+ public function render() {
+ return json_encode($this->data, JSON_HEX_TAG | JSON_THROW_ON_ERROR | $this->encodeFlags, 2048);
+ }
+
+ /**
+ * Sets values in the data json array
+ * @psalm-suppress InvalidTemplateParam
+ * @param T $data an array or object which will be transformed
+ * to JSON
+ * @return JSONResponse Reference to this object
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function setData($data) {
+ $this->data = $data;
+
+ return $this;
+ }
+
+
+ /**
+ * @return T the data
+ * @since 6.0.0
+ */
+ public function getData() {
+ return $this->data;
+ }
+}
diff --git a/lib/public/AppFramework/Http/NotFoundResponse.php b/lib/public/AppFramework/Http/NotFoundResponse.php
new file mode 100644
index 00000000000..137d1a26655
--- /dev/null
+++ b/lib/public/AppFramework/Http/NotFoundResponse.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * A generic 404 response showing an 404 error page as well to the end-user
+ * @since 8.1.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends TemplateResponse<Http::STATUS_*, array<string, mixed>>
+ */
+class NotFoundResponse extends TemplateResponse {
+ /**
+ * @param S $status
+ * @param H $headers
+ * @since 8.1.0
+ */
+ public function __construct(int $status = Http::STATUS_NOT_FOUND, array $headers = []) {
+ parent::__construct('core', '404', [], 'guest', $status, $headers);
+
+ $this->setContentSecurityPolicy(new ContentSecurityPolicy());
+ }
+}
diff --git a/lib/public/AppFramework/Http/ParameterOutOfRangeException.php b/lib/public/AppFramework/Http/ParameterOutOfRangeException.php
new file mode 100644
index 00000000000..3286917d4d0
--- /dev/null
+++ b/lib/public/AppFramework/Http/ParameterOutOfRangeException.php
@@ -0,0 +1,62 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCP\AppFramework\Http;
+
+/**
+ * @since 29.0.0
+ */
+class ParameterOutOfRangeException extends \OutOfRangeException {
+ /**
+ * @since 29.0.0
+ */
+ public function __construct(
+ protected string $parameterName,
+ protected int $actualValue,
+ protected int $minValue,
+ protected int $maxValue,
+ ) {
+ parent::__construct(
+ sprintf(
+ 'Parameter %s must be between %d and %d',
+ $this->parameterName,
+ $this->minValue,
+ $this->maxValue,
+ )
+ );
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getParameterName(): string {
+ return $this->parameterName;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getActualValue(): int {
+ return $this->actualValue;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getMinValue(): int {
+ return $this->minValue;
+ }
+
+ /**
+ * @since 29.0.0
+ */
+ public function getMaxValue(): int {
+ return $this->maxValue;
+ }
+}
diff --git a/lib/public/AppFramework/Http/RedirectResponse.php b/lib/public/AppFramework/Http/RedirectResponse.php
new file mode 100644
index 00000000000..74847205976
--- /dev/null
+++ b/lib/public/AppFramework/Http/RedirectResponse.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * Redirects to a different URL
+ * @since 7.0.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class RedirectResponse extends Response {
+ private $redirectURL;
+
+ /**
+ * Creates a response that redirects to a url
+ * @param string $redirectURL the url to redirect to
+ * @param S $status
+ * @param H $headers
+ * @since 7.0.0
+ */
+ public function __construct(string $redirectURL, int $status = Http::STATUS_SEE_OTHER, array $headers = []) {
+ parent::__construct($status, $headers);
+
+ $this->redirectURL = $redirectURL;
+ $this->addHeader('Location', $redirectURL);
+ }
+
+
+ /**
+ * @return string the url to redirect
+ * @since 7.0.0
+ */
+ public function getRedirectURL() {
+ return $this->redirectURL;
+ }
+}
diff --git a/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php b/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php
new file mode 100644
index 00000000000..0a0c04f671d
--- /dev/null
+++ b/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php
@@ -0,0 +1,36 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+use OCP\IURLGenerator;
+
+/**
+ * Redirects to the default app
+ *
+ * @since 16.0.0
+ * @deprecated 23.0.0 Use RedirectResponse() with IURLGenerator::linkToDefaultPageUrl() instead
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends RedirectResponse<Http::STATUS_*, array<string, mixed>>
+ */
+class RedirectToDefaultAppResponse extends RedirectResponse {
+ /**
+ * Creates a response that redirects to the default app
+ *
+ * @param S $status
+ * @param H $headers
+ * @since 16.0.0
+ * @deprecated 23.0.0 Use RedirectResponse() with IURLGenerator::linkToDefaultPageUrl() instead
+ */
+ public function __construct(int $status = Http::STATUS_SEE_OTHER, array $headers = []) {
+ $urlGenerator = \OCP\Server::get(IURLGenerator::class);
+ parent::__construct($urlGenerator->linkToDefaultPageUrl(), $status, $headers);
+ }
+}
diff --git a/lib/public/AppFramework/Http/Response.php b/lib/public/AppFramework/Http/Response.php
new file mode 100644
index 00000000000..bdebb12c00d
--- /dev/null
+++ b/lib/public/AppFramework/Http/Response.php
@@ -0,0 +1,408 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\IConfig;
+use OCP\IRequest;
+use Psr\Log\LoggerInterface;
+
+/**
+ * Base class for responses. Also used to just send headers.
+ *
+ * It handles headers, HTTP status code, last modified and ETag.
+ * @since 6.0.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ */
+class Response {
+ /**
+ * Headers
+ * @var H
+ */
+ private $headers;
+
+
+ /**
+ * Cookies that will be need to be constructed as header
+ * @var array
+ */
+ private $cookies = [];
+
+
+ /**
+ * HTTP status code - defaults to STATUS OK
+ * @var S
+ */
+ private $status;
+
+
+ /**
+ * Last modified date
+ * @var \DateTime
+ */
+ private $lastModified;
+
+
+ /**
+ * ETag
+ * @var string
+ */
+ private $ETag;
+
+ /** @var ContentSecurityPolicy|null Used Content-Security-Policy */
+ private $contentSecurityPolicy = null;
+
+ /** @var FeaturePolicy */
+ private $featurePolicy;
+
+ /** @var bool */
+ private $throttled = false;
+ /** @var array */
+ private $throttleMetadata = [];
+
+ /**
+ * @param S $status
+ * @param H $headers
+ * @since 17.0.0
+ */
+ public function __construct(int $status = Http::STATUS_OK, array $headers = []) {
+ $this->setStatus($status);
+ $this->setHeaders($headers);
+ }
+
+ /**
+ * Caches the response
+ *
+ * @param int $cacheSeconds amount of seconds the response is fresh, 0 to disable cache.
+ * @param bool $public whether the page should be cached by public proxy. Usually should be false, unless this is a static resources.
+ * @param bool $immutable whether browser should treat the resource as immutable and not ask the server for each page load if the resource changed.
+ * @return $this
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function cacheFor(int $cacheSeconds, bool $public = false, bool $immutable = false) {
+ if ($cacheSeconds > 0) {
+ $cacheStore = $public ? 'public' : 'private';
+ $this->addHeader('Cache-Control', sprintf('%s, max-age=%s, %s', $cacheStore, $cacheSeconds, ($immutable ? 'immutable' : 'must-revalidate')));
+
+ // Set expires header
+ $expires = new \DateTime();
+ $time = \OCP\Server::get(ITimeFactory::class);
+ $expires->setTimestamp($time->getTime());
+ $expires->add(new \DateInterval('PT' . $cacheSeconds . 'S'));
+ $this->addHeader('Expires', $expires->format(\DateTimeInterface::RFC7231));
+ } else {
+ $this->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate');
+ unset($this->headers['Expires']);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Adds a new cookie to the response
+ * @param string $name The name of the cookie
+ * @param string $value The value of the cookie
+ * @param \DateTime|null $expireDate Date on that the cookie should expire, if set
+ * to null cookie will be considered as session
+ * cookie.
+ * @param string $sameSite The samesite value of the cookie. Defaults to Lax. Other possibilities are Strict or None
+ * @return $this
+ * @since 8.0.0
+ */
+ public function addCookie($name, $value, ?\DateTime $expireDate = null, $sameSite = 'Lax') {
+ $this->cookies[$name] = ['value' => $value, 'expireDate' => $expireDate, 'sameSite' => $sameSite];
+ return $this;
+ }
+
+
+ /**
+ * Set the specified cookies
+ * @param array $cookies array('foo' => array('value' => 'bar', 'expire' => null))
+ * @return $this
+ * @since 8.0.0
+ */
+ public function setCookies(array $cookies) {
+ $this->cookies = $cookies;
+ return $this;
+ }
+
+
+ /**
+ * Invalidates the specified cookie
+ * @param string $name
+ * @return $this
+ * @since 8.0.0
+ */
+ public function invalidateCookie($name) {
+ $this->addCookie($name, 'expired', new \DateTime('1971-01-01 00:00'));
+ return $this;
+ }
+
+ /**
+ * Invalidates the specified cookies
+ * @param array $cookieNames array('foo', 'bar')
+ * @return $this
+ * @since 8.0.0
+ */
+ public function invalidateCookies(array $cookieNames) {
+ foreach ($cookieNames as $cookieName) {
+ $this->invalidateCookie($cookieName);
+ }
+ return $this;
+ }
+
+ /**
+ * Returns the cookies
+ * @return array
+ * @since 8.0.0
+ */
+ public function getCookies() {
+ return $this->cookies;
+ }
+
+ /**
+ * Adds a new header to the response that will be called before the render
+ * function
+ * @param string $name The name of the HTTP header
+ * @param string $value The value, null will delete it
+ * @return $this
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function addHeader($name, $value) {
+ $name = trim($name); // always remove leading and trailing whitespace
+ // to be able to reliably check for security
+ // headers
+
+ if ($this->status === Http::STATUS_NOT_MODIFIED
+ && stripos($name, 'x-') === 0) {
+ /** @var IConfig $config */
+ $config = \OCP\Server::get(IConfig::class);
+
+ if ($config->getSystemValueBool('debug', false)) {
+ \OCP\Server::get(LoggerInterface::class)->error('Setting custom header on a 304 is not supported (Header: {header})', [
+ 'header' => $name,
+ ]);
+ }
+ }
+
+ if (is_null($value)) {
+ unset($this->headers[$name]);
+ } else {
+ $this->headers[$name] = $value;
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Set the headers
+ * @template NewH as array<string, mixed>
+ * @param NewH $headers value header pairs
+ * @psalm-this-out static<S, NewH>
+ * @return static
+ * @since 8.0.0
+ */
+ public function setHeaders(array $headers): static {
+ /** @psalm-suppress InvalidPropertyAssignmentValue Expected due to @psalm-this-out */
+ $this->headers = $headers;
+
+ return $this;
+ }
+
+
+ /**
+ * Returns the set headers
+ * @return array{X-Request-Id: string, Cache-Control: string, Content-Security-Policy: string, Feature-Policy: string, X-Robots-Tag: string, Last-Modified?: string, ETag?: string, ...H} the headers
+ * @since 6.0.0
+ */
+ public function getHeaders() {
+ /** @var IRequest $request */
+ /**
+ * @psalm-suppress UndefinedClass
+ */
+ $request = \OCP\Server::get(IRequest::class);
+ $mergeWith = [
+ 'X-Request-Id' => $request->getId(),
+ 'Cache-Control' => 'no-cache, no-store, must-revalidate',
+ 'Content-Security-Policy' => $this->getContentSecurityPolicy()->buildPolicy(),
+ 'Feature-Policy' => $this->getFeaturePolicy()->buildPolicy(),
+ 'X-Robots-Tag' => 'noindex, nofollow',
+ ];
+
+ if ($this->lastModified) {
+ $mergeWith['Last-Modified'] = $this->lastModified->format(\DateTimeInterface::RFC7231);
+ }
+
+ if ($this->ETag) {
+ $mergeWith['ETag'] = '"' . $this->ETag . '"';
+ }
+
+ return array_merge($mergeWith, $this->headers);
+ }
+
+
+ /**
+ * By default renders no output
+ * @return string
+ * @since 6.0.0
+ */
+ public function render() {
+ return '';
+ }
+
+
+ /**
+ * Set response status
+ * @template NewS as int
+ * @param NewS $status a HTTP status code, see also the STATUS constants
+ * @psalm-this-out static<NewS, H>
+ * @return static
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function setStatus($status): static {
+ /** @psalm-suppress InvalidPropertyAssignmentValue Expected due to @psalm-this-out */
+ $this->status = $status;
+
+ return $this;
+ }
+
+ /**
+ * Set a Content-Security-Policy
+ * @param EmptyContentSecurityPolicy $csp Policy to set for the response object
+ * @return $this
+ * @since 8.1.0
+ */
+ public function setContentSecurityPolicy(EmptyContentSecurityPolicy $csp) {
+ $this->contentSecurityPolicy = $csp;
+ return $this;
+ }
+
+ /**
+ * Get the currently used Content-Security-Policy
+ * @return EmptyContentSecurityPolicy|null Used Content-Security-Policy or null if
+ * none specified.
+ * @since 8.1.0
+ */
+ public function getContentSecurityPolicy() {
+ if ($this->contentSecurityPolicy === null) {
+ $this->setContentSecurityPolicy(new EmptyContentSecurityPolicy());
+ }
+ return $this->contentSecurityPolicy;
+ }
+
+
+ /**
+ * @since 17.0.0
+ */
+ public function getFeaturePolicy(): EmptyFeaturePolicy {
+ if ($this->featurePolicy === null) {
+ $this->setFeaturePolicy(new EmptyFeaturePolicy());
+ }
+ return $this->featurePolicy;
+ }
+
+ /**
+ * @since 17.0.0
+ */
+ public function setFeaturePolicy(EmptyFeaturePolicy $featurePolicy): self {
+ $this->featurePolicy = $featurePolicy;
+
+ return $this;
+ }
+
+
+
+ /**
+ * Get response status
+ * @since 6.0.0
+ * @return S
+ */
+ public function getStatus() {
+ return $this->status;
+ }
+
+
+ /**
+ * Get the ETag
+ * @return string the etag
+ * @since 6.0.0
+ */
+ public function getETag() {
+ return $this->ETag;
+ }
+
+
+ /**
+ * Get "last modified" date
+ * @return \DateTime RFC2822 formatted last modified date
+ * @since 6.0.0
+ */
+ public function getLastModified() {
+ return $this->lastModified;
+ }
+
+
+ /**
+ * Set the ETag
+ * @param string $ETag
+ * @return Response Reference to this object
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function setETag($ETag) {
+ $this->ETag = $ETag;
+
+ return $this;
+ }
+
+
+ /**
+ * Set "last modified" date
+ * @param \DateTime $lastModified
+ * @return Response Reference to this object
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function setLastModified($lastModified) {
+ $this->lastModified = $lastModified;
+
+ return $this;
+ }
+
+ /**
+ * Marks the response as to throttle. Will be throttled when the
+ * @BruteForceProtection annotation is added.
+ *
+ * @param array $metadata
+ * @since 12.0.0
+ */
+ public function throttle(array $metadata = []) {
+ $this->throttled = true;
+ $this->throttleMetadata = $metadata;
+ }
+
+ /**
+ * Returns the throttle metadata, defaults to empty array
+ *
+ * @return array
+ * @since 13.0.0
+ */
+ public function getThrottleMetadata() {
+ return $this->throttleMetadata;
+ }
+
+ /**
+ * Whether the current response is throttled.
+ *
+ * @since 12.0.0
+ */
+ public function isThrottled() {
+ return $this->throttled;
+ }
+}
diff --git a/lib/public/AppFramework/Http/StandaloneTemplateResponse.php b/lib/public/AppFramework/Http/StandaloneTemplateResponse.php
new file mode 100644
index 00000000000..244a6b80f9f
--- /dev/null
+++ b/lib/public/AppFramework/Http/StandaloneTemplateResponse.php
@@ -0,0 +1,24 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * A template response that does not emit the loadAdditionalScripts events.
+ *
+ * This is useful for pages that are authenticated but do not yet show the
+ * full nextcloud UI. Like the 2FA page, or the grant page in the login flow.
+ *
+ * @since 16.0.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends TemplateResponse<Http::STATUS_*, array<string, mixed>>
+ */
+class StandaloneTemplateResponse extends TemplateResponse {
+}
diff --git a/lib/public/AppFramework/Http/StreamResponse.php b/lib/public/AppFramework/Http/StreamResponse.php
new file mode 100644
index 00000000000..d0e6e3e148a
--- /dev/null
+++ b/lib/public/AppFramework/Http/StreamResponse.php
@@ -0,0 +1,53 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * Class StreamResponse
+ *
+ * @since 8.1.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class StreamResponse extends Response implements ICallbackResponse {
+ /** @var string */
+ private $filePath;
+
+ /**
+ * @param string|resource $filePath the path to the file or a file handle which should be streamed
+ * @param S $status
+ * @param H $headers
+ * @since 8.1.0
+ */
+ public function __construct(mixed $filePath, int $status = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($status, $headers);
+
+ $this->filePath = $filePath;
+ }
+
+
+ /**
+ * Streams the file using readfile
+ *
+ * @param IOutput $output a small wrapper that handles output
+ * @since 8.1.0
+ */
+ public function callback(IOutput $output) {
+ // handle caching
+ if ($output->getHttpResponseCode() !== Http::STATUS_NOT_MODIFIED) {
+ if (!(is_resource($this->filePath) || file_exists($this->filePath))) {
+ $output->setHttpResponseCode(Http::STATUS_NOT_FOUND);
+ } elseif ($output->setReadfile($this->filePath) === false) {
+ $output->setHttpResponseCode(Http::STATUS_BAD_REQUEST);
+ }
+ }
+ }
+}
diff --git a/lib/public/AppFramework/Http/StrictContentSecurityPolicy.php b/lib/public/AppFramework/Http/StrictContentSecurityPolicy.php
new file mode 100644
index 00000000000..4b074331fc8
--- /dev/null
+++ b/lib/public/AppFramework/Http/StrictContentSecurityPolicy.php
@@ -0,0 +1,70 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Class StrictContentSecurityPolicy is a simple helper which allows applications to
+ * modify the Content-Security-Policy sent by Nextcloud. Per default only JavaScript,
+ * stylesheets, images, fonts, media and connections from the same domain
+ * ('self') are allowed.
+ *
+ * Even if a value gets modified above defaults will still get appended. Please
+ * note that Nextcloud ships already with sensible defaults and those policies
+ * should require no modification at all for most use-cases.
+ *
+ * This class represents out strictest defaults. They may get change from release
+ * to release if more strict CSP directives become available.
+ *
+ * @since 14.0.0
+ * @deprecated 17.0.0
+ */
+class StrictContentSecurityPolicy extends EmptyContentSecurityPolicy {
+ /** @var bool Whether inline JS snippets are allowed */
+ protected $inlineScriptAllowed = false;
+ /** @var bool Whether eval in JS scripts is allowed */
+ protected $evalScriptAllowed = false;
+ /** @var bool Whether WebAssembly compilation is allowed */
+ protected ?bool $evalWasmAllowed = false;
+ /** @var array Domains from which scripts can get loaded */
+ protected $allowedScriptDomains = [
+ '\'self\'',
+ ];
+ /** @var bool Whether inline CSS is allowed */
+ protected $inlineStyleAllowed = false;
+ /** @var array Domains from which CSS can get loaded */
+ protected $allowedStyleDomains = [
+ '\'self\'',
+ ];
+ /** @var array Domains from which images can get loaded */
+ protected $allowedImageDomains = [
+ '\'self\'',
+ 'data:',
+ 'blob:',
+ ];
+ /** @var array Domains to which connections can be done */
+ protected $allowedConnectDomains = [
+ '\'self\'',
+ ];
+ /** @var array Domains from which media elements can be loaded */
+ protected $allowedMediaDomains = [
+ '\'self\'',
+ ];
+ /** @var array Domains from which object elements can be loaded */
+ protected $allowedObjectDomains = [];
+ /** @var array Domains from which iframes can be loaded */
+ protected $allowedFrameDomains = [];
+ /** @var array Domains from which fonts can be loaded */
+ protected $allowedFontDomains = [
+ '\'self\'',
+ ];
+ /** @var array Domains from which web-workers and nested browsing content can load elements */
+ protected $allowedChildSrcDomains = [];
+
+ /** @var array Domains which can embed this Nextcloud instance */
+ protected $allowedFrameAncestors = [];
+}
diff --git a/lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php b/lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php
new file mode 100644
index 00000000000..b59dd0fcce7
--- /dev/null
+++ b/lib/public/AppFramework/Http/StrictEvalContentSecurityPolicy.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Class StrictEvalContentSecurityPolicy is a simple helper which allows applications to
+ * modify the Content-Security-Policy sent by Nextcloud. Per default only JavaScript,
+ * stylesheets, images, fonts, media and connections from the same domain
+ * ('self') are allowed.
+ *
+ * Even if a value gets modified above defaults will still get appended. Please
+ * note that Nextcloud ships already with sensible defaults and those policies
+ * should require no modification at all for most use-cases.
+ *
+ * This is a temp helper class from the default ContentSecurityPolicy to allow slow
+ * migration to a stricter CSP. This does not allow unsafe eval.
+ *
+ * @since 14.0.0
+ * @deprecated 17.0.0
+ */
+class StrictEvalContentSecurityPolicy extends ContentSecurityPolicy {
+ /**
+ * @since 14.0.0
+ */
+ public function __construct() {
+ $this->evalScriptAllowed = false;
+ }
+}
diff --git a/lib/public/AppFramework/Http/StrictInlineContentSecurityPolicy.php b/lib/public/AppFramework/Http/StrictInlineContentSecurityPolicy.php
new file mode 100644
index 00000000000..e80d37c74cf
--- /dev/null
+++ b/lib/public/AppFramework/Http/StrictInlineContentSecurityPolicy.php
@@ -0,0 +1,33 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+/**
+ * Class StrictInlineContentSecurityPolicy is a simple helper which allows applications to
+ * modify the Content-Security-Policy sent by Nextcloud. Per default only JavaScript,
+ * stylesheets, images, fonts, media and connections from the same domain
+ * ('self') are allowed.
+ *
+ * Even if a value gets modified above defaults will still get appended. Please
+ * note that Nextcloud ships already with sensible defaults and those policies
+ * should require no modification at all for most use-cases.
+ *
+ * This is a temp helper class from the default ContentSecurityPolicy to allow slow
+ * migration to a stricter CSP. This does not allow inline styles.
+ *
+ * @since 14.0.0
+ * @deprecated 17.0.0
+ */
+class StrictInlineContentSecurityPolicy extends ContentSecurityPolicy {
+ /**
+ * @since 14.0.0
+ */
+ public function __construct() {
+ $this->inlineStyleAllowed = false;
+ }
+}
diff --git a/lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php b/lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php
new file mode 100644
index 00000000000..281bb559a10
--- /dev/null
+++ b/lib/public/AppFramework/Http/Template/ExternalShareMenuAction.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http\Template;
+
+/**
+ * Class LinkMenuAction
+ *
+ * @since 14.0.0
+ */
+class ExternalShareMenuAction extends SimpleMenuAction {
+
+ /**
+ * ExternalShareMenuAction constructor.
+ *
+ * @param string $label Translated label
+ * @param string $icon Icon CSS class
+ * @param string $owner Owner user ID (unused)
+ * @param string $displayname Display name of the owner (unused)
+ * @param string $shareName Name of the share (unused)
+ * @since 14.0.0
+ */
+ public function __construct(string $label, string $icon, string $owner, string $displayname, string $shareName) {
+ parent::__construct('save', $label, $icon);
+ }
+}
diff --git a/lib/public/AppFramework/Http/Template/IMenuAction.php b/lib/public/AppFramework/Http/Template/IMenuAction.php
new file mode 100644
index 00000000000..124e95fe019
--- /dev/null
+++ b/lib/public/AppFramework/Http/Template/IMenuAction.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http\Template;
+
+/**
+ * Interface IMenuAction
+ *
+ * @since 14.0
+ */
+interface IMenuAction {
+ /**
+ * @since 14.0.0
+ * @return string
+ */
+ public function getId(): string;
+
+ /**
+ * The translated label of the menu item.
+ *
+ * @since 14.0.0
+ * @return string
+ */
+ public function getLabel(): string;
+
+ /**
+ * The link this menu item points to.
+ *
+ * @since 14.0.0
+ * @return string
+ */
+ public function getLink(): string;
+
+ /**
+ * @since 14.0.0
+ * @return int
+ */
+ public function getPriority(): int;
+
+ /**
+ * Custom render function.
+ * The returned HTML will be wrapped within a listitem element (`<li>...</li>`).
+ *
+ * @since 14.0.0
+ * @return string
+ */
+ public function render(): string;
+}
diff --git a/lib/public/AppFramework/Http/Template/LinkMenuAction.php b/lib/public/AppFramework/Http/Template/LinkMenuAction.php
new file mode 100644
index 00000000000..391802a1dce
--- /dev/null
+++ b/lib/public/AppFramework/Http/Template/LinkMenuAction.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http\Template;
+
+/**
+ * Class LinkMenuAction
+ *
+ * @since 14.0.0
+ */
+class LinkMenuAction extends SimpleMenuAction {
+ /**
+ * LinkMenuAction constructor.
+ *
+ * @param string $label
+ * @param string $icon
+ * @param string $link
+ * @since 14.0.0
+ */
+ public function __construct(string $label, string $icon, string $link) {
+ parent::__construct('directLink', $label, $icon, $link);
+ }
+}
diff --git a/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php b/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php
new file mode 100644
index 00000000000..4c156cdecea
--- /dev/null
+++ b/lib/public/AppFramework/Http/Template/PublicTemplateResponse.php
@@ -0,0 +1,176 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http\Template;
+
+use InvalidArgumentException;
+use OCP\AppFramework\Http;
+use OCP\AppFramework\Http\TemplateResponse;
+use OCP\IInitialStateService;
+
+/**
+ * Class PublicTemplateResponse
+ *
+ * @since 14.0.0
+ * @template H of array<string, mixed>
+ * @template S of Http::STATUS_*
+ * @template-extends TemplateResponse<Http::STATUS_*, array<string, mixed>>
+ */
+class PublicTemplateResponse extends TemplateResponse {
+ private $headerTitle = '';
+ private $headerDetails = '';
+ /** @var IMenuAction[] */
+ private $headerActions = [];
+ private $footerVisible = true;
+
+ /**
+ * PublicTemplateResponse constructor.
+ *
+ * @param string $appName
+ * @param string $templateName
+ * @param array $params
+ * @param S $status
+ * @param H $headers
+ * @since 14.0.0
+ */
+ public function __construct(
+ string $appName,
+ string $templateName,
+ array $params = [],
+ $status = Http::STATUS_OK,
+ array $headers = [],
+ ) {
+ parent::__construct($appName, $templateName, $params, 'public', $status, $headers);
+ \OCP\Util::addScript('core', 'public-page-menu');
+ \OCP\Util::addScript('core', 'public-page-user-menu');
+
+ $state = \OCP\Server::get(IInitialStateService::class);
+ $state->provideLazyInitialState('core', 'public-page-menu', function () {
+ $response = [];
+ foreach ($this->headerActions as $action) {
+ // First try in it is a custom action that provides rendered HTML
+ $rendered = $action->render();
+ if ($rendered === '') {
+ // If simple action, add the response data
+ if ($action instanceof SimpleMenuAction) {
+ $response[] = $action->getData();
+ }
+ } else {
+ // custom action so add the rendered output
+ $response[] = [
+ 'id' => $action->getId(),
+ 'label' => $action->getLabel(),
+ 'html' => $rendered,
+ ];
+ }
+ }
+ return $response;
+ });
+ }
+
+ /**
+ * @param string $title
+ * @since 14.0.0
+ */
+ public function setHeaderTitle(string $title) {
+ $this->headerTitle = $title;
+ }
+
+ /**
+ * @return string
+ * @since 14.0.0
+ */
+ public function getHeaderTitle(): string {
+ return $this->headerTitle;
+ }
+
+ /**
+ * @param string $details
+ * @since 14.0.0
+ */
+ public function setHeaderDetails(string $details) {
+ $this->headerDetails = $details;
+ }
+
+ /**
+ * @return string
+ * @since 14.0.0
+ */
+ public function getHeaderDetails(): string {
+ return $this->headerDetails;
+ }
+
+ /**
+ * @param array $actions
+ * @since 14.0.0
+ * @throws InvalidArgumentException
+ */
+ public function setHeaderActions(array $actions) {
+ foreach ($actions as $action) {
+ if ($actions instanceof IMenuAction) {
+ throw new InvalidArgumentException('Actions must be of type IMenuAction');
+ }
+ $this->headerActions[] = $action;
+ }
+ usort($this->headerActions, function (IMenuAction $a, IMenuAction $b) {
+ return $a->getPriority() <=> $b->getPriority();
+ });
+ }
+
+ /**
+ * @return IMenuAction
+ * @since 14.0.0
+ * @throws \Exception
+ */
+ public function getPrimaryAction(): IMenuAction {
+ if ($this->getActionCount() > 0) {
+ return $this->headerActions[0];
+ }
+ throw new \Exception('No header actions have been set');
+ }
+
+ /**
+ * @return int
+ * @since 14.0.0
+ */
+ public function getActionCount(): int {
+ return count($this->headerActions);
+ }
+
+ /**
+ * @return IMenuAction[]
+ * @since 14.0.0
+ */
+ public function getOtherActions(): array {
+ return array_slice($this->headerActions, 1);
+ }
+
+ /**
+ * @since 14.0.0
+ */
+ public function setFooterVisible(bool $visible = false) {
+ $this->footerVisible = $visible;
+ }
+
+ /**
+ * @since 14.0.0
+ */
+ public function getFooterVisible(): bool {
+ return $this->footerVisible;
+ }
+
+ /**
+ * @return string
+ * @since 14.0.0
+ */
+ public function render(): string {
+ $params = array_merge($this->getParams(), [
+ 'template' => $this,
+ ]);
+ $this->setParams($params);
+ return parent::render();
+ }
+}
diff --git a/lib/public/AppFramework/Http/Template/SimpleMenuAction.php b/lib/public/AppFramework/Http/Template/SimpleMenuAction.php
new file mode 100644
index 00000000000..03cb9b4c7ea
--- /dev/null
+++ b/lib/public/AppFramework/Http/Template/SimpleMenuAction.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http\Template;
+
+/**
+ * Class SimpleMenuAction
+ *
+ * @since 14.0.0
+ */
+class SimpleMenuAction implements IMenuAction {
+ /** @var string */
+ private $id;
+
+ /** @var string */
+ private $label;
+
+ /** @var string */
+ private $icon;
+
+ /** @var string */
+ private $link;
+
+ /** @var int */
+ private $priority;
+
+ /** @var string */
+ private $detail;
+
+ /**
+ * SimpleMenuAction constructor.
+ *
+ * @param string $id
+ * @param string $label
+ * @param string $icon
+ * @param string $link
+ * @param int $priority
+ * @param string $detail
+ * @since 14.0.0
+ */
+ public function __construct(string $id, string $label, string $icon, string $link = '', int $priority = 100, string $detail = '') {
+ $this->id = $id;
+ $this->label = $label;
+ $this->icon = $icon;
+ $this->link = $link;
+ $this->priority = $priority;
+ $this->detail = $detail;
+ }
+
+ /**
+ * @return string
+ * @since 14.0.0
+ */
+ public function getId(): string {
+ return $this->id;
+ }
+
+ /**
+ * @return string
+ * @since 14.0.0
+ */
+ public function getLabel(): string {
+ return $this->label;
+ }
+
+ /**
+ * The icon CSS class to use.
+ *
+ * @return string
+ * @since 14.0.0
+ */
+ public function getIcon(): string {
+ return $this->icon;
+ }
+
+ /**
+ * @return string
+ * @since 14.0.0
+ */
+ public function getLink(): string {
+ return $this->link;
+ }
+
+ /**
+ * @return int
+ * @since 14.0.0
+ */
+ public function getPriority(): int {
+ return $this->priority;
+ }
+
+ /**
+ * Custom render function.
+ * The returned HTML must be wrapped within a listitem (`<li>...</li>`).
+ * * If an empty string is returned, the default design is used (based on the label and link specified).
+ * @return string
+ * @since 14.0.0
+ */
+ public function render(): string {
+ return '';
+ }
+
+ /**
+ * Return JSON data to let the frontend render the menu entry.
+ * @return array{id: string, label: string, href: string, icon: string, details: string|null}
+ * @since 31.0.0
+ */
+ public function getData(): array {
+ return [
+ 'id' => $this->id,
+ 'label' => $this->label,
+ 'href' => $this->link,
+ 'icon' => $this->icon,
+ 'details' => $this->detail,
+ ];
+ }
+}
diff --git a/lib/public/AppFramework/Http/TemplateResponse.php b/lib/public/AppFramework/Http/TemplateResponse.php
new file mode 100644
index 00000000000..af37a1a2313
--- /dev/null
+++ b/lib/public/AppFramework/Http/TemplateResponse.php
@@ -0,0 +1,197 @@
+<?php
+
+declare(strict_types=1);
+
+
+/**
+ * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-FileCopyrightText: 2016 ownCloud, Inc.
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+use OCP\Server;
+use OCP\Template\ITemplateManager;
+
+/**
+ * Response for a normal template
+ * @since 6.0.0
+ *
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class TemplateResponse extends Response {
+ /**
+ * @since 20.0.0
+ */
+ public const RENDER_AS_GUEST = 'guest';
+ /**
+ * @since 20.0.0
+ */
+ public const RENDER_AS_BLANK = '';
+ /**
+ * @since 20.0.0
+ */
+ public const RENDER_AS_BASE = 'base';
+ /**
+ * @since 20.0.0
+ */
+ public const RENDER_AS_USER = 'user';
+ /**
+ * @since 20.0.0
+ */
+ public const RENDER_AS_ERROR = 'error';
+ /**
+ * @since 20.0.0
+ */
+ public const RENDER_AS_PUBLIC = 'public';
+
+ /**
+ * name of the template
+ * @var string
+ */
+ protected $templateName;
+
+ /**
+ * parameters
+ * @var array
+ */
+ protected $params;
+
+ /**
+ * rendering type (admin, user, blank)
+ * @var string
+ */
+ protected $renderAs;
+
+ /**
+ * app name
+ * @var string
+ */
+ protected $appName;
+
+ /**
+ * constructor of TemplateResponse
+ * @param string $appName the name of the app to load the template from
+ * @param string $templateName the name of the template
+ * @param array $params an array of parameters which should be passed to the
+ * template
+ * @param string $renderAs how the page should be rendered, defaults to user
+ * @param S $status
+ * @param H $headers
+ * @since 6.0.0 - parameters $params and $renderAs were added in 7.0.0
+ */
+ public function __construct(string $appName, string $templateName, array $params = [], string $renderAs = self::RENDER_AS_USER, int $status = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($status, $headers);
+
+ $this->templateName = $templateName;
+ $this->appName = $appName;
+ $this->params = $params;
+ $this->renderAs = $renderAs;
+
+ $this->setContentSecurityPolicy(new ContentSecurityPolicy());
+ $this->setFeaturePolicy(new FeaturePolicy());
+ }
+
+
+ /**
+ * Sets template parameters
+ * @param array $params an array with key => value structure which sets template
+ * variables
+ * @return TemplateResponse Reference to this object
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function setParams(array $params) {
+ $this->params = $params;
+
+ return $this;
+ }
+
+
+ /**
+ * Used for accessing the set parameters
+ * @return array the params
+ * @since 6.0.0
+ */
+ public function getParams() {
+ return $this->params;
+ }
+
+
+ /**
+ * @return string the app id of the used template
+ * @since 25.0.0
+ */
+ public function getApp(): string {
+ return $this->appName;
+ }
+
+
+ /**
+ * Used for accessing the name of the set template
+ * @return string the name of the used template
+ * @since 6.0.0
+ */
+ public function getTemplateName() {
+ return $this->templateName;
+ }
+
+
+ /**
+ * Sets the template page
+ * @param string $renderAs admin, user or blank. Admin also prints the admin
+ * settings header and footer, user renders the normal
+ * normal page including footer and header and blank
+ * just renders the plain template
+ * @return TemplateResponse Reference to this object
+ * @since 6.0.0 - return value was added in 7.0.0
+ */
+ public function renderAs($renderAs) {
+ $this->renderAs = $renderAs;
+
+ return $this;
+ }
+
+
+ /**
+ * Returns the set renderAs
+ * @return string the renderAs value
+ * @since 6.0.0
+ */
+ public function getRenderAs() {
+ return $this->renderAs;
+ }
+
+
+ /**
+ * Returns the rendered html
+ * @return string the rendered html
+ * @since 6.0.0
+ */
+ public function render() {
+ $renderAs = self::RENDER_AS_USER;
+ if ($this->renderAs === 'blank') {
+ // Legacy fallback as \OCP\Template needs an empty string instead of 'blank' for an unwrapped response
+ $renderAs = self::RENDER_AS_BLANK;
+ } elseif (in_array($this->renderAs, [
+ self::RENDER_AS_GUEST,
+ self::RENDER_AS_BLANK,
+ self::RENDER_AS_BASE,
+ self::RENDER_AS_ERROR,
+ self::RENDER_AS_PUBLIC,
+ self::RENDER_AS_USER], true)) {
+ $renderAs = $this->renderAs;
+ }
+
+ $template = Server::get(ITemplateManager::class)->getTemplate($this->appName, $this->templateName, $renderAs);
+
+ foreach ($this->params as $key => $value) {
+ $template->assign($key, $value);
+ }
+
+ return $template->fetchPage($this->params);
+ }
+}
diff --git a/lib/public/AppFramework/Http/TextPlainResponse.php b/lib/public/AppFramework/Http/TextPlainResponse.php
new file mode 100644
index 00000000000..9dfa2c5544d
--- /dev/null
+++ b/lib/public/AppFramework/Http/TextPlainResponse.php
@@ -0,0 +1,47 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+
+/**
+ * A renderer for text responses
+ * @since 22.0.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class TextPlainResponse extends Response {
+ /** @var string */
+ private $text = '';
+
+ /**
+ * constructor of TextPlainResponse
+ * @param string $text The text body
+ * @param S $statusCode the Http status code, defaults to 200
+ * @param H $headers
+ * @since 22.0.0
+ */
+ public function __construct(string $text = '', int $statusCode = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($statusCode, $headers);
+
+ $this->text = $text;
+ $this->addHeader('Content-Type', 'text/plain');
+ }
+
+
+ /**
+ * Returns the text
+ * @return string
+ * @since 22.0.0
+ * @throws \Exception If data could not get encoded
+ */
+ public function render() : string {
+ return $this->text;
+ }
+}
diff --git a/lib/public/AppFramework/Http/TooManyRequestsResponse.php b/lib/public/AppFramework/Http/TooManyRequestsResponse.php
new file mode 100644
index 00000000000..f7084ec768d
--- /dev/null
+++ b/lib/public/AppFramework/Http/TooManyRequestsResponse.php
@@ -0,0 +1,41 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+use OCP\AppFramework\Http;
+use OCP\Server;
+use OCP\Template\ITemplateManager;
+
+/**
+ * A generic 429 response showing an 404 error page as well to the end-user
+ * @since 19.0.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class TooManyRequestsResponse extends Response {
+ /**
+ * @param S $status
+ * @param H $headers
+ * @since 19.0.0
+ */
+ public function __construct(int $status = Http::STATUS_TOO_MANY_REQUESTS, array $headers = []) {
+ parent::__construct($status, $headers);
+
+ $this->setContentSecurityPolicy(new ContentSecurityPolicy());
+ }
+
+ /**
+ * @return string
+ * @since 19.0.0
+ */
+ public function render() {
+ $template = Server::get(ITemplateManager::class)->getTemplate('core', '429', TemplateResponse::RENDER_AS_BLANK);
+ return $template->fetchPage();
+ }
+}
diff --git a/lib/public/AppFramework/Http/ZipResponse.php b/lib/public/AppFramework/Http/ZipResponse.php
new file mode 100644
index 00000000000..a552eb1294f
--- /dev/null
+++ b/lib/public/AppFramework/Http/ZipResponse.php
@@ -0,0 +1,77 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+namespace OCP\AppFramework\Http;
+
+use OC\Streamer;
+use OCP\AppFramework\Http;
+use OCP\IRequest;
+
+/**
+ * Public library to send several files in one zip archive.
+ *
+ * @since 15.0.0
+ * @template S of Http::STATUS_*
+ * @template H of array<string, mixed>
+ * @template-extends Response<Http::STATUS_*, array<string, mixed>>
+ */
+class ZipResponse extends Response implements ICallbackResponse {
+ /** @var array{internalName: string, resource: resource, size: int, time: int}[] Files to be added to the zip response */
+ private array $resources = [];
+ /** @var string Filename that the zip file should have */
+ private string $name;
+ private IRequest $request;
+
+ /**
+ * @param S $status
+ * @param H $headers
+ * @since 15.0.0
+ */
+ public function __construct(IRequest $request, string $name = 'output', int $status = Http::STATUS_OK, array $headers = []) {
+ parent::__construct($status, $headers);
+
+ $this->name = $name;
+ $this->request = $request;
+ }
+
+ /**
+ * @since 15.0.0
+ */
+ public function addResource($r, string $internalName, int $size, int $time = -1) {
+ if (!\is_resource($r)) {
+ throw new \InvalidArgumentException('No resource provided');
+ }
+
+ $this->resources[] = [
+ 'resource' => $r,
+ 'internalName' => $internalName,
+ 'size' => $size,
+ 'time' => $time,
+ ];
+ }
+
+ /**
+ * @since 15.0.0
+ */
+ public function callback(IOutput $output) {
+ $size = 0;
+ $files = count($this->resources);
+
+ foreach ($this->resources as $resource) {
+ $size += $resource['size'];
+ }
+
+ $zip = new Streamer($this->request, $size, $files);
+ $zip->sendHeaders($this->name);
+
+ foreach ($this->resources as $resource) {
+ $zip->addFileFromStream($resource['resource'], $resource['internalName'], $resource['size'], $resource['time']);
+ }
+
+ $zip->finalize();
+ }
+}