Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e5ed4e3d7c | ||
|
|
d3dc59b141 | ||
|
|
e8b3948ad0 | ||
|
|
1003cd2c6d | ||
|
|
0805f77f45 | ||
|
|
441e9cec52 | ||
|
|
40fef895ba | ||
|
|
b51b942103 | ||
|
|
0c955f6abd | ||
|
|
fc0a385027 |
23 changed files with 537 additions and 306 deletions
139
doc/apps.rst
139
doc/apps.rst
|
|
@ -1,21 +1,27 @@
|
||||||
.. _libervia-app-config:
|
.. _libervia-app-config:
|
||||||
|
|
||||||
=================================
|
Libervia Apps
|
||||||
Libervia App Configuration (YAML)
|
=============
|
||||||
=================================
|
|
||||||
|
|
||||||
The Libervia application uses a YAML configuration file to define various aspects of the
|
Libervia Apps are third party software, embedded and integrated in Libervia, usually as
|
||||||
application's behavior, environment, and settings.
|
containers (Podman, Docker).
|
||||||
|
|
||||||
The file will be parsed by `PyYAML`_ which is a full-featured YAML implementation. Check
|
Each app is defined by a YAML configuration file, which describes how to run it and which
|
||||||
its documentation for details.
|
settings are exposed. App files are named ``libervia_app_<name>.yaml`` and are discovered
|
||||||
|
from the app manager plugin directories.
|
||||||
|
|
||||||
Below is the documentation explaining the structure and functionality of each field in the
|
Below is the documentation of each field of the YAML file.
|
||||||
YAML file.
|
|
||||||
|
|
||||||
=================
|
-----------------
|
||||||
Root-Level Fields
|
Root-Level Fields
|
||||||
=================
|
-----------------
|
||||||
|
|
||||||
|
- ``name``: Human readable name of the application. The file name (without the
|
||||||
|
``libervia_app_`` prefix) is used when not specified.
|
||||||
|
|
||||||
|
- ``single_instance``: When ``True`` (default), only one instance of the application can
|
||||||
|
be started at a time, and its data are kept between restarts. When ``False``, each start
|
||||||
|
creates a new temporary instance directory, removed when the backend is unloaded.
|
||||||
|
|
||||||
- ``type``: Specifies the type of the application. See :ref:`libervia-app_type`.
|
- ``type``: Specifies the type of the application. See :ref:`libervia-app_type`.
|
||||||
|
|
||||||
|
|
@ -27,16 +33,18 @@ Root-Level Fields
|
||||||
|
|
||||||
- ``expose``: Specifies configurations exposed to frontends or administrators. See :ref:`libervia-app_expose`.
|
- ``expose``: Specifies configurations exposed to frontends or administrators. See :ref:`libervia-app_expose`.
|
||||||
|
|
||||||
==================
|
------------------
|
||||||
Detailed Sections
|
Detailed Sections
|
||||||
===================
|
------------------
|
||||||
|
|
||||||
.. _libervia-app_type:
|
.. _libervia-app_type:
|
||||||
|
|
||||||
type
|
type
|
||||||
^^^^
|
^^^^
|
||||||
|
|
||||||
Currently, the only supported type is ``docker-compose``.
|
Currently, the only supported type is ``docker-compose``. The container engine is chosen
|
||||||
|
with the ``engine`` plugin setting: ``auto`` (default, Podman is tried first), ``docker``
|
||||||
|
or ``podman``.
|
||||||
|
|
||||||
.. _libervia-app_prepare:
|
.. _libervia-app_prepare:
|
||||||
|
|
||||||
|
|
@ -46,7 +54,7 @@ prepare
|
||||||
The ``prepare`` section specifies things like git repositories to be cloned before running the application.
|
The ``prepare`` section specifies things like git repositories to be cloned before running the application.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
~~~~~~~
|
||||||
Cloning the repository at `https://example.org/some/repository.git` to prepare the app:
|
Cloning the repository at `https://example.org/some/repository.git` to prepare the app:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
@ -65,7 +73,7 @@ The YAML key is the name of the file to be created, and the content specified wi
|
||||||
populate that file.
|
populate that file.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
~~~~~~~
|
||||||
Creating a file named `settings-override.py` with the content `USE_X_FORWARDED_HOST = True`:
|
Creating a file named `settings-override.py` with the content `USE_X_FORWARDED_HOST = True`:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
@ -86,7 +94,7 @@ creates a ``docker-compose.override.yml`` file that will be merged with the defa
|
||||||
<https://docs.docker.com/compose/reference/#specifying-multiple-compose-files>`_.
|
<https://docs.docker.com/compose/reference/#specifying-multiple-compose-files>`_.
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
~~~~~~~
|
||||||
Overriding the `ports` for the `example_app` service to expose port `8080`:
|
Overriding the `ports` for the `example_app` service to expose port `8080`:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
@ -102,9 +110,31 @@ Overriding the `ports` for the `example_app` service to expose port `8080`:
|
||||||
Exposing Data
|
Exposing Data
|
||||||
^^^^^^^^^^^^^
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
The `expose` section specifies the configurations that are exposed to users or frontends.
|
The ``expose`` section specifies the data exposed to users, frontends or administrators.
|
||||||
See :ref:`libervia-app_param` for a reference of some exposed data. Ports and generated
|
|
||||||
passwords are exposed in dicts, respectively at the ``ports`` and ``passwords`` key.
|
The following keys are used by the Web frontend:
|
||||||
|
|
||||||
|
url_prefix
|
||||||
|
The internal URL where the app will be served.
|
||||||
|
web_label
|
||||||
|
Label which will be used in the Web frontend.
|
||||||
|
web_url_path
|
||||||
|
Public facing URL path which will be used in the Web frontend to access the app.
|
||||||
|
web_external
|
||||||
|
If True, the Web frontend will open the app in a separated page instead of embedding
|
||||||
|
it.
|
||||||
|
front_url
|
||||||
|
Whole URL to access the app, useful when it has its own domain or subdomain. The
|
||||||
|
``https`` scheme is added if not specified.
|
||||||
|
|
||||||
|
Ports and generated passwords are exposed in dicts, respectively at the ``ports`` and
|
||||||
|
``passwords`` keys. Each entry of ``ports`` maps a name to the compose ``service`` and
|
||||||
|
``private`` (container) port, it is resolved to the exposed ``host:port`` when the app is
|
||||||
|
running. Each entry of ``passwords`` maps a name to the list of keys leading to the value
|
||||||
|
to expose in the application data.
|
||||||
|
|
||||||
|
``url_prefix`` and ``front_url`` can also be specified as a list of keys leading to the
|
||||||
|
value in the application data.
|
||||||
|
|
||||||
.. _libervia-app_yaml-tags:
|
.. _libervia-app_yaml-tags:
|
||||||
|
|
||||||
|
|
@ -116,32 +146,49 @@ The following YAML tags can be used in the Libervia App configuration file:
|
||||||
.. _libervia-app_conf:
|
.. _libervia-app_conf:
|
||||||
|
|
||||||
!libervia_conf
|
!libervia_conf
|
||||||
++++++++++++++
|
^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Get a value from Libervia configuration. A list is expected with either:
|
Get a value from the Libervia configuration. It takes a list of 1 to 4 items:
|
||||||
|
|
||||||
- `name` of a config parameter
|
- ``[NAME]``: the ``NAME`` option of the ``[common]`` section
|
||||||
- `section` and `name` of a config parameter
|
- ``[SECTION, NAME]``: the ``NAME`` option of the section named ``SECTION``
|
||||||
- `section`, `name`, and `default` value of a config parameter
|
- ``[SECTION, NAME, DEFAULT]``: use ``DEFAULT`` if the option is not set
|
||||||
- `section`, `name`, `default` value, and `filter` (either `first` or `not`)
|
- ``[SECTION, NAME, DEFAULT, FILTER]``: apply a filter to the value (see below)
|
||||||
|
|
||||||
Filter options:
|
The ``application.<name>`` section refers to the configuration section of the
|
||||||
|
``<name>`` application (e.g. ``application.peertube``).
|
||||||
|
|
||||||
- `first`: get the first item of the value
|
Available filters:
|
||||||
- `not`: get the opposite value (to be used with booleans)
|
|
||||||
|
|
||||||
Example
|
- ``first``: get the first item of the value
|
||||||
-------
|
- ``not``: get the opposite value (to be used with booleans)
|
||||||
Getting a value from Libervia configuration:
|
|
||||||
|
Examples
|
||||||
|
~~~~~~~~
|
||||||
|
Getting a value from an application configuration section:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
!libervia_conf [section, name, default]
|
!libervia_conf [application.peertube, hostname]
|
||||||
|
|
||||||
|
Getting the SMTP server from the ``[email]`` section (an empty default is
|
||||||
|
used when the option is not set):
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
!libervia_conf [email, server, ""]
|
||||||
|
|
||||||
|
Getting the first email of the ``[common]`` section admin list (using the
|
||||||
|
``first`` filter):
|
||||||
|
|
||||||
|
.. code-block:: yaml
|
||||||
|
|
||||||
|
!libervia_conf [common, email_admins_list, "", first]
|
||||||
|
|
||||||
.. _libervia-app_generate_pwd:
|
.. _libervia-app_generate_pwd:
|
||||||
|
|
||||||
!libervia_generate_pwd
|
!libervia_generate_pwd
|
||||||
+++++++++++++++++++++
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Generate a password and store it in persistent data. If the password has already been generated previously, it is reused.
|
Generate a password and store it in persistent data. If the password has already been generated previously, it is reused.
|
||||||
|
|
||||||
|
|
@ -152,7 +199,7 @@ Arguments:
|
||||||
- `size` (int): Size of the password to generate. Default to 30
|
- `size` (int): Size of the password to generate. Default to 30
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
~~~~~~~
|
||||||
Generating a password named ``some_password`` with a size of 32 characters:
|
Generating a password named ``some_password`` with a size of 32 characters:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
@ -162,35 +209,23 @@ Generating a password named ``some_password`` with a size of 32 characters:
|
||||||
.. _libervia-app_param:
|
.. _libervia-app_param:
|
||||||
|
|
||||||
!libervia_param
|
!libervia_param
|
||||||
++++++++++++++
|
^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Get a parameter specified when starting the application. The value can be either the name
|
Get a parameter specified when starting the application. The value can be either the name
|
||||||
of the parameter to get, or a list as `[name, default_value]`.
|
of the parameter to get, or a list as `[name, default_value]`.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Available parameters:
|
Available parameters:
|
||||||
|
|
||||||
url_prefix
|
url_prefix
|
||||||
The internal URL where the app will be served.
|
The internal URL where the app will be served, it is given by the Web frontend.
|
||||||
web_label
|
front_url_path
|
||||||
Label which will be used in Web frontend.
|
The URL path which will be used in the Web frontend to access the app.
|
||||||
web_url_path
|
|
||||||
Public facing URL path which will be used in the web frontend to access the app.
|
|
||||||
web_external
|
|
||||||
If True, the web frontend will open the app in a separated page instead of embedding
|
|
||||||
it.
|
|
||||||
front_url
|
|
||||||
Whole URL to access the app. It is specified when the app has its own domain or
|
|
||||||
subdomain.
|
|
||||||
|
|
||||||
Example
|
Example
|
||||||
-------
|
~~~~~~~
|
||||||
Getting a parameter:
|
Getting a parameter:
|
||||||
|
|
||||||
.. code-block:: yaml
|
.. code-block:: yaml
|
||||||
|
|
||||||
!libervia_param [url_prefix, /some_app]
|
!libervia_param [url_prefix, /some_app]
|
||||||
|
|
||||||
.. _PyYAML: https://pyyaml.org/
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
.. _components:
|
.. _components:
|
||||||
|
|
||||||
===================
|
==========
|
||||||
Libervia Components
|
Components
|
||||||
===================
|
==========
|
||||||
|
|
||||||
Libervia can act as an XMPP server component, which can be seen as a generic plugin for
|
Libervia can act as an XMPP server component, which can be seen as a generic plugin for
|
||||||
XMPP servers.
|
XMPP servers.
|
||||||
|
|
@ -38,23 +38,20 @@ OpenFire
|
||||||
Prosody
|
Prosody
|
||||||
https://prosody.im/doc/components
|
https://prosody.im/doc/components
|
||||||
|
|
||||||
Tigase
|
.. Tigase documentation is down (2026-11-03, the whole docs.tigase.net site is 404),
|
||||||
https://docs.tigase.net/en/latest/Tigase_Administration/Components/_Components.html#external-component-configuration
|
uncomment once it's back:
|
||||||
|
.. Tigase
|
||||||
|
.. https://docs.tigase.net/en/latest/Tigase_Administration/Components/_Components.html#external-component-configuration
|
||||||
|
|
||||||
Simplified Component Configuration
|
Simplified Component Configuration
|
||||||
==================================
|
==================================
|
||||||
|
|
||||||
Most user will want to use this simplified configuration. To set up a component, you
|
Most user will want to use this simplified configuration. To set up a component, you
|
||||||
simply need to create a configuration section with ``[component.<entry_point>]`` where
|
simply need to create a configuration section named after the entry point, with dashes
|
||||||
you'll set the ``jid`` and ``secret`` options. Everything will be automatically configured
|
replaced by underscores (e.g. the ``email-gateway`` entry point uses the
|
||||||
on backend startup, and the component will start automatically.
|
``[component.email_gateway]`` section), where you'll set the ``jid`` and ``secret``
|
||||||
|
options. Everything will be automatically configured on backend startup, and the
|
||||||
settings
|
component will start automatically.
|
||||||
--------
|
|
||||||
|
|
||||||
In ``[component.<entry_point>]``:
|
|
||||||
|
|
||||||
.. autopydantic_settings:: libervia.backend.tools.config.ComponentSettings
|
|
||||||
|
|
||||||
example
|
example
|
||||||
-------
|
-------
|
||||||
|
|
@ -69,6 +66,10 @@ find that it's ``pubsub``, then you simply put in your configuration (usually
|
||||||
jid = "pubsub.example.org"
|
jid = "pubsub.example.org"
|
||||||
secret = "your_shared_secret"
|
secret = "your_shared_secret"
|
||||||
|
|
||||||
|
To run the component in an isolated backend instance (e.g. as a dedicated systemd
|
||||||
|
service), see :ref:`installation_system`. To work with such an instance from the CLI,
|
||||||
|
use the ``-i/--isolation`` option (see :ref:`li_isolation`).
|
||||||
|
|
||||||
Manual Component Configuration
|
Manual Component Configuration
|
||||||
==============================
|
==============================
|
||||||
|
|
||||||
|
|
@ -108,6 +109,14 @@ wants to connect it immediately, she can use::
|
||||||
|
|
||||||
$ li profile connect -cp ap-gateway
|
$ li profile connect -cp ap-gateway
|
||||||
|
|
||||||
|
Common Component Settings
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Besides component specific options (see the component sections below), every
|
||||||
|
component supports these common options in its configuration section:
|
||||||
|
|
||||||
|
.. autopydantic_settings:: libervia.backend.tools.config.ComponentSettings
|
||||||
|
|
||||||
Available Components
|
Available Components
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
|
@ -139,7 +148,7 @@ entities (JIDs). Permissions can be set through Ad-Hoc Commands.
|
||||||
Configuration
|
Configuration
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
All options are to be set in ``[components.file_sharing]`` section.
|
All options are to be set in ``[component.file_sharing]`` section.
|
||||||
|
|
||||||
``http_upload_port``
|
``http_upload_port``
|
||||||
port to use for HTTP File Upload
|
port to use for HTTP File Upload
|
||||||
|
|
@ -216,7 +225,13 @@ documentation to find how this must be done.
|
||||||
Configuration
|
Configuration
|
||||||
~~~~~~~~~~~~~
|
~~~~~~~~~~~~~
|
||||||
|
|
||||||
All options are to be set in ``[components.ap_gateway]`` section.
|
All options are to be set in ``[component.ap_gateway]`` section:
|
||||||
|
|
||||||
|
.. autopydantic_settings:: libervia.backend.plugins.plugin_comp_ap_gateway.settings.APGatewaySettings
|
||||||
|
|
||||||
|
By default, the HTTP server uses HTTPS, thus a TLS certificate is required, either in the
|
||||||
|
``tls`` option or in the global ``[tls]`` section. If an HTTPS proxy already handles TLS,
|
||||||
|
use ``http_connection_type = "http"`` instead.
|
||||||
|
|
||||||
.. _ap-actor-from-xmpp:
|
.. _ap-actor-from-xmpp:
|
||||||
|
|
||||||
|
|
@ -745,7 +760,7 @@ any kind of extra informations) while AP only indicates if one will attend or no
|
||||||
event.
|
event.
|
||||||
|
|
||||||
|
|
||||||
.. _Mobilizon: https://joinmobilizon.org/
|
.. _Mobilizon: https://mobilizon.org/
|
||||||
|
|
||||||
Using the Component (for developers)
|
Using the Component (for developers)
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
@ -812,6 +827,8 @@ All options are to be set in the ``[component.conferences]`` section.
|
||||||
.. _Galène project: https://galene.org/
|
.. _Galène project: https://galene.org/
|
||||||
|
|
||||||
|
|
||||||
|
.. _email-gateway:
|
||||||
|
|
||||||
Email Gateway
|
Email Gateway
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
|
|
|
||||||
22
doc/conf.py
22
doc/conf.py
|
|
@ -60,6 +60,9 @@ rst_prolog = """
|
||||||
# ones.
|
# ones.
|
||||||
extensions = ["sphinx.ext.autodoc", "docstring", "sphinxcontrib.autodoc_pydantic"]
|
extensions = ["sphinx.ext.autodoc", "docstring", "sphinxcontrib.autodoc_pydantic"]
|
||||||
|
|
||||||
|
# do not show pydantic field constraints (custom validators are not meaningful here)
|
||||||
|
autodoc_pydantic_field_show_constraints = False
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = [".templates"]
|
templates_path = [".templates"]
|
||||||
|
|
||||||
|
|
@ -82,7 +85,8 @@ language = "en"
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
# This pattern also affects html_static_path and html_extra_path.
|
# This pattern also affects html_static_path and html_extra_path.
|
||||||
exclude_patterns = [".build", "Thumbs.db", ".DS_Store"]
|
# the TUI is temporarily on hold and broken, its documentation is excluded
|
||||||
|
exclude_patterns = [".build", "Thumbs.db", ".DS_Store", "libervia-tui"]
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = None
|
pygments_style = None
|
||||||
|
|
@ -120,7 +124,7 @@ html_static_path = [".static"]
|
||||||
# -- Options for HTMLHelp output ---------------------------------------------
|
# -- Options for HTMLHelp output ---------------------------------------------
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = "SalutToidoc"
|
htmlhelp_basename = "LiberviaBackenddoc"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output ------------------------------------------------
|
# -- Options for LaTeX output ------------------------------------------------
|
||||||
|
|
@ -144,7 +148,13 @@ latex_elements = {
|
||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
(master_doc, "SalutToi.tex", "Libervia Documentation", "Libervia team", "manual"),
|
(
|
||||||
|
master_doc,
|
||||||
|
"LiberviaBackend.tex",
|
||||||
|
"Libervia Backend Documentation",
|
||||||
|
"Libervia team",
|
||||||
|
"manual",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -163,10 +173,10 @@ man_pages = [(master_doc, "saluttoi", "Libervia Documentation", [author], 1)]
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(
|
(
|
||||||
master_doc,
|
master_doc,
|
||||||
"SalutToi",
|
"LiberviaBackend",
|
||||||
"Libervia Documentation",
|
"Libervia Backend Documentation",
|
||||||
author,
|
author,
|
||||||
"SalutToi",
|
"LiberviaBackend",
|
||||||
"One line description of project.",
|
"One line description of project.",
|
||||||
"Miscellaneous",
|
"Miscellaneous",
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ A backup is made of data *types*, included or excluded with ``-i/--include`` and
|
||||||
- ``private``: private storage: PEP-like client data, OMEMO crypto keys, PGP trust state
|
- ``private``: private storage: PEP-like client data, OMEMO crypto keys, PGP trust state
|
||||||
- ``roster``: contact lists (from the roster cache in the database, kept up to date on
|
- ``roster``: contact lists (from the roster cache in the database, kept up to date on
|
||||||
every connection)
|
every connection)
|
||||||
- ``pep``: client PEP nodes and items (nick, avatar metadata, ...)
|
- ``pep``: client PEP nodes and items (nick, avatar metadata, …)
|
||||||
- ``gpg``: GPG home per profile (public and secret keys)
|
- ``gpg``: GPG home per profile (public and secret keys)
|
||||||
- ``files``: shared files (the blobs of ``~/.sat/files/``)
|
- ``files``: shared files (the blobs of ``~/.sat/files/``)
|
||||||
- ``db``: full copy of the database (not portable, SQLite only)
|
- ``db``: full copy of the database (not portable, SQLite only)
|
||||||
|
|
|
||||||
|
|
@ -4,18 +4,23 @@
|
||||||
How-To: Email Gateway
|
How-To: Email Gateway
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
This document explains how to configure and use the Libervia Email Gateway component. **Note that the Libervia backend must be installed first** (see :ref:`installation`).
|
This document explains how to configure and use the Libervia Email Gateway component.
|
||||||
|
|
||||||
The Email Gateway component allows users to send and receive emails through XMPP clients and convert mailing list to XMPP forums. It connects to IMAP/SMTP servers using credentials provided by users during registration.
|
The Email Gateway component allows users to send and receive emails through XMPP clients
|
||||||
|
and connect mailing lists to XMPP forums. It connects to IMAP/SMTP servers using
|
||||||
|
credentials provided by users during registration.
|
||||||
|
|
||||||
|
For the supported features and other details, refer to the :ref:`email-gateway` section
|
||||||
|
of the components documentation.
|
||||||
|
|
||||||
Prerequisites
|
Prerequisites
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
Before configuring the Email Gateway, ensure you have:
|
Before configuring the Email Gateway, ensure you have:
|
||||||
|
|
||||||
1. Libervia backend installed and running (see :ref:`installation`)
|
1. Libervia backend installed and working (see :ref:`installation`)
|
||||||
2. An XMPP server configured with component support (such as Prosody, Ejabberd, Openfire,
|
2. An XMPP server configured with component support (such as Prosody, Ejabberd,
|
||||||
Tigase, MongooseIM, etc).
|
Openfire, Tigase, MongooseIM, etc).
|
||||||
|
|
||||||
Configuration Steps
|
Configuration Steps
|
||||||
-------------------
|
-------------------
|
||||||
|
|
@ -30,7 +35,7 @@ Configuration Steps
|
||||||
|
|
||||||
-- Add to /etc/prosody/prosody.cfg.lua
|
-- Add to /etc/prosody/prosody.cfg.lua
|
||||||
Component "email.example.org"
|
Component "email.example.org"
|
||||||
component_secret = "your_unique_secret"
|
component_secret = "secret"
|
||||||
|
|
||||||
*For Ejabberd:*
|
*For Ejabberd:*
|
||||||
|
|
||||||
|
|
@ -46,30 +51,26 @@ Configuration Steps
|
||||||
module: ejabberd_service
|
module: ejabberd_service
|
||||||
hosts:
|
hosts:
|
||||||
"email.example.org":
|
"email.example.org":
|
||||||
password: "your_unique_secret"
|
password: "secret"
|
||||||
|
|
||||||
**Important:** Replace `email.example.org` with your actual domain and `your_unique_secret` with a secure random string (you may use `openssl rand -hex 16` to generate).
|
Of course replace ``email.example.org`` with your actual domain and ``secret`` with a
|
||||||
|
secure random string (you may use ``openssl rand -hex 16`` to generate one).
|
||||||
|
|
||||||
2. **Create Libervia Profile for Email Gateway**
|
2. **Configure the Email Gateway Component in Libervia**
|
||||||
|
|
||||||
Use the Libervia CLI to create the gateway profile:
|
Add the following section to your Libervia configuration file (usually
|
||||||
|
``libervia.toml``), using the same JID and shared secret as the ones configured on
|
||||||
|
your XMPP server:
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: toml
|
||||||
|
|
||||||
$ li profile create email-gateway \
|
[component.email_gateway]
|
||||||
-j email.example.org \
|
jid = "email.example.org"
|
||||||
-p "" \
|
secret = "secret"
|
||||||
-x "your_unique_secret" \
|
|
||||||
-C email-gateway \
|
|
||||||
-A
|
|
||||||
|
|
||||||
This creates a profile named `email-gateway` with:
|
The component profile is then created automatically on backend startup, and the
|
||||||
|
component starts automatically. See :ref:`components` for details about the
|
||||||
- JID: `email.example.org`
|
components configuration.
|
||||||
- No profile password (`-p ""`), to avoid password prompts during Libervia backend startup.
|
|
||||||
- XMPP server shared secret (`-x "your_unique_secret"`)
|
|
||||||
- Component entry point (`-C email-gateway`)
|
|
||||||
- Auto-connect enabled (`-A`)
|
|
||||||
|
|
||||||
3. **User Registration**
|
3. **User Registration**
|
||||||
|
|
||||||
|
|
@ -81,7 +82,7 @@ Configuration Steps
|
||||||
|
|
||||||
.. code-block:: bash
|
.. code-block:: bash
|
||||||
|
|
||||||
$ li gateway register -cp your_user_profile email.example.org
|
$ li gateway register -c -p your_user_profile email.example.org
|
||||||
|
|
||||||
**Registration Fields**
|
**Registration Fields**
|
||||||
|
|
||||||
|
|
@ -117,7 +118,7 @@ Configuration Steps
|
||||||
SMTP Password
|
SMTP Password
|
||||||
Password for SMTP authentication
|
Password for SMTP authentication
|
||||||
|
|
||||||
**Important Notes**
|
.. note::
|
||||||
|
|
||||||
- Ensure your IMAP/SMTP servers are accessible from the Libervia backend
|
- Ensure your IMAP/SMTP servers are accessible from the Libervia backend
|
||||||
- The IMAP server must support the IDLE extension for real-time notifications
|
- The IMAP server must support the IDLE extension for real-time notifications
|
||||||
|
|
@ -125,47 +126,8 @@ Configuration Steps
|
||||||
Configuration
|
Configuration
|
||||||
-------------
|
-------------
|
||||||
|
|
||||||
The Email Gateway uses user-specific registration data - **no additional configuration is needed** in Libervia's main config file. All settings are stored per-user during registration.
|
Besides the component configuration described above, no other configuration is needed:
|
||||||
|
the gateway uses user-specific data provided during registration.
|
||||||
Supported Features
|
|
||||||
------------------
|
|
||||||
|
|
||||||
- **Normal Email Conversion**: Standard email messages are converted to XMPP chat
|
|
||||||
messages. Each email appears as a regular message in the user's XMPP client.
|
|
||||||
|
|
||||||
- **Mailing List Handling**: Mailing list emails are converted to PubSub blog nodes
|
|
||||||
(microblogging) using XEP-0277:
|
|
||||||
|
|
||||||
* Each mailing list becomes a separate pubsub node
|
|
||||||
* Each email in the list becomes a blog item
|
|
||||||
* Replies to specific topics appear as comments to the corresponding item
|
|
||||||
* Title tags within square brackets (e.g., `[tag]`) are converted to blog categories
|
|
||||||
* Only the recipient JID associated with the mailing list can access the pubsub node by
|
|
||||||
default.
|
|
||||||
|
|
||||||
- **Extended Addressing Support**: Properly handles email addressing metadata (To, CC,
|
|
||||||
BCC) via XEP‑0033, converting it into XMPP addressing information for correct delivery.
|
|
||||||
This allows multi‑party discussions without relying on group chat (XEP‑0045 MUC), which
|
|
||||||
can be tricky to implement in email‑based communication.
|
|
||||||
|
|
||||||
- **Header Conversion**: Converts important email headers to XMPP metadata, such as:
|
|
||||||
|
|
||||||
* Keywords → XMPP "Keywords" header (XEP-0131)
|
|
||||||
* Importance → XMPP "Urgency" header (XEP-0131)
|
|
||||||
|
|
||||||
- **Message Cleaning**: Automatically detects noisy elements in mailing list emails
|
|
||||||
(quoted replies, signatures) and tags them with a CSS class named "noise" so XMPP
|
|
||||||
clients can selectively hide or visually distinguish these parts of the message.
|
|
||||||
|
|
||||||
- **Attachment Management**: Allows users to retrieve and delete email attachments via
|
|
||||||
PubSub File Sharing (XEP-0498).
|
|
||||||
|
|
||||||
.. note::
|
|
||||||
Attachments cannot yet be sent with messages (this
|
|
||||||
functionality is in development).
|
|
||||||
|
|
||||||
- **End-to-End Encryption**: Supports Gateway Relayed Encryption (not yet an official XEP)
|
|
||||||
for encrypted email messages. Requires XMPP clients that implement this specification.
|
|
||||||
|
|
||||||
Troubleshooting
|
Troubleshooting
|
||||||
---------------
|
---------------
|
||||||
|
|
@ -178,37 +140,33 @@ Troubleshooting
|
||||||
- Solution
|
- Solution
|
||||||
* - XMPP component connection failed
|
* - XMPP component connection failed
|
||||||
- Verify the shared secret matches on **both** Libervia and the XMPP server.
|
- Verify the shared secret matches on **both** Libervia and the XMPP server.
|
||||||
The component JID must *exactly* match the JID configured on the XMPP server (e.g.
|
The component JID must *exactly* match the JID configured on the XMPP server
|
||||||
“email.example.org” in Prosody must match the JID you set when creating the
|
(e.g. "email.example.org" in Prosody must match the JID you set in the Libervia
|
||||||
Libervia profile).
|
configuration).
|
||||||
* - User registration fails
|
* - User registration fails
|
||||||
- * Check the IMAP/SMTP credentials and make sure the servers are reachable from the
|
- * Check the IMAP credentials and make sure the IMAP server is reachable from the
|
||||||
Libervia host.
|
Libervia host.
|
||||||
* Verify that the IMAP server supports the **IDLE** extension (required for
|
* Verify that the IMAP server supports the **IDLE** extension (required for
|
||||||
real‑time notifications).
|
real-time notifications).
|
||||||
* If you use self‑signed certificates, add them to the system trust store.
|
* If you use self-signed certificates, add them to the system trust store.
|
||||||
* - Email not received
|
* - Email not received
|
||||||
- * Confirm the user’s email credentials (you can re-register to check).
|
- * Confirm the user's email credentials (you can re-register to check).
|
||||||
* Check Libervia Backend logs to ensure that the IMAP connection succeeded.
|
* Check Libervia Backend logs to ensure that the IMAP connection succeeded.
|
||||||
* Verify that messages are arriving in the user’s INBOX.
|
* Verify that messages are arriving in the user's INBOX.
|
||||||
* - “DBus not launched” error
|
* - Email not sent
|
||||||
- Follow :ref:`installation_dbus` instructions for headless servers – the
|
- * Check the SMTP credentials and make sure the SMTP server is reachable from the
|
||||||
Libervia backend relies on D‑Bus to run.
|
Libervia host.
|
||||||
|
* Check the Libervia backend logs for the sending error.
|
||||||
* - Attachments not appearing
|
* - Attachments not appearing
|
||||||
- The Email Gateway can *retrieve* attachments, but does not yet deliver them with
|
- The Email Gateway can *retrieve* attachments, but does not yet deliver them with
|
||||||
outgoing messages (feature work in progress).
|
outgoing messages (feature work in progress).
|
||||||
* - Encrypted emails not working
|
* - Encrypted emails not working
|
||||||
- “Gateway Relayed Encryption” is a work‑in‑progress extension (not yet an official XEP).
|
- "Gateway Relayed Encryption" is a work-in-progress extension (not yet an official
|
||||||
Use a client that implements this specification.
|
XEP). Use a client that implements this specification.
|
||||||
* - Mailing lists not converted
|
* - Mailing lists not converted
|
||||||
- Verify that the incoming e‑mail contains a **valid “List‑ID”** header. The value
|
- Verify that the incoming e-mail contains a **valid "List-ID"** header. The value
|
||||||
must match the format expected for PubSub node creation.
|
must match the format expected for PubSub node creation.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
The Email Gateway is under active development. Some features may have limitations.
|
The Email Gateway is under active development. Some features may have limitations.
|
||||||
|
|
||||||
See Also
|
|
||||||
--------
|
|
||||||
|
|
||||||
- For Libervia components overview: :ref:`components`
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ Libervia-specific sections, so a backup remains readable by future Libervia vers
|
||||||
A backup is made of data *types*, selected with ``-i/--include`` and ``-e/--exclude``
|
A backup is made of data *types*, selected with ``-i/--include`` and ``-e/--exclude``
|
||||||
(repeatable): ``params``, ``private``, ``roster``, ``pep``, ``gpg``, ``files`` and ``db``
|
(repeatable): ``params``, ``private``, ``roster``, ``pep``, ``gpg``, ``files`` and ``db``
|
||||||
(full copy of the database, included by default on export). A backup is complete by
|
(full copy of the database, included by default on export). A backup is complete by
|
||||||
default (passwords, OMEMO keys, GPG secret keys...), so the archive should be kept
|
default (passwords, OMEMO keys, GPG secret keys…), so the archive should be kept
|
||||||
private: it is written with mode 0600, and ``--encrypt`` can be used for untrusted
|
private: it is written with mode 0600, and ``--encrypt`` can be used for untrusted
|
||||||
storage.
|
storage.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,7 +38,7 @@ The common arguments for profile are:
|
||||||
history of your shell. If the profile password is needed and not specified, it will be
|
history of your shell. If the profile password is needed and not specified, it will be
|
||||||
prompted (with echo disabled). If you are on a shared machine or if anybody can access
|
prompted (with echo disabled). If you are on a shared machine or if anybody can access
|
||||||
your shell history at some point, you should either connect first your profile with an
|
your shell history at some point, you should either connect first your profile with an
|
||||||
other frontend (Primitivus for instance), or avoid the ``--pwd`` option and use the
|
other frontend (Libervia TUI for instance), or avoid the ``--pwd`` option and use the
|
||||||
prompt instead.
|
prompt instead.
|
||||||
|
|
||||||
.. _pubsub_common:
|
.. _pubsub_common:
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
==========================
|
===========================
|
||||||
gateway: Gateway management
|
gateway: Gateway management
|
||||||
==========================
|
===========================
|
||||||
|
|
||||||
``gateway`` command allows you to list, register, and unregister from gateways (usually used to access other networks like ActivityPub or Email) on an XMPP server.
|
``gateway`` command allows you to list, register, and unregister from gateways (usually used to access other networks like ActivityPub or Email) on an XMPP server.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,37 +37,29 @@ After the commands come the options. For instance if you want to send a message,
|
||||||
get the available options with ``--help`` as explained above::
|
get the available options with ``--help`` as explained above::
|
||||||
|
|
||||||
$ li message send --help
|
$ li message send --help
|
||||||
usage: li message send [-h] [-p PROFILE] [--pwd PASSWORD] [-c] [-l LANG] [-s]
|
usage: li message send [-h] [-p PROFILE] [--pwd PASSWORD] [-c]
|
||||||
[-n] [-S SUBJECT] [-L SUBJECT_LANG]
|
|
||||||
[-t {chat,error,groupchat,headline,normal,auto}]
|
[-t {chat,error,groupchat,headline,normal,auto}]
|
||||||
[-e ALGORITHM] [--encrypt-noreplace] [-x | -r]
|
[-e ALGORITHM] [--encrypt-noreplace] […]
|
||||||
jid
|
jid
|
||||||
|
|
||||||
positional arguments:
|
positional arguments:
|
||||||
jid the destination jid
|
jid the destination jid
|
||||||
|
|
||||||
optional arguments:
|
options:
|
||||||
-h, --help show this help message and exit
|
-h, --help show this help message and exit
|
||||||
-p PROFILE, --profile PROFILE
|
-p, --profile PROFILE
|
||||||
Use PROFILE profile key (default: @DEFAULT@)
|
Use PROFILE profile key (default: @DEFAULT@)
|
||||||
--pwd PASSWORD Password used to connect profile, if necessary
|
--pwd PASSWORD Password used to connect profile, if necessary
|
||||||
-c, --connect Connect the profile before doing anything else
|
-c, --connect Connect the profile before doing anything else
|
||||||
-l LANG, --lang LANG language of the message
|
-t, --type {chat,error,groupchat,headline,normal,auto}
|
||||||
-s, --separate separate xmpp messages: send one message per line
|
|
||||||
instead of one message alone.
|
|
||||||
-n, --new-line add a new line at the beginning of the input (usefull
|
|
||||||
for ascii art ;))
|
|
||||||
-S SUBJECT, --subject SUBJECT
|
|
||||||
subject of the message
|
|
||||||
-L SUBJECT_LANG, --subject_lang SUBJECT_LANG
|
|
||||||
language of subject
|
|
||||||
-t {chat,error,groupchat,headline,normal,auto}, --type {chat,error,groupchat,headline,normal,auto}
|
|
||||||
type of the message
|
type of the message
|
||||||
-e ALGORITHM, --encrypt ALGORITHM
|
-e, --encrypt ALGORITHM
|
||||||
encrypt message using given algorithm
|
encrypt message using given algorithm
|
||||||
--encrypt-noreplace don't replace encryption algorithm if an other one is
|
--encrypt-noreplace don't replace encryption algorithm if another one is
|
||||||
already used
|
already used
|
||||||
-x, --xhtml XHTML body
|
[…]
|
||||||
|
|
||||||
|
The output above has been shortened: use ``--help`` to see the full list of options.
|
||||||
|
|
||||||
If you want to send a message to, say, ``pierre@example.net``, and encrypt it with OMEMO,
|
If you want to send a message to, say, ``pierre@example.net``, and encrypt it with OMEMO,
|
||||||
just do the following::
|
just do the following::
|
||||||
|
|
@ -77,6 +69,25 @@ just do the following::
|
||||||
(note that with OMEMO, you need to have previously validated fingerprint of your contact
|
(note that with OMEMO, you need to have previously validated fingerprint of your contact
|
||||||
for this to work).
|
for this to work).
|
||||||
|
|
||||||
|
.. _li_isolation:
|
||||||
|
|
||||||
|
Isolation
|
||||||
|
---------
|
||||||
|
|
||||||
|
Several Libervia instances can run side by side, each with its own data, this is called
|
||||||
|
isolation (see :ref:`configuration_isolation` for details). The instance to work with is
|
||||||
|
selected with the ``-i/--isolation NAME`` option, which must be used before the command:
|
||||||
|
|
||||||
|
.. code-block:: console
|
||||||
|
|
||||||
|
$ li -i pubsub profile list
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
If the ``LIBERVIA_ISOLATION`` environment variable is already set, this option is not
|
||||||
|
needed, unless you want to work with another instance (the option takes priority over
|
||||||
|
the environment variable).
|
||||||
|
|
||||||
The different commands are explained in dedicated sections.
|
The different commands are explained in dedicated sections.
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
|
|
||||||
|
|
@ -1,26 +1,26 @@
|
||||||
.. _primitivus-documentation:
|
.. _libervia-tui-documentation:
|
||||||
|
|
||||||
==========
|
============
|
||||||
Primitivus
|
Libervia TUI
|
||||||
==========
|
============
|
||||||
|
|
||||||
``Primitivus`` is the TUI (Terminal User Interface) frontend of Libervia
|
``Libervia TUI`` is the TUI (Terminal User Interface) frontend of Libervia
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
========
|
========
|
||||||
|
|
||||||
``Primitivus`` is a text based frontend. It is specially adapted for systems without
|
``Libervia TUI`` is a text based frontend. It is specially adapted for systems without
|
||||||
graphical environments (e.g. servers), for low bandwidth remote shells (e.g. ssh) or for
|
graphical environments (e.g. servers), for low bandwidth remote shells (e.g. ssh) or for
|
||||||
people who like straightforward interfaces without distracting images or animations.
|
people who like straightforward interfaces without distracting images or animations.
|
||||||
|
|
||||||
For the moment, Primitivus implements one 2 one chat, group chat (also called *MUC* for
|
For the moment, Libervia TUI implements one 2 one chat, group chat (also called *MUC* for
|
||||||
Multi-Users Chat), and some related features (end 2 end encryption, bookmarks, gateways
|
Multi-Users Chat), and some related features (end 2 end encryption, bookmarks, gateways
|
||||||
interaction, file sending, etc.).
|
interaction, file sending, etc.).
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
=====
|
=====
|
||||||
|
|
||||||
Primitivus is modal (vi-like), one can switch from one mode to another
|
Libervia TUI is modal (vi-like), one can switch from one mode to another
|
||||||
in the same way as in vi/vim:
|
in the same way as in vi/vim:
|
||||||
|
|
||||||
- From any mode, press ``[Esc]`` to switch to normal mode.
|
- From any mode, press ``[Esc]`` to switch to normal mode.
|
||||||
|
|
@ -28,7 +28,7 @@ in the same way as in vi/vim:
|
||||||
- From normal mode, press ``i`` to switch to insert mode – the one you
|
- From normal mode, press ``i`` to switch to insert mode – the one you
|
||||||
use to write messages to your contacts.
|
use to write messages to your contacts.
|
||||||
|
|
||||||
Primitivus can be handled either with the mouse, in a very intuitive way, or with the
|
Libervia TUI can be handled either with the mouse, in a very intuitive way, or with the
|
||||||
keyboard. Below, the keyboard shortcuts are explained.
|
keyboard. Below, the keyboard shortcuts are explained.
|
||||||
|
|
||||||
Keyboard handling
|
Keyboard handling
|
||||||
|
|
@ -46,8 +46,8 @@ Main keys
|
||||||
|
|
||||||
``[Tab]`` and/or ``CTRL + up/down arrows``
|
``[Tab]`` and/or ``CTRL + up/down arrows``
|
||||||
change focus
|
change focus
|
||||||
``CTRL + X``
|
``CTRL + X``
|
||||||
quit Primitivus
|
quit Libervia TUI
|
||||||
``CTRL + F, 1`` or ``CTRL + F, M``
|
``CTRL + F, 1`` or ``CTRL + F, M``
|
||||||
focus on the menu
|
focus on the menu
|
||||||
``CTRL + F, 2`` or ``CTRL + F, B``
|
``CTRL + F, 2`` or ``CTRL + F, B``
|
||||||
|
|
@ -128,12 +128,12 @@ The following commands must be typed in command mode (type ``[Esc]`` to switch t
|
||||||
mode, then the ``:`` at the beginning of the command will switch to command mode).
|
mode, then the ``:`` at the beginning of the command will switch to command mode).
|
||||||
|
|
||||||
``:quit``
|
``:quit``
|
||||||
quit Primitivus
|
quit Libervia TUI
|
||||||
``:messages``
|
``:messages``
|
||||||
display log messages (see also the logging configuration)
|
display log messages (see also the logging configuration)
|
||||||
|
|
||||||
..
|
..
|
||||||
FIXME: :presence and :status are currently disabled in Primitivus
|
FIXME: :presence and :status are currently disabled in Libervia TUI
|
||||||
``:presence [status]``
|
``:presence [status]``
|
||||||
set your presence status. Invoked without argument, a pop-up will allow you to choose your presence status; otherwise, you can use the following arguments:
|
set your presence status. Invoked without argument, a pop-up will allow you to choose your presence status; otherwise, you can use the following arguments:
|
||||||
- ``online``
|
- ``online``
|
||||||
|
|
|
||||||
|
|
@ -113,24 +113,25 @@ The `official Libervia website`_ is made with Libervia web framework.
|
||||||
.. _Jinja2: http://jinja.pocoo.org/
|
.. _Jinja2: http://jinja.pocoo.org/
|
||||||
.. _official Libervia website: https://salut-a-toi.org
|
.. _official Libervia website: https://salut-a-toi.org
|
||||||
|
|
||||||
Libervia TUI (Primitivus)
|
Libervia TUI
|
||||||
-------------------------
|
------------
|
||||||
|
|
||||||
Libervia TUI (alias ``Primitivus``) is the Terminal User Interface. In other words, it
|
Libervia TUI is the Terminal User Interface. In other words, it works in console and is
|
||||||
works in console and is intended for people at ease with it. Its text only interface has
|
intended for people at ease with it. Its text only interface has several advantages: you
|
||||||
several advantages: you get rid of many distracting things (like images), it works without
|
get rid of many distracting things (like images), it works without graphical environment
|
||||||
graphical environment installed (which is often the case on servers) and it works on
|
installed (which is often the case on servers) and it works on distant shell (like `ssh`_)
|
||||||
distant shell (like `ssh`_) while staying gentle with your bandwidth.
|
while staying gentle with your bandwidth.
|
||||||
|
|
||||||
Primitivus is shipped with the backend, so it should be always available once Libervia is
|
Libervia TUI is shipped with the backend, so it should be always available once Libervia
|
||||||
installed (but some distributions may provide it separately).
|
is installed (but some distributions may provide it separately).
|
||||||
|
|
||||||
You can check :doc:`Libervia TUI documentation <libervia-tui/index>` for more details.
|
.. The TUI is temporarily on hold and broken, its documentation is not
|
||||||
|
.. included in the build for the moment.
|
||||||
|
|
||||||
.. _ssh: https://en.wikipedia.org/wiki/Secure_Shell
|
.. _ssh: https://en.wikipedia.org/wiki/Secure_Shell
|
||||||
|
|
||||||
Libervia CLI (jp)
|
Libervia CLI
|
||||||
-----------------
|
------------
|
||||||
|
|
||||||
Libervia CLI (alias ``li`` or ``jp``) is the Command Line Interface. It's a
|
Libervia CLI (alias ``li`` or ``jp``) is the Command Line Interface. It's a
|
||||||
powerful tool which allows to do nearly everything you can do with other frontends.
|
powerful tool which allows to do nearly everything you can do with other frontends.
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ class MiscAccount(object):
|
||||||
host.bridge.add_method(
|
host.bridge.add_method(
|
||||||
"credentials_xmpp_connect",
|
"credentials_xmpp_connect",
|
||||||
".plugin",
|
".plugin",
|
||||||
in_sign="ss",
|
in_sign="ssb",
|
||||||
out_sign="b",
|
out_sign="b",
|
||||||
method=self.credentials_xmpp_connect,
|
method=self.credentials_xmpp_connect,
|
||||||
async_=True,
|
async_=True,
|
||||||
|
|
@ -293,11 +293,11 @@ class MiscAccount(object):
|
||||||
# there is no email when an existing XMPP account is used
|
# there is no email when an existing XMPP account is used
|
||||||
body = f"New account created on {domain}: {profile} [{email or '<no email>'}]"
|
body = f"New account created on {domain}: {profile} [{email or '<no email>'}]"
|
||||||
d_admin = sat_email.send_email(
|
d_admin = sat_email.send_email(
|
||||||
self.get_email_conf(),
|
|
||||||
self.host.memory.config,
|
self.host.memory.config,
|
||||||
admins_emails,
|
admins_emails,
|
||||||
subject,
|
subject,
|
||||||
body,
|
body,
|
||||||
|
email_settings=self.get_email_conf(),
|
||||||
)
|
)
|
||||||
|
|
||||||
admins_emails_txt = ", ".join(["<" + addr + ">" for addr in admins_emails])
|
admins_emails_txt = ", ".join(["<" + addr + ">" for addr in admins_emails])
|
||||||
|
|
@ -326,7 +326,13 @@ class MiscAccount(object):
|
||||||
# XXX: this will not fail when the email address doesn't exist
|
# XXX: this will not fail when the email address doesn't exist
|
||||||
# FIXME: check email reception to validate email given by the user
|
# FIXME: check email reception to validate email given by the user
|
||||||
# FIXME: delete the profile if the email could not been sent?
|
# FIXME: delete the profile if the email could not been sent?
|
||||||
d_user = sat_email.send_email(self.get_email_conf, [email], subject, body)
|
d_user = sat_email.send_email(
|
||||||
|
self.host.memory.config,
|
||||||
|
[email],
|
||||||
|
subject,
|
||||||
|
body,
|
||||||
|
email_settings=self.get_email_conf(),
|
||||||
|
)
|
||||||
d_user.addCallbacks(
|
d_user.addCallbacks(
|
||||||
lambda __: log.debug(
|
lambda __: log.debug(
|
||||||
"Account creation confirmation sent to <{}>".format(email)
|
"Account creation confirmation sent to <{}>".format(email)
|
||||||
|
|
@ -689,12 +695,13 @@ class MiscAccount(object):
|
||||||
d.addCallbacks(deleted, errback)
|
d.addCallbacks(deleted, errback)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def credentials_xmpp_connect(self, jid_s, password):
|
def credentials_xmpp_connect(self, jid_s, password, send_email=True):
|
||||||
"""Create and connect a new SàT profile using the given XMPP credentials.
|
"""Create and connect a new SàT profile using the given XMPP credentials.
|
||||||
|
|
||||||
Re-use given JID and XMPP password for the profile name and profile password.
|
Re-use given JID and XMPP password for the profile name and profile password.
|
||||||
@param jid_s (unicode): JID
|
@param jid_s (unicode): JID
|
||||||
@param password (unicode): XMPP password
|
@param password (unicode): XMPP password
|
||||||
|
@param send_email (bool): send the administrator notification email
|
||||||
@return Deferred(bool)
|
@return Deferred(bool)
|
||||||
@raise exceptions.PasswordError, exceptions.ConflictError
|
@raise exceptions.PasswordError, exceptions.ConflictError
|
||||||
"""
|
"""
|
||||||
|
|
@ -716,6 +723,7 @@ class MiscAccount(object):
|
||||||
)
|
)
|
||||||
|
|
||||||
def connected(result):
|
def connected(result):
|
||||||
|
if send_email:
|
||||||
self.send_emails(None, profile=jid_s)
|
self.send_emails(None, profile=jid_s)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ def send_email(
|
||||||
subject: str = "",
|
subject: str = "",
|
||||||
body: str = "",
|
body: str = "",
|
||||||
from_email: str | None = None,
|
from_email: str | None = None,
|
||||||
|
email_settings: tools_config.EmailSettings | None = None,
|
||||||
) -> defer.Deferred:
|
) -> defer.Deferred:
|
||||||
"""Send an email using Libervia configuration
|
"""Send an email using Libervia configuration
|
||||||
|
|
||||||
|
|
@ -43,12 +44,14 @@ def send_email(
|
||||||
@param subject: subject of the message
|
@param subject: subject of the message
|
||||||
@param body: body of the message
|
@param body: body of the message
|
||||||
@param from_email: address of the sender
|
@param from_email: address of the sender
|
||||||
|
@param email_settings: ``EmailSettings`` to use, default to ``config.email``
|
||||||
@return:Same as smtp.sendmail.
|
@return:Same as smtp.sendmail.
|
||||||
"""
|
"""
|
||||||
if isinstance(to_emails, str):
|
if isinstance(to_emails, str):
|
||||||
to_emails = to_emails.split()
|
to_emails = to_emails.split()
|
||||||
email_host = config.email.server
|
settings = email_settings or config.email
|
||||||
email_from = from_email or config.email.from_
|
email_host = settings.server
|
||||||
|
email_from = from_email or settings.from_
|
||||||
|
|
||||||
# we suppose that email domain and XMPP domain are identical
|
# we suppose that email domain and XMPP domain are identical
|
||||||
domain = config.common.xmpp_domain
|
domain = config.common.xmpp_domain
|
||||||
|
|
@ -60,12 +63,12 @@ def send_email(
|
||||||
|
|
||||||
if email_from is None:
|
if email_from is None:
|
||||||
email_from = "no_reply@" + domain
|
email_from = "no_reply@" + domain
|
||||||
email_sender_domain = config.email.sender_domain or domain
|
email_sender_domain = settings.sender_domain or domain
|
||||||
email_port = config.email.port
|
email_port = settings.port
|
||||||
email_username = config.email.username
|
email_username = settings.username
|
||||||
email_password = config.email.password
|
email_password = settings.password
|
||||||
email_auth = config.email.auth
|
email_auth = settings.auth
|
||||||
email_starttls = config.email.starttls
|
email_starttls = settings.starttls
|
||||||
|
|
||||||
msg = MIMEText(body, "plain", "UTF-8")
|
msg = MIMEText(body, "plain", "UTF-8")
|
||||||
msg["Subject"] = subject
|
msg["Subject"] = subject
|
||||||
|
|
|
||||||
|
|
@ -349,7 +349,12 @@ class QuickApp:
|
||||||
log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_))
|
log.warning(_("Can't retrieve encryption plugins: {msg}").format(msg=failure_))
|
||||||
|
|
||||||
def on_bridge_connected(self):
|
def on_bridge_connected(self):
|
||||||
self.bridge.ready_get(self.on_backend_ready)
|
d = self.bridge.ready_get()
|
||||||
|
d.addErrback(self._ready_get_eb)
|
||||||
|
d.addCallback(lambda __: self.on_backend_ready())
|
||||||
|
|
||||||
|
def _ready_get_eb(self, failure_):
|
||||||
|
log.error("Can't check backend readiness: {msg}".format(msg=failure_))
|
||||||
|
|
||||||
def _bridge_cb(self):
|
def _bridge_cb(self):
|
||||||
self.register_signal("connected")
|
self.register_signal("connected")
|
||||||
|
|
|
||||||
118
libervia/frontends/quick_frontend/quick_bridge.py
Normal file
118
libervia/frontends/quick_frontend/quick_bridge.py
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# Libervia: an XMPP client
|
||||||
|
# Copyright (C) 2009-2026 Jérôme Poisson (goffi@goffi.org)
|
||||||
|
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero 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 Affero General Public License for more details.
|
||||||
|
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
"""Adapte the legacy callback/errback bridge API to bridges"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import inspect
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
from twisted.internet import defer
|
||||||
|
from twisted.python.failure import Failure
|
||||||
|
|
||||||
|
from libervia.backend.core.log import getLogger
|
||||||
|
|
||||||
|
log = getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class QuickBridgeAdapter:
|
||||||
|
"""Adapt legacy callback/errback to bridges
|
||||||
|
|
||||||
|
@param bridge: bridge instance
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, bridge: Any) -> None:
|
||||||
|
self._bridge = bridge
|
||||||
|
|
||||||
|
def __getattr__(self, name: str) -> Any:
|
||||||
|
attr = getattr(self._bridge, name)
|
||||||
|
if not callable(attr):
|
||||||
|
return attr
|
||||||
|
|
||||||
|
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
||||||
|
callback = kwargs.pop("callback", None)
|
||||||
|
errback = kwargs.pop("errback", None)
|
||||||
|
result = attr(*args, **kwargs)
|
||||||
|
if inspect.iscoroutine(result):
|
||||||
|
result = asyncio.ensure_future(result)
|
||||||
|
if isinstance(result, asyncio.Future):
|
||||||
|
# async bridges (e.g. AIOBridge) return asyncio futures
|
||||||
|
result = defer.Deferred.fromFuture(result)
|
||||||
|
if (callback is None and errback is None) or not hasattr(
|
||||||
|
result, "addCallback"
|
||||||
|
):
|
||||||
|
return result
|
||||||
|
if callback is not None:
|
||||||
|
result.addCallback(callback)
|
||||||
|
if errback is not None:
|
||||||
|
result.addErrback(errback)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
def register_signal(self, name: str, handler: Callable, iface: str = "core") -> None:
|
||||||
|
"""Register a signal handler, adapting sync handlers to async bridges
|
||||||
|
|
||||||
|
@param name: signal name
|
||||||
|
@param handler: signal handler, sync or async
|
||||||
|
@param iface: bridge interface ("core" or "plugin")
|
||||||
|
"""
|
||||||
|
if not inspect.iscoroutinefunction(handler):
|
||||||
|
|
||||||
|
async def async_handler(*args: Any,
|
||||||
|
_handler: Callable = handler) -> None:
|
||||||
|
_handler(*args)
|
||||||
|
|
||||||
|
handler = async_handler
|
||||||
|
|
||||||
|
try:
|
||||||
|
self._bridge.register_signal(name, handler, iface)
|
||||||
|
except AttributeError:
|
||||||
|
log.warning(f"Signal {name} is not available on the bridge")
|
||||||
|
|
||||||
|
def bridge_connect(self, callback: Callable | None = None,
|
||||||
|
errback: Callable | None = None) -> None:
|
||||||
|
"""Connect to the bridge
|
||||||
|
|
||||||
|
@param callback: called when the bridge is connected
|
||||||
|
@param errback: called with a twisted Failure on connection error
|
||||||
|
"""
|
||||||
|
|
||||||
|
async def _connect() -> None:
|
||||||
|
try:
|
||||||
|
await self._bridge.bridge_connect()
|
||||||
|
except Exception as e:
|
||||||
|
if errback is not None:
|
||||||
|
errback(Failure(e))
|
||||||
|
else:
|
||||||
|
if callback is not None:
|
||||||
|
callback()
|
||||||
|
|
||||||
|
try:
|
||||||
|
asyncio.get_running_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
# the event loop is not running yet (frontends usually call connect_bridge()
|
||||||
|
# before starting their loop)
|
||||||
|
try:
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
loop.call_soon(asyncio.ensure_future, _connect())
|
||||||
|
else:
|
||||||
|
asyncio.ensure_future(_connect())
|
||||||
|
|
@ -233,9 +233,16 @@ class QuickProfileManager(object):
|
||||||
|
|
||||||
def refill_profiles(self):
|
def refill_profiles(self):
|
||||||
"""Rebuild the list of profiles"""
|
"""Rebuild the list of profiles"""
|
||||||
profiles = self.host.bridge.profiles_list_get()
|
self.host.bridge.profiles_list_get(
|
||||||
profiles.sort()
|
callback=self._profiles_loaded,
|
||||||
self.set_profiles(profiles)
|
errback=self._profiles_list_eb,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _profiles_loaded(self, profiles):
|
||||||
|
self.set_profiles(sorted(profiles))
|
||||||
|
|
||||||
|
def _profiles_list_eb(self, failure_):
|
||||||
|
log.error("Can't get profiles list: {msg}".format(msg=failure_))
|
||||||
|
|
||||||
## Method which must be implemented by frontends ##
|
## Method which must be implemented by frontends ##
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,9 +28,10 @@ from libervia.backend.core import log as logging
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
from libervia.backend.tools import config as tools_config
|
from libervia.backend.tools import config as tools_config
|
||||||
import urwid
|
import urwid
|
||||||
from urwid.util import is_wide_char
|
from urwid import is_wide_char
|
||||||
from urwid_satext import sat_widgets
|
from urwid_satext import sat_widgets
|
||||||
from libervia.frontends.quick_frontend.quick_app import QuickApp
|
from libervia.frontends.quick_frontend.quick_app import QuickApp
|
||||||
|
from libervia.frontends.quick_frontend import quick_bridge
|
||||||
from libervia.frontends.quick_frontend import quick_utils
|
from libervia.frontends.quick_frontend import quick_utils
|
||||||
from libervia.frontends.quick_frontend import quick_chat
|
from libervia.frontends.quick_frontend import quick_chat
|
||||||
from libervia.tui.profile_manager import ProfileManager
|
from libervia.tui.profile_manager import ProfileManager
|
||||||
|
|
@ -45,6 +46,7 @@ from libervia.tui.keys import action_key_map as a_key
|
||||||
from libervia.frontends.tools.misc import InputHistory
|
from libervia.frontends.tools.misc import InputHistory
|
||||||
from libervia.backend.tools.common import dynamic_import
|
from libervia.backend.tools.common import dynamic_import
|
||||||
from libervia.frontends.tools import jid
|
from libervia.frontends.tools import jid
|
||||||
|
import asyncio
|
||||||
import signal
|
import signal
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
@ -67,10 +69,19 @@ class TUISettings(tools_config.FrontendSettings, key="tui"):
|
||||||
## bridge handling
|
## bridge handling
|
||||||
# we get bridge name from conf and initialise the right class accordingly
|
# we get bridge name from conf and initialise the right class accordingly
|
||||||
main_config = tools_config.parse_main_conf(ignore_cache=True)
|
main_config = tools_config.parse_main_conf(ignore_cache=True)
|
||||||
bridge_name = next(iter(main_config.common.bridges)).type
|
bridge_name = next(iter(main_config.bridges)).type
|
||||||
if "dbus" not in bridge_name:
|
if "dbus" not in bridge_name:
|
||||||
print("only D-Bus bridge is currently supported")
|
print("only D-Bus bridge is currently supported")
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
# install the asyncio twisted reactor
|
||||||
|
try:
|
||||||
|
asyncio.get_event_loop()
|
||||||
|
except RuntimeError:
|
||||||
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
from twisted.internet import asyncioreactor
|
||||||
|
|
||||||
|
asyncioreactor.install()
|
||||||
|
|
||||||
|
|
||||||
class EditBar(sat_widgets.ModalEdit):
|
class EditBar(sat_widgets.ModalEdit):
|
||||||
|
|
@ -338,9 +349,7 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
AVATARS_HANDLER = False
|
AVATARS_HANDLER = False
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
bridge_module = dynamic_import.import_bridge(
|
bridge_module = dynamic_import.import_bridge(bridge_name)
|
||||||
bridge_name, "libervia.frontends.bridge"
|
|
||||||
)
|
|
||||||
if bridge_module is None:
|
if bridge_module is None:
|
||||||
log.error("Can't import {} bridge".format(bridge_name))
|
log.error("Can't import {} bridge".format(bridge_name))
|
||||||
sys.exit(3)
|
sys.exit(3)
|
||||||
|
|
@ -348,16 +357,18 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
log.debug("Loading {} bridge".format(bridge_name))
|
log.debug("Loading {} bridge".format(bridge_name))
|
||||||
QuickApp.__init__(
|
QuickApp.__init__(
|
||||||
self,
|
self,
|
||||||
bridge_factory=bridge_module.bridge,
|
# AIOBridge is the only bridge compatible with the asyncio loop
|
||||||
|
bridge_factory=lambda: quick_bridge.QuickBridgeAdapter(
|
||||||
|
bridge_module.AIOBridge()
|
||||||
|
),
|
||||||
xmlui=xmlui,
|
xmlui=xmlui,
|
||||||
check_options=quick_utils.check_options,
|
check_options=quick_utils.check_options,
|
||||||
connect_bridge=False,
|
connect_bridge=False,
|
||||||
)
|
)
|
||||||
self.tui_settings = main_config.get_settings(TUISettings)
|
self.tui_settings = main_config.get_settings(TUISettings)
|
||||||
## main loop setup ##
|
## main loop setup ##
|
||||||
event_loop = (
|
# the new D-Bus bridge is twisted based (txdbus)
|
||||||
urwid.GLibEventLoop if "dbus" in bridge_name else urwid.TwistedEventLoop
|
event_loop = urwid.TwistedEventLoop
|
||||||
)
|
|
||||||
self.loop = urwid.MainLoop(
|
self.loop = urwid.MainLoop(
|
||||||
urwid.SolidFill(),
|
urwid.SolidFill(),
|
||||||
C.PALETTE,
|
C.PALETTE,
|
||||||
|
|
@ -373,6 +384,10 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
def on_bridge_connected(self):
|
def on_bridge_connected(self):
|
||||||
|
|
||||||
##misc setup##
|
##misc setup##
|
||||||
|
self._version = None
|
||||||
|
self.bridge.version_get(
|
||||||
|
callback=self._version_get_cb, errback=lambda __: None
|
||||||
|
)
|
||||||
self._visible_widgets = set()
|
self._visible_widgets = set()
|
||||||
self.notif_bar = sat_widgets.NotificationBar()
|
self.notif_bar = sat_widgets.NotificationBar()
|
||||||
urwid.connect_signal(self.notif_bar, "change", self.on_notification)
|
urwid.connect_signal(self.notif_bar, "change", self.on_notification)
|
||||||
|
|
@ -400,6 +415,9 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
self.loop.widget = self.main_widget = ProfileManager(self)
|
self.loop.widget = self.main_widget = ProfileManager(self)
|
||||||
self.post_init()
|
self.post_init()
|
||||||
|
|
||||||
|
def _version_get_cb(self, version):
|
||||||
|
self._version = version
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def visible_widgets(self):
|
def visible_widgets(self):
|
||||||
return self._visible_widgets
|
return self._visible_widgets
|
||||||
|
|
@ -579,7 +597,7 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
self.__saved_overlay = None
|
self.__saved_overlay = None
|
||||||
|
|
||||||
elif (
|
elif (
|
||||||
input_ == a_key["DEBUG"] and ".dev0" in self.bridge.version_get()
|
input_ == a_key["DEBUG"] and ".dev0" in (self._version or "")
|
||||||
): # Debug only for dev versions
|
): # Debug only for dev versions
|
||||||
self.debug()
|
self.debug()
|
||||||
elif input_ == a_key["CONTACTS_HIDE"]: # user wants to (un)hide the contact lists
|
elif input_ == a_key["CONTACTS_HIDE"]: # user wants to (un)hide the contact lists
|
||||||
|
|
@ -614,6 +632,9 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
|
|
||||||
def add_menus(self, menu, type_filter, menu_data=None):
|
def add_menus(self, menu, type_filter, menu_data=None):
|
||||||
"""Add cached menus to instance
|
"""Add cached menus to instance
|
||||||
|
|
||||||
|
The menus are retrieved asynchronously from the bridge.
|
||||||
|
|
||||||
@param menu: sat_widgets.Menu instance
|
@param menu: sat_widgets.Menu instance
|
||||||
@param type_filter: menu type like is sat.core.sat_main.import_menu
|
@param type_filter: menu type like is sat.core.sat_main.import_menu
|
||||||
@param menu_data: data to send with these menus
|
@param menu_data: data to send with these menus
|
||||||
|
|
@ -623,17 +644,25 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
def add_menu_cb(callback_id):
|
def add_menu_cb(callback_id):
|
||||||
self.action_launch(callback_id, menu_data, profile=self.current_profile)
|
self.action_launch(callback_id, menu_data, profile=self.current_profile)
|
||||||
|
|
||||||
for id_, type_, path, path_i18n, extra in self.bridge.menus_get(
|
def menus_get_cb(menus):
|
||||||
"", C.NO_SECURITY_LIMIT
|
for id_, type_, path, path_i18n, extra in menus: # TODO: manage extra
|
||||||
): # TODO: manage extra
|
|
||||||
if type_ != type_filter:
|
if type_ != type_filter:
|
||||||
continue
|
continue
|
||||||
if len(path) != 2:
|
if len(path) != 2:
|
||||||
raise NotImplementedError("Menu with a path != 2 are not implemented yet")
|
raise NotImplementedError(
|
||||||
|
"Menu with a path != 2 are not implemented yet"
|
||||||
|
)
|
||||||
menu.add_menu(
|
menu.add_menu(
|
||||||
path_i18n[0], path_i18n[1], lambda dummy, id_=id_: add_menu_cb(id_)
|
path_i18n[0], path_i18n[1], lambda dummy, id_=id_: add_menu_cb(id_)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def menus_get_eb(failure):
|
||||||
|
log.error("Can't get menus: {}".format(failure))
|
||||||
|
|
||||||
|
self.bridge.menus_get(
|
||||||
|
"", C.NO_SECURITY_LIMIT, callback=menus_get_cb, errback=menus_get_eb
|
||||||
|
)
|
||||||
|
|
||||||
def _build_menu_roller(self):
|
def _build_menu_roller(self):
|
||||||
menu = sat_widgets.Menu(self.loop)
|
menu = sat_widgets.Menu(self.loop)
|
||||||
general = _("General")
|
general = _("General")
|
||||||
|
|
@ -1016,10 +1045,12 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
|
|
||||||
def on_join_room_request(self, menu):
|
def on_join_room_request(self, menu):
|
||||||
"""User wants to join a MUC room"""
|
"""User wants to join a MUC room"""
|
||||||
|
|
||||||
|
def show_join_dialog(default_service):
|
||||||
pop_up_widget = sat_widgets.InputDialog(
|
pop_up_widget = sat_widgets.InputDialog(
|
||||||
_("Entering a MUC room"),
|
_("Entering a MUC room"),
|
||||||
_("Please enter MUC's JID"),
|
_("Please enter MUC's JID"),
|
||||||
default_txt=self.bridge.muc_get_default_service(),
|
default_txt=default_service,
|
||||||
ok_cb=self.on_join_room,
|
ok_cb=self.on_join_room,
|
||||||
)
|
)
|
||||||
pop_up_widget.set_callback(
|
pop_up_widget.set_callback(
|
||||||
|
|
@ -1027,8 +1058,12 @@ class LiberviaTUIApp(QuickApp, InputHistory):
|
||||||
)
|
)
|
||||||
self.show_pop_up(pop_up_widget)
|
self.show_pop_up(pop_up_widget)
|
||||||
|
|
||||||
|
self.bridge.muc_get_default_service(
|
||||||
|
callback=show_join_dialog, errback=lambda __: show_join_dialog("")
|
||||||
|
)
|
||||||
|
|
||||||
def on_about_request(self, menu):
|
def on_about_request(self, menu):
|
||||||
self.alert(_("About"), C.APP_NAME + " v" + self.bridge.version_get())
|
self.alert(_("About"), C.APP_NAME + " v" + (self._version or ""))
|
||||||
|
|
||||||
# MISC CALLBACKS#
|
# MISC CALLBACKS#
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -174,10 +174,13 @@ class Card(TarotCard):
|
||||||
return CardDisplayer(self)
|
return CardDisplayer(self)
|
||||||
|
|
||||||
|
|
||||||
class Table(urwid.FlowWidget):
|
class Table(urwid.Widget):
|
||||||
"""Represent the cards currently on the table"""
|
"""Represent the cards currently on the table"""
|
||||||
|
|
||||||
|
_sizing = frozenset([urwid.Sizing.FLOW])
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
self.top = self.left = self.bottom = self.right = None
|
self.top = self.left = self.bottom = self.right = None
|
||||||
|
|
||||||
def put_card(self, location, card):
|
def put_card(self, location, card):
|
||||||
|
|
@ -313,7 +316,7 @@ class TarotGame(QuickTarotGame, urwid.WidgetWrap):
|
||||||
if not winners and not loosers:
|
if not winners and not loosers:
|
||||||
title = _("Draw game")
|
title = _("Draw game")
|
||||||
else:
|
else:
|
||||||
title = _("You win \o/") if self.player_nick in winners else _("You loose :(")
|
title = _("You win \\o/") if self.player_nick in winners else _("You loose :(")
|
||||||
form = xmlui.create(
|
form = xmlui.create(
|
||||||
self.parent.host,
|
self.parent.host,
|
||||||
xml_data,
|
xml_data,
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,10 @@
|
||||||
# You should have received a copy of the GNU Affero General Public License
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import dbus
|
try:
|
||||||
|
import dbus
|
||||||
|
except ImportError:
|
||||||
|
dbus = None
|
||||||
|
|
||||||
|
|
||||||
class Notify(object):
|
class Notify(object):
|
||||||
|
|
|
||||||
|
|
@ -37,11 +37,11 @@ class ProfileManager(QuickProfileManager, urwid.WidgetWrap):
|
||||||
self.pass_wid = sat_widgets.Password(_("Password:"), align="center")
|
self.pass_wid = sat_widgets.Password(_("Password:"), align="center")
|
||||||
|
|
||||||
style = ["no_first_select"]
|
style = ["no_first_select"]
|
||||||
profiles = host.bridge.profiles_list_get()
|
|
||||||
profiles.sort()
|
|
||||||
self.list_profile = sat_widgets.List(
|
self.list_profile = sat_widgets.List(
|
||||||
profiles, style=style, align="center", on_change=self.on_profile_change
|
[], style=style, align="center", on_change=self.on_profile_change
|
||||||
)
|
)
|
||||||
|
# the profiles list is retrieved asynchronously
|
||||||
|
self.refill_profiles()
|
||||||
|
|
||||||
# new & delete buttons
|
# new & delete buttons
|
||||||
buttons = [
|
buttons = [
|
||||||
|
|
|
||||||
|
|
@ -54,25 +54,32 @@ class Progress(urwid.WidgetWrap, quick_widgets.QuickWidget):
|
||||||
|
|
||||||
def progress_cb(self, loop, data):
|
def progress_cb(self, loop, data):
|
||||||
progress_id, message, profile = data
|
progress_id, message, profile = data
|
||||||
data = self.host.bridge.progress_get(progress_id, profile)
|
|
||||||
pbar = self.progress_dict[(progress_id, profile)]["progress"]
|
def progress_get_cb(data):
|
||||||
|
entry = self.progress_dict.get((progress_id, profile))
|
||||||
|
if entry is None:
|
||||||
|
# the bar has been removed in the meantime
|
||||||
|
return
|
||||||
|
pbar = entry["progress"]
|
||||||
if data:
|
if data:
|
||||||
if self.progress_dict[(progress_id, profile)]["state"] == "init":
|
if entry["state"] == "init":
|
||||||
# first answer, we must construct the bar
|
# first answer, we must construct the bar
|
||||||
self.progress_dict[(progress_id, profile)]["state"] = "progress"
|
entry["state"] = "progress"
|
||||||
pbar.done = float(data["size"])
|
pbar.done = float(data["size"])
|
||||||
|
|
||||||
pbar.set_completion(float(data["position"]))
|
pbar.set_completion(float(data["position"]))
|
||||||
self.update_not_bar()
|
self.update_not_bar()
|
||||||
else:
|
else:
|
||||||
if self.progress_dict[(progress_id, profile)]["state"] == "progress":
|
if entry["state"] == "progress":
|
||||||
self.progress_dict[(progress_id, profile)]["state"] = "done"
|
entry["state"] = "done"
|
||||||
pbar.set_completion(pbar.done)
|
pbar.set_completion(pbar.done)
|
||||||
self.update_not_bar()
|
self.update_not_bar()
|
||||||
return
|
return
|
||||||
|
|
||||||
loop.set_alarm_in(0.2, self.progress_cb, (progress_id, message, profile))
|
loop.set_alarm_in(0.2, self.progress_cb, (progress_id, message, profile))
|
||||||
|
|
||||||
|
self.host.bridge.progress_get(progress_id, profile, callback=progress_get_cb)
|
||||||
|
|
||||||
def _remove_bar(self, progress_id, profile):
|
def _remove_bar(self, progress_id, profile):
|
||||||
wid = self.progress_dict[(progress_id, profile)]["full"]
|
wid = self.progress_dict[(progress_id, profile)]["full"]
|
||||||
self.progress_list.remove(wid)
|
self.progress_list.remove(wid)
|
||||||
|
|
|
||||||
|
|
@ -45,11 +45,12 @@ class StatusBar(urwid.Columns):
|
||||||
urwid.connect_signal(self.status, "click", self.on_status_click)
|
urwid.connect_signal(self.status, "click", self.on_status_click)
|
||||||
|
|
||||||
def on_presence_click(self, sender=None):
|
def on_presence_click(self, sender=None):
|
||||||
if not self.host.bridge.is_connected(
|
def show_presence_popup(connected):
|
||||||
self.host.current_profile
|
if not connected:
|
||||||
): # FIXME: manage multi-profiles
|
|
||||||
return
|
return
|
||||||
options = [commonConst.PRESENCE[presence] for presence in commonConst.PRESENCE]
|
options = [
|
||||||
|
commonConst.PRESENCE[presence] for presence in commonConst.PRESENCE
|
||||||
|
]
|
||||||
list_widget = sat_widgets.GenericList(
|
list_widget = sat_widgets.GenericList(
|
||||||
options=options,
|
options=options,
|
||||||
option_type=sat_widgets.ClickableText,
|
option_type=sat_widgets.ClickableText,
|
||||||
|
|
@ -60,10 +61,14 @@ class StatusBar(urwid.Columns):
|
||||||
)
|
)
|
||||||
self.host.show_pop_up(decorated)
|
self.host.show_pop_up(decorated)
|
||||||
|
|
||||||
|
# FIXME: manage multi-profiles
|
||||||
|
self.host.bridge.is_connected(
|
||||||
|
self.host.current_profile, callback=show_presence_popup
|
||||||
|
)
|
||||||
|
|
||||||
def on_status_click(self, sender=None):
|
def on_status_click(self, sender=None):
|
||||||
if not self.host.bridge.is_connected(
|
def show_status_popup(connected):
|
||||||
self.host.current_profile
|
if not connected:
|
||||||
): # FIXME: manage multi-profiles
|
|
||||||
return
|
return
|
||||||
pop_up_widget = sat_widgets.InputDialog(
|
pop_up_widget = sat_widgets.InputDialog(
|
||||||
_("Set your status"),
|
_("Set your status"),
|
||||||
|
|
@ -74,6 +79,11 @@ class StatusBar(urwid.Columns):
|
||||||
)
|
)
|
||||||
self.host.show_pop_up(pop_up_widget)
|
self.host.show_pop_up(pop_up_widget)
|
||||||
|
|
||||||
|
# FIXME: manage multi-profiles
|
||||||
|
self.host.bridge.is_connected(
|
||||||
|
self.host.current_profile, callback=show_status_popup
|
||||||
|
)
|
||||||
|
|
||||||
def on_change(self, sender=None, user_data=None):
|
def on_change(self, sender=None, user_data=None):
|
||||||
new_value = user_data.get_text()
|
new_value = user_data.get_text()
|
||||||
previous = (
|
previous = (
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ dependencies = [
|
||||||
"twisted[tls] >= 22.10",
|
"twisted[tls] >= 22.10",
|
||||||
"twomemo >= 1.0.0, < 2",
|
"twomemo >= 1.0.0, < 2",
|
||||||
"txdbus",
|
"txdbus",
|
||||||
"urwid >= 1.2.0, < 3",
|
"urwid >= 4.2.5, < 5",
|
||||||
"urwid-satext",
|
"urwid-satext",
|
||||||
"xmlschema < 4.3.1",
|
"xmlschema < 4.3.1",
|
||||||
"tx-xmpp>=0.15",
|
"tx-xmpp>=0.15",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue