Skip to content

HTTP 500 on legacy ajax endpoints: front controller rewrite skips paths that exist on disk #41740

Description

@DeepDiver1975

Steps to reproduce

  1. Run owncloud/server:11.0.0-rc2 (or current master) with 'htaccess.RewriteBase' => '/' in config.php, so that .htaccess contains the front controller rewrite block.
  2. Log in and open Settings → Personal → General.
  3. Pick a different language in the language selector.

Expected behaviour

POST to the language endpoint returns 200 with {"data":{"message":"Language changed"},"status":"success"} and the UI reloads translated.

Actual behaviour

POST http://localhost:8080/settings/ajax/setlanguage.php 500 (Internal Server Error)

The language is never stored. Apache error log:

PHP Fatal error:  Uncaught Error: Class "OC" not found in /var/www/owncloud/settings/ajax/setlanguage.php:27

Root cause

This is not specific to the language setting — it is a whole class of broken endpoints.

  1. settings/js/panels/profile.js posts to the relative url 'ajax/setlanguage.php', which the browser resolves against /settings/personal/settings/ajax/setlanguage.php.

  2. That path is a real file on disk, and the front controller rewrite generated by lib/private/Setup.php::updateHtaccess() only forwards a request to index.php when the target does not exist:

    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule . index.php [PT,E=PATH_INFO:$1]
  3. Apache therefore executes the script directly, bypassing the router that would have included it with a bootstrap (Route::actionInclude()require_once from inside index.php). Run standalone, the script's very first statement (settings/ajax/setlanguage.php:27, \OC::$server->getL10N('settings')) dies with Class "OC" not found.

This is a regression introduced after 10.16.3

Ingredient 1 (js posting a real script path) already existed at v10.16.3. Ingredient 2 was introduced by 5bb332b"fix: htaccess RewriteBase rules block API requests for files with common extensions (#41606)" (fixes #41418) — which replaced two extension-based RewriteConds with RewriteCond %{REQUEST_FILENAME} !-f.

5bb332b7aa is not an ancestor of v10.16.3 but is an ancestor of master, so 10.16.3 is unaffected.

A/B verified in one container by swapping only that single RewriteCond line:

.htaccess variant POST /settings/ajax/setlanguage.php (anonymous)
RewriteCond %{REQUEST_FILENAME} !-f (current) 500 — executed directly, no bootstrap
extension-based cond (pre-#41606) 401 — routed through index.php

It only bites when htaccess.RewriteBase is configured (the docker image sets /), since the whole <IfModule mod_rewrite.c> block is only emitted then. That is likely why CI did not catch it.

Blast radius

19 legacy routes declare a url that is also a real .php file on disk. Probed against the running instance — every one of these returns 500 when requested directly, and works when requested through /index.php/...:

Endpoint User-visible impact Caller
settings/ajax/setlanguage.php cannot change personal language settings/js/panels/profile.js
core/ajax/share.php share-dialog email autocomplete core/js/sharedialogmailview.js:211
apps/files_sharing/ajax/publicpreview.php anonymous public-link thumbnails apps/files_sharing/js/public.js:231
apps/files_trashbin/ajax/preview.php trashbin previews Trashbin.php:1162, files_trashbin/js/filelist.js:278
apps/files_external/ajax/oauth2.php Google Drive OAuth flow apps/files_external/js/gdrive.js:3
core/ajax/appconfig.php legacy appconfig ajax

The remaining ~11 are latent: they only work because OC.filePath() unconditionally injects /index.php/ into the url, so the request never hits the file. Any caller switching to OC.generateUrl() would break them. core/ajax/update.php is unaffected — it has an explicit rewrite exemption and self-bootstraps.

Note on #41720

#41720 "Cannot change language (11.0.0-rc2 docker image)" is closed as completed, closed by a9d5c97 / 99245f0 "fix(l10n): guard vsprintf against malformed translations (#41720)". That is a different bug:

  • the rc2 image was built before that fix and still contains bare \vsprintf in lib/private/legacy/l10n/string.php;
  • more decisively, the fatal is Class "OC" not found on the file's first statement — the request dies before any l10n code runs.

So #41720 was closed against the wrong root cause and the bug still reproduces.

Server configuration

Operating system: Debian (docker image owncloud/server:11.0.0-rc2)

Web server: Apache 2.4.58 with mod_rewrite

Database: sqlite3

PHP version: 8.3.32

ownCloud version: 11.0.0-rc2 (also reproduces on master)

Updated from an older ownCloud or fresh install: fresh install

Where did you install ownCloud from: docker hub, owncloud/server:11.0.0-rc2

Relevant config: 'htaccess.RewriteBase' => '/' (required to reproduce)

Client configuration

Browser: any

Logs

Web server error log

PHP Fatal error:  Uncaught Error: Class "OC" not found in /var/www/owncloud/settings/ajax/setlanguage.php:27
Stack trace:
#0 {main}
  thrown in /var/www/owncloud/settings/ajax/setlanguage.php on line 27

Browser log

POST http://localhost:8080/settings/ajax/setlanguage.php 500 (Internal Server Error)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions