/* # Copyright (c) 2009 - OpenSLX Project, Computer Center University of Freiburg # # This program is free software distributed under the GPL version 2. # See http://openslx.org/COPYING # # If you have any feedback please consult http://openslx.org/feedback and # send your suggestions, praise, or complaints to feedback@openslx.org # # General information about OpenSLX can be found at http://openslx.org/ # -------------------------------------------------------------------------- # inputEventHandler.h: # - Common definitions for input event handlers # -------------------------------------------------------------------------- */ #ifndef INPUTEVENTHANDLER_H_ #define INPUTEVENTHANDLER_H_ #include #include #include #include #include #include #include "detail/policyChain.h" #include "detail/systemTraits.h" struct InputEventContext { InputEventContext() { hasBeenDenied = false; } virtual pid_t senderPid() const = 0; virtual uid_t senderUid() const = 0; virtual gid_t senderGid() const = 0; mutable bool hasBeenDenied; }; namespace input_policy { ///////////////////////////////////////////////////////////////////////// // 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: // 1. If the user is on a local seat, allow. If the user is privileged, // allow. Else deny. // 2. Allow everybody. // Additional security policies can be added by following the example // set by AllowLocalOrPrivileged ///////////////////////////////////////////////////////////////////////// template BEGIN_POLICY_CLASS(Security) { bool allow(InputEventContext const* context) { return PolicyImpl::allow(context); } } END_POLICY_CLASS struct AllowLocalOrPrivileged { static bool allow(InputEventContext const*); }; struct AllowEverybody { static bool allow(InputEventContext const*); }; typedef Security 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. ///////////////////////////////////////////////////////////////////////// template BEGIN_POLICY_CLASS(Require) { static const bool areSystemRequirementsFulfilled = NextPolicy::areSystemRequirementsFulfilled || detail::Matches, detail::SystemTraits>::value; } END_POLICY_CLASS ///////////////////////////////////////////////////////////////////////// // 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. ///////////////////////////////////////////////////////////////////////// enum { HANDLER_CODE_DONT_CARE = 0xffff, HANDLER_VALUE_DONT_CARE = 0xffffffff }; struct T; template BEGIN_POLICY_CLASS(Match) { 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 namespace detail { ///////////////////////////////////////////////////////////////////////// // Base case: If no policies are given: ///////////////////////////////////////////////////////////////////////// struct InputEventHandlerPolicyBase { // The default security policy applies typedef AllowLocalOrPrivileged DefaultSecurityPolicyImpl; bool allow(InputEventContext const* context) { return DefaultSecurityPolicyImpl::allow(context); } // A handler that does not specify requirements works // everywhere static const bool areSystemRequirementsFulfilled = true; // 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() { } }; } } ///////////////////////////////////////////////////////////////////////// // We want a nice non-generic base so we can make a list of polymorphic // handlers. // // The actual handler class need to provide doHandle and can override // allow and isApplicable. ///////////////////////////////////////////////////////////////////////// class InputEventHandlerBase { public: enum HandlerStatus { HANDLER_MATCHED, HANDLER_NOT_ALLOWED, HANDLER_NOT_APPLICABLE }; virtual void initialize() = 0; HandlerStatus handle(InputEvent const& evt, InputEventContext const* context = 0); protected: virtual bool allow(InputEvent const& event, InputEventContext const* context = 0) = 0; virtual bool isApplicable(InputEvent const& event, InputEventContext const* context = 0) = 0; virtual void doHandle(InputEvent const& event, InputEventContext const* context = 0) = 0; }; ///////////////////////////////////////////////////////////////////////// // Now that the machinery is in place, we can finally define what it // is like to be an input event handler: ///////////////////////////////////////////////////////////////////////// template class InputEventHandler : public InputEventHandlerBase { protected: typedef USE_POLICY(input_policy::detail::InputEventHandlerPolicyBase) policy_type; policy_type policy; public: void initialize() { policy.initialize(); } // Export this so the handler chain can decide whether to include this handler static const bool areSystemRequirementsFulfilled = policy_type::areSystemRequirementsFulfilled; protected: typedef InputEventHandler super; // allow and isApplicable are actually provided by Policy. If the // handler class wishes to override any of them, the policy implementation // can be called by means of super. bool allow(InputEvent const&, InputEventContext const* context = 0) { return policy.allow(context); } bool isApplicable(InputEvent const& event, InputEventContext const* = 0) { return policy.isApplicable(event); } }; ///////////////////////////////////////////////////////////////////////// // And we can chain input handlers together: ///////////////////////////////////////////////////////////////////////// class InputEventHandlerChain { private: QList handlers; ///////////////////////////////////////////////////////////////////////// // 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 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); } }; template struct ConditionallyAppend { static void doIt(InputEventHandlerChain*) { } }; public: // Add an event handler to the chain. The argument is only for // compilers which cannot handle template member functions // correctly. template InputEventHandlerChain& add(HandlerType const* = 0) { ConditionallyAppend::doIt(this); return *this; } void initialize() { QListIterator i(handlers); while(i.hasNext()) { i.next()->initialize(); } } void handle(InputEvent const& event, InputEventContext const* context = 0) { QListIterator i(handlers); while(i.hasNext()) { switch(i.next()->handle(event, context)) { case InputEventHandlerBase::HANDLER_MATCHED: break; case InputEventHandlerBase::HANDLER_NOT_ALLOWED: context->hasBeenDenied = true; case InputEventHandlerBase::HANDLER_NOT_APPLICABLE: continue; } } } }; #endif /* INPUTEVENTHANDLER_H_ */