|
/**
|
|
* These are classes to automate the filling of what would be
|
|
* the plugin_config and plugin_data structures if we were using
|
|
* C, making the following valid (or close to valid):
|
|
* struct mod_blank
|
|
* {
|
|
* mod_blank( ... ) : hostname( "some-config-tag" [, &validator [, &default_setter] ] ) { ... }
|
|
* std::string& default_setter( std::string& s ){ ... return std::string( "asdf" ); }
|
|
* config_option< std::string > hostname;
|
|
* };
|
|
*
|
|
* So a config options type is specified at runtime, and we can add new types
|
|
* sit on top of the lighttpd ones. i.e. we could have a network mask type,
|
|
* getting options from T_CONFIG_STRING and that checks for the correct formatting.
|
|
* If incorrect we could throw an exception to be caught by the set_defaults
|
|
* handler, or we could set it to the value returned by default_setter.
|
|
*/
|
|
|
|
#ifndef _LIGHTTPD_DATATYPE_HELPERS_HPP_
|
|
#define _LIGHTTPD_DATATYPE_HELPERS_HPP_
|
|
|
|
#include <vector>
|
|
#include <algorithm>
|
|
#include <string>
|
|
|
|
#include "c++-compat/base.h"
|
|
#include "c++-compat/plugin.h"
|
|
|
|
// Something for all config_options to have in common.
|
|
// From here we can call set_defaults for all config options
|
|
// using the set_defaults static function.
|
|
struct config_option_base
|
|
{
|
|
typedef std::vector< config_option_base* > registry_type;
|
|
typedef registry_type::iterator registry_iterator;
|
|
typedef registry_type::const_iterator registry_const_iterator;
|
|
|
|
config_option_base( const char* key ) : key( key )
|
|
{
|
|
registry.push_back( this );
|
|
}
|
|
|
|
~config_option_base( )
|
|
{
|
|
registry.erase( std::find( registry.begin(), registry.end(), this ) );
|
|
}
|
|
|
|
virtual handler_t set_defaults( const server& srv ) = 0;
|
|
virtual handler_t set_defaults( ) = 0;
|
|
|
|
static handler_t set_all_defaults( )
|
|
{
|
|
for( registry_iterator i = registry.begin( ); i != registry.end( ); ++i )
|
|
{
|
|
if( (*i)->set_defaults( ) != HANDLER_ERROR ) continue;
|
|
return HANDLER_ERROR;
|
|
}
|
|
return HANDLER_GO_ON;
|
|
}
|
|
|
|
const char* key;
|
|
const server* srv;
|
|
|
|
static registry_type registry;
|
|
};
|
|
|
|
// Careful that we only get one of these per module.
|
|
// i.e. only one translation unit.
|
|
config_option_base::registry_type config_option_base::registry;
|
|
|
|
// Traits for the config_values_type_t enum values from lighttpd base.h
|
|
// These traits classes specify how the types should be initialized.
|
|
template < std::size_t ConfigValuesType >
|
|
struct config_values_traits
|
|
{};
|
|
|
|
// Keep a record of the T_CONFIG_* value in this base type
|
|
template < std::size_t ConfigValuesType >
|
|
struct config_values_traits_base
|
|
{
|
|
static const config_values_type_t config_values_type;
|
|
};
|
|
|
|
template < std::size_t ConfigValuesType >
|
|
const config_values_type_t
|
|
config_values_traits_base< ConfigValuesType >::config_values_type( ConfigValuesType );
|
|
|
|
// derive from this if we just use the default constructor
|
|
// Its annoying that we have to re-typedef things in derived
|
|
// classes but it seems that we do in gcc. I haven't checked the
|
|
// standard on this. If it is a standard, then it enables overloading of
|
|
// types to an extent.
|
|
template < typename Type, std::size_t ConfigValuesType >
|
|
struct config_values_traits_default
|
|
: config_values_traits_base< ConfigValuesType >
|
|
{
|
|
typedef Type value_type;
|
|
|
|
struct initializer
|
|
{
|
|
typedef value_type result;
|
|
static result* act( )
|
|
{
|
|
return new result( );
|
|
}
|
|
};
|
|
|
|
struct cleanup
|
|
{
|
|
typedef bool result;
|
|
static result act( value_type* pvalue )
|
|
{
|
|
delete pvalue;
|
|
return true;
|
|
}
|
|
};
|
|
};
|
|
|
|
// We need a traits class for each of these types:
|
|
// typedef enum { T_CONFIG_UNSET,
|
|
// T_CONFIG_STRING,
|
|
// T_CONFIG_SHORT,
|
|
// T_CONFIG_INT,
|
|
// T_CONFIG_BOOLEAN,
|
|
// T_CONFIG_ARRAY,
|
|
// T_CONFIG_LOCAL,
|
|
// T_CONFIG_DEPRECATED,
|
|
// T_CONFIG_UNSUPPORTED
|
|
//} config_values_type_t;
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_STRING >
|
|
: config_values_traits_base< T_CONFIG_STRING >
|
|
{
|
|
typedef buffer value_type;
|
|
|
|
struct initializer
|
|
{
|
|
typedef value_type result;
|
|
static result* act( )
|
|
{
|
|
return buffer_init( );
|
|
}
|
|
};
|
|
|
|
struct cleanup
|
|
{
|
|
typedef bool result;
|
|
static result act( value_type* pvalue )
|
|
{
|
|
buffer_free( pvalue );
|
|
return true;
|
|
}
|
|
};
|
|
};
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_SHORT >
|
|
: config_values_traits_default< short, T_CONFIG_SHORT > {};
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_INT >
|
|
: config_values_traits_default< int, T_CONFIG_INT >
|
|
{
|
|
typedef config_values_traits_default< int, T_CONFIG_INT > super_type;
|
|
typedef super_type::initializer initializer;
|
|
typedef super_type::cleanup cleanup;
|
|
};
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_BOOLEAN >
|
|
: config_values_traits_default< unsigned short, T_CONFIG_BOOLEAN > {};
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_ARRAY >
|
|
: config_values_traits_base< T_CONFIG_ARRAY >
|
|
{
|
|
typedef array value_type;
|
|
|
|
struct initializer
|
|
{
|
|
typedef value_type result;
|
|
|
|
static result* act( )
|
|
{
|
|
return array_init( );
|
|
}
|
|
};
|
|
|
|
struct cleanup
|
|
{
|
|
typedef bool result;
|
|
|
|
static result act( value_type* pvalue )
|
|
{
|
|
array_free( pvalue );
|
|
return true;
|
|
}
|
|
};
|
|
};
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_LOCAL >
|
|
{
|
|
};
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_DEPRECATED >
|
|
{
|
|
};
|
|
|
|
template <>
|
|
struct config_values_traits< T_CONFIG_UNSUPPORTED >
|
|
{
|
|
};
|
|
|
|
// The traits class gives us a mapping from OptionType to the associated
|
|
// lighttpd enum T_CONFIG_* values. Using this we can select a method of
|
|
// conversion from buffer/array/etc to our OptionType.
|
|
// OptionType must have a constructor that takes i.e. buf->ptr or array->data etc
|
|
template < typename OptionType, std::size_t ConfigValueType >
|
|
struct config_option_traits_base
|
|
{
|
|
static const config_values_type_t value_enum;
|
|
typedef config_values_traits< ConfigValueType > values_type_traits;
|
|
typedef typename values_type_traits::value_type value_type;
|
|
typedef OptionType option_type;
|
|
|
|
// A default initializer. We can do this in gcc, not sure if it
|
|
// is standard.
|
|
struct initializer
|
|
{
|
|
typedef option_type result;
|
|
static result* act( const value_type* pvalue )
|
|
{
|
|
return new option_type;
|
|
}
|
|
};
|
|
};
|
|
|
|
template < typename OptionType, std::size_t ConfigValueType >
|
|
const config_values_type_t config_option_traits_base< OptionType, ConfigValueType >
|
|
::value_enum( static_cast< config_values_type_t >( ConfigValueType ) );
|
|
|
|
template < typename OptionType >
|
|
struct config_option_traits
|
|
{};
|
|
|
|
// Configure some default types
|
|
template <>
|
|
struct config_option_traits< std::string > : config_option_traits_base< std::string, T_CONFIG_STRING >
|
|
{
|
|
// templating seems to mean we have to pull typedefs down
|
|
typedef config_option_traits_base< std::string, T_CONFIG_STRING > super_type;
|
|
typedef super_type::value_type value_type;
|
|
typedef super_type::values_type_traits values_type_traits;
|
|
typedef std::string option_type;
|
|
|
|
struct initializer
|
|
{
|
|
typedef option_type result;
|
|
static result* act( const value_type* buf )
|
|
{
|
|
return new option_type( buf->ptr );
|
|
}
|
|
};
|
|
};
|
|
|
|
template <>
|
|
struct config_option_traits< int > : config_option_traits_base< int, T_CONFIG_INT >
|
|
{
|
|
typedef config_option_traits_base< int, T_CONFIG_INT > super_type;
|
|
typedef super_type::initializer initializer;
|
|
};
|
|
|
|
template <>
|
|
struct config_option_traits< short > : config_option_traits_base< short, T_CONFIG_SHORT >
|
|
{
|
|
typedef config_option_traits_base< short, T_CONFIG_SHORT > super_type;
|
|
typedef super_type::initializer initializer;
|
|
};
|
|
|
|
template <>
|
|
struct config_option_traits< bool > : config_option_traits_base< bool, T_CONFIG_BOOLEAN >
|
|
{
|
|
typedef config_option_traits_base< bool, T_CONFIG_BOOLEAN > super_type;
|
|
typedef super_type::initializer initializer;
|
|
};
|
|
|
|
// For now I'll just allow vectors of string. A boost::any would be nice here maybe.
|
|
// I'd like to try and stay in with the people are thinking I'm nuts including boost
|
|
// in here, but I think I'm already through the looking glass so I'll probably end up
|
|
// going all out with the templating and boost inclusion.
|
|
template <>
|
|
struct config_option_traits< std::vector< std::string > >
|
|
: config_option_traits_base< std::vector< std::string >, T_CONFIG_ARRAY >
|
|
{};
|
|
|
|
// The config_option structures deal with condition decisions so we can write
|
|
// option[ con ] where option is a L(config_option< SomeType >), con is a L(connection)
|
|
// and get back the appropriate L(OptionType) option from the "defaults" memeber data.
|
|
template < typename OptionType,
|
|
std::size_t ConfigScopeType = T_CONFIG_SCOPE_CONNECTION,
|
|
typename OptionTraits = config_option_traits< OptionType > >
|
|
struct config_option : public config_option_base
|
|
{
|
|
typedef std::vector< const OptionType* > defaults_list_type;
|
|
typedef typename defaults_list_type::const_iterator defaults_iterator;
|
|
typedef OptionTraits option_traits;
|
|
typedef typename option_traits::values_type_traits values_type_traits;
|
|
|
|
typedef bool (*validator_type)( const OptionType& );
|
|
typedef bool (*defaults_setter_type)( OptionType& );
|
|
|
|
config_option( const char* key,
|
|
validator_type val = 0,
|
|
defaults_setter_type def = 0 )
|
|
: config_option_base( key )
|
|
{ }
|
|
|
|
virtual handler_t set_defaults( )
|
|
{
|
|
return set_defaults( *srv );
|
|
}
|
|
|
|
virtual handler_t set_defaults( const server& s )
|
|
{
|
|
// initializer defines the method that the lighttpd
|
|
// config type should be created with, before it is
|
|
// passed to config_insert_values_global.
|
|
typedef typename values_type_traits::initializer initializer;
|
|
typedef typename initializer::result initializer_result_type;
|
|
|
|
typedef typename values_type_traits::cleanup cleanup;
|
|
typedef typename cleanup::result cleanup_result_type;
|
|
|
|
// next be have information how the data structure created
|
|
// with the above should be converted to our chosen option type.
|
|
typedef typename option_traits::initializer option_initializer;
|
|
|
|
srv = &s;
|
|
config_values_t cv[] = {
|
|
{ key, NULL, option_traits::value_enum, static_cast< config_scope_type_t >( ConfigScopeType ) },
|
|
{ NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
|
|
};
|
|
|
|
for( std::size_t i = 0; i < s.config_context->used; i++ )
|
|
{
|
|
initializer_result_type* res = initializer::act();
|
|
cv[0].destination = reinterpret_cast< void* >( res );
|
|
|
|
if ( 0 != config_insert_values_global( const_cast< server* >( srv ),
|
|
((data_config *)srv->config_context->data[i])->value, cv) )
|
|
{
|
|
return HANDLER_ERROR;
|
|
}
|
|
|
|
// Initialize option, but don't take control of the
|
|
// res memory allocation ( i.e. just make a copy and leave as is )
|
|
OptionType* option = option_initializer::act( res );
|
|
if( ( validator && defaults_setter ) && !validator( *option ) )
|
|
defaults_setter( *option );
|
|
defaults.push_back( option );
|
|
|
|
// Although we don't do anything with the result,
|
|
// I'll keep the option available
|
|
cleanup_result_type cleanup_result = cleanup::act( res );
|
|
}
|
|
|
|
return HANDLER_GO_ON;
|
|
}
|
|
|
|
// Return the appropriate value for the options, depending on the
|
|
// server and connection.
|
|
const OptionType& operator[]( const connection& con ) const
|
|
{
|
|
// Lets start with the global context. Front must exist.
|
|
// Plus I'd hope that config_context->used and the size of
|
|
// defaults are the same.
|
|
defaults_iterator di = defaults.begin();
|
|
const OptionType* option = *di;
|
|
const data_config** dc = ++((data_config **)srv->config_context->data);
|
|
|
|
// skip the first, the global context
|
|
while( di != defaults.end() )
|
|
{
|
|
// condition match
|
|
if( config_check_cond( srv, &con, *dc ) )
|
|
{
|
|
// merge config
|
|
data_unset **du = (*dc)->value->data;
|
|
data_unset **du_end = du + (*dc)->value->used;
|
|
while( du++ != du_end )
|
|
{
|
|
if ( buffer_is_equal_string( (*du)->key, CONST_STR_LEN(key) ) )
|
|
{
|
|
option = *di;
|
|
}
|
|
}
|
|
}
|
|
|
|
++dc; ++di;
|
|
}
|
|
|
|
return *option;
|
|
}
|
|
|
|
validator_type validator;
|
|
defaults_setter_type defaults_setter;
|
|
defaults_list_type defaults;
|
|
|
|
static const config_scope_type_t config_scope;
|
|
};
|
|
|
|
// Record what type of config we are
|
|
template < typename OptionType, std::size_t ConfigScopeType, typename OptionTraits >
|
|
const config_scope_type_t config_option< OptionType, ConfigScopeType, OptionTraits >
|
|
::config_scope( static_cast< config_scope_type_t >( ConfigScopeType ) );
|
|
|
|
#endif // _LIGHTTPD_DATATYPE_HELPERS_HPP_
|
|
|
|
|