Preview: ManifestGeneration.h
Size: 22.29 KB
//opt/cpanel/ea-ruby27/src/passenger-release-6.1.2/src/apache2_module/ConfigGeneral/ManifestGeneration.h
/*
* Phusion Passenger - https://www.phusionpassenger.com/
* Copyright (c) 2017-2025 Asynchronous B.V.
*
* "Passenger", "Phusion Passenger" and "Union Station" are registered
* trademarks of Asynchronous 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.
*/
#ifndef _PASSENGER_APACHE2_MODULE_CONFIG_GENERAL_MANIFEST_GENERATION_H_
#define _PASSENGER_APACHE2_MODULE_CONFIG_GENERAL_MANIFEST_GENERATION_H_
#include <string>
#include <cstddef>
#include <string>
#include <boost/function.hpp>
#include <jsoncpp/json.h>
#include "../Config.h"
#include "../Utils.h"
#include <StaticString.h>
#include <FileTools/PathManip.h>
// The APR headers must come after the Passenger headers.
// See Hooks.cpp to learn why.
#include <httpd.h>
#include <http_config.h>
// In Apache < 2.4, this macro was necessary for core_dir_config and other structs
#define CORE_PRIVATE
#include <http_core.h>
extern "C" module AP_MODULE_DECLARE_DATA passenger_module;
#ifndef ap_get_core_module_config
#define ap_get_core_module_config(s) ap_get_module_config(s, &core_module)
#endif
namespace Passenger {
namespace Apache2Module {
using namespace std;
class ConfigManifestGenerator {
private:
Json::Value manifest;
server_rec *serverRec;
apr_pool_t *pool;
void autoGenerated_generateConfigManifestForServerConfig();
void autoGenerated_generateConfigManifestForDirConfig(server_rec *serverRec,
core_server_config *csconf,core_dir_config *cdconf, DirConfig *pdconf,
DirConfigContext context);
void autoGenerated_setGlobalConfigDefaults();
void autoGenerated_setAppConfigDefaults();
void autoGenerated_setLocationConfigDefaults();
void processDirConfig(server_rec *serverRec, core_server_config *csconf,
core_dir_config *cdconf, DirConfig *pdconf, DirConfigContext context)
{
if (pdconf->getEnabled()) {
autoGenerated_generateConfigManifestForDirConfig(
serverRec, csconf, cdconf, pdconf, context);
}
}
void findOrCreateAppAndLocOptionsContainers(server_rec *serverRec,
core_server_config *csconf, core_dir_config *cdconf,
DirConfig *pdconf, DirConfigContext context,
Json::Value **appOptionsContainer, Json::Value **locOptionsContainer)
{
if (*appOptionsContainer != NULL && *locOptionsContainer != NULL) {
return;
}
if (context == DCC_GLOBAL) {
*appOptionsContainer = &manifest["default_application_configuration"];
*locOptionsContainer = &manifest["default_location_configuration"];
} else if (context == DCC_VHOST) {
string appGroupName = inferLocConfAppGroupName(csconf, pdconf);
Json::Value &appConfigContainer = findOrCreateAppConfigContainer(appGroupName);
*appOptionsContainer = &appConfigContainer["options"];
*locOptionsContainer = &appConfigContainer["default_location_configuration"];
// Create a default value for PassengerAppGroupName and
// PassengerAppRoot if we just created this config container
if ((*appOptionsContainer)->empty()) {
addOptionsContainerInferredDefaultStr(**appOptionsContainer,
"PassengerAppGroupName",
appGroupName);
addOptionsContainerInferredDefaultStr(**appOptionsContainer,
"PassengerAppRoot",
inferDefaultAppRoot(csconf));
}
} else {
// We are inside a <Directory> or <Location>
string appGroupName = inferLocConfAppGroupName(csconf, pdconf);
Json::Value &appConfigContainer = findOrCreateAppConfigContainer(appGroupName);
Json::Value &locConfigContainer = findOrCreateLocConfigContainer(appConfigContainer,
serverRec, cdconf, pdconf);
*appOptionsContainer = &appConfigContainer["options"];
*locOptionsContainer = &locConfigContainer["options"];
}
}
string inferLocConfAppGroupName(core_server_config *csconf, DirConfig *pdconf) {
if (pdconf->getAppGroupName().empty()) {
string appRoot;
StaticString appEnv;
if (pdconf->getAppRoot().empty()) {
// ap_document_root is already relativized against
// the ServerRoot (see Apache server/core.c set_document_root())
appRoot = csconf->ap_document_root + P_STATIC_STRING("/..");
} else {
appRoot = ap_server_root_relative(pool, pdconf->getAppRoot().c_str());
}
appRoot = absolutizePath(appRoot);
if (pdconf->getAppEnv().empty()) {
appEnv = P_STATIC_STRING(DEFAULT_APP_ENV);
} else {
appEnv = pdconf->getAppEnv();
}
return appRoot + " (" + appEnv + ")";
} else {
return pdconf->getAppGroupName();
}
}
string inferDefaultAppRoot(core_server_config *csconf) {
return absolutizePath(csconf->ap_document_root + P_STATIC_STRING("/.."));
}
Json::Value &findOrCreateAppConfigContainer(const string &appGroupName) {
Json::Value &result = manifest["application_configurations"][appGroupName];
if (result.isNull()) {
result["options"] = Json::objectValue;
result["default_location_configuration"] = Json::objectValue;
result["location_configurations"] = Json::arrayValue;
}
return result;
}
Json::Value &findOrCreateLocConfigContainer(Json::Value &appConfigContainer,
server_rec *serverRec, core_dir_config *cdconf, DirConfig *pdconf)
{
Json::Value &locConfigsContainer = appConfigContainer["location_configurations"];
Json::Value *locConfigContainer = findLocConfigContainer(
locConfigsContainer, serverRec, cdconf, pdconf);
if (locConfigContainer == NULL) {
locConfigContainer = &createLocConfigContainer(locConfigsContainer,
serverRec, cdconf, pdconf);
}
return *locConfigContainer;
}
Json::Value *findLocConfigContainer(Json::Value &locConfigsContainer,
server_rec *serverRec, core_dir_config *cdconf, DirConfig *pdconf)
{
Json::Value::iterator it, end = locConfigsContainer.end();
for (it = locConfigsContainer.begin(); it != end; it++) {
Json::Value &locConfigContainer = *it;
Json::Value &locationMatcherDoc = locConfigContainer["location_matcher"];
string jsonLocationMatcherType = locationMatcherDoc["type"].asString();
if (cdconf->r != NULL) {
if (jsonLocationMatcherType != "regex") {
continue;
}
} else {
if (jsonLocationMatcherType != "prefix") {
continue;
}
}
string jsonLocationMatcherValue = locationMatcherDoc["value"].asString();
if (jsonLocationMatcherValue != cdconf->d) {
continue;
}
Json::Value &serverNamesDoc =
locConfigContainer["web_server_virtual_host"]["server_names"];
if (!matchesAnyServerNames(serverRec, serverNamesDoc)) {
continue;
}
return &locConfigContainer;
}
return NULL;
}
Json::Value &createLocConfigContainer(Json::Value &locConfigsContainer,
server_rec *serverRec, core_dir_config *cdconf, DirConfig *pdconf)
{
Json::Value vhostDoc;
if (serverRec->defn_name) {
vhostDoc["server_names"].append(serverRec->defn_name);
} else {
vhostDoc["server_names"].append("NOT_RECEIVED");
}
Json::Value locationMatcherDoc;
locationMatcherDoc["value"] = cdconf->d;
if (cdconf->r != NULL) {
locationMatcherDoc["type"] = "regex";
} else {
locationMatcherDoc["type"] = "prefix";
}
Json::Value locConfigContainer;
locConfigContainer["web_server_virtual_host"] = vhostDoc;
locConfigContainer["location_matcher"] = locationMatcherDoc;
locConfigContainer["options"] = Json::objectValue;
return locConfigsContainer.append(locConfigContainer);
}
bool matchesAnyServerNames(server_rec *serverRec, const Json::Value &serverNamesDoc) {
Json::Value::const_iterator it, end = serverNamesDoc.end();
for (it = serverNamesDoc.begin(); it != end; it++) {
// TODO: lowercase match
if (it->asString() == serverRec->defn_name) {
return true;
}
}
return false;
}
Json::Value &findOrCreateOptionContainer(Json::Value &optionsContainer,
const char *optionName, size_t optionNameLen)
{
Json::Value &result = optionsContainer[string(optionName, optionNameLen)];
if (result.isNull()) {
initOptionContainer(result);
}
return result;
}
void initOptionContainer(Json::Value &doc) {
doc["value_hierarchy"] = Json::arrayValue;
}
Json::Value &addOptionContainerHierarchyMember(Json::Value &optionContainer,
const StaticString &sourceFile, unsigned int sourceLine)
{
Json::Value hierarchyMember;
hierarchyMember["source"]["type"] = "web-server-config";
hierarchyMember["source"]["path"] = Json::Value(sourceFile.data(),
sourceFile.data() + sourceFile.size());
hierarchyMember["source"]["line"] = sourceLine;
return optionContainer["value_hierarchy"].append(hierarchyMember);
}
void reverseValueHierarchies() {
Json::Value &appConfigsContainer = manifest["application_configurations"];
Json::Value::iterator it, end = appConfigsContainer.end();
reverseValueHierarchiesInOptionsContainer(manifest["global_configuration"]);
reverseValueHierarchiesInOptionsContainer(manifest["default_application_configuration"]);
reverseValueHierarchiesInOptionsContainer(manifest["default_location_configuration"]);
for (it = appConfigsContainer.begin(); it != end; it++) {
Json::Value &appConfigContainer = *it;
reverseValueHierarchiesInOptionsContainer(
appConfigContainer["options"]);
reverseValueHierarchiesInOptionsContainer(
appConfigContainer["default_location_configuration"]);
if (appConfigContainer.isMember("location_configurations")) {
Json::Value &locationConfigsContainer = appConfigContainer["location_configurations"];
Json::Value::iterator it2, end2 = locationConfigsContainer.end();
for (it2 = locationConfigsContainer.begin(); it2 != end2; it2++) {
Json::Value &locationConfigContainer = *it2;
reverseValueHierarchiesInOptionsContainer(
locationConfigContainer["options"]);
}
}
}
}
void reverseValueHierarchiesInOptionsContainer(Json::Value &optionsContainer) {
Json::Value::iterator it, end = optionsContainer.end();
for (it = optionsContainer.begin(); it != end; it++) {
Json::Value &optionContainer = *it;
Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"];
unsigned int len = valueHierarchyDoc.size();
for (unsigned int i = 0; i < len / 2; i++) {
valueHierarchyDoc[i].swap(valueHierarchyDoc[len - i - 1]);
}
}
}
void inheritApplicationValueHierarchies() {
Json::Value &appConfigsContainer = manifest["application_configurations"];
Json::Value &defaultAppConfigContainer = manifest["default_application_configuration"];
Json::Value::iterator it, end = appConfigsContainer.end();
/* Iterate through all 'application_configurations' objects */
for (it = appConfigsContainer.begin(); it != end; it++) {
Json::Value &appConfigContainer = *it;
Json::Value::iterator it2, end2;
/* Iterate through all its 'options' objects */
Json::Value &optionsContainer = appConfigContainer["options"];
end2 = optionsContainer.end();
for (it2 = optionsContainer.begin(); it2 != end2; it2++) {
/* For each option, inherit the value hierarchies
* from the 'default_application_configuration' object.
*
* Since the value hierarchy array is already in
* most-to-least-specific order, simply appending
* the 'default_application_configuration' hierarchy is
* enough.
*/
const char *optionNameEnd;
const char *optionName = it2.memberName(&optionNameEnd);
if (defaultAppConfigContainer.isMember(optionName, optionNameEnd)) {
Json::Value &optionContainer = *it2;
Json::Value &defaultAppConfig = defaultAppConfigContainer[optionName];
Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"];
Json::Value &valueHierarchyFromDefault = defaultAppConfig["value_hierarchy"];
jsonAppendValues(valueHierarchyDoc, valueHierarchyFromDefault);
maybeInheritStringArrayHierarchyValues(valueHierarchyDoc);
maybeInheritStringKeyvalHierarchyValues(valueHierarchyDoc);
}
}
/* Iterate through all 'default_application_configuration' options */
end2 = defaultAppConfigContainer.end();
for (it2 = defaultAppConfigContainer.begin(); it2 != end2; it2++) {
/* For each default app config object, if there is no object in
* the current context's 'options' with the same name, then add
* it there.
*/
const char *optionNameEnd;
const char *optionName = it2.memberName(&optionNameEnd);
if (!optionsContainer.isMember(optionName, optionNameEnd)) {
Json::Value &optionContainer = *it2;
optionsContainer[optionName] = optionContainer;
}
}
}
}
void maybeInheritStringArrayHierarchyValues(Json::Value &valueHierarchyDoc) {
if (valueHierarchyDoc.empty()) {
return;
}
if (!valueHierarchyDoc[0]["value"].isArray()) {
return;
}
unsigned int len = valueHierarchyDoc.size();
for (unsigned int i = len - 1; i >= 1; i--) {
Json::Value ¤t = valueHierarchyDoc[i];
Json::Value &next = valueHierarchyDoc[i - 1];
Json::Value ¤tValue = current["value"];
Json::Value &nextValue = next["value"];
Json::Value::iterator it, end = currentValue.end();
for (it = currentValue.begin(); it != end; it++) {
if (!jsonArrayContains(nextValue, *it)) {
nextValue.append(*it);
}
}
}
}
void maybeInheritStringKeyvalHierarchyValues(Json::Value &valueHierarchyDoc) {
if (valueHierarchyDoc.empty()) {
return;
}
if (!valueHierarchyDoc[0]["value"].isObject()) {
return;
}
unsigned int len = valueHierarchyDoc.size();
for (unsigned int i = len - 1; i >= 1; i--) {
Json::Value ¤t = valueHierarchyDoc[i];
Json::Value &next = valueHierarchyDoc[i - 1];
Json::Value ¤tValue = current["value"];
Json::Value &nextValue = next["value"];
Json::Value::iterator it, end = currentValue.end();
for (it = currentValue.begin(); it != end; it++) {
const char *nameEnd;
const char *name = it.memberName(&nameEnd);
if (!nextValue.isMember(name, nameEnd)) {
nextValue[name] = *it;
}
}
}
}
void inheritLocationValueHierarchies() {
Json::Value &appConfigsContainer = manifest["application_configurations"];
Json::Value &defaultLocConfigContainer = manifest["default_location_configuration"];
Json::Value::iterator it, end = appConfigsContainer.end();
/* Iterate through all 'application_configurations' objects */
for (it = appConfigsContainer.begin(); it != end; it++) {
Json::Value &appConfigContainer = *it;
Json::Value::iterator it2, end2;
/* Iterate through all its 'default_location_configuration' options */
Json::Value &appDefaultLocationConfigs = appConfigContainer[
"default_location_configuration"];
end2 = appDefaultLocationConfigs.end();
for (it2 = appDefaultLocationConfigs.begin(); it2 != end2; it2++) {
/* For each option, inherit the value hierarchies
* from the top-level 'default_application_configuration' object.
*
* Since the value hierarchy array is already in
* most-to-least-specific order, simply appending
* the 'default_application_configuration' hierarchy is
* enough.
*/
const char *optionNameEnd;
const char *optionName = it2.memberName(&optionNameEnd);
if (defaultLocConfigContainer.isMember(optionName, optionNameEnd)) {
Json::Value &optionContainer = *it2;
Json::Value &defaultLocationConfig = defaultLocConfigContainer[optionName];
Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"];
Json::Value &valueHierarchyFromDefault = defaultLocationConfig["value_hierarchy"];
jsonAppendValues(valueHierarchyDoc, valueHierarchyFromDefault);
maybeInheritStringArrayHierarchyValues(valueHierarchyDoc);
maybeInheritStringKeyvalHierarchyValues(valueHierarchyDoc);
}
}
/* Iterate through all top-level 'default_location_configuration' options */
end2 = defaultLocConfigContainer.end();
for (it2 = defaultLocConfigContainer.begin(); it2 != end2; it2++) {
/* For each default top-level 'default_location_configuration' option,
* if there is no object in the current context's 'default_application_configuration'
* with the same name, then add it there.
*/
const char *optionNameEnd;
const char *optionName = it2.memberName(&optionNameEnd);
if (!appDefaultLocationConfigs.isMember(optionName, optionNameEnd)) {
appDefaultLocationConfigs[optionName] = *it2;
}
}
/* Iterate through all its 'location_configurations' options */
if (appConfigContainer.isMember("location_configurations")) {
Json::Value &locationConfigsContainer = appConfigContainer["location_configurations"];
end2 = locationConfigsContainer.end();
for (it2 = locationConfigsContainer.begin(); it2 != end2; it2++) {
Json::Value &locationContainer = *it2;
Json::Value &optionsContainer = locationContainer["options"];
Json::Value::iterator it3, end3 = optionsContainer.end();
for (it3 = optionsContainer.begin(); it3 != end3; it3++) {
/* For each option, inherit the value hierarchies
* from the 'default_location_configuration' belonging
* to the current app (which also contains the global
* location config defaults).
*
* Since the value hierarchy array is already in
* most-to-least-specific order, simply appending
* the 'default_location_configuration' hierarchy is
* enough.
*/
const char *optionNameEnd;
const char *optionName = it3.memberName(&optionNameEnd);
if (appDefaultLocationConfigs.isMember(optionName, optionNameEnd)) {
Json::Value &optionContainer = *it3;
Json::Value &defaultLocationConfig = appDefaultLocationConfigs[optionName];
Json::Value &valueHierarchyDoc = optionContainer["value_hierarchy"];
Json::Value &valueHierarchyFromDefault = defaultLocationConfig["value_hierarchy"];
jsonAppendValues(valueHierarchyDoc, valueHierarchyFromDefault);
maybeInheritStringArrayHierarchyValues(valueHierarchyDoc);
maybeInheritStringKeyvalHierarchyValues(valueHierarchyDoc);
}
}
}
}
}
}
void addOptionsContainerDynamicDefault(Json::Value &optionsContainer,
const char *optionName, const StaticString &desc)
{
Json::Value &optionContainer = optionsContainer[optionName];
if (optionContainer.isNull()) {
initOptionContainer(optionContainer);
}
Json::Value hierarchyMember;
hierarchyMember["source"]["type"] = "dynamic-default-description";
hierarchyMember["value"] = Json::Value(desc.data(),
desc.data() + desc.size());
optionContainer["value_hierarchy"].append(hierarchyMember);
}
Json::Value &addOptionsContainerDefault(Json::Value &optionsContainer,
const char *defaultType, const char *optionName)
{
Json::Value &optionContainer = optionsContainer[optionName];
if (optionContainer.isNull()) {
initOptionContainer(optionContainer);
}
Json::Value hierarchyMember;
hierarchyMember["source"]["type"] = defaultType;
return optionContainer["value_hierarchy"].append(hierarchyMember);
}
void addOptionsContainerStaticDefaultStr(Json::Value &optionsContainer,
const char *optionName, const StaticString &value)
{
Json::Value &hierarchyMember = addOptionsContainerDefault(
optionsContainer, "default", optionName);
hierarchyMember["value"] = Json::Value(value.data(),
value.data() + value.size());
}
void addOptionsContainerStaticDefaultInt(Json::Value &optionsContainer,
const char *optionName, int value)
{
Json::Value &hierarchyMember = addOptionsContainerDefault(
optionsContainer, "default", optionName);
hierarchyMember["value"] = value;
}
void addOptionsContainerStaticDefaultBool(Json::Value &optionsContainer,
const char *optionName, bool value)
{
Json::Value &hierarchyMember = addOptionsContainerDefault(
optionsContainer, "default", optionName);
hierarchyMember["value"] = value;
}
void addOptionsContainerInferredDefaultStr(Json::Value &optionsContainer,
const char *optionName, const StaticString &value)
{
Json::Value &hierarchyMember = addOptionsContainerDefault(
optionsContainer, "inferred-default", optionName);
hierarchyMember["value"] = Json::Value(value.data(),
value.data() + value.size());
}
void jsonAppendValues(Json::Value &doc, const Json::Value &doc2) {
Json::Value::const_iterator it, end = doc2.end();
for (it = doc2.begin(); it != end; it++) {
doc.append(*it);
}
}
bool jsonArrayContains(const Json::Value &doc, const Json::Value &elem) {
Json::Value::const_iterator it, end = doc.end();
for (it = doc.begin(); it != end; it++) {
if (*it == elem) {
return true;
}
}
return false;
}
public:
ConfigManifestGenerator(server_rec *_serverRec, apr_pool_t *_pool)
: serverRec(_serverRec),
pool(_pool)
{
manifest["global_configuration"] = Json::objectValue;
manifest["default_application_configuration"] = Json::objectValue;
manifest["default_location_configuration"] = Json::objectValue;
manifest["application_configurations"] = Json::objectValue;
}
const Json::Value &execute() {
autoGenerated_generateConfigManifestForServerConfig();
traverseAllDirConfigs(serverRec, pool,
boost::bind<void>(&ConfigManifestGenerator::processDirConfig, this,
boost::placeholders::_1, boost::placeholders::_2,
boost::placeholders::_3, boost::placeholders::_4,
boost::placeholders::_5));
reverseValueHierarchies();
autoGenerated_setGlobalConfigDefaults();
autoGenerated_setAppConfigDefaults();
autoGenerated_setLocationConfigDefaults();
inheritApplicationValueHierarchies();
inheritLocationValueHierarchies();
return manifest;
}
};
} // namespace Apache2Module
} // namespace Passenger
#if !defined(INTELLISENSE)
#include "../ServerConfig/AutoGeneratedManifestGeneration.cpp"
#include "../DirConfig/AutoGeneratedManifestGeneration.cpp"
#include "AutoGeneratedManifestDefaultsInitialization.cpp"
#endif
#endif /* _PASSENGER_APACHE2_MODULE_CONFIG_GENERAL_MANIFEST_GENERATION_H_ */
Directory Contents
Dirs: 0 × Files: 9