Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion endpoints/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

from orm import DB
from services import Service
from tools import formats
from tools import formats, mconf
from tools.tasq import TasQServer


Expand Down Expand Up @@ -152,3 +152,19 @@ def getDisabledPluginsOfUser():
disabledPlugins = [p[0] for p in disabledPlugins]

return jsonify({ "data": disabledPlugins })


@API.route(api.BaseRoute+"/oofSubjectPrefix", methods=["GET"])
@secure(requireAuth=False)

@StefanAkie StefanAkie Sep 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would allow anyone to read any OOF message across the server, which might include sensitive information, violating Austria's DSGVO

def getOofSubjectPrefix():
"""Report the gromox.cfg autoreply_subject_prefix directive.

A client offering an out-of-office form needs it to tell the user what an
empty subject will send. An unset directive is reported as
configured=false rather than guessed at, because the default that then
applies is compiled into the running gromox build.
"""
prefix, error = mconf.readGromoxDirective("autoreply_subject_prefix")
if error is not None:
return jsonify(message=error), 500
return jsonify({"data": {"prefix": prefix, "configured": prefix is not None}})
35 changes: 35 additions & 0 deletions res/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2293,6 +2293,41 @@ paths:
'503':
$ref: '#/components/responses/DatabaseError'

/oofSubjectPrefix:
get:
summary: Get the configured out-of-office reply subject prefix
description: >
Reports the gromox.cfg autoreply_subject_prefix directive, which gromox
prepends to the subject of the incoming message when a mailbox has no
out-of-office subject of its own. Clients offering an out-of-office form
use it to show what an empty subject will send.
operationId: oofSubjectPrefix
tags:
- Misc
responses:
'200':
description: Prefix state returned
content:
application/json:
schema:
type: object
properties:
data:
type: object
properties:
prefix:
type: string
nullable: true
description: The configured prefix, or null when the directive is not set
configured:
type: boolean
description: >
Whether the directive is set. When false, gromox applies the
default compiled into the running build, which cannot be read
from the configuration file.
'500':
$ref: '#/components/responses/ServerError'

/domains/{domainID}/users:
get:
summary: Get lists of users
Expand Down
3 changes: 2 additions & 1 deletion tools/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ def _defaultConfig():
},
"mconf": {
"ldapPath": "/etc/gromox/ldap_adaptor.cfg",
"authmgrPath": "/etc/gromox/authmgr.cfg"
"authmgrPath": "/etc/gromox/authmgr.cfg",
"gromoxPath": "/etc/gromox/gromox.cfg"
},
"logs": {},
"sync": {
Expand Down
28 changes: 28 additions & 0 deletions tools/mconf.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from .misc import setDirectoryOwner, setDirectoryPermission
from services import Service

from os.path import exists

import logging
logger = logging.getLogger("mconf")

Expand Down Expand Up @@ -235,6 +237,32 @@ def dumpAuthmgr(conf=None, file=None, reloadServices=False):
###############################################################################


def readGromoxDirective(name):
"""Read a single directive from the main gromox configuration file.

Not cached, so an admin editing gromox.cfg need not restart the API.
gromox trims trailing whitespace off every configuration line and
_loadConf does too, so this returns the value gromox itself sees.

Returns
-------
tuple
(value, error). value is None when the directive is not set.
"""
if "gromoxPath" not in Config["mconf"]:
return None, "mconf.gromoxPath not set"
path = Config["mconf"]["gromoxPath"]
if not exists(path):
return None, "'{}' does not exist".format(path)
try:
return _loadConf(path).get(name), None
except Exception as err:
return None, " - ".join((str(arg) for arg in err.args))


###############################################################################


def load():
error = loadLdap()
if error:
Expand Down