Module Gnumed.pycommon.gmHooks

GNUmed hooks framework.

This module provides convenience functions and definitions for accessing the GNUmed hooks framework.

This framework calls the script

    ~/.config/gnumed/scripts/hook_script.py

at various times during client execution. The script must contain a function

def run_script(hook=None): pass

which accepts a single argument . That argument will contain the hook that is being activated.

Functions

def import_hook_module(reimport=False)
Expand source code
def import_hook_module(reimport=False):

        global hook_module
        if not reimport:
                if hook_module is not None:
                        return True

        if not os.access(HOOK_SCRIPT_FULL_NAME, os.F_OK):
                _log.warning('creating default hook script')
                f = open(HOOK_SCRIPT_FULL_NAME, mode = 'wt', encoding = 'utf8')
                f.write("""
# known hooks:
#  %s

def run_script(hook=None):
        pass
""" % '\n#\t'.join(known_hooks))
                f.close()
                os.chmod(HOOK_SCRIPT_FULL_NAME, 384)

        if os.path.islink(HOOK_SCRIPT_FULL_NAME):
                gmDispatcher.send (
                        signal = 'statustext',
                        msg = _('Script must not be a link: [%s].') % HOOK_SCRIPT_FULL_NAME
                )
                return False

        if not os.access(HOOK_SCRIPT_FULL_NAME, os.R_OK):
                gmDispatcher.send (
                        signal = 'statustext',
                        msg = _('Script must be readable by the calling user: [%s].') % HOOK_SCRIPT_FULL_NAME
                )
                return False

        script_stat_val = os.stat(HOOK_SCRIPT_FULL_NAME)
        _log.debug('hook script stat(): %s', script_stat_val)
        script_perms = stat.S_IMODE(script_stat_val.st_mode)
        _log.debug('hook script mode: %s (oktal: %s)', script_perms, oct(script_perms))
        if script_perms != 384:                         # octal 0600
                if os.name in ['nt']:
                        _log.warning('this platform does not support os.stat() file permission checking')
                else:
                        gmDispatcher.send (
                                signal = 'statustext',
                                msg = _('Script must be readable by the calling user only (permissions "0600"): [%s].') % HOOK_SCRIPT_FULL_NAME
                        )
                        return False

        try:
                tmp = gmTools.import_module_from_directory(HOOK_SCRIPT_DIR, HOOK_SCRIPT_NAME)
        except Exception:
                _log.exception('cannot import hook script')
                return False

        hook_module = tmp
#       if reimport:
#               imp.reload(tmp)                 # this has well-known shortcomings !

        _log.info('hook script: %s', HOOK_SCRIPT_FULL_NAME)
        return True
def run_hook_script(hook=None)
Expand source code
def run_hook_script(hook=None):
        # NOTE: this just *might* be a huge security hole

        _log.info('told to pull hook [%s]', hook)

        if hook not in known_hooks:
                raise ValueError('run_hook_script(): unknown hook [%s]' % hook)

        if not import_hook_module(reimport = False):
                _log.debug('cannot import hook module, not pulling hook')
                return False

        if hook in __current_hook_stack:
                _log.error('hook-code cycle detected, aborting')
                _log.error('current hook stack: %s', __current_hook_stack)
                return False

        __current_hook_stack.append(hook)

        try:
                hook_module.run_script(hook = hook)
        except Exception:
                _log.exception('error running hook script for [%s]', hook)
                gmDispatcher.send (
                        signal = 'statustext',
                        msg = _('Error running hook [%s] script.') % hook,
                        beep = True
                )
                if __current_hook_stack[-1] != hook:
                        _log.error('hook nesting error detected')
                        _log.error('latest hook: expected [%s], found [%s]', hook, __current_hook_stack[-1])
                        _log.error('current hook stack: %s', __current_hook_stack)
                else:
                        __current_hook_stack.pop()
                return False

        if __current_hook_stack[-1] != hook:
                _log.error('hook nesting error detected')
                _log.error('latest hook: expected [%s], found [%s]', hook, __current_hook_stack[-1])
                _log.error('current hook stack: %s', __current_hook_stack)
        else:
                __current_hook_stack.pop()

        return True
def setup_hook_dir()
Expand source code
def setup_hook_dir():
        _old_path = os.path.join(gmTools.gmPaths().home_dir, '.gnumed', 'scripts')
        if os.path.isdir(_old_path):
                print('obsolete: [%s], use [%s]' %(_old_path, HOOK_SCRIPT_DIR))
                _log.debug('obsolete: %s', _old_path)
        _log.debug('known hooks:')
        for hook in known_hooks:
                _log.debug(hook)
        gmTools.mkdir(HOOK_SCRIPT_DIR)
        gmTools.create_directory_description_file(directory = HOOK_SCRIPT_DIR, readme = README_hook_dir)
        # create hook script example/template
        example_name = os.path.join(HOOK_SCRIPT_DIR, HOOK_SCRIPT_NAME + '.example')
        example = open(example_name, mode = 'wt', encoding = 'utf8')
        example.write(HOOK_SCRIPT_EXAMPLE)
        example.close()
        os.chmod(example_name, 384)