/*
 *  Phusion Passenger - https://www.phusionpassenger.com/
 *  Copyright (c) 2010-2016 Phusion Holding B.V.
 *
 *  "Passenger", "Phusion Passenger" and "Union Station" are registered
 *  trademarks of Phusion Holding B.V.
 *
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 *
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */
#include <algorithm>
#include <cstdlib>
#include <climits>

/* ap_config.h checks whether the compiler has support for C99's designated
 * initializers, and defines AP_HAVE_DESIGNATED_INITIALIZER if it does. However,
 * g++ does not support designated initializers, even when ap_config.h thinks
 * it does. Here we undefine the macro to force httpd_config.h to not use
 * designated initializers. This should fix compilation problems on some systems.
 */
#include <ap_config.h>
#undef AP_HAVE_DESIGNATED_INITIALIZER

#include "Configuration.hpp"
#include <Utils.h>
#include <Constants.h>
#include <UnionStationFilterSupport.h>

/* The APR/Apache headers must come after the Passenger headers. See Hooks.cpp
 * to learn why.
 */
#include <apr_strings.h>
// In Apache < 2.4, this macro was necessary for core_dir_config and other structs
#define CORE_PRIVATE
#include <http_core.h>

using namespace Passenger;

extern "C" module AP_MODULE_DECLARE_DATA passenger_module;

namespace Passenger { ServerConfig serverConfig; }

#define MERGE_THREEWAY_CONFIG(field) \
	config->field = (add->field == DirConfig::UNSET) ? base->field : add->field
#define MERGE_STR_CONFIG(field) \
	config->field = (add->field == NULL) ? base->field : add->field
#define MERGE_STRING_CONFIG(field) \
	config->field = (add->field.empty()) ? base->field : add->field
#define MERGE_INT_CONFIG(field) \
	config->field = (add->field ## Specified) ? add->field : base->field; \
	config->field ## Specified = base->field ## Specified || add->field ## Specified

#define DEFINE_DIR_STR_CONFIG_SETTER(functionName, fieldName)                    \
	static const char *                                                      \
	functionName(cmd_parms *cmd, void *pcfg, const char *arg) {              \
		DirConfig *config = (DirConfig *) pcfg;                          \
		config->fieldName = arg;                                         \
		return NULL;                                                     \
	}
#define DEFINE_DIR_INT_CONFIG_SETTER(functionName, fieldName, integerType, minValue)            \
	static const char *                                                                     \
	functionName(cmd_parms *cmd, void *pcfg, const char *arg) {                             \
		DirConfig *config = (DirConfig *) pcfg;                                         \
		char *end;                                                                      \
		long int result;                                                                \
		                                                                                \
		result = strtol(arg, &end, 10);                                                 \
		if (*end != '\0') {                                                             \
			string message = "Invalid number specified for ";                       \
			message.append(cmd->directive->directive);                              \
			message.append(".");                                                    \
			                                                                        \
			char *messageStr = (char *) apr_palloc(cmd->temp_pool,                  \
				message.size() + 1);                                            \
			memcpy(messageStr, message.c_str(), message.size() + 1);                \
			return messageStr;                                                      \
		} else if (result < minValue) {                                                 \
			string message = "Value for ";                                          \
			message.append(cmd->directive->directive);                              \
			message.append(" must be greater than or equal to " #minValue ".");     \
			                                                                        \
			char *messageStr = (char *) apr_palloc(cmd->temp_pool,                  \
				message.size() + 1);                                            \
			memcpy(messageStr, message.c_str(), message.size() + 1);                \
			return messageStr;                                                      \
		} else {                                                                        \
			config->fieldName = (integerType) result;                               \
			config->fieldName ## Specified = true;                                  \
			return NULL;                                                            \
		}                                                                               \
	}
#define DEFINE_DIR_THREEWAY_CONFIG_SETTER(functionName, fieldName)           \
	static const char *                                                  \
	functionName(cmd_parms *cmd, void *pcfg, const char *arg) {          \
		DirConfig *config = (DirConfig *) pcfg;                      \
		if (arg) {                                                   \
			config->fieldName = DirConfig::ENABLED;              \
		} else {                                      	             \
			config->fieldName = DirConfig::DISABLED;             \
		}                                                            \
		return NULL;                                                 \
	}

#define DEFINE_SERVER_STR_CONFIG_SETTER(functionName, fieldName)                 \
	static const char *                                                      \
	functionName(cmd_parms *cmd, void *dummy, const char *arg) {             \
		serverConfig.fieldName = arg;                                    \
		return NULL;                                                     \
	}
#define DEFINE_SERVER_BOOLEAN_CONFIG_SETTER(functionName, fieldName)             \
	static const char *                                                      \
	functionName(cmd_parms *cmd, void *dummy, int arg) {                     \
		serverConfig.fieldName = arg;                                    \
		return NULL;                                                     \
	}
#define DEFINE_SERVER_INT_CONFIG_SETTER(functionName, fieldName, integerType, minValue)         \
	static const char *                                                                     \
	functionName(cmd_parms *cmd, void *pcfg, const char *arg) {                             \
		char *end;                                                                      \
		long int result;                                                                \
		                                                                                \
		result = strtol(arg, &end, 10);                                                 \
		if (*end != '\0') {                                                             \
			string message = "Invalid number specified for ";                       \
			message.append(cmd->directive->directive);                              \
			message.append(".");                                                    \
			                                                                        \
			char *messageStr = (char *) apr_palloc(cmd->temp_pool,                  \
				message.size() + 1);                                            \
			memcpy(messageStr, message.c_str(), message.size() + 1);                \
			return messageStr;                                                      \
		} else if (result < minValue) {                                                 \
			string message = "Value for ";                                          \
			message.append(cmd->directive->directive);                              \
			message.append(" must be greater than or equal to " #minValue ".");     \
			                                                                        \
			char *messageStr = (char *) apr_palloc(cmd->temp_pool,                  \
				message.size() + 1);                                            \
			memcpy(messageStr, message.c_str(), message.size() + 1);                \
			return messageStr;                                                      \
		} else {                                                                        \
			serverConfig.fieldName = (integerType) result;                          \
			return NULL;                                                            \
		}                                                                               \
	}


template<typename T> static apr_status_t
destroy_config_struct(void *x) {
	delete (T *) x;
	return APR_SUCCESS;
}

template<typename Collection, typename T> static bool
contains(const Collection &coll, const T &item) {
	typename Collection::const_iterator it;
	for (it = coll.begin(); it != coll.end(); it++) {
		if (*it == item) {
			return true;
		}
	}
	return false;
}


extern "C" {

static DirConfig *
create_dir_config_struct(apr_pool_t *pool) {
	DirConfig *config = new DirConfig();
	apr_pool_cleanup_register(pool, config, destroy_config_struct<DirConfig>, apr_pool_cleanup_null);
	return config;
}

void *
passenger_config_create_dir(apr_pool_t *p, char *dirspec) {
	DirConfig *config = create_dir_config_struct(p);

	#include "CreateDirConfig.cpp"

	config->appRoot = NULL;
	config->resolveSymlinksInDocRoot = DirConfig::UNSET;
	config->allowEncodedSlashes = DirConfig::UNSET;
	config->unionStationSupport = DirConfig::UNSET;
	config->bufferResponse = DirConfig::UNSET;
	/*************************************/
	return config;
}

void *
passenger_config_merge_dir(apr_pool_t *p, void *basev, void *addv) {
	DirConfig *config = create_dir_config_struct(p);
	DirConfig *base = (DirConfig *) basev;
	DirConfig *add = (DirConfig *) addv;

	#include "MergeDirConfig.cpp"

	config->baseURIs = base->baseURIs;
	for (set<string>::const_iterator it(add->baseURIs.begin()); it != add->baseURIs.end(); it++) {
		config->baseURIs.insert(*it);
	}

	MERGE_STR_CONFIG(appRoot);
	MERGE_STRING_CONFIG(unionStationKey);
	config->unionStationFilters = base->unionStationFilters;
	for (vector<string>::const_iterator it = add->unionStationFilters.begin(); it != add->unionStationFilters.end(); it++) {
		if (!contains(config->unionStationFilters, *it)) {
			config->unionStationFilters.push_back(*it);
		}
	}
	MERGE_THREEWAY_CONFIG(resolveSymlinksInDocRoot);
	MERGE_THREEWAY_CONFIG(allowEncodedSlashes);
	MERGE_THREEWAY_CONFIG(unionStationSupport);
	MERGE_THREEWAY_CONFIG(bufferResponse);
	/*************************************/
	return config;
}

static void
postprocessDirConfig(server_rec *s, core_dir_config *core_dconf,
	DirConfig *psg_dconf, bool isTopLevel = false)
{
	if (psg_dconf->unionStationSupport == DirConfig::ENABLED) {
		serverConfig.unionStationSupport = true;
	}
}

#ifndef ap_get_core_module_config
	#define ap_get_core_module_config(s) ap_get_module_config(s, &core_module)
#endif

void
passenger_postprocess_config(server_rec *s) {
	core_server_config *sconf;
	core_dir_config *core_dconf;
	DirConfig *psg_dconf;
	int nelts;
    ap_conf_vector_t **elts;
    int i;

	serverConfig.finalize();

	for (; s != NULL; s = s->next) {
		sconf = (core_server_config *) ap_get_core_module_config(s->module_config);
		core_dconf = (core_dir_config *) ap_get_core_module_config(s->lookup_defaults);
		psg_dconf = (DirConfig *) ap_get_module_config(s->lookup_defaults, &passenger_module);
		postprocessDirConfig(s, core_dconf, psg_dconf, true);

		nelts = sconf->sec_dir->nelts;
		elts  = (ap_conf_vector_t **) sconf->sec_dir->elts;
		for (i = 0; i < nelts; ++i) {
			core_dconf = (core_dir_config *) ap_get_core_module_config(elts[i]);
			psg_dconf = (DirConfig *) ap_get_module_config(elts[i], &passenger_module);
			if (core_dconf != NULL && psg_dconf != NULL) {
				postprocessDirConfig(s, core_dconf, psg_dconf);
			}
		}

		nelts = sconf->sec_url->nelts;
		elts  = (ap_conf_vector_t **) sconf->sec_url->elts;
		for (i = 0; i < nelts; ++i) {
			core_dconf = (core_dir_config *) ap_get_core_module_config(elts[i]);
			psg_dconf = (DirConfig *) ap_get_module_config(elts[i], &passenger_module);
			if (core_dconf != NULL && psg_dconf != NULL) {
				postprocessDirConfig(s, core_dconf, psg_dconf);
			}
		}
	}
}


/*************************************************
 * Passenger settings
 *************************************************/

DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_root, root)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_default_ruby, defaultRuby)
DEFINE_SERVER_INT_CONFIG_SETTER(cmd_passenger_log_level, logLevel, unsigned int, 0)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_log_file, logFile)
DEFINE_SERVER_INT_CONFIG_SETTER(cmd_passenger_socket_backlog, socketBacklog, unsigned int, 0)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_file_descriptor_log_file, fileDescriptorLogFile)
DEFINE_SERVER_INT_CONFIG_SETTER(cmd_passenger_max_pool_size, maxPoolSize, unsigned int, 1)
DEFINE_SERVER_INT_CONFIG_SETTER(cmd_passenger_pool_idle_time, poolIdleTime, unsigned int, 0)
DEFINE_SERVER_INT_CONFIG_SETTER(cmd_passenger_response_buffer_high_watermark, responseBufferHighWatermark, unsigned int, 0)
DEFINE_SERVER_INT_CONFIG_SETTER(cmd_passenger_stat_throttle_rate, statThrottleRate, unsigned int, 0)
DEFINE_SERVER_BOOLEAN_CONFIG_SETTER(cmd_passenger_user_switching, userSwitching)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_default_user, defaultUser)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_default_group, defaultGroup)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_data_buffer_dir, dataBufferDir)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_instance_registry_dir, instanceRegistryDir)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_union_station_gateway_address, unionStationGatewayAddress)
DEFINE_SERVER_INT_CONFIG_SETTER(cmd_union_station_gateway_port, unionStationGatewayPort, int, 1)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_union_station_gateway_cert, unionStationGatewayCert)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_union_station_proxy_address, unionStationProxyAddress)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_analytics_log_user, analyticsLogUser)
DEFINE_SERVER_STR_CONFIG_SETTER(cmd_passenger_analytics_log_group, analyticsLogGroup)
DEFINE_SERVER_BOOLEAN_CONFIG_SETTER(cmd_passenger_turbocaching, turbocaching)

static const char *
cmd_passenger_ctl(cmd_parms *cmd, void *dummy, const char *name, const char *value) {
	serverConfig.ctl.set(name, value);
	return NULL;
}

static const char *
cmd_passenger_pre_start(cmd_parms *cmd, void *pcfg, const char *arg) {
	serverConfig.prestartURLs.insert(arg);
	return NULL;
}

#include "ConfigurationSetters.cpp"

DEFINE_DIR_STR_CONFIG_SETTER(cmd_passenger_app_root, appRoot)
DEFINE_DIR_STR_CONFIG_SETTER(cmd_union_station_key, unionStationKey)
DEFINE_DIR_THREEWAY_CONFIG_SETTER(cmd_passenger_resolve_symlinks_in_document_root, resolveSymlinksInDocRoot)
DEFINE_DIR_THREEWAY_CONFIG_SETTER(cmd_passenger_allow_encoded_slashes, allowEncodedSlashes)
DEFINE_DIR_THREEWAY_CONFIG_SETTER(cmd_union_station_support, unionStationSupport)
DEFINE_DIR_THREEWAY_CONFIG_SETTER(cmd_passenger_buffer_response, bufferResponse)

#ifndef PASSENGER_IS_ENTERPRISE
static const char *
cmd_passenger_enterprise_only(cmd_parms *cmd, void *pcfg, const char *arg) {
	return "this feature is only available in Phusion Passenger Enterprise. "
		"You are currently running the open source Phusion Passenger Enterprise. "
		"Please learn more about and/or buy Phusion Passenger Enterprise at https://www.phusionpassenger.com/enterprise";
}
#endif

static const char *
cmd_passenger_spawn_method(cmd_parms *cmd, void *pcfg, const char *arg) {
	DirConfig *config = (DirConfig *) pcfg;
	if (strcmp(arg, "smart") == 0 || strcmp(arg, "smart-lv2") == 0) {
		config->spawnMethod = "smart";
	} else if (strcmp(arg, "conservative") == 0 || strcmp(arg, "direct") == 0) {
		config->spawnMethod = "direct";
	} else {
		return "PassengerSpawnMethod may only be 'smart', 'direct'.";
	}
	return NULL;
}

static const char *
cmd_passenger_base_uri(cmd_parms *cmd, void *pcfg, const char *arg) {
	DirConfig *config = (DirConfig *) pcfg;
	if (strlen(arg) == 0) {
		return "PassengerBaseURI may not be set to the empty string";
	} else if (arg[0] != '/') {
		return "PassengerBaseURI must start with a slash (/)";
	} else if (strlen(arg) > 1 && arg[strlen(arg) - 1] == '/') {
		return "PassengerBaseURI must not end with a slash (/)";
	} else {
		config->baseURIs.insert(arg);
		return NULL;
	}
}

static const char *
cmd_union_station_filter(cmd_parms *cmd, void *pcfg, const char *arg) {
	DirConfig *config = (DirConfig *) pcfg;
	if (strlen(arg) == 0) {
		return "UnionStationFilter may not be set to the empty string";
	} else {
		try {
			FilterSupport::Filter f(arg);
			config->unionStationFilters.push_back(arg);
			return NULL;
		} catch (const SyntaxError &e) {
			string message = "Syntax error in Union Station filter: ";
			message.append(e.what());
			return strdup(message.c_str());
		}
	}
}


/*************************************************
 * Obsolete settings
 *************************************************/

static const char *
cmd_rails_spawn_server(cmd_parms *cmd, void *pcfg, const char *arg) {
	fprintf(stderr, "WARNING: The 'RailsSpawnServer' option is obsolete. "
		"Please specify 'PassengerRoot' instead. The correct value was "
		"given to you by 'passenger-install-apache2-module'.\n");
	fflush(stderr);
	return NULL;
}

static const char *
cmd_rails_allow_mod_rewrite(cmd_parms *cmd, void *pcfg, int arg) {
	fprintf(stderr, "WARNING: The 'RailsAllowModRewrite' option is obsolete: "
		"Phusion Passenger now fully supports mod_rewrite. "
		"Please remove this option from your configuration file.\n");
	fflush(stderr);
	return NULL;
}

static const char *
cmd_rails_framework_spawner_idle_time(cmd_parms *cmd, void *pcfg, const char *arg) {
	fprintf(stderr, "WARNING: The 'RailsFrameworkSpawnerIdleTime' option is obsolete. "
		"Please use 'PassengerMaxPreloaderIdleTime' instead.\n");
	fflush(stderr);
	return NULL;
}

static const char *
cmd_passenger_use_global_queue(cmd_parms *cmd, void *pcfg, int arg) {
	fprintf(stderr, "WARNING: The 'PassengerUseGlobalQueue' option is obsolete: "
		"global queueing is now always turned on. "
		"Please remove this option from your configuration file.\n");
	fflush(stderr);
	return NULL;
}


typedef const char * (*Take1Func)();
typedef const char * (*Take2Func)();
typedef const char * (*FlagFunc)();

const command_rec passenger_commands[] = {
	// Passenger settings.
	AP_INIT_TAKE1("PassengerRoot",
		(Take1Func) cmd_passenger_root,
		NULL,
		RSRC_CONF,
		"The Passenger root folder."),
	AP_INIT_TAKE2("PassengerCtl",
		(Take2Func) cmd_passenger_ctl,
		NULL,
		RSRC_CONF,
		"Set advanced options."),
	AP_INIT_TAKE1("PassengerDefaultRuby",
		(Take1Func) cmd_passenger_default_ruby,
		NULL,
		RSRC_CONF,
		"The default Ruby interpreter to use."),
	AP_INIT_TAKE1("PassengerLogLevel",
		(Take1Func) cmd_passenger_log_level,
		NULL,
		RSRC_CONF,
		"Passenger log verbosity."),
	AP_INIT_TAKE1("PassengerLogFile",
		(Take1Func) cmd_passenger_log_file,
		NULL,
		RSRC_CONF,
		"Passenger log file."),
	AP_INIT_TAKE1("PassengerSocketBacklog",
		(Take1Func) cmd_passenger_socket_backlog,
		NULL,
		RSRC_CONF,
		"Override size of the socket backlog."),
	AP_INIT_TAKE1("PassengerFileDescriptorLogFile",
		(Take1Func) cmd_passenger_file_descriptor_log_file,
		NULL,
		RSRC_CONF,
		"Passenger file descriptor log file."),
	AP_INIT_TAKE1("PassengerMaxPoolSize",
		(Take1Func) cmd_passenger_max_pool_size,
		NULL,
		RSRC_CONF,
		"The maximum number of simultaneously alive application instances."),
	AP_INIT_TAKE1("PassengerPoolIdleTime",
		(Take1Func) cmd_passenger_pool_idle_time,
		NULL,
		RSRC_CONF,
		"The maximum number of seconds that an application may be idle before it gets terminated."),
	AP_INIT_TAKE1("PassengerResponseBufferHighWatermark",
		(Take1Func) cmd_passenger_response_buffer_high_watermark,
		NULL,
		RSRC_CONF,
		"The maximum size of the response buffer."),
	AP_INIT_FLAG("PassengerUserSwitching",
		(FlagFunc) cmd_passenger_user_switching,
		NULL,
		RSRC_CONF,
		"Whether to enable user switching support."),
	AP_INIT_TAKE1("PassengerDefaultUser",
		(Take1Func) cmd_passenger_default_user,
		NULL,
		RSRC_CONF,
		"The user that Ruby applications must run as when user switching fails or is disabled."),
	AP_INIT_TAKE1("PassengerDefaultGroup",
		(Take1Func) cmd_passenger_default_group,
		NULL,
		RSRC_CONF,
		"The group that Ruby applications must run as when user switching fails or is disabled."),
	AP_INIT_TAKE1("PassengerDataBufferDir",
		(Take1Func) cmd_passenger_data_buffer_dir,
		NULL,
		RSRC_CONF,
		"The directory that data buffers should be stored into."),
	AP_INIT_TAKE1("PassengerInstanceRegistryDir",
		(Take1Func) cmd_passenger_instance_registry_dir,
		NULL,
		RSRC_CONF,
		"The directory to register the instance to."),
	AP_INIT_TAKE1("PassengerMaxPreloaderIdleTime",
		(Take1Func) cmd_passenger_max_preloader_idle_time,
		NULL,
		RSRC_CONF,
		"The maximum number of seconds that a preloader process may be idle before it is shutdown."),
	AP_INIT_TAKE1("PassengerStatThrottleRate",
		(Take1Func) cmd_passenger_stat_throttle_rate,
		NULL,
		RSRC_CONF,
		"Limit the number of stat calls to once per given seconds."),
	AP_INIT_TAKE1("UnionStationGatewayAddress",
		(Take1Func) cmd_union_station_gateway_address,
		NULL,
		RSRC_CONF,
		"The Union Station Gateway host name."),
	AP_INIT_TAKE1("UnionStationGatewayPort",
		(Take1Func) cmd_union_station_gateway_port,
		NULL,
		RSRC_CONF,
		"The Union Station Gateway port number."),
	AP_INIT_TAKE1("UnionStationGatewayCert",
		(Take1Func) cmd_union_station_gateway_cert,
		NULL,
		RSRC_CONF,
		"The Union Station Gateway certificate."),
	AP_INIT_TAKE1("UnionStationProxyAddress",
		(Take1Func) cmd_union_station_proxy_address,
		NULL,
		RSRC_CONF,
		"The address of the proxy that should be used for sending data to Union Station."),
	AP_INIT_TAKE1("PassengerAnalyticsLogUser",
		(Take1Func) cmd_passenger_analytics_log_user,
		NULL,
		RSRC_CONF,
		"The owner of analytics files."),
	AP_INIT_TAKE1("PassengerAnalyticsLogGroup",
		(Take1Func) cmd_passenger_analytics_log_group,
		NULL,
		RSRC_CONF,
		"The group of analytics files."),
	AP_INIT_TAKE1("PassengerPreStart",
		(Take1Func) cmd_passenger_pre_start,
		NULL,
		RSRC_CONF,
		"Prestart the given web applications during startup."),
	AP_INIT_FLAG("PassengerTurbocaching",
		(FlagFunc) cmd_passenger_turbocaching,
		NULL,
		RSRC_CONF,
		"Whether to enable turbocaching."),

	#include "ConfigurationCommands.cpp"

	AP_INIT_TAKE1("PassengerAppRoot",
		(Take1Func) cmd_passenger_app_root,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"The application's root directory."),
	AP_INIT_TAKE1("UnionStationKey",
		(Take1Func) cmd_union_station_key,
		NULL,
		OR_ALL,
		"The Union Station key."),
	AP_INIT_TAKE1("UnionStationFilter",
		(Take1Func) cmd_union_station_filter,
		NULL,
		OR_ALL,
		"A filter for Union Station data."),
	AP_INIT_FLAG("PassengerBufferResponse",
		(FlagFunc) cmd_passenger_buffer_response,
		NULL,
		OR_ALL,
		"Whether to enable buffering response."),
	AP_INIT_FLAG("PassengerResolveSymlinksInDocumentRoot",
		(FlagFunc) cmd_passenger_resolve_symlinks_in_document_root,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Whether to resolve symlinks in the DocumentRoot path"),
	AP_INIT_FLAG("PassengerAllowEncodedSlashes",
		(FlagFunc) cmd_passenger_allow_encoded_slashes,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Whether to support encoded slashes in the URL"),
	AP_INIT_TAKE1("PassengerBaseURI",
		(Take1Func) cmd_passenger_base_uri,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Declare the given base URI as belonging to an application."),
	AP_INIT_FLAG("UnionStationSupport",
		(Take1Func) cmd_union_station_support,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Whether to enable logging through Union Station."),

	/*****************************/
	AP_INIT_TAKE1("PassengerMemoryLimit",
		(Take1Func) cmd_passenger_enterprise_only,
		NULL,
		OR_LIMIT | ACCESS_CONF | RSRC_CONF,
		"The maximum amount of memory in MB that an application instance may use."),
	AP_INIT_TAKE1("PassengerMaxInstances",
		(Take1Func) cmd_passenger_enterprise_only,
		NULL,
		OR_LIMIT | ACCESS_CONF | RSRC_CONF,
		"The maximum number of instances for the current application that Passenger may spawn."),
	AP_INIT_TAKE1("PassengerMaxRequestTime",
		(Take1Func) cmd_passenger_enterprise_only,
		NULL,
		OR_ALL,
		"The maximum time (in seconds) that the current application may spend on a request."),
	AP_INIT_FLAG("PassengerRollingRestarts",
		(FlagFunc) cmd_passenger_enterprise_only,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Whether to turn on rolling restarts"),
	AP_INIT_FLAG("PassengerResistDeploymentErrors",
		(FlagFunc) cmd_passenger_enterprise_only,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Whether to turn on deployment error resistance"),
	AP_INIT_FLAG("PassengerDebugger",
		(FlagFunc) cmd_passenger_enterprise_only,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Whether to turn on debugger support"),
	AP_INIT_TAKE1("PassengerConcurrencyModel",
		(Take1Func) cmd_passenger_enterprise_only,
		NULL,
		OR_ALL,
		"The concurrency model that should be used for applications."),
	AP_INIT_TAKE1("PassengerThreadCount",
		(Take1Func) cmd_passenger_enterprise_only,
		NULL,
		OR_ALL,
		"The number of threads that Phusion Passenger should spawn per application."),

	// Backwards compatibility options.
	AP_INIT_TAKE1("PassengerDebugLogFile",
		(Take1Func) cmd_passenger_log_file,
		NULL,
		RSRC_CONF,
		"Passenger log file."),
	AP_INIT_TAKE1("RailsRuby",
		(Take1Func) cmd_passenger_ruby,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Deprecated option."),
	AP_INIT_TAKE1("RailsMaxPoolSize",
		(Take1Func) cmd_passenger_max_pool_size,
		NULL,
		RSRC_CONF,
		"Deprecated option."),
	AP_INIT_TAKE1("RailsMaxInstancesPerApp",
		(Take1Func) cmd_passenger_max_instances_per_app,
		NULL,
		RSRC_CONF,
		"Deprecated option"),
	AP_INIT_TAKE1("RailsPoolIdleTime",
		(Take1Func) cmd_passenger_pool_idle_time,
		NULL,
		RSRC_CONF,
		"Deprecated option."),
	AP_INIT_FLAG("RailsUserSwitching",
		(FlagFunc) cmd_passenger_user_switching,
		NULL,
		RSRC_CONF,
		"Deprecated option."),
	AP_INIT_TAKE1("RailsDefaultUser",
		(Take1Func) cmd_passenger_default_user,
		NULL,
		RSRC_CONF,
		"Deprecated option."),
	AP_INIT_TAKE1("RailsAppSpawnerIdleTime",
		(Take1Func) cmd_passenger_max_preloader_idle_time,
		NULL,
		RSRC_CONF,
		"Deprecated option."),
	AP_INIT_TAKE1("RailsBaseURI",
		(Take1Func) cmd_passenger_base_uri,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Deprecated option."),
	AP_INIT_TAKE1("RackBaseURI",
		(Take1Func) cmd_passenger_base_uri,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Deprecated option."),

	// Obsolete options.
	AP_INIT_TAKE1("RailsSpawnServer",
		(Take1Func) cmd_rails_spawn_server,
		NULL,
		RSRC_CONF,
		"Obsolete option."),
	AP_INIT_FLAG("RailsAllowModRewrite",
		(FlagFunc) cmd_rails_allow_mod_rewrite,
		NULL,
		RSRC_CONF,
		"Obsolete option."),
	AP_INIT_TAKE1("RailsFrameworkSpawnerIdleTime",
		(Take1Func) cmd_rails_framework_spawner_idle_time,
		NULL,
		RSRC_CONF,
		"Obsolete option."),
	AP_INIT_FLAG("PassengerUseGlobalQueue",
		(FlagFunc) cmd_passenger_use_global_queue,
		NULL,
		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
		"Obsolete option."),

	{ NULL }
};

} // extern "C"
