diff options
Diffstat (limited to 'src/input/inputEventHandler.h')
-rw-r--r-- | src/input/inputEventHandler.h | 499 |
1 files changed, 301 insertions, 198 deletions
diff --git a/src/input/inputEventHandler.h b/src/input/inputEventHandler.h index 52e3338..783640f 100644 --- a/src/input/inputEventHandler.h +++ b/src/input/inputEventHandler.h @@ -22,274 +22,377 @@ #include <QList> #include <QString> #include <QCoreApplication> -#include <boost/mpl/contains.hpp> -#include <boost/mpl/if.hpp> -#include <boost/mpl/vector.hpp> #include <src/input/inputEvent.h> +#include "detail/policyChain.h" +#include "detail/systemTraits.h" -#define HANDLER_TYPE_DONT_CARE 0xffff -#define HANDLER_CODE_DONT_CARE 0xffff -#define HANDLER_VALUE_DONT_CARE 0xffffffff - -class InputEventContext -{ -public: - virtual pid_t getSenderPid() const = 0; - virtual uid_t getSenderUid() const = 0; - virtual gid_t getSenderGid() const = 0; -}; - -struct SpecialInputEventDescription +/** + * For handling access control, this specifies who sent the input event. + * This only really makes sense in the privileged input handler chain. + */ +struct InputEventContext { - SpecialInputEventDescription(QString const& d, quint16 t, quint16 c, quint32 v = 0) - : descriptionString(d), evtType(t), evtCode(c), evtValue(v) + InputEventContext() { + hasBeenDenied = false; } - QString descriptionString; - quint16 evtType; - quint16 evtCode; - quint32 evtValue; + virtual pid_t senderPid() const = 0; /**< PID of the sending process */ + virtual uid_t senderUid() const = 0; /**< UID of the sending process */ + virtual gid_t senderGid() const = 0; /**< GID of the sending process */ - InputEvent toEvent() const - { - return InputEvent(evtType, evtCode, evtValue); - } + /** Support the generation of meaningful log messages. */ + mutable bool hasBeenDenied; }; -template<quint16 Type = HANDLER_TYPE_DONT_CARE, - quint16 Code = HANDLER_CODE_DONT_CARE, - quint32 Value = HANDLER_VALUE_DONT_CARE> -class DefaultInputEventHandler { -protected: - static QString tr(char const* string) - { - return QCoreApplication::translate("InputEventHandler", string); - } - -public: - virtual bool matches(InputEvent const& evt, InputEventContext const*) { - if(Type != HANDLER_TYPE_DONT_CARE) { - if(evt.type() != Type) - return false; - } - if(Code != HANDLER_CODE_DONT_CARE) { - if(evt.code() != Code) - return false; - } - if(Value != HANDLER_VALUE_DONT_CARE) { - if(evt.value() != Value) - return false; - } - return true; - } - - virtual void initialize() - { - } - - virtual void handle(InputEvent const& evt, InputEventContext const*) = 0; +namespace input_policy +{ - static void describeInto(QList<SpecialInputEventDescription>& description) +///////////////////////////////////////////////////////////////////////// +// Policy: +// Modifies the behaviour of an input handler class. +// +// There are several kinds of policy: +// - Security Policy (When to allow a certain action) +// - System Requirements (When to enable a certain handler) +// - Applicability (When to consider a certain handler) +// +// Policies are tied together using the detail::PolicyChain class. +///////////////////////////////////////////////////////////////////////// + +/** + * Security Policy. + * At the moment there are two security policies: + * -* If the user is on a local seat, allow. If the user is privileged, + * allow. Else deny. + * -* Allow everybody. + * + * Additional security policies can be added by following the example + * set by \ref AllowLocalOrPrivileged + * + * \param PolicyImpl The implementation class that security decisions + * should be delegated to. + * \see {AllowLocalOrPrivileged} + * \see {AllowEverybody} + */ +template<typename PolicyImpl> +BEGIN_POLICY_CLASS(Security) +{ + bool allow(InputEventContext const* context) { + return PolicyImpl::allow(context); } -}; - -namespace policy { - -enum SecurityFlags { - SEC_FREE_FOR_ALL, - SEC_PHYSICAL_OR_PRIVILEGED -}; - -bool allowPhysicalSeat(InputEvent const& evt, InputEventContext const* ctx); -bool allowPrivilegedUser(InputEvent const& evt, InputEventContext const* ctx); +} +END_POLICY_CLASS -struct SecurityAllowAny +/** + * Check the default security model. + */ +struct AllowLocalOrPrivileged { - bool allow(InputEvent const& evt, InputEventContext const* ctx) - { - return true; - } + static bool allow(InputEventContext const*); }; -struct SecurityAllowPhysicalOrPrivileged +/** + * Do not restrict execution. + */ +struct AllowEverybody { - bool allow(InputEvent const& evt, InputEventContext const* ctx) - { - if(allowPhysicalSeat(evt, ctx)) - return true; - else if(allowPrivilegedUser(evt, ctx)) - return true; - return false; - } + static bool allow(InputEventContext const*); }; -struct UnixLike; -struct Linux; -struct Windows; - -#if defined(__linux) -typedef boost::mpl::vector2<UnixLike,Linux>::type Systems; -#elif defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__) || defined(__WINDOWS__) -typedef boost::mpl::vector1<Windows>::type Systems; +/** + * Shorthand for unrestricted execution. + */ +typedef Security<AllowEverybody> Unprivileged; + +/** + * System Requirements. + * + * At the moment, this is trivial, as PVS only runs on Linux. But, + * as porting efforts are already underway, we include the necessary + * machinery anyway. + * + * \param Trait... a list of system traits that need to be + * present in order for the handler that this policy is applied to + * to run. + */ +#ifdef DOXYGEN_RUNNING +template<typename Trait...> #else -# error "Porting is needed!" +template<IMPLICIT_TYPE_LIST_PARAMS(Trait)> #endif - -struct SystemEnabled; -struct SystemDisabled; - -template<typename System> -struct RequireSystem +BEGIN_POLICY_CLASS(Require) { - typedef typename boost::mpl::contains<Systems, System>::type enabled_type; - static const bool enabled = enabled_type::value; + static const bool areSystemRequirementsFulfilled = + (NextPolicy::areSystemRequirementsFulfilled + && !NextPolicy::areSystemRequirementsVacuouslyFulfilled) + || detail::Matches<AllOf<IMPLICIT_TYPE_LIST_ARGS(Trait)>, detail::SystemTraits>::value; + static const bool areSystemRequirementsVacuouslyFulfilled = false; +} +END_POLICY_CLASS + +#ifndef DOXYGEN_RUNNING +enum { + HANDLER_CODE_DONT_CARE = 0xffff, + HANDLER_VALUE_DONT_CARE = 0xffffffff }; +#endif -struct RequireNoSystem +/** + * Event selection. + * + * This policy makes the handler it is applied to applicable if the + * template parameters \c EventType, \c EventCode and \c EventValue + * match the corresponding fields of the incoming \ref InputEvent. + * + * Can be applied multiple times, and will be combined by logical + * OR. + * + * \param EventType Match the \ref InputEvent::type() field. + * \param EventCode (optional:) Match the \ref InputEvent::code() field. + * \param EventValue (optional:) Match the \ref InputEvent::value() field. + */ +template<unsigned short EventType, + unsigned short EventCode = HANDLER_CODE_DONT_CARE, + unsigned int EventValue = HANDLER_VALUE_DONT_CARE> +BEGIN_POLICY_CLASS(Match) { - typedef boost::mpl::bool_<true>::type enabled_type; - static const bool enabled = enabled_type::value; + bool isApplicable(InputEvent const& evt) + { + if(evt.type() != EventType) + return NextPolicy::isApplicable(evt); + if(EventCode != HANDLER_CODE_DONT_CARE && evt.code() != EventCode) + return NextPolicy::isApplicable(evt); + if(EventValue != HANDLER_VALUE_DONT_CARE && evt.value() != EventValue) + return NextPolicy::isApplicable(evt); + return true; + } }; +END_POLICY_CLASS -} +#ifndef DOXYGEN_RUNNING +namespace detail +{ -template<bool Enabled, typename Delegate, typename SecurityPolicy> -class HandlerHelper +///////////////////////////////////////////////////////////////////////// +// Base case: If no policies are given: +///////////////////////////////////////////////////////////////////////// +struct InputEventHandlerPolicyBase { -public: - bool handle(InputEvent const& evt, InputEventContext const* context = 0) { - if(!securityPolicy.allow(evt, context)) - { - std::string evtStr = evt.toString(); - qWarning("Input Event %s has been denied by security policy", evtStr.c_str()); - return true; - } - if(delegate.matches(evt, context)) { - delegate.handle(evt, context); - return true; - } else { - return false; - } - } + // The default security policy applies + typedef AllowLocalOrPrivileged DefaultSecurityPolicyImpl; - void initialize() + bool allow(InputEventContext const* context) { - delegate.initialize(); + return DefaultSecurityPolicyImpl::allow(context); } - static void describeInto(QList<SpecialInputEventDescription>& list) - { - Delegate::describeInto(list); - } + // A handler that does not specify requirements works + // everywhere + static const bool areSystemRequirementsFulfilled = true; -private: - Delegate delegate; - SecurityPolicy securityPolicy; -}; + // We need this to implement proper logical OR + static const bool areSystemRequirementsVacuouslyFulfilled = true; -template<typename Delegate, typename SecurityPolicy> -class HandlerHelper<false, Delegate, SecurityPolicy> -{ -public: - bool handle(InputEvent const& evt, InputEventContext const* context = 0) { + // Generate an error when no match policy is given. + bool isApplicable(InputEvent const&) + { return false; } + // If any policy implementation needs an initialization hook: + // Don't forget to call NextPolicy::initialize() in your + // implementation! void initialize() { } - - static void describeInto(QList<SpecialInputEventDescription>&) - { - } }; -template<typename Delegate, typename SystemPolicy = policy::RequireNoSystem, typename SecurityPolicy = void> -struct Handler : public HandlerHelper<SystemPolicy::enabled, Delegate, SecurityPolicy> -{ -}; +} // namespace detail +#endif // DOXYGEN_RUNNING -template<typename DefaultSecurityPolicy, typename HandlerType> -struct ApplyDefaultSecurityPolicy -{ - typedef HandlerType type; -}; +} -template<typename DefaultSecurityPolicy, typename Delegate, typename SystemPolicy> -struct ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, Handler<Delegate, SystemPolicy, void> > +/** + * Base class without template parameters to enable making a list of + * polymorphic event handlers. + * + * The actual handler class needs to provide \ref doHandle and can + * override \ref allow and \ref isApplicable. + * + * \note Do not derive from InputEventHandlerBase to implement + * new event handlers! Derive from InputEventHandler instead. + */ +class InputEventHandlerBase { - typedef Handler<Delegate, SystemPolicy, DefaultSecurityPolicy> type; -}; +public: + enum HandlerStatus + { + HANDLER_MATCHED, /**< The handler matched the input event and was executed. */ + HANDLER_NOT_ALLOWED, /**< Execution of the handler was prevented by security policy. */ + HANDLER_NOT_APPLICABLE /**< The handler did not match the input event. */ + }; -template<typename DefaultSecurityPolicy, typename Begin, typename End> -struct InputEventHandlerChainHelper -{ -private: - typedef typename boost::mpl::next<Begin>::type next_iterator_type; - typedef InputEventHandlerChainHelper<DefaultSecurityPolicy, next_iterator_type, End> next_in_chain; + virtual void initialize() = 0; + HandlerStatus handle(InputEvent const& evt, InputEventContext const* context = 0); - typedef typename boost::mpl::deref<Begin>::type handler_entry_type; - typedef typename ApplyDefaultSecurityPolicy<DefaultSecurityPolicy, handler_entry_type>::type handler_type; +protected: + /** + * Check security preconditions for the execution of this handler. + * This is normally handled by the \ref input_policy::Security policy. + */ + virtual bool allow(InputEvent const& event, InputEventContext const* context = 0) = 0; + + /** + * Check if this handler can handle the incoming input event. + * This is normally handled by the \ref input_policy::Match policy. + */ + virtual bool isApplicable(InputEvent const& event, InputEventContext const* context = 0) = 0; + + /** + * Actually handle the incoming event. + * It is assumed that all preconditions have been checked and the handler + * has been initialized at the point where this method is called. + */ + virtual void doHandle(InputEvent const& event, InputEventContext const* context = 0) = 0; +}; - handler_type _handler; - next_in_chain _next; +/** + * Base class for input event handlers. + */ +#ifdef DOXYGEN_RUNNING +template<typename Policy...> +#else +template<POLICY_PARAMS> +#endif +class InputEventHandler : public InputEventHandlerBase +{ +#ifndef DOXYGEN_RUNNING +protected: + // instantiate our policy: + typedef USE_POLICY(input_policy::detail::InputEventHandlerPolicyBase) policy_type; + policy_type policy; +#endif public: - void handle(InputEvent const& evt, InputEventContext const* context = 0) { - if(!_handler.handle(evt, context)) { - _next.handle(evt, context); - } + void initialize() + { + policy.initialize(); } - void initialize() { - _handler.initialize(); - _next.initialize(); - } + /** Export this so the handler chain can decide whether to include this handler */ + static const bool areSystemRequirementsFulfilled = policy_type::areSystemRequirementsFulfilled; + +protected: - static void describeInto(QList<SpecialInputEventDescription>& list) + bool allow(InputEvent const&, InputEventContext const* context = 0) { - handler_type::describeInto(list); - next_in_chain::describeInto(list); + return policy.allow(context); } - static QList<SpecialInputEventDescription> describe() + bool isApplicable(InputEvent const& event, InputEventContext const* = 0) { - QList<SpecialInputEventDescription> list; - describeInto(list); - return list; + return policy.isApplicable(event); } }; -template<typename DefaultSecurityPolicy, typename End> -struct InputEventHandlerChainHelper<DefaultSecurityPolicy, End, End> +/** + * Chain \ref InputEventHandler instances together. + * The chain is also responsible for the creation of instances. + * + * \see privilegedInputEventHandlerChain.cpp + * \see unprivilegedInputEventHandlerChain.cpp + */ +class InputEventHandlerChain { - void handle(InputEvent const&, InputEventContext const* context = 0) { - // do nothing - } +private: + QList<InputEventHandlerBase*> handlers; + +#ifndef DOXYGEN_RUNNING // Implementation detail + ///////////////////////////////////////////////////////////////////////// + // We need to statically dispatch on a static member of HandlerType. + // Unfortunately, we cannot specialize member functions of a template. + // So, we need to make this a class with a static non-template member. + ///////////////////////////////////////////////////////////////////////// + template<bool Compatible, typename HandlerType> + struct ConditionallyAppend + { + ///////////////////////////////////////////////////////////////////////// + // This method will never be instantiated for handlers that are + // not Compatible, thus generating no reference to HandlerType + // and permitting compilation to proceed without + // tedious nested preprocessor conditionals. + ///////////////////////////////////////////////////////////////////////// + static void doIt(InputEventHandlerChain* chain) + { + chain->handlers.append(new HandlerType); + } + }; - void initialize() { - // do nothing - } + template<typename HandlerType> + struct ConditionallyAppend<false, HandlerType> + { + static void doIt(InputEventHandlerChain*) + { + } + }; +#endif // DOXYGEN_RUNNING - static void describeInto(QList<SpecialInputEventDescription>&) +public: + /** + * Add an event handler to the chain. + * + * \param fake_parameter The parameter is only for + * compilers which cannot handle template member functions + * correctly. + * \return A reference to the receiver, for invocation chaining. + * + * \note Do not pass a value to this function. The function + * handles creation of an instance itself. + */ + template<typename HandlerType> + InputEventHandlerChain& add(HandlerType const* fake_parameter = 0) { - // do nothing + ConditionallyAppend<HandlerType::areSystemRequirementsFulfilled, HandlerType>::doIt(this); + return *this; } - static QList<SpecialInputEventDescription> describe() + /** + * Call \ref InputEventHandlerBase::initialize() on all + * handlers in the chain. + */ + void initialize() { - return QList<SpecialInputEventDescription>(); + QListIterator<InputEventHandlerBase*> i(handlers); + while(i.hasNext()) + { + i.next()->initialize(); + } } -}; -template<typename DefaultSecurityPolicy, typename Collection> -struct InputEventHandlerChain : - public InputEventHandlerChainHelper<DefaultSecurityPolicy, - typename boost::mpl::begin<Collection>::type, - typename boost::mpl::end<Collection>::type> -{ + /** + * Handle an input event. All handlers in the chain are tried + * in the order they were added, until one handler's + * implementation of InputEventHandlerBase::handle() returns + * InputEventHandlerBase::HANDLER_MATCHED. + */ + void handle(InputEvent const& event, InputEventContext const* context = 0) + { + QListIterator<InputEventHandlerBase*> i(handlers); + while(i.hasNext()) + { + switch(i.next()->handle(event, context)) + { + case InputEventHandlerBase::HANDLER_MATCHED: + return; + case InputEventHandlerBase::HANDLER_NOT_ALLOWED: + context->hasBeenDenied = true; + case InputEventHandlerBase::HANDLER_NOT_APPLICABLE: + continue; + } + } + } }; #endif /* INPUTEVENTHANDLER_H_ */ |