/*++
/* NAME
/* postconf_user 3
/* SUMMARY
/* support for user-defined main.cf parameter names
/* SYNOPSIS
/* #include <postconf.h>
/*
/* void pcf_register_user_parameters(int mode)
/* DESCRIPTION
/* Postfix has multiple parameter name spaces: the global
/* main.cf parameter name space, and the local parameter name
/* space of each master.cf entry. Parameters in local name
/* spaces take precedence over global parameters.
/*
/* There are three categories of known parameter names: built-in,
/* service-defined (see postconf_service.c), and valid
/* user-defined.
/*
/* There are two categories of valid user-defined parameter
/* names:
/*
/* - Parameters whose user-defined-name appears in the value
/* of smtpd_restriction_classes in main.cf or master.cf.
/*
/* - Parameters whose $user-defined-name appear in the value
/* of "name=value" entries in main.cf or master.cf.
/*
/* - In both cases the parameters must have a
/* "user-defined-name=value" entry in main.cf or master.cf.
/*
/* Other user-defined parameter names are flagged as "unused".
/*
/* pcf_register_user_parameters() scans the global and per-service
/* name spaces for user-defined parameters and flags parameters
/* as "valid" in the global name space (pcf_param_table) or
/* in the per-service name space (valid_params).
/*
/* This function also invokes pcf_register_dbms_parameters() to
/* to instantiate legacy per-dbms parameters, and to examine
/* per-dbms configuration files. This is limited to the content
/* of global and local, built-in and per-service, parameters.
/*
/* Arguments:
/* .IP mode
/* Passed on to pcf_register_dbms_parameters().
/* DIAGNOSTICS
/* Problems are reported to the standard error stream.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*--*/
/* System library. */
#include <sys_defs.h>
#include <string.h>
/* Utility library. */
#include <msg.h>
#include <mymalloc.h>
#include <htable.h>
#include <mac_expand.h>
#include <stringops.h>
/* Global library. */
#include <mail_conf.h>
#include <mail_params.h>
/* Application-specific. */
#include <postconf.h>
/*
* Hash with all user - defined names in the global smtpd_restriction_classes
* value . This is used when validating " - o user - defined - name = value " entries
* in master . cf .
*/
static HTABLE *pcf_rest_class_table;
/*
* SLMs .
*/
#define STR(x) vstring_str(x)
/*
* Macros to make code with obscure constants more readable .
*/
#define NO_SCAN_RESULT ((VSTRING *) 0 )
#define NO_SCAN_FILTER ((char *) 0 )
/* SCAN_USER_PARAMETER_VALUE - examine macro names in parameter value */
#define SCAN_USER_PARAMETER_VALUE(value, class , scope) do { \
PCF_PARAM_CTX _ctx; \
_ctx.local_scope = (scope); \
_ctx.param_class = (class ); \
/* No MAC_EXP_FLAG_RECURSE, therefore no need for DONT_PARSE feature. */ \
(void ) mac_expand(NO_SCAN_RESULT, (value), MAC_EXP_FLAG_SCAN, \
NO_SCAN_FILTER, pcf_flag_user_parameter_wrapper, (void *) &_ctx); \
} while (0 )
/* pcf_convert_user_parameter - get user-defined parameter string value */
static const char *pcf_convert_user_parameter(void *unused_ptr)
{
return ("" ); /* can't happen */
}
/* pcf_flag_user_parameter - flag user-defined name "valid" if it has name=value */
static const char *pcf_flag_user_parameter(const char *mac_name,
int param_class,
PCF_MASTER_ENT *local_scope)
{
const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE;
int user_supplied = 0 ;
/*
* If the name = value exists in the local ( or global ) name space , update
* the local ( or global ) " valid " parameter name table .
*
* Do not " validate " user - defined parameters whose name appears only as
* macro expansion ; this is how Postfix historically implements backwards
* compatibility after a feature name change .
*/
if (local_scope && dict_get(local_scope->all_params, mac_name)) {
user_supplied = 1 ;
/* $name in master.cf references name=value in master.cf. */
if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0 ) {
PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name,
param_class, PCF_PARAM_NO_DATA,
pcf_convert_user_parameter);
if (msg_verbose)
msg_info("$%s in %s:%s validates %s=value in %s:%s" ,
mac_name, MASTER_CONF_FILE,
local_scope->name_space,
mac_name, MASTER_CONF_FILE,
local_scope->name_space);
}
} else if (mail_conf_lookup(mac_name) != 0 ) {
user_supplied = 1 ;
/* $name in main/master.cf references name=value in main.cf. */
if (PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0 ) {
PCF_PARAM_TABLE_ENTER(pcf_param_table, mac_name, param_class,
PCF_PARAM_NO_DATA, pcf_convert_user_parameter);
if (msg_verbose) {
if (local_scope)
msg_info("$%s in %s:%s validates %s=value in %s" ,
mac_name, MASTER_CONF_FILE,
local_scope->name_space,
mac_name, MAIN_CONF_FILE);
else
msg_info("$%s in %s validates %s=value in %s" ,
mac_name, MAIN_CONF_FILE,
mac_name, MAIN_CONF_FILE);
}
}
}
if (local_scope == 0 ) {
for (local_scope = pcf_master_table; local_scope->argv; local_scope++) {
if (local_scope->all_params != 0
&& dict_get(local_scope->all_params, mac_name) != 0 ) {
user_supplied = 1 ;
/* $name in main.cf references name=value in master.cf. */
if (PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, mac_name) == 0 ) {
PCF_PARAM_TABLE_ENTER(local_scope->valid_names, mac_name,
param_class, PCF_PARAM_NO_DATA,
pcf_convert_user_parameter);
if (msg_verbose)
msg_info("$%s in %s validates %s=value in %s:%s" ,
mac_name, MAIN_CONF_FILE,
mac_name, MASTER_CONF_FILE,
local_scope->name_space);
}
}
}
}
/*
* Warn about a $ name that has no user - supplied explicit value or
* Postfix - supplied default value . We don ' t enforce this for legacy DBMS
* parameters because they exist only for backwards compatibility , so we
* don ' t bother to figure out which parameters come without defaults .
*/
if (user_supplied == 0 && (param_class & PCF_PARAM_FLAG_DBMS) == 0
&& PCF_PARAM_TABLE_LOCATE(pcf_param_table, mac_name) == 0 )
msg_warn("%s/%s: undefined parameter: %s" ,
var_config_dir, source, mac_name);
return (0 );
}
/* pcf_flag_user_parameter_wrapper - mac_expand call-back helper */
static const char *pcf_flag_user_parameter_wrapper(const char *mac_name,
int unused_mode,
void *context)
{
PCF_PARAM_CTX *ctx = (PCF_PARAM_CTX *) context;
return (pcf_flag_user_parameter(mac_name, ctx->param_class, ctx->local_scope));
}
/* pcf_lookup_eval - generalized mail_conf_lookup_eval */
static const char *pcf_lookup_eval(const char *dict_name, const char *name)
{
const char *value;
#define RECURSIVE 1
if ((value = dict_lookup(dict_name, name)) != 0 )
value = dict_eval(dict_name, value, RECURSIVE);
return (value);
}
/* pcf_scan_user_parameter_namespace - scan parameters in name space */
static void pcf_scan_user_parameter_namespace(int mode, const char *dict_name,
PCF_MASTER_ENT *local_scope)
{
const char *myname = "pcf_scan_user_parameter_namespace" ;
const char *class_list;
char *saved_class_list;
char *cp;
DICT *dict;
char *param_name;
int how;
const char *cparam_name;
const char *cparam_value;
PCF_PARAM_NODE *node;
const char *source = local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE;
/*
* Flag parameter names in smtpd_restriction_classes as " valid " , but only
* if they have a " name = value " entry . If we are in not in a local name
* space , update the global restriction class name table , so that we can
* query the global table from within a local master . cf name space .
*/
if ((class_list = pcf_lookup_eval(dict_name, VAR_REST_CLASSES)) != 0 ) {
cp = saved_class_list = mystrdup(class_list);
while ((param_name = mystrtok(&cp, CHARS_COMMA_SP)) != 0 ) {
if (local_scope == 0
&& htable_locate(pcf_rest_class_table, param_name) == 0 )
htable_enter(pcf_rest_class_table, param_name, "" );
pcf_flag_user_parameter(param_name, PCF_PARAM_FLAG_USER, local_scope);
}
myfree(saved_class_list);
}
/*
* For all " name = value " instances : a ) if the name space is local and the
* name appears in the global restriction class table , flag the name as
* " valid " in the local name space ; b ) scan the value for macro
* expansions of unknown parameter names , and flag those parameter names
* as " valid " if they have a " name = value " entry .
*
* We delete name = value entries for read - only parameters , to maintain
* compatibility with Postfix programs that ignore such settings .
*/
if ((dict = dict_handle(dict_name)) == 0 )
msg_panic("%s: parameter dictionary %s not found" ,
myname, dict_name);
if (dict->sequence == 0 )
msg_panic("%s: parameter dictionary %s has no iterator" ,
myname, dict_name);
for (how = DICT_SEQ_FUN_FIRST;
dict->sequence(dict, how, &cparam_name, &cparam_value) == 0 ;
how = DICT_SEQ_FUN_NEXT) {
if (local_scope != 0
&& PCF_PARAM_TABLE_LOCATE(local_scope->valid_names, cparam_name) == 0
&& htable_locate(pcf_rest_class_table, cparam_name) != 0 )
PCF_PARAM_TABLE_ENTER(local_scope->valid_names, cparam_name,
PCF_PARAM_FLAG_USER, PCF_PARAM_NO_DATA,
pcf_convert_user_parameter);
if ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, cparam_name)) != 0 ) {
if (PCF_READONLY_PARAMETER(node)) {
msg_warn("%s/%s: read-only parameter assignment: %s=%s" ,
var_config_dir, source, cparam_name, cparam_value);
/* Can't use dict_del() with Postfix<2.10 htable_sequence(). */
if (dict_del(dict, cparam_name) != 0 )
msg_panic("%s: can't delete %s/%s parameter entry for %s" ,
myname, var_config_dir, source, cparam_name);
continue ;
}
/* Re-label legacy parameter as user-defined, so it's printed. */
if (PCF_LEGACY_PARAMETER(node))
PCF_PARAM_CLASS_OVERRIDE(node, PCF_PARAM_FLAG_USER);
/* Skip "do not expand" parameters. */
if (PCF_RAW_PARAMETER(node))
continue ;
}
SCAN_USER_PARAMETER_VALUE(cparam_value, PCF_PARAM_FLAG_USER, local_scope);
#ifdef LEGACY_DBMS_SUPPORT
/*
* Scan global or local parameters that are built - in or per - service
* ( when node = = 0 , the parameter doesn ' t exist in the global
* namespace and therefore it can ' t be built - in or per - service ) .
*/
if (node != 0
&& (PCF_BUILTIN_PARAMETER(node) || PCF_SERVICE_PARAMETER(node)))
pcf_register_dbms_parameters(mode, cparam_value, pcf_flag_user_parameter,
local_scope);
#endif
}
}
/* pcf_scan_default_parameter_values - scan parameters at implicit defaults */
static void pcf_scan_default_parameter_values(HTABLE *valid_params,
const char *dict_name,
PCF_MASTER_ENT *local_scope)
{
const char *myname = "pcf_scan_default_parameter_values" ;
PCF_PARAM_INFO **list;
PCF_PARAM_INFO **ht;
const char *param_value;
list = PCF_PARAM_TABLE_LIST(valid_params);
for (ht = list; *ht; ht++) {
/* Skip "do not expand" parameters. */
if (PCF_RAW_PARAMETER(PCF_PARAM_INFO_NODE(*ht)))
continue ;
/* Skip parameters with a non-default value. */
if (dict_lookup(dict_name, PCF_PARAM_INFO_NAME(*ht)))
continue ;
if ((param_value = pcf_convert_param_node(PCF_SHOW_DEFS, PCF_PARAM_INFO_NAME(*ht),
PCF_PARAM_INFO_NODE(*ht))) == 0 )
msg_panic("%s: parameter %s has no default value" ,
myname, PCF_PARAM_INFO_NAME(*ht));
SCAN_USER_PARAMETER_VALUE(param_value, PCF_PARAM_FLAG_USER, local_scope);
/* No need to scan default values for legacy DBMS configuration. */
}
myfree((void *) list);
}
/* pcf_register_user_parameters - add parameters with user-defined names */
void pcf_register_user_parameters(int mode)
{
const char *myname = "pcf_register_user_parameters" ;
PCF_MASTER_ENT *masterp;
ARGV *argv;
char *arg;
char *aval;
int field;
char *saved_arg;
char *param_name;
char *param_value;
DICT *dict;
/*
* Sanity checks .
*/
if (pcf_param_table == 0 )
msg_panic("%s: global parameter table is not initialized" , myname);
if (pcf_master_table == 0 )
msg_panic("%s: master table is not initialized" , myname);
if (pcf_rest_class_table != 0 )
msg_panic("%s: restriction class table is already initialized" , myname);
/*
* Initialize the table with global restriction class names .
*/
pcf_rest_class_table = htable_create(1 );
/*
* Initialize the per - service parameter name spaces .
*/
for (masterp = pcf_master_table; (argv = masterp->argv) != 0 ; masterp++) {
for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0 ; field++) {
arg = argv->argv[field];
if (arg[0 ] != '-' || strcmp(arg, "--" ) == 0 )
break ;
if (strchr(pcf_daemon_options_expecting_value, arg[1 ]) == 0
|| (aval = argv->argv[field + 1 ]) == 0 )
continue ;
if (strcmp(arg, "-o" ) == 0 ) {
saved_arg = mystrdup(aval);
if (split_nameval(saved_arg, ¶m_name, ¶m_value) == 0 )
dict_update(masterp->name_space, param_name, param_value);
myfree(saved_arg);
}
field += 1 ;
}
if ((dict = dict_handle(masterp->name_space)) != 0 ) {
masterp->all_params = dict;
masterp->valid_names = htable_create(1 );
}
}
/*
* Scan the " - o parameter = value " instances in each master . cf name space .
*/
for (masterp = pcf_master_table; masterp->argv != 0 ; masterp++)
if (masterp->all_params != 0 )
pcf_scan_user_parameter_namespace(mode, masterp->name_space, masterp);
/*
* Scan parameter values that are left at their defaults in the global
* name space . Some defaults contain the $ name of an obsolete parameter
* for backwards compatibility purposes . We might warn that an explicit
* name = value is obsolete , but we must not warn that the parameter is
* unused .
*/
pcf_scan_default_parameter_values(pcf_param_table, CONFIG_DICT,
(PCF_MASTER_ENT *) 0 );
/*
* Scan the explicit name = value entries in the global name space .
*/
pcf_scan_user_parameter_namespace(mode, CONFIG_DICT, (PCF_MASTER_ENT *) 0 );
}
Messung V0.5 in Prozent C=74 H=94 G=84
¤ Dauer der Verarbeitung: 0.13 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland