Welcome to mirror list, hosted at ThFree Co, Russian Federation.

dev.gajim.org/gajim/gajim.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gajim/command_system/__init__.py20
-rw-r--r--gajim/command_system/dispatcher.py134
-rw-r--r--gajim/command_system/errors.py54
-rw-r--r--gajim/command_system/framework.py353
-rw-r--r--gajim/command_system/implementation/__init__.py20
-rw-r--r--gajim/command_system/implementation/custom.py131
-rw-r--r--gajim/command_system/implementation/execute.py136
-rw-r--r--gajim/command_system/implementation/hosts.py45
-rw-r--r--gajim/command_system/implementation/middleware.py161
-rw-r--r--gajim/command_system/implementation/standard.py460
-rw-r--r--gajim/command_system/mapping.py349
-rw-r--r--gajim/command_system/tools.py34
-rw-r--r--gajim/common/setting_values.py5
-rw-r--r--setup.cfg1
14 files changed, 0 insertions, 1903 deletions
diff --git a/gajim/command_system/__init__.py b/gajim/command_system/__init__.py
deleted file mode 100644
index 2fb336264..000000000
--- a/gajim/command_system/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-The command system providing scalable, clean and convenient architecture
-in combination with declarative way of defining commands and a fair
-amount of automatization for routine processes.
-"""
diff --git a/gajim/command_system/dispatcher.py b/gajim/command_system/dispatcher.py
deleted file mode 100644
index 4f292dafc..000000000
--- a/gajim/command_system/dispatcher.py
+++ /dev/null
@@ -1,134 +0,0 @@
-# Copyright (c) 2010, Alexander Cherniuk (ts33kr@gmail.com)
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""
-Backbone of the command system. Provides smart and controllable
-dispatching mechanism with an auto-discovery functionality. In addition
-to automatic discovery and dispatching, also features manual control
-over the process.
-"""
-
-from typing import Any
-
-from gajim.command_system.tools import remove
-
-COMMANDS: dict[Any, Any] = {}
-CONTAINERS: dict[Any, Any] = {}
-
-
-def add_host(host):
- CONTAINERS[host] = []
-
-
-def remove_host(host):
- remove(CONTAINERS, host)
-
-
-def add_container(container):
- for host in container.HOSTS:
- CONTAINERS[host].append(container)
-
-
-def remove_container(container):
- for host in container.HOSTS:
- remove(CONTAINERS[host], container)
-
-
-def add_commands(container):
- commands = COMMANDS.setdefault(container, {})
- for command in traverse_commands(container):
- for name in command.names:
- commands[name] = command
-
-
-def remove_commands(container):
- remove(COMMANDS, container)
-
-
-def traverse_commands(container):
- for name in dir(container):
- attribute = getattr(container, name)
- if is_command(attribute):
- yield attribute
-
-
-def is_command(attribute):
- from gajim.command_system.framework import Command
- return isinstance(attribute, Command)
-
-
-def is_root(namespace):
- metaclass = namespace.get("__metaclass__", None)
- if not metaclass:
- return False
- return issubclass(metaclass, Dispatchable)
-
-
-def get_command(host, name):
- for container in CONTAINERS[host]:
- command = COMMANDS[container].get(name)
- if command:
- return command
-
-
-def list_commands(host):
- for container in CONTAINERS[host]:
- commands = COMMANDS[container]
- for name, command in commands.items():
- yield name, command
-
-
-class Dispatchable(type):
- # pylint: disable=no-value-for-parameter
- def __init__(cls, name, bases, namespace):
- parents = super(Dispatchable, cls)
- parents.__init__(name, bases, namespace)
- if not is_root(namespace):
- cls.dispatch()
-
- def dispatch(cls):
- if cls.AUTOMATIC:
- cls.enable()
-
-
-class Host(Dispatchable):
-
- def enable(cls):
- add_host(cls)
-
- def disable(cls):
- remove_host(cls)
-
-
-class Container(Dispatchable):
-
- def enable(cls):
- add_container(cls)
- add_commands(cls)
-
- def disable(cls):
- remove_commands(cls)
- remove_container(cls)
diff --git a/gajim/command_system/errors.py b/gajim/command_system/errors.py
deleted file mode 100644
index 1f02051c0..000000000
--- a/gajim/command_system/errors.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-
-class BaseError(Exception):
- """
- Common base for errors which relate to a specific command.
- Encapsulates everything needed to identify a command, by either its
- object or name.
- """
-
- def __init__(self, message, command=None, name=None):
- self.message = message
-
- self.command = command
- self.name = name
-
- if command and not name:
- self.name = command.first_name
-
- super(BaseError, self).__init__()
-
- def __str__(self):
- return self.message
-
-
-class DefinitionError(BaseError):
- """
- Used to indicate errors occurred on command definition.
- """
-
-
-class CommandError(BaseError):
- """
- Used to indicate errors occurred during command execution.
- """
-
-
-class NoCommandError(BaseError):
- """
- Used to indicate an inability to find the specified command.
- """
diff --git a/gajim/command_system/framework.py b/gajim/command_system/framework.py
deleted file mode 100644
index a738fca64..000000000
--- a/gajim/command_system/framework.py
+++ /dev/null
@@ -1,353 +0,0 @@
-# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Provides a tiny framework with simple, yet powerful and extensible
-architecture to implement commands in a straight and flexible,
-declarative way.
-"""
-
-from types import FunctionType
-from inspect import getfullargspec
-from inspect import getdoc
-
-from gajim.command_system.dispatcher import Host
-from gajim.command_system.dispatcher import Container
-from gajim.command_system.dispatcher import get_command
-from gajim.command_system.dispatcher import list_commands
-from gajim.command_system.mapping import parse_arguments
-from gajim.command_system.mapping import adapt_arguments
-from gajim.command_system.errors import DefinitionError
-from gajim.command_system.errors import CommandError
-from gajim.command_system.errors import NoCommandError
-
-
-class CommandHost(metaclass=Host):
- """
- Command host is a hub between numerous command processors and
- command containers. Aimed to participate in a dispatching process in
- order to provide clean and transparent architecture.
-
- The AUTOMATIC class variable, which must be defined by a command
- host, specifies whether the command host should be automatically
- dispatched and enabled by the dispatcher or not.
- """
- __metaclass__ = Host
-
-
-class CommandContainer(metaclass=Container):
- """
- Command container is an entity which holds defined commands,
- allowing them to be dispatched and processed correctly. Each
- command container may be bound to a one or more command hosts.
-
- The AUTOMATIC class variable, which must be defined by a command
- processor, specifies whether the command processor should be
- automatically dispatched and enabled by the dispatcher or not.
-
- Bounding is controlled by the HOSTS class variable, which must be
- defined by the command container. This variable should contain a
- sequence of hosts to bound to, as a tuple or list.
- """
- __metaclass__ = Container
-
-
-class CommandProcessor:
- """
- Command processor is an immediate command emitter. It does not
- participate in the dispatching process directly, but must define a
- host to bound to.
-
- Bounding is controlled by the COMMAND_HOST variable, which must be
- defined in the body of the command processor. This variable should
- be set to a specific command host.
- """
-
- # This defines a command prefix (or an initializer), which should
- # precede a text in order for it to be processed as a command.
- COMMAND_PREFIX = '/'
-
- def process_as_command(self, text):
- """
- Try to process text as a command. Returns True if it has been
- processed as a command and False otherwise.
- """
- # pylint: disable=assignment-from-no-return
- prefix = text.startswith(self.COMMAND_PREFIX)
- length = len(text) > len(self.COMMAND_PREFIX)
- if not (prefix and length):
- return False
-
- body = text[len(self.COMMAND_PREFIX):]
- body = body.strip()
-
- parts = body.split(None, 1)
- name, arguments = parts if len(parts) > 1 else (parts[0], None)
-
- flag = self.looks_like_command(text, body, name, arguments)
- if flag is not None:
- return flag
-
- self.execute_command(name, arguments)
-
- return True
-
- def execute_command(self, name, arguments):
- cmd = self.get_command(name)
-
- args, opts = parse_arguments(arguments) if arguments else ([], [])
- args, kwargs = adapt_arguments(cmd, arguments, args, opts)
-
- if self.command_preprocessor(cmd, name, arguments, args, kwargs):
- return
- value = cmd(self, *args, **kwargs)
- self.command_postprocessor(cmd, name, arguments, args, kwargs, value)
-
- def command_preprocessor(self, cmd, name, arguments, args, kwargs):
- """
- Redefine this method in the subclass to execute custom code
- before command gets executed.
-
- If returns True then command execution will be interrupted and
- command will not be executed.
- """
-
- def command_postprocessor(self, cmd, name, arguments, args, kwargs, value):
- """
- Redefine this method in the subclass to execute custom code
- after command gets executed.
- """
-
- def looks_like_command(self, text, body, name, arguments):
- """
- This hook is being called before any processing, but after it
- was determined that text looks like a command.
-
- If returns value other then None - then further processing will
- be interrupted and that value will be used to return from
- process_as_command.
- """
-
- def get_command(self, name):
- cmd = get_command(self.COMMAND_HOST, name)
- if not cmd:
- raise NoCommandError("Command does not exist", name=name)
- return cmd
-
- def list_commands(self):
- commands = list_commands(self.COMMAND_HOST)
- commands = dict(commands)
- return sorted(set(commands.values()), key=repr)
-
-
-class Command:
-
- def __init__(self, handler, *names, **properties):
- self.handler = handler
- self.names = names
-
- # Automatically set all the properties passed to a constructor
- # by the command decorator.
- for key, value in properties.items():
- setattr(self, key, value)
-
- def __call__(self, *args, **kwargs):
- try:
- return self.handler(*args, **kwargs)
-
- # This allows to use a shortcut way of raising an exception
- # inside a handler. That is to raise a CommandError without
- # command or name attributes set. They will be set to a
- # corresponding values right here in case if they was not set by
- # the one who raised an exception.
- except CommandError as error:
- if not error.command and not error.name:
- raise CommandError(error.message, self)
- raise
-
- # This one is a little bit too wide, but as Python does not have
- # anything more constrained - there is no other choice. Take a
- # look here if command complains about invalid arguments while
- # they are ok.
- except TypeError:
- raise CommandError("Command received invalid arguments", self)
-
- def __repr__(self):
- return "<Command %s>" % ', '.join(self.names)
-
- def __cmp__(self, other):
- if self.first_name > other.first_name:
- return 1
- if self.first_name < other.first_name:
- return -1
- return 0
-
- @property
- def first_name(self):
- return self.names[0]
-
- @property
- def native_name(self):
- return self.handler.__name__
-
- def extract_documentation(self):
- """
- Extract handler's documentation which is a doc-string and
- transform it to a usable format.
- """
- return getdoc(self.handler)
-
- def extract_description(self):
- """
- Extract handler's description (which is a first line of the
- documentation). Try to keep them simple yet meaningful.
- """
- documentation = self.extract_documentation()
- return documentation.split('\n', 1)[0] if documentation else None
-
- def extract_specification(self):
- """
- Extract handler's arguments specification, as it was defined
- preserving their order.
- """
- names, var_args, var_kwargs, defaults, _, _, _ = getfullargspec(
- self.handler)
-
- # Behavior of this code need to be checked. Might yield
- # incorrect results on some rare occasions.
- spec_args = names[:-len(defaults) if defaults else len(names)]
- spec_kwargs = list(
- zip(names[-len(defaults):], defaults)) if defaults else {}
-
- # Removing self from arguments specification. Command handler
- # should receive the processors as a first argument, which
- # should be self by the canonical means.
- if spec_args.pop(0) != 'self':
- raise DefinitionError("First argument must be self", self)
-
- return spec_args, spec_kwargs, var_args, var_kwargs
-
-
-def command(*names, **properties):
- """
- A decorator for defining commands in a declarative way. Provides
- facilities for setting command's names and properties.
-
- Names should contain a set of names (aliases) by which the command
- can be reached. If no names are given - the native name (the one
- extracted from the command handler) will be used.
-
- If native=True is given (default) and names is non-empty - then the
- native name of the command will be prepended in addition to the
- given names.
-
- If usage=True is given (default) - then command help will be
- appended with autogenerated usage info, based of the command handler
- arguments introspection.
-
- If source=True is given - then the first argument of the command
- will receive the source arguments, as a raw, unprocessed string. The
- further mapping of arguments and options will not be affected.
-
- If raw=True is given - then command considered to be raw and should
- define positional arguments only. If it defines only one positional
- argument - this argument will receive all the raw and unprocessed
- arguments. If the command defines more then one positional argument
- - then all the arguments except the last one will be processed
- normally; the last argument will get what is left after the
- processing as raw and unprocessed string.
-
- If empty=True is given - this will allow to call a raw command
- without arguments.
-
- If extra=True is given - then all the extra arguments passed to a
- command will be collected into a sequence and given to the last
- positional argument.
-
- If overlap=True is given - then all the extra arguments will be
- mapped as if they were values for the keyword arguments.
-
- If expand=True is given (default) - then short, one-letter options
- will be expanded to a verbose ones, based of the comparison of the
- first letter. If more then one option with the same first letter is
- given - then only first one will be used in the expansion.
- """
- names = list(names)
-
- native = properties.get('native', True)
-
- usage = properties.get('usage', True)
- source = properties.get('source', False)
- raw = properties.get('raw', False)
- empty = properties.get('empty', False)
- extra = properties.get('extra', False)
- overlap = properties.get('overlap', False)
- expand = properties.get('expand', True)
-
- if empty and not raw:
- raise DefinitionError("Empty option can be used only with raw commands")
-
- if extra and overlap:
- raise DefinitionError("Extra and overlap options can not be used "
- "together")
-
- properties = {
- 'usage': usage,
- 'source': source,
- 'raw': raw,
- 'extra': extra,
- 'overlap': overlap,
- 'empty': empty,
- 'expand': expand
- }
-
- def decorator(handler):
- """
- Decorator which receives handler as a first argument and then
- wraps it in the command which then returns back.
- """
- cmd = Command(handler, *names, **properties)
-
- # Extract and inject a native name if either no other names are
- # specified or native property is enabled, while making
- # sure it is going to be the first one in the list.
- if not names or native:
- names.insert(0, cmd.native_name)
- cmd.names = tuple(names)
-
- return cmd
-
- # Workaround if we are getting called without parameters. Keep in
- # mind that in that case - first item in the names will be the
- # handler.
- if names and isinstance(names[0], FunctionType):
- return decorator(names.pop(0))
-
- return decorator
-
-
-def doc(text):
- """
- This decorator is used to bind a documentation (a help) to a
- command.
- """
- def decorator(target):
- if isinstance(target, Command):
- target.handler.__doc__ = text
- else:
- target.__doc__ = text
- return target
-
- return decorator
diff --git a/gajim/command_system/implementation/__init__.py b/gajim/command_system/implementation/__init__.py
deleted file mode 100644
index 1c3d8ab65..000000000
--- a/gajim/command_system/implementation/__init__.py
+++ /dev/null
@@ -1,20 +0,0 @@
-# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-The implementation and auxiliary systems which implement the standard
-Gajim commands and also provide an infrastructure for adding custom
-commands.
-"""
diff --git a/gajim/command_system/implementation/custom.py b/gajim/command_system/implementation/custom.py
deleted file mode 100644
index af2b9e8c6..000000000
--- a/gajim/command_system/implementation/custom.py
+++ /dev/null
@@ -1,131 +0,0 @@
-# Copyright (c) 2009-2010, Alexander Cherniuk (ts33kr@gmail.com)
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""
-This module contains examples of how to create your own commands, by
-creating a new command container, bounded to a specific command host,
-and defining a set of commands inside of it.
-
-Keep in mind that this module is not being loaded from anywhere, so the
-code in here will not be executed and commands defined here will not be
-detected.
-"""
-
-from gajim.common.i18n import _
-from gajim.command_system.framework import CommandContainer
-from gajim.command_system.framework import command
-from gajim.command_system.framework import doc
-from gajim.command_system.implementation.hosts import ChatCommands
-from gajim.command_system.implementation.hosts import PrivateChatCommands
-from gajim.command_system.implementation.hosts import GroupChatCommands
-
-
-class CustomCommonCommands(CommandContainer):
- """
- The AUTOMATIC class variable, set to a positive value, instructs the
- command system to automatically discover the command container and
- enable it.
-
- This command container bounds to all three available in the default
- implementation command hosts. This means that commands defined in
- this container will be available to all: chat, private chat and a
- group chat.
- """
-
- AUTOMATIC = True
- HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
-
- @command
- def dance(self):
- """
- First line of the doc string is called a description and will be
- programmatically extracted and formatted.
-
- After that you can give more help, like explanation of the
- options. This one will be programmatically extracted and
- formatted too.
-
- After all the documentation - there will be autogenerated (based
- on the method signature) usage information appended. You can
- turn it off, if you want.
- """
- return "I don't dance."
-
-
-class CustomChatCommands(CommandContainer):
- """
- This command container bounds only to the ChatCommands command host.
- Therefore commands defined inside of the container will be available
- only to a chat.
- """
-
- AUTOMATIC = True
- HOSTS = (ChatCommands,)
-
- @command("squal", "bawl")
- def sing(self):
- """
- This command has an additional aliases. It means the command will
- be available under three names: sing (the native name), squal
- (the first alias), bawl (the second alias).
-
- You can turn off the usage of the native name, if you want, and
- specify a name or a set of names, as aliases, under which a
- command will be available.
- """
- return "Buy yourself a stereo."
-
-
-class CustomPrivateChatCommands(CommandContainer):
- """
- This command container bounds only to the PrivateChatCommands
- command host. Therefore commands defined inside of the container
- will be available only to a private chat.
- """
-
- AUTOMATIC = True
- HOSTS = (PrivateChatCommands,)
-
- @command
- #Example string. Do not translate
- @doc(_("The same as using a doc-string, except it supports translation"))
- def make_coffee(self):
- return "I'm not a coffee machine!"
-
-
-class CustomGroupChatCommands(CommandContainer):
- """
- This command container bounds only to the GroupChatCommands command
- host. Therefore commands defined inside of the container will be
- available only to a group chat.
- """
-
- AUTOMATIC = True
- HOSTS = (GroupChatCommands,)
-
- @command
- def fetch(self):
- return "Buy yourself a dog."
diff --git a/gajim/command_system/implementation/execute.py b/gajim/command_system/implementation/execute.py
deleted file mode 100644
index 58205e768..000000000
--- a/gajim/command_system/implementation/execute.py
+++ /dev/null
@@ -1,136 +0,0 @@
-# Copyright (c) 2010, Alexander Cherniuk (ts33kr@gmail.com)
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""
-Provides facilities to safely execute expressions inside a shell process
-and capture the resulting output, in an asynchronous fashion, avoiding
-deadlocks. If the process execution time reaches the threshold - it is
-forced to terminate. Consists of a tiny framework and a couple of
-commands as a frontend.
-"""
-
-from subprocess import Popen, PIPE
-from os.path import expanduser
-
-from gi.repository import GLib
-
-from gajim.common import app
-from gajim.common.i18n import _
-from gajim.command_system.framework import CommandContainer
-from gajim.command_system.framework import command
-from gajim.command_system.framework import doc
-from gajim.command_system.implementation.hosts import ChatCommands
-from gajim.command_system.implementation.hosts import PrivateChatCommands
-from gajim.command_system.implementation.hosts import GroupChatCommands
-
-
-class Execute(CommandContainer):
- AUTOMATIC = True
- HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
-
- DIRECTORY = "~"
-
- POLL_INTERVAL = 100
- POLL_COUNT = 5
-
- @command("exec", raw=True)
- @doc(_("Execute expression inside a shell, show output"))
- def execute(self, expression):
- Execute.spawn(self, expression)
-
- @classmethod
- def spawn(cls, processor, expression):
- command_system_execute = app.settings.get('command_system_execute')
- if command_system_execute:
- pipes = dict(stdout=PIPE, stderr=PIPE)
- directory = expanduser(cls.DIRECTORY)
- popen = Popen(expression, shell=True, cwd=directory, **pipes)
- cls.monitor(processor, popen)
- else:
- processor.echo_error(
- _('Command disabled. This command can be enabled by '
- 'setting \'command_system_execute\' to True in ACE '
- '(Advanced Configuration Editor).'))
- return
-
- @classmethod
- def monitor(cls, processor, popen):
- poller = cls.poller(processor, popen)
- GLib.timeout_add(cls.POLL_INTERVAL, next, poller)
-
- @classmethod
- def poller(cls, processor, popen):
- for _ in range(cls.POLL_COUNT):
- yield cls.brush(processor, popen)
- cls.overdue(processor, popen)
- yield False
-
- @classmethod
- def brush(cls, processor, popen):
- if popen.poll() is not None:
- cls.terminated(processor, popen)
- return False
- return True
-
- @classmethod
- def terminated(cls, processor, popen):
- stdout, stderr = cls.fetch(popen)
- success = popen.returncode == 0
- if success and stdout:
- processor.echo(stdout)
- elif not success and stderr:
- processor.echo_error(stderr)
-
- @classmethod
- def overdue(cls, processor, popen):
- popen.terminate()
-
- @classmethod
- def fetch(cls, popen):
- data = popen.communicate()
- return map(cls.clean, data)
-
- @staticmethod
- def clean(text):
- strip = chr(10) + chr(32)
- return text.decode().strip(strip)
-
-
-class Show(Execute):
-
- @command("sh", raw=True)
- @doc(_("Execute expression inside a shell, send output"))
- def show(self, expression):
- Show.spawn(self, expression)
-
- @classmethod
- def terminated(cls, processor, popen):
- stdout, stderr = cls.fetch(popen)
- success = popen.returncode == 0
- if success and stdout:
- processor.send(stdout)
- elif not success and stderr:
- processor.echo_error(stderr)
diff --git a/gajim/command_system/implementation/hosts.py b/gajim/command_system/implementation/hosts.py
deleted file mode 100644
index d01005580..000000000
--- a/gajim/command_system/implementation/hosts.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-The module defines a set of command hosts, which are bound to a
-different command processors, which are the source of commands.
-"""
-
-from gajim.command_system.framework import CommandHost
-
-
-class ChatCommands(CommandHost):
- """
- This command host is bound to the command processor which processes
- commands from a chat.
- """
- AUTOMATIC = True
-
-
-class PrivateChatCommands(CommandHost):
- """
- This command host is bound to the command processor which processes
- commands from a private chat.
- """
- AUTOMATIC = True
-
-
-class GroupChatCommands(CommandHost):
- """
- This command host is bound to the command processor which processes
- commands from a group chat.
- """
- AUTOMATIC = True
diff --git a/gajim/command_system/implementation/middleware.py b/gajim/command_system/implementation/middleware.py
deleted file mode 100644
index d1f9bb9c5..000000000
--- a/gajim/command_system/implementation/middleware.py
+++ /dev/null
@@ -1,161 +0,0 @@
-# Copyright (c) 2009-2010, Alexander Cherniuk (ts33kr@gmail.com)
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""
-Provides a glue to tie command system framework and the actual code
-where it would be dropped in. Defines a little bit of scaffolding to
-support interaction between the two and a few utility methods so you
-don't need to dig up the code itself to write basic commands.
-"""
-
-from traceback import print_exc
-
-# from gi.repository import Pango
-
-from gajim.common import app
-from gajim.common.i18n import _
-
-from gajim.command_system.framework import CommandProcessor
-from gajim.command_system.errors import CommandError
-from gajim.command_system.errors import NoCommandError
-
-
-class ChatCommandProcessor(CommandProcessor):
- """
- A basic scaffolding to provide convenient interaction between the
- command system and chat controls. It will be merged directly into
- the controls, by ChatCommandProcessor being among superclasses of
- the controls.
- """
-
- def process_as_command(self, text):
- self.command_succeeded = False
- parents = super(ChatCommandProcessor, self)
- flag = parents.process_as_command(text)
- if flag and self.command_succeeded:
- self.clear_input()
- return flag
-
- def execute_command(self, name, arguments):
- try:
- parents = super(ChatCommandProcessor, self)
- parents.execute_command(name, arguments)
- except NoCommandError as error:
- details = dict(name=error.name, message=error.message)
- message = "%(name)s: %(message)s\n" % details
- message += "Try using the //%(name)s or /say /%(name)s " % details
- message += "construct if you intended to send it as a text."
- self.echo_error(message)
- except CommandError as error:
- self.echo_error("%s: %s" % (error.name, error.message))
- except Exception:
- self.echo_error(_("Error during command execution!"))
- print_exc()
- else:
- self.command_succeeded = True
-
- def looks_like_command(self, text, body, name, arguments):
- # Command escape stuff goes here. If text was prepended by the
- # command prefix twice, like //not_a_command (if prefix is set
- # to /) then it will be escaped, that is sent just as a regular
- # message with one (only one) prefix removed, so message will be
- # /not_a_command.
- if body.startswith(self.COMMAND_PREFIX):
- self.send(body)
- return True
-
- def command_preprocessor(self, command, name, arguments, args, kwargs):
- # If command argument contain h or help option - forward it to
- # the /help command. Don't forget to pass self, as all commands
- # are unbound. And also don't forget to print output.
- if 'h' in kwargs or 'help' in kwargs:
- help_ = self.get_command('help')
- self.echo(help_(self, name))
- return True
-
- def command_postprocessor(self, command, name, arguments, args, kwargs,
- value):
- # If command returns a string - print it to a user. A convenient
- # and sufficient in most simple cases shortcut to a using echo.
- if value and isinstance(value, str):
- self.echo(value)
-
-
-class CommandTools:
- """
- Contains a set of basic tools and shortcuts you can use in your
- commands to perform some simple operations. These will be merged
- directly into the controls, by CommandTools being among superclasses
- of the controls.
- """
-
- def __init__(self):
- pass
-
- def echo(self, text, is_error=False):
- """
- Print given text to the user, as a regular command output.
- """
- self.conversation_view.add_command_output(text, is_error)
-
- def echo_error(self, text):
- """
- Print given text to the user, as an error command output.
- """
- self.echo(text, is_error=True)
-
- def send(self, text):
- """
- Send a message to the contact.
- """
- self.send_message(text, process_commands=False)
-
- def set_input(self, text):
- """
- Set given text into the input.
- """
- buffer = self.msg_textview.get_buffer()
- buffer.set_text(text)
-
- def clear_input(self):
- """
- Clear input.
- """
- self.set_input(str())
-
- @property
- def connection(self):
- """
- Get the current connection object.
- """
- return app.connections[self.account]
-
- @property
- def full_jid(self):
- """
- Get a full JID of the contact.
- """
- return self.contact.jid
diff --git a/gajim/command_system/implementation/standard.py b/gajim/command_system/implementation/standard.py
deleted file mode 100644
index aa8a2e5f7..000000000
--- a/gajim/command_system/implementation/standard.py
+++ /dev/null
@@ -1,460 +0,0 @@
-# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Provides an actual implementation for the standard commands.
-"""
-
-from time import localtime
-from time import strftime
-from datetime import date
-
-from gi.repository import GLib
-
-from gajim.common import app
-from gajim.common import helpers
-from gajim.common.i18n import _
-from gajim.common.const import KindConstant
-
-from gajim.command_system.errors import CommandError
-from gajim.command_system.framework import CommandContainer
-from gajim.command_system.framework import command
-from gajim.command_system.framework import doc
-from gajim.command_system.mapping import generate_usage
-
-from gajim.command_system.implementation.hosts import ChatCommands
-from gajim.command_system.implementation.hosts import PrivateChatCommands
-from gajim.command_system.implementation.hosts import GroupChatCommands
-
-
-class StandardCommonCommands(CommandContainer):
- """
- This command container contains standard commands which are common
- to all - chat, private chat, group chat.
- """
-
- AUTOMATIC = True
- HOSTS = ChatCommands, PrivateChatCommands, GroupChatCommands
-
- @command(overlap=True)
- @doc(_("Show help on a given command or a list of available commands if "
- "-a is given"))
- def help(self, cmd=None, all_=False):
- if cmd:
- cmd = self.get_command(cmd)
-
- documentation = _(cmd.extract_documentation())
- usage = generate_usage(cmd)
-
- text = []
-
- if documentation:
- text.append(documentation)
- if cmd.usage:
- text.append(usage)
-
- return '\n\n'.join(text)
-
- if all_:
- for cmd_ in self.list_commands():
- names = ', '.join(cmd_.names)
- description = cmd_.extract_description()
-
- self.echo("%s - %s" % (names, description))
- else:
- help_ = self.get_command('help')
- self.echo(help_(self, 'help'))
-
- @command(raw=True)
- @doc(_("Send a message to the contact"))
- def say(self, message):
- self.send(message)
-
- @command(raw=True)
- @doc(_("Send action (in the third person) to the current chat"))
- def me(self, action):
- self.send("/me %s" % action)
-
- @command('lastlog', overlap=True)
- @doc(_("Show logged messages which mention given text"))
- def grep(self, text, limit=None):
- results = app.storage.archive.search_log(self.account, self.contact.jid, text)
-
- if not results:
- raise CommandError(_("%s: Nothing found") % text)
-
- if limit:
- try:
- results = results[len(results) - int(limit):]
- except ValueError:
- raise CommandError(_("Limit must be an integer"))
-
- for row in results:
- contact = row.contact_name
- if not contact:
- if row.kind == KindConstant.CHAT_MSG_SENT:
- contact = app.nicks[self.account]
- else:
- contact = self.contact.name
-
- time_obj = localtime(row.time)
- date_obj = date.fromtimestamp(row.time)
- date_ = strftime('%Y-%m-%d', time_obj)
- time_ = strftime('%H:%M:%S', time_obj)
-
- if date_obj == date.today():
- formatted = "[%s] %s: %s" % (time_, contact, row.message)
- else:
- formatted = "[%s, %s] %s: %s" % (
- date_, time_, contact, row.message)
-
- self.echo(formatted)
-
- @command(raw=True, empty=True)
- # Do not translate online, away, chat, xa, dnd
- @doc(_("""
- Set the current status
-
- Status can be given as one of the following values:
- online, away, chat, xa, dnd.
- """))
- def status(self, status, message):
- if status not in ('online', 'away', 'chat', 'xa', 'dnd'):
- raise CommandError("Invalid status given")
- for connection in app.connections.values():
- if not app.settings.get_account_setting(connection.name,
- 'sync_with_global_status'):
- continue
- if not connection.state.is_available:
- continue
- connection.change_status(status, message)
-
- @command(raw=True, empty=True)
- @doc(_("Set the current status to away"))
- def away(self, message):
- if not message:
- message = _("Away")
-
- for connection in app.connections.values():
- if not app.settings.get_account_setting(connection.name,
- 'sync_with_global_status'):
- continue
- if not connection.state.is_available:
- continue
- connection.change_status('away', message)
-
- @command('back', raw=True, empty=True)
- @doc(_("Set the current status to online"))
- def online(self, message):
- if not message:
- message = _("Available")
-
- for connection in app.connections.values():
- if not app.settings.get_account_setting(connection.name,
- 'sync_with_global_status'):
- continue
- if not connection.state.is_available:
- continue
- connection.change_status('online', message)
-
- @command
- @doc(_("Send a disco info request"))
- def disco(self):
- client = app.get_client(self.account)
- if not client.state.is_available:
- return
-
- client.get_module('Discovery').disco_contact(self.contact)
-
-
-class StandardCommonChatCommands(CommandContainer):
- """
- This command container contains standard commands, which are common
- to a chat and a private chat only.
- """
-
- AUTOMATIC = True
- HOSTS = ChatCommands, PrivateChatCommands
-
- @command
- @doc(_("Clear the text window"))
- def clear(self):
- self.conversation_view.clear()
-
- @command
- @doc(_("Send a ping to the contact"))
- def ping(self):
- app.connections[self.account].get_module('Ping').send_ping(self.contact)
-
- @command
- @doc(_("Send DTMF sequence through an open voice chat"))
- def dtmf(self, sequence):
- if not self.audio_sid:
- raise CommandError(_("No open voice chats with the contact"))
- for tone in sequence:
- if not (tone in ("*", "#") or tone.isdigit()):
- raise CommandError(_("%s is not a valid tone") % tone)
- gjs = self.connection.get_module('Jingle').get_jingle_session
- session = gjs(self.full_jid, self.audio_sid)
- content = session.get_content("audio")
- content.batch_dtmf(sequence)
-
- @command
- @doc(_("Toggle Voice Chat"))
- def audio(self):
- if not self.audio_available:
- raise CommandError(_("Voice chats are not available"))
- # An audio session is toggled by inverting the state of the
- # appropriate button.
- state = self._audio_button.get_active()
- self._audio_button.set_active(not state)
-
- @command
- @doc(_("Toggle Video Chat"))
- def video(self):
- if not self.video_available:
- raise CommandError(_("Video chats are not available"))
- # A video session is toggled by inverting the state of the
- # appropriate button.
- state = self._video_button.get_active()
- self._video_button.set_active(not state)
-
- @command(raw=True)
- @doc(_("Send a message to the contact that will attract their attention"))
- def attention(self, message):
- self.send_message(message, process_commands=False, attention=True)
-
-
-class StandardChatCommands(CommandContainer):
- """
- This command container contains standard commands which are unique
- to a chat.
- """
-
- AUTOMATIC = True
- HOSTS = (ChatCommands,)
-
-
-class StandardPrivateChatCommands(CommandContainer):
- """
- This command container contains standard commands which are unique
- to a private chat.
- """
-
- AUTOMATIC = True
- HOSTS = (PrivateChatCommands,)
-
-
-class StandardGroupChatCommands(CommandContainer):
- """
- This command container contains standard commands which are unique
- to a group chat.
- """
-
- AUTOMATIC = True
- HOSTS = (GroupChatCommands,)
-
- @command
- @doc(_("Clear the text window"))
- def clear(self):
- self.conversation_view.clear()
-
- @command(raw=True)
- @doc(_("Change your nickname in a group chat"))
- def nick(self, new_nick):
- try:
- new_nick = helpers.parse_resource(new_nick)
- except Exception:
- raise CommandError(_("Invalid nickname"))
- # FIXME: Check state of MUC
- self.connection.get_module('MUC').change_nick(
- self.room_jid, new_nick)
- self.new_nick = new_nick
-
- @command('query', raw=True)
- @doc(_("Open a private chat window with a specified participant"))
- def chat(self, nick):
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
- nicks = groupchat_contact.get_user_nicknames()
- if nick in nicks:
- self.send_pm(nick)
- else:
- raise CommandError(_("Nickname not found"))
-
- @command('msg', raw=True)
- @doc(_("Open a private chat window with a specified participant and send "
- "him a message"))
- def message(self, nick, message):
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
- nicks = groupchat_contact.get_user_nicknames()
- if nick in nicks:
- self.send_pm(nick, message)
- else:
- raise CommandError(_("Nickname not found"))
-
- @command(raw=True, empty=True)
- @doc(_("Display or change a group chat topic"))
- def topic(self, new_topic):
- if new_topic:
- self.connection.get_module('MUC').set_subject(
- self.room_jid, new_topic)
- else:
- return self.subject
-
- @command(raw=True, empty=True)
- @doc(_("Invite a user to a group chat for a reason"))
- def invite(self, jid, _reason):
- control = app.window.get_control(self.account, self.room_jid)
- if control is not None:
- control.invite(jid)
-
- @command(raw=True, empty=True)
- @doc(_("Join a group chat given by an XMPP Address"))
- def join(self, jid):
- if '@' not in jid:
- jid = jid + '@' + app.get_server_from_jid(self.room_jid)
-
- app.app.activate_action(
- 'groupchat-join',
- GLib.Variant('as', [self.account, jid]))
-
- @command('part', 'close')
- @doc(_("Leave the group chat"))
- def leave(self):
- # Use idle_add to let command system finish printing
- variant = GLib.Variant('as', [self.account, str(self.room_jid)])
- GLib.idle_add(app.window.activate_action, 'remove-chat', variant)
-
- @command(raw=True, empty=True)
- @doc(_("""
- Ban user by a nick or a JID from a groupchat
-
- If given nickname is not found it will be treated as a JID.
- """))
- def ban(self, who, reason=''):
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
- nick_list = groupchat_contact.get_user_nicknames()
- if who in nick_list:
- contact = groupchat_contact.get_resource(who)
- who = contact.jid
- self.connection.get_module('MUC').set_affiliation(
- self.room_jid,
- {who: {'affiliation': 'outcast',
- 'reason': reason}})
-
- @command(raw=True, empty=True)
- @doc(_("Kick user from group chat by nickname"))
- def kick(self, who, reason):
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
- nick_list = groupchat_contact.get_user_nicknames()
- if who not in nick_list:
- raise CommandError(_("Nickname not found"))
- self.connection.get_module('MUC').set_role(
- self.room_jid, who, 'none', reason)
-
- @command(raw=True)
- # Do not translate moderator, participant, visitor, none
- @doc(_("""Set participant role in group chat.
- Role can be given as one of the following values:
- moderator, participant, visitor, none"""))
- def role(self, who, role):
- if role not in ('moderator', 'participant', 'visitor', 'none'):
- raise CommandError(_("Invalid role given"))
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
- nick_list = groupchat_contact.get_user_nicknames()
- if who not in nick_list:
- raise CommandError(_("Nickname not found"))
- self.connection.get_module('MUC').set_role(self.room_jid, who, role)
-
- @command(raw=True)
- # Do not translate owner, admin, member, outcast, none
- @doc(_("""Set participant affiliation in group chat.
- Affiliation can be given as one of the following values:
- owner, admin, member, outcast, none"""))
- def affiliate(self, who, affiliation):
- if affiliation not in ('owner', 'admin', 'member', 'outcast', 'none'):
- raise CommandError(_("Invalid affiliation given"))
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
- nick_list = groupchat_contact.get_user_nicknames()
- if who not in nick_list:
- raise CommandError(_("Nickname not found"))
-
- contact = groupchat_contact.get_resource(who)
-
- self.connection.get_module('MUC').set_affiliation(
- self.room_jid,
- {contact.jid: {'affiliation': affiliation}})
-
- @command
- @doc(_("Display names of all group chat participants"))
- def names(self, verbose=False):
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
-
- def get_contact(nick):
- return groupchat_contact.get_resource(nick)
-
- def get_role(nick):
- return get_contact(nick).role
-
- nicks = groupchat_contact.get_user_nicknames()
-
- nicks = sorted(nicks)
- nicks = sorted(nicks, key=get_role)
-
- if not verbose:
- return ", ".join(nicks)
-
- for nick in nicks:
- contact = get_contact(nick)
- role = helpers.get_uf_role(contact.role)
- affiliation = helpers.get_uf_affiliation(contact.affiliation)
- self.echo("%s - %s - %s" % (nick, role, affiliation))
-
- @command('ignore', raw=True)
- @doc(_("Forbid a participant to send you public or private messages"))
- def block(self, who):
- self.on_block(None, who)
-
- @command('unignore', raw=True)
- @doc(_("Allow a participant to send you public or private messages"))
- def unblock(self, who):
- self.on_unblock(None, who)
-
- @command
- @doc(_("Send a ping to the contact"))
- def ping(self, nick):
- client = app.get_client(self.account)
- groupchat_contact = client.get_module('Contacts').get_contact(
- self.room_jid, groupchat=True)
- nick_list = groupchat_contact.get_user_nicknames()
- if nick not in nick_list:
- raise CommandError(_("Unknown nickname"))
-
- client.get_module('Ping').send_ping(
- groupchat_contact.get_resource(nick))
diff --git a/gajim/command_system/mapping.py b/gajim/command_system/mapping.py
deleted file mode 100644
index c0546a726..000000000
--- a/gajim/command_system/mapping.py
+++ /dev/null
@@ -1,349 +0,0 @@
-# Copyright (C) 2009-2010 Alexander Cherniuk <ts33kr@gmail.com>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-The module contains routines to parse command arguments and map them to
-the command handler's positional and keyword arguments.
-
-Mapping is done in two stages: 1) parse arguments into positional
-arguments and options; 2) adapt them to the specific command handler
-according to the command properties.
-"""
-
-import re
-from operator import itemgetter
-
-from gajim.common.i18n import _
-
-from gajim.command_system.errors import DefinitionError
-from gajim.command_system.errors import CommandError
-
-# Quite complex piece of regular expression logic to parse options and
-# arguments. Might need some tweaking along the way.
-ARG_PATTERN = re.compile(r'(\'|")?(?P<body>(?(1).+?|\S+))(?(1)\1)')
-OPT_PATTERN = re.compile(r'(?<!\w)--?(?P<key>[\w-]+)(?:(?:=|\s)(\'|")?(?P<value>(?(2)[^-]+?|[^-\s]+))(?(2)\2))?')
-
-# Option keys needs to be encoded to a specific encoding as Python does
-# not allow to expand dictionary with raw Unicode strings as keys from a
-# **kwargs.
-KEY_ENCODING = 'UTF-8'
-
-# Defines how complete representation of command usage (generated based
-# on command handler argument specification) will be rendered.
-USAGE_PATTERN = 'Usage: %s %s'
-
-
-def parse_arguments(arguments):
- """
- Simple yet effective and sufficient in most cases parser which
- parses command arguments and returns them as two lists.
-
- First list represents positional arguments as (argument, position),
- and second representing options as (key, value, position) tuples,
- where position is a (start, end) span tuple of where it was found in
- the string.
-
- Options may be given in --long or -short format. As --option=value
- or --option value or -option value. Keys without values will get
- None as value.
-
- Arguments and option values that contain spaces may be given as 'one
- two three' or "one two three"; that is between single or double
- quotes.
- """
- args, opts = [], []
-
- def intersects_opts(given_start, given_end):
- """
- Check if given span intersects with any of options.
- """
- for _key, _value, (start, end) in opts:
- if given_start >= start and given_end <= end:
- return True
- return False
-
- def intersects_args(given_start, given_end):
- """
- Check if given span intersects with any of arguments.
- """
- for _arg, (start, end) in args:
- if given_start >= start and given_end <= end:
- return True
- return False
-
- for match in re.finditer(OPT_PATTERN, arguments):
- if match:
- key = match.group('key')
- value = match.group('value') or None
- position = match.span()
- opts.append((key, value, position))
-
- for match in re.finditer(ARG_PATTERN, arguments):
- if match:
- body = match.group('body')
- position = match.span()
- args.append((body, position))
-
- # Primitive but sufficiently effective way of disposing of
- # conflicted sectors. Remove any arguments that intersect with
- # options.
- for arg, position in args[:]:
- if intersects_opts(*position):
- args.remove((arg, position))
-
- # Primitive but sufficiently effective way of disposing of
- # conflicted sectors. Remove any options that intersect with
- # arguments.
- for key, value, position in opts[:]:
- if intersects_args(*position):
- opts.remove((key, value, position))
-
- return args, opts
-
-
-def adapt_arguments(command, arguments, args, opts):
- """
- Adapt args and opts got from the parser to a specific handler by
- means of arguments specified on command definition. That is
- transform them to *args and **kwargs suitable for passing to a
- command handler.
-
- Dashes (-) in the option names will be converted to underscores. So
- you can map --one-more-option to a one_more_option=None.
-
- If the initial value of a keyword argument is a boolean (False in
- most cases) - then this option will be treated as a switch, that is
- an option which does not take an argument. If a switch is followed
- by an argument - then this argument will be treated just like a
- normal positional argument.
- """
- spec_args, spec_kwargs, var_args, _var_kwargs = command.extract_specification()
- norm_kwargs = dict(spec_kwargs)
-
- # Quite complex piece of neck-breaking logic to extract raw
- # arguments if there is more, then one positional argument specified
- # by the command. In case if it's just one argument which is the
- # collector - this is fairly easy. But when it's more then one
- # argument - the neck-breaking logic of how to retrieve residual
- # arguments as a raw, all in one piece string, kicks in.
- if command.raw:
- if arguments:
- spec_fix = 1 if command.source else 0
- spec_len = len(spec_args) - spec_fix
- arguments_end = len(arguments) - 1
-
- # If there are any optional arguments given they should be
- # either an unquoted positional argument or part of the raw
- # argument. So we find all optional arguments that can
- # possibly be unquoted argument and append them as is to the
- # args.
- for key, value, (start, end) in opts[:spec_len]:
- if value:
- end -= len(value) + 1
- args.append((arguments[start:end], (start, end)))
- args.append((value, (end, end + len(value) + 1)))
- else:
- args.append((arguments[start:end], (start, end)))
-
- # We need in-place sort here because after manipulations
- # with options order of arguments might be wrong and we just
- # can't have more complex logic to not let that happen.
- args.sort(key=itemgetter(1))
-
- if spec_len > 1:
- try:
- _stopper, (start, end) = args[spec_len - 2]
- except IndexError:
- raise CommandError(_("Missing arguments"), command)
-
- # The essential point of the whole play. After
- # boundaries are being determined (supposedly correct)
- # we separate raw part from the rest of arguments, which
- # should be normally processed.
- raw = arguments[end:]
- raw = raw.strip() or None
-
- if not raw and not command.empty:
- raise CommandError(_("Missing arguments"), command)
-
- # Discard residual arguments and all of the options as
- # raw command does not support options and if an option
- # is given it is rather a part of a raw argument.
- args = args[:spec_len - 1]
- opts = []
-
- args.append((raw, (end, arguments_end)))
- else:
- # Substitute all of the arguments with only one, which
- # contain raw and unprocessed arguments as a string. And
- # discard all the options, as raw command does not
- # support them.
- args = [(arguments, (0, arguments_end))]
- opts = []
- else:
- if command.empty:
- args.append((None, (0, 0)))
- else:
- raise CommandError(_("Missing arguments"), command)
-
- # The first stage of transforming options we have got to a format
- # that can be used to associate them with declared keyword
- # arguments. Substituting dashes (-) in their names with
- # underscores (_).
- for index, (key, value, position) in enumerate(opts):
- if '-' in key:
- opts[index] = (key.replace('-', '_'), value, position)
-
- # The second stage of transforming options to an associable state.
- # Expanding short, one-letter options to a verbose ones, if
- # corresponding opt-in has been given.
- if command.expand:
- expanded = []
- for spec_key in norm_kwargs.keys():
- letter = spec_key[0] if len(spec_key) > 1 else None
- if letter and letter not in expanded:
- for index, (key, value, position) in enumerate(opts):
- if key == letter:
- expanded.append(letter)
- opts[index] = (spec_key, value, position)
- break
-
- # Detect switches and set their values accordingly. If any of them
- # carries a value - append it to args.
- for index, (key, value, position) in enumerate(opts):
- if isinstance(norm_kwargs.get(key), bool):
- opts[index] = (key, True, position)
- if value:
- args.append((value, position))
-
- # Sorting arguments and options (just to be sure) in regarding to
- # their positions in the string.
- args.sort(key=itemgetter(1))
- opts.sort(key=itemgetter(2))
-
- # Stripping down position information supplied with arguments and
- # options as it won't be needed again.
- args = list(map(lambda t: t[0], args))
- opts = list(map(lambda t: (t[0], t[1]), opts))
-
- # If command has extra option enabled - collect all extra arguments
- # and pass them to a last positional argument command defines as a
- # list.
- if command.extra:
- if not var_args:
- spec_fix = 1 if not command.source else 2
- spec_len = len(spec_args) - spec_fix
- extra = args[spec_len:]
- args = args[:spec_len]
- args.append(extra)
- else:
- raise DefinitionError("Can not have both, extra and *args")
-
- # Detect if positional arguments overlap keyword arguments. If so
- # and this is allowed by command options - then map them directly to
- # their options, so they can get proper further processing.
- spec_fix = 1 if command.source else 0
- spec_len = len(spec_args) - spec_fix
- if len(args) > spec_len:
- if command.overlap:
- overlapped = args[spec_len:]
- args = args[:spec_len]
- for arg, spec_key, _spec_value in zip(overlapped, spec_kwargs):
- opts.append((spec_key, arg))
- else:
- raise CommandError(_("Too many arguments"), command)
-
- # Detect every switch and ensure it will not receive any arguments.
- # Normally this does not happen unless overlapping is enabled.
- for key, value in opts:
- initial = norm_kwargs.get(key)
- if isinstance(initial, bool):
- if not isinstance(value, bool):
- raise CommandError(
- "%s: Switch can not take an argument" % key, command)
-
- # Inject the source arguments as a string as a first argument, if
- # command has enabled the corresponding option.
- if command.source:
- args.insert(0, arguments)
-
- # Return *args and **kwargs in the form suitable for passing to a
- # command handler and being expanded.
- return tuple(args), dict(opts)
-
-
-def generate_usage(command, complete=True):
- """
- Extract handler's arguments specification and wrap them in a
- human-readable format usage information. If complete is given - then
- USAGE_PATTERN will be used to render the specification completely.
- """
- spec_args, spec_kwargs, var_args, var_kwargs = command.extract_specification()
-
- # Remove some special positional arguments from the specification,
- # but store their names so they can be used for usage info
- # generation.
- _sp_source = spec_args.pop(0) if command.source else None
- sp_extra = spec_args.pop() if command.extra else None
-
- kwargs = []
- letters = []
-
- for key, value in spec_kwargs:
- letter = key[0]
- key = key.replace('_', '-')
-
- if isinstance(value, bool):
- value = str()
- else:
- value = '=%s' % value
-
- if letter not in letters:
- kwargs.append('-(-%s)%s%s' % (letter, key[1:], value))
- letters.append(letter)
- else:
- kwargs.append('--%s%s' % (key, value))
-
- usage = str()
- args = str()
-
- if command.raw:
- spec_len = len(spec_args) - 1
- if spec_len:
- args += ('<%s>' % ', '.join(spec_args[:spec_len])) + ' '
- args += ('(|%s|)' if command.empty else '|%s|') % spec_args[-1]
- else:
- if spec_args:
- args += '<%s>' % ', '.join(spec_args)
- if var_args or sp_extra:
- args += (' ' if spec_args else str()) + '<<%s>>' % (
- var_args or sp_extra)
-
- usage += args
-
- if kwargs or var_kwargs:
- if kwargs:
- usage += (' ' if args else str()) + '[%s]' % ', '.join(kwargs)
- if var_kwargs:
- usage += (' ' if args else str()) + '[[%s]]' % var_kwargs
-
- # Native name will be the first one if it is included. Otherwise,
- # names will be in the order they were specified.
- if len(command.names) > 1:
- names = '%s (%s)' % (command.first_name, ', '.join(command.names[1:]))
- else:
- names = command.first_name
-
- return USAGE_PATTERN % (names, usage) if complete else usage
diff --git a/gajim/command_system/tools.py b/gajim/command_system/tools.py
deleted file mode 100644
index cde3e17d9..000000000
--- a/gajim/command_system/tools.py
+++ /dev/null
@@ -1,34 +0,0 @@
-# Copyright (c) 2010, Alexander Cherniuk (ts33kr@gmail.com)
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright
-# notice, this list of conditions and the following disclaimer in the
-# documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-
-def remove(sequence, target):
- if isinstance(sequence, list):
- if target in sequence:
- sequence.remove(target)
- elif isinstance(sequence, dict):
- if target in sequence:
- del sequence[target]
diff --git a/gajim/common/setting_values.py b/gajim/common/setting_values.py
index ccf14f1da..63c293d16 100644
--- a/gajim/common/setting_values.py
+++ b/gajim/common/setting_values.py
@@ -45,7 +45,6 @@ BoolSettings = Literal[
'change_roster_title',
'chat_merge_consecutive_nickname',
'check_for_update',
- 'command_system_execute',
'confirm_close_muc',
'confirm_close_multiple_tabs',
'confirm_on_window_delete',
@@ -191,7 +190,6 @@ APP_SETTINGS = {
'chat_merge_consecutive_nickname': True,
'chat_timestamp_format': '%H:%M',
'check_for_update': True,
- 'command_system_execute': False,
'confirm_block': '',
'confirm_close_muc': True,
'confirm_close_multiple_tabs': True,
@@ -647,9 +645,6 @@ ADVANCED_SETTINGS = {
'if there are multiple messages from the same sender within a '
'specific timespan.'),
'chat_timestamp_format': 'https://docs.python.org/3/library/time.html#time.strftime', # noqa: E501
- 'command_system_execute': _(
- 'If enabled, Gajim will execute commands '
- '(/show, /sh, /execute, /exec).'),
'confirm_block': _(
'Show a confirmation dialog to block a contact? Empty string '
'means never show the dialog.'),
diff --git a/setup.cfg b/setup.cfg
index 536cd60d8..4095bb2ce 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -7,7 +7,6 @@ exclude =
debian_build,
test,
typings,
- gajim/command_system,
gajim/common/config.py,
gajim/common/optparser.py,
gajim/common/socks5.py,